fix: replay annotated history in Web Console
This commit is contained in:
@@ -165,6 +165,108 @@ Deno.test("segment rotation retains a live error beside the real SegmentStart hi
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("reload snapshot projects provenance-annotated history entries", () => {
|
||||||
|
const metadata = {
|
||||||
|
entry_id: "history-entry-1",
|
||||||
|
origin: {
|
||||||
|
kind: "model_output",
|
||||||
|
worker: {
|
||||||
|
workspace_id: "workspace-secret",
|
||||||
|
runtime_id: "runtime-secret",
|
||||||
|
worker_id: "worker-secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const annotated = (item: unknown) => ({ item, metadata });
|
||||||
|
const projection = projectConsole([{
|
||||||
|
eventId: "annotated-reload",
|
||||||
|
event: snapshotEvent("/repo", [
|
||||||
|
{
|
||||||
|
kind: "annotated_segment_start",
|
||||||
|
ts: 1,
|
||||||
|
session_id: "session-1",
|
||||||
|
system_prompt: null,
|
||||||
|
config: {},
|
||||||
|
history: [annotated({
|
||||||
|
kind: "message",
|
||||||
|
role: "assistant",
|
||||||
|
content: [{ kind: "text", text: "older committed reply" }],
|
||||||
|
})],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "annotated_user_input",
|
||||||
|
ts: 2,
|
||||||
|
segments: [{ kind: "text", content: "latest user message" }],
|
||||||
|
history: [annotated({
|
||||||
|
kind: "message",
|
||||||
|
role: "user",
|
||||||
|
content: [{ kind: "text", text: "latest user message" }],
|
||||||
|
})],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "annotated_assistant_item",
|
||||||
|
ts: 3,
|
||||||
|
entry: annotated({
|
||||||
|
kind: "message",
|
||||||
|
role: "assistant",
|
||||||
|
content: [{ kind: "text", text: "latest committed reply" }],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "annotated_assistant_item",
|
||||||
|
ts: 4,
|
||||||
|
entry: annotated({
|
||||||
|
kind: "tool_call",
|
||||||
|
call_id: "annotated-call",
|
||||||
|
name: "Read",
|
||||||
|
arguments: '{"file_path":"/repo/a.md"}',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "annotated_tool_result",
|
||||||
|
ts: 5,
|
||||||
|
entry: annotated({
|
||||||
|
kind: "tool_result",
|
||||||
|
call_id: "annotated-call",
|
||||||
|
summary: "Read 1 line from /repo/a.md",
|
||||||
|
content: "1→content",
|
||||||
|
is_error: false,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "annotated_system_item",
|
||||||
|
ts: 6,
|
||||||
|
entry: annotated({
|
||||||
|
kind: "notification",
|
||||||
|
message: "Worker completed",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
}]);
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
projection.lines.map((line) =>
|
||||||
|
`${line.kind}:${line.toolCallLabel ?? line.body}`
|
||||||
|
),
|
||||||
|
[
|
||||||
|
"assistant:older committed reply",
|
||||||
|
"user:latest user message",
|
||||||
|
"assistant:latest committed reply",
|
||||||
|
"tool:Read(1 file)",
|
||||||
|
"system:Worker completed",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
const visible = JSON.stringify(projection.lines);
|
||||||
|
assert(
|
||||||
|
!visible.includes("workspace-secret"),
|
||||||
|
"history metadata must not enter Console rows",
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
!visible.includes("runtime-secret"),
|
||||||
|
"history origin must remain non-visible metadata",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("workerConsoleHref encodes runtime and worker target authority", () => {
|
Deno.test("workerConsoleHref encodes runtime and worker target authority", () => {
|
||||||
assert(
|
assert(
|
||||||
workerConsoleHref({
|
workerConsoleHref({
|
||||||
|
|||||||
@@ -1960,24 +1960,34 @@ function applyLogEntry(
|
|||||||
applyLoggedItem(projection, `${eventId}-history-${index}`, item)
|
applyLoggedItem(projection, `${eventId}-history-${index}`, item)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "user_input":
|
case "annotated_segment_start":
|
||||||
projection.lines.push(
|
arrayField(entry, "history").forEach((historyEntry, index) =>
|
||||||
line(
|
applyLoggedHistoryEntry(
|
||||||
eventId,
|
projection,
|
||||||
"user",
|
`${eventId}-history-${index}`,
|
||||||
"User",
|
historyEntry,
|
||||||
segmentsToText(arrayField(entry, "segments") as Segment[]),
|
)
|
||||||
),
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "user_input":
|
||||||
|
case "annotated_user_input":
|
||||||
|
applyLoggedUserInput(projection, eventId, entry);
|
||||||
|
break;
|
||||||
case "system_item":
|
case "system_item":
|
||||||
projection.lines.push(systemItemLine(eventId, entry["item"]));
|
projection.lines.push(systemItemLine(eventId, entry["item"]));
|
||||||
applyTaskSystemItem(projection, entry["item"]);
|
applyTaskSystemItem(projection, entry["item"]);
|
||||||
break;
|
break;
|
||||||
|
case "annotated_system_item":
|
||||||
|
applyLoggedSystemEntry(projection, eventId, entry["entry"]);
|
||||||
|
break;
|
||||||
case "assistant_item":
|
case "assistant_item":
|
||||||
case "tool_result":
|
case "tool_result":
|
||||||
applyLoggedItem(projection, eventId, entry["item"]);
|
applyLoggedItem(projection, eventId, entry["item"]);
|
||||||
break;
|
break;
|
||||||
|
case "annotated_assistant_item":
|
||||||
|
case "annotated_tool_result":
|
||||||
|
applyLoggedHistoryEntry(projection, eventId, entry["entry"]);
|
||||||
|
break;
|
||||||
case "run_errored":
|
case "run_errored":
|
||||||
projection.lines.push(
|
projection.lines.push(
|
||||||
line(
|
line(
|
||||||
@@ -2056,6 +2066,52 @@ function compactMessageForState(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyLoggedUserInput(
|
||||||
|
projection: ConsoleProjection,
|
||||||
|
eventId: string,
|
||||||
|
entry: Record<string, unknown>,
|
||||||
|
): void {
|
||||||
|
let body = segmentsToText(arrayField(entry, "segments") as Segment[]);
|
||||||
|
if (!body && stringField(entry, "kind") === "annotated_user_input") {
|
||||||
|
body = loggedUserText(arrayField(entry, "history"));
|
||||||
|
}
|
||||||
|
projection.lines.push(line(eventId, "user", "User", body));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loggedUserText(history: unknown[]): string {
|
||||||
|
for (const historyEntry of history) {
|
||||||
|
if (!isRecord(historyEntry) || !isRecord(historyEntry["item"])) continue;
|
||||||
|
const item = historyEntry["item"];
|
||||||
|
if (
|
||||||
|
stringField(item, "kind") !== "message" ||
|
||||||
|
stringField(item, "role") !== "user"
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return loggedContentText(arrayField(item, "content"));
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyLoggedHistoryEntry(
|
||||||
|
projection: ConsoleProjection,
|
||||||
|
eventId: string,
|
||||||
|
historyEntry: unknown,
|
||||||
|
): void {
|
||||||
|
if (!isRecord(historyEntry)) return;
|
||||||
|
applyLoggedItem(projection, eventId, historyEntry["item"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyLoggedSystemEntry(
|
||||||
|
projection: ConsoleProjection,
|
||||||
|
eventId: string,
|
||||||
|
historyEntry: unknown,
|
||||||
|
): void {
|
||||||
|
if (!isRecord(historyEntry)) return;
|
||||||
|
projection.lines.push(systemItemLine(eventId, historyEntry["item"]));
|
||||||
|
applyTaskSystemItem(projection, historyEntry["item"]);
|
||||||
|
}
|
||||||
|
|
||||||
function applyLoggedItem(
|
function applyLoggedItem(
|
||||||
projection: ConsoleProjection,
|
projection: ConsoleProjection,
|
||||||
eventId: string,
|
eventId: string,
|
||||||
|
|||||||
Reference in New Issue
Block a user