fix: preserve system item identity in live events

This commit is contained in:
2026-10-03 21:44:36 +09:00
parent 00c7e223b1
commit 3875c8e675
9 changed files with 120 additions and 13 deletions
File diff suppressed because one or more lines are too long
+5 -1
View File
@@ -367,7 +367,11 @@ export type Event = { "event": "submission_accepted", "data": { submission_reque
/**
* Stable durable history identity. Older mixed-version producers omit it.
*/
entry_id?: string | null, segments: Array<Segment>, } } | { "event": "session_entry_committed", "data": { entry: SessionSnapshotEntry, } } | { "event": "system_item", "data": { item: unknown, } } | { "event": "invoke_start", "data": { kind: InvokeKind, } } | { "event": "turn_start", "data": { turn: number, } } | { "event": "turn_end", "data": { turn: number, result: TurnResult, } } | { "event": "llm_call_start", "data": { llm_call: number, } } | { "event": "llm_call_end", "data": { llm_call: number, } } | { "event": "llm_retry", "data": { llm_call: number,
entry_id?: string | null, segments: Array<Segment>, } } | { "event": "session_entry_committed", "data": { entry: SessionSnapshotEntry, } } | { "event": "system_item", "data": {
/**
* Stable durable history identity. Synthetic compatibility events omit it.
*/
entry_id?: string | null, item: unknown, } } | { "event": "invoke_start", "data": { kind: InvokeKind, } } | { "event": "turn_start", "data": { turn: number, } } | { "event": "turn_end", "data": { turn: number, result: TurnResult, } } | { "event": "llm_call_start", "data": { llm_call: number, } } | { "event": "llm_call_end", "data": { llm_call: number, } } | { "event": "llm_retry", "data": { llm_call: number,
/**
* The attempt that just failed. 1 origin.
*/
@@ -3781,6 +3781,37 @@ Deno.test("committed history reconciles overlapping Read groups before counting
}
});
Deno.test("live system items reconcile with committed history by entry identity", () => {
const item = {
kind: "resident_summary_refresh",
body: "Resident memory refresh",
};
const event: Event = {
event: "system_item",
data: { entry_id: "resident-refresh-1", item },
};
const current = projectConsole([
{ eventId: "live-1", event },
{ eventId: "live-retry", event },
]).lines;
assertEquals(current.length, 1);
assertEquals(current[0].entryId, "resident-refresh-1");
const history = projectSessionHistoryEntries([{
kind: "system_item",
entry_id: "resident-refresh-1",
timestamp: 1,
provenance: "backend_instruction",
item_kind: "resident_summary_refresh",
content: item.body,
data: item,
}], null);
const merged = mergeCommittedHistoryLines(history, current);
assertEquals(merged.length, 1);
assertEquals(merged[0].entryId, "resident-refresh-1");
assertEquals(merged[0].body, item.body);
});
Deno.test("committed history keeps identical run failures with distinct entry identities", () => {
const message = "the same failure happened again";
const runError = (entryId: string): SessionSnapshotEntry => ({
@@ -1067,10 +1067,16 @@ export function applyProtocolEvent(
}
break;
}
case "system_item":
next.lines.push(systemItemLine(envelope.eventId, event.data.item));
case "system_item": {
const entryId = event.data.entry_id ?? undefined;
if (entryId && next.lines.some((line) => line.entryId === entryId)) break;
next.lines.push({
...systemItemLine(envelope.eventId, event.data.item),
entryId,
});
applyTaskSystemItem(next, event.data.item);
break;
}
case "text_delta":
appendStreaming(
next,