Merge pull request #2334 from pikasTech/issue-1373-architecture-cleanup-2

fix: preserve Workbench sealed final response body
This commit is contained in:
Lyon
2026-07-01 17:54:34 +08:00
committed by GitHub
2 changed files with 29 additions and 1 deletions
@@ -5,7 +5,7 @@ import assert from "node:assert/strict";
import test from "node:test";
import type { AgentChatResultResponse, ChatMessage } from "../src/types/index.ts";
import { clearRunnerTraceTransientDiagnostics, mergeTerminalResultTrace, messageStatusPatchForTerminalMerge, messageTimingPatchForMerge, nonBlockingProjection, normalizeAgentError, projectionFromResult, shouldClearCompletedTurnDiagnostics, terminalMessagePatchFromTurnResult, terminalMessageTimingPatchForNormalize } from "../src/stores/workbench-message-projection-runtime.ts";
import { clearRunnerTraceTransientDiagnostics, mergeTerminalResultTrace, messageStatusPatchForTerminalMerge, messageTimingPatchForMerge, messageTimingSealPatchForProjectionMerge, nonBlockingProjection, normalizeAgentError, projectionFromResult, shouldClearCompletedTurnDiagnostics, terminalMessagePatchFromTurnResult, terminalMessageTimingPatchForNormalize } from "../src/stores/workbench-message-projection-runtime.ts";
test("terminal result merge preserves runner trace evidence and timing", () => {
const previous: ChatMessage["runnerTrace"] = {
@@ -199,3 +199,29 @@ test("terminal seal blocks stale running snapshots after session restore", () =>
assert.equal(terminal?.durationMs, 4000);
assert.equal(terminal?.error?.message, "provider stream disconnected");
});
test("terminal projection page seal preserves final response object", () => {
const previous = {
role: "agent",
status: "completed",
text: "final answer",
finalResponse: { text: "final answer", status: "completed", sealed: true },
traceAutoLifecycle: "terminal",
timing: { startedAt: "2026-01-01T00:00:00.000Z", finishedAt: "2026-01-01T00:00:05.000Z", durationMs: 5000, valuesRedacted: true }
} as ChatMessage;
const incoming = {
role: "agent",
status: "running",
text: "",
traceAutoLifecycle: "running",
timing: { startedAt: "2026-01-01T00:00:00.000Z", valuesRedacted: true }
} as ChatMessage;
const patch = messageTimingSealPatchForProjectionMerge(incoming, previous);
assert.equal(patch.status, "completed");
assert.equal(patch.traceAutoLifecycle, "terminal");
assert.equal(patch.text, "final answer");
assert.deepEqual(patch.finalResponse, { text: "final answer", status: "completed", sealed: true });
assert.equal(patch.durationMs, 5000);
});
@@ -217,6 +217,8 @@ function messageTerminalSealPatchForProjectionMerge(message: ChatMessage, previo
if (!previous || !isTerminalMessageStatus(previous.status)) return incomingTimingPatch;
const patch: Partial<ChatMessage> = { status: previous.status, traceAutoLifecycle: previous.traceAutoLifecycle ?? "terminal" };
if (typeof previous.text === "string" && previous.text.trim()) patch.text = previous.text;
const finalResponse = (previous as Record<string, unknown>).finalResponse;
if (finalResponse) patch.finalResponse = finalResponse;
const previousTiming = normalizeTimingProjection(previous);
if (firstPositiveFiniteNumber(previousTiming?.durationMs) === null) return { ...terminalMessageTimingPatchForNormalize(message), ...patch };
return { ...messageTimingPatch(previous), ...patch };