Compare commits

...

1 Commits

Author SHA1 Message Date
Dax 642d6c52c4 fix(opencode): order replay rows by creation time 2026-08-07 03:57:15 +00:00
4 changed files with 44 additions and 13 deletions
+1 -1
View File
@@ -365,7 +365,7 @@ async function runInteractiveRuntime(input: RunRuntimeInput, deps: RunRuntimeDep
})
const footer = shell.footer
const rememberLocal = (commit: StreamCommit, after?: LocalReplayAnchor) => {
state.localRows = [...state.localRows, { commit, after }].slice(-LOCAL_REPLAY_ROW_LIMIT)
state.localRows = [...state.localRows, { commit, after, created: Date.now() }].slice(-LOCAL_REPLAY_ROW_LIMIT)
}
const loadCatalog = async (): Promise<void> => {
@@ -266,7 +266,7 @@ export function replayLocalRows(
rows: LocalReplayRow[],
): StreamCommit[] {
const persisted = new Set(messages.map((message) => message.info.id))
return rows.reduce((out, local) => {
return rows.reduce((out, local, index) => {
const row = local.commit
if (row.kind === "user" && row.messageID && persisted.has(row.messageID)) {
return out
@@ -317,12 +317,35 @@ export function replayLocalRows(
return [...out.slice(0, after + 1), row, ...out.slice(after + 1)]
}
const before = out.findIndex((commit) => commit.messageID && row.messageID! < commit.messageID)
if (before === -1) {
return [...out, row]
const nextAnchor = rows
.slice(index + 1)
.find((next) => next.commit.messageID === row.messageID && next.after)?.after
if (nextAnchor) {
const before = out.findIndex((commit) =>
nextAnchor.partID
? commit.partID === nextAnchor.partID
: commit.kind === nextAnchor.kind && commit.messageID === nextAnchor.messageID,
)
if (before !== -1) return [...out.slice(0, before), row, ...out.slice(before)]
}
return [...out.slice(0, before), row, ...out.slice(before)]
if (local.created !== undefined) {
const created = local.created
const messageID = row.messageID
const later = new Set(
messages
.filter(
(message) =>
message.info.time.created > created ||
(message.info.time.created === created && message.info.id.localeCompare(messageID) > 0),
)
.map((message) => message.info.id),
)
const before = out.findIndex((commit) => commit.messageID && later.has(commit.messageID))
if (before !== -1) return [...out.slice(0, before), row, ...out.slice(before)]
}
return [...out, row]
}, commits)
}
@@ -332,6 +332,7 @@ export type LocalReplayAnchor = {
export type LocalReplayRow = {
commit: StreamCommit
after?: LocalReplayAnchor
created?: number
}
// The public contract between the stream transport / prompt queue and
@@ -416,7 +416,14 @@ describe("run session replay", () => {
} as const
expect(
replayLocalRows([userMessage("msg-user-2", "successful")], [persisted], [{ commit: failed }, { commit: error }]),
replayLocalRows(
[userMessage("msg-user-2", "successful")],
[persisted],
[
{ commit: failed, created: 0 },
{ commit: error, created: 0 },
],
),
).toEqual([failed, error, persisted])
})
@@ -657,35 +664,35 @@ describe("run session replay", () => {
).toEqual([prompt, running, error, completed])
})
test("retains an unpersisted local diagnostic before later persisted prompts", () => {
test("appends an unanchored local diagnostic without inferring chronology from message IDs", () => {
const first = {
kind: "user",
text: "before",
phase: "start",
source: "system",
messageID: "msg-user-1",
messageID: "msg-user-z",
} as const
const error = {
kind: "error",
text: "failed to start new session",
phase: "start",
source: "system",
messageID: "msg-user-2",
messageID: "msg-user-m",
} as const
const second = {
kind: "user",
text: "after",
phase: "start",
source: "system",
messageID: "msg-user-3",
messageID: "msg-user-a",
} as const
expect(
replayLocalRows(
[userMessage("msg-user-1", "before"), userMessage("msg-user-3", "after")],
[userMessage("msg-user-z", "before"), userMessage("msg-user-a", "after")],
[first, second],
[{ commit: error }],
),
).toEqual([first, error, second])
).toEqual([first, second, error])
})
})