web: show system items in worker console

This commit is contained in:
2026-08-05 03:36:50 +09:00
parent f98a123e40
commit 50726e4cf3
2 changed files with 66 additions and 4 deletions
@@ -718,7 +718,7 @@ Deno.test("projectConsole preserves in-progress assistant protocol stream", () =
); );
}); });
Deno.test("projectConsole keeps protocol lifecycle events out of the console surface", () => { Deno.test("projectConsole hides lifecycle events and renders system items", () => {
const projection = projectConsole([ const projection = projectConsole([
{ {
eventId: "30", eventId: "30",
@@ -743,12 +743,24 @@ Deno.test("projectConsole keeps protocol lifecycle events out of the console sur
eventId: "34", eventId: "34",
event: { event: {
event: "system_item", event: "system_item",
data: { item: { kind: "note", content: "internal" } }, data: {
item: {
kind: "notification",
message: "Ticket queued",
body: "Reread Ticket 00001KZ6TSGG5 before acting.",
},
},
} satisfies Event, } satisfies Event,
}, },
]); ]);
assertEquals(projection.lines, []); assertEquals(projection.lines.length, 1);
assertEquals(projection.lines[0].kind, "system");
assertEquals(projection.lines[0].title, "System · notification");
assertEquals(
projection.lines[0].body,
"Reread Ticket 00001KZ6TSGG5 before acting.",
);
assertEquals(projection.status, "running"); assertEquals(projection.status, "running");
}); });
@@ -859,6 +871,42 @@ Deno.test("projectConsole renders snapshot entries and in-flight output", () =>
); );
}); });
Deno.test("projectConsole restores system items from snapshot entries", () => {
const projection = projectConsole([{
eventId: "system-snapshot",
event: {
event: "snapshot",
data: {
entries: [{
kind: "system_item",
ts: 1,
item: {
kind: "notification",
message: "Worker completed",
body: "Child Worker coder-1 completed.",
},
}],
greeting: {
worker_name: "Worker",
cwd: "/repo",
provider: "provider",
model: "model",
scope_summary: "bounded",
tools: [],
context_window: 100,
context_tokens: 20,
},
status: "idle",
},
} satisfies Event,
}]);
assertEquals(projection.lines.length, 1);
assertEquals(projection.lines[0].kind, "system");
assertEquals(projection.lines[0].title, "System · notification");
assertEquals(projection.lines[0].body, "Child Worker coder-1 completed.");
});
Deno.test("projectConsole reseeds visible rows from segment rotation", () => { Deno.test("projectConsole reseeds visible rows from segment rotation", () => {
const projection = projectConsole([ const projection = projectConsole([
{ {
@@ -208,7 +208,7 @@ export function applyProtocolEvent(
); );
break; break;
case "system_item": case "system_item":
// System items are protocol/internal context, not console output. next.lines.push(systemItemLine(envelope.eventId, event.data.item));
break; break;
case "text_delta": case "text_delta":
appendStreaming( appendStreaming(
@@ -397,6 +397,17 @@ function line(
}; };
} }
function systemItemLine(eventId: string, item: unknown): ConsoleLine {
if (!isRecord(item)) {
return line(eventId, "system", "System item", jsonPreview(item));
}
const itemKind = stringField(item, "kind") ?? "item";
const title = `System · ${itemKind.replaceAll("_", " ")}`;
const body = stringField(item, "body") ?? stringField(item, "message") ??
stringField(item, "content") ?? jsonPreview(item);
return line(eventId, "system", title, body);
}
function upsertStatusLine( function upsertStatusLine(
projection: ConsoleProjection, projection: ConsoleProjection,
id: string, id: string,
@@ -1140,6 +1151,9 @@ function applyLogEntry(
), ),
); );
break; break;
case "system_item":
projection.lines.push(systemItemLine(eventId, entry["item"]));
break;
case "assistant_item": case "assistant_item":
case "tool_result": case "tool_result":
applyLoggedItem(projection, eventId, entry["item"]); applyLoggedItem(projection, eventId, entry["item"]);