// SPEC: PJ2026-0106050514 Workbench实时运行面 draft-2026-06-30-p0-1297-spec-first; PJ2026-0104010803 Workbench唯一投影 draft-2026-06-18-p0-unique-projection. // Responsibility: Regression tests for pure Workbench trace/message/projection merge helpers before Pinia integration. import assert from "node:assert/strict"; import test from "node:test"; import type { AgentChatResultResponse, ChatMessage } from "../src/types/index.ts"; import { clearRunnerTraceTransientDiagnostics, mergeTerminalResultTrace, messageHasTerminalResponse, messageStatusPatchForTerminalMerge, messageTimingPatchForMerge, messageTimingSealPatchForProjectionMerge, nonBlockingProjection, normalizeAgentError, projectionFromResult, shouldClearCompletedTurnDiagnostics, terminalAuthorityMessageFromTurnResult, terminalMessagePatchFromTurnResult, terminalMessageTimingPatchForNormalize } from "../src/stores/workbench-message-projection-runtime.ts"; test("terminal result merge preserves runner trace evidence and timing", () => { const previous: ChatMessage["runnerTrace"] = { traceId: "trc_1", sessionId: "ses_1", events: [{ id: "evt_old", type: "tool" } as never], eventCount: 1, projection: { projectionStatus: "blocked", projectionHealth: "degraded", blocker: { code: "old" } }, updatedAt: "2026-01-01T00:00:00.000Z" }; const result = { traceId: "trc_1", sessionId: "ses_1", status: "completed", terminal: true, events: [{ id: "evt_new", type: "finish" }], eventCount: 1, startedAt: "2026-01-01T00:00:00.000Z", finishedAt: "2026-01-01T00:00:05.000Z", durationMs: 5000, projectionStatus: "caught-up", projectionHealth: "healthy", agentRun: { runId: "run_1", backendProfile: "codex-api" } } as unknown as AgentChatResultResponse; const merged = mergeTerminalResultTrace(previous, result); assert.equal(merged.traceId, "trc_1"); assert.equal(merged.sessionId, "ses_1"); assert.equal(merged.eventCount, 1); assert.equal(merged.events?.[0]?.id, "evt_old"); assert.equal(merged.terminalEvidence, undefined); assert.equal(merged.timing?.durationMs, 5000); assert.equal((merged.agentRun as { runId?: string } | undefined)?.runId, "run_1"); assert.equal(merged.projectionStatus, "caught-up"); }); test("message projection runtime clears only non-blocking completed diagnostics", () => { const error = normalizeAgentError({ message: "provider failed", diagnostic: { code: "provider_error", source: "provider" } }); assert.equal(error?.message, "provider failed"); assert.equal(error?.diagnostic?.code, "provider_error"); assert.equal(shouldClearCompletedTurnDiagnostics("completed", error), false); assert.equal(shouldClearCompletedTurnDiagnostics("completed", null), true); const projection = projectionFromResult({ projectionStatus: "caught-up", projectionHealth: "healthy" } as AgentChatResultResponse); assert.equal(nonBlockingProjection(projection)?.projectionStatus, "caught-up"); assert.equal(nonBlockingProjection({ projectionStatus: "blocked", projectionHealth: "degraded", blocker: { code: "read_model_gap" } }), null); const cleared = clearRunnerTraceTransientDiagnostics({ traceId: "trc_1", error, projection, projectionStatus: "caught-up", projectionHealth: "healthy", staleMs: 10, blocker: { code: "old" } }); assert.equal(cleared.error, undefined); assert.equal(cleared.projection, null); assert.equal(cleared.blocker, null); }); test("message timing and terminal status patches stay independent from store state", () => { const message = { role: "agent", status: "running", startedAt: "2026-01-01T00:00:00.000Z", timing: { startedAt: "2026-01-01T00:00:00.000Z", valuesRedacted: true } } as ChatMessage; const result = { status: "completed", terminal: true, startedAt: "2026-01-01T00:00:00.000Z", finishedAt: "2026-01-01T00:00:03.500Z" }; const timingPatch = messageTimingPatchForMerge(message, result); assert.equal(timingPatch.durationMs, 3500); assert.equal(timingPatch.finishedAt, "2026-01-01T00:00:03.500Z"); assert.deepEqual(messageStatusPatchForTerminalMerge(message, "completed", true), { status: "completed" }); const terminalPatch = terminalMessageTimingPatchForNormalize(result); assert.equal(terminalPatch.durationMs, 3500); }); test("terminal turn result seals completed final response body", () => { const restored = { role: "agent", status: "running", text: "", traceAutoLifecycle: "running", traceId: "trc_unit_final", runnerTrace: { traceId: "trc_unit_final", status: "running" } } as ChatMessage; const terminal = terminalMessagePatchFromTurnResult(restored, { traceId: "trc_unit_final", status: "completed", terminal: true, finalResponse: { text: "final answer" }, startedAt: "2026-01-01T00:00:00.000Z", finishedAt: "2026-01-01T00:00:04.000Z", durationMs: 4000 } as AgentChatResultResponse); assert.equal(terminal?.status, "completed"); assert.equal(terminal?.traceAutoLifecycle, "terminal"); assert.equal(terminal?.text, "final answer"); assert.equal((terminal?.finalResponse as { text?: string } | undefined)?.text, "final answer"); }); test("terminal turn authority can project final response without a session message page row", () => { const terminal = terminalAuthorityMessageFromTurnResult({ traceId: "trc_direct_terminal", sessionId: "ses_direct_terminal", threadId: "thr_direct_terminal", status: "completed", terminal: true, finalResponse: { text: "direct final body" }, startedAt: "2026-07-02T13:18:40.000Z", finishedAt: "2026-07-02T13:18:47.000Z" } as AgentChatResultResponse); assert.equal(terminal?.id, "msg_trc_direct_terminal_terminal_authority"); assert.equal(terminal?.status, "completed"); assert.equal(terminal?.traceAutoLifecycle, "terminal"); assert.equal(terminal?.text, "direct final body"); assert.equal(messageHasTerminalResponse(terminal), true); }); test("terminal turn authority does not synthesize an empty completed message", () => { const terminal = terminalAuthorityMessageFromTurnResult({ traceId: "trc_direct_terminal_empty", status: "completed", terminal: true, terminalSealBlocked: true, waitingFor: "final_response" } as AgentChatResultResponse); assert.equal(terminal, null); }); test("terminal turn result accepts authoritative assistant trace final", () => { const restored = { role: "agent", status: "running", text: "", traceId: "trc_trace_final", runnerTrace: { traceId: "trc_trace_final", status: "running" } } as ChatMessage; const terminal = terminalMessagePatchFromTurnResult(restored, { traceId: "trc_trace_final", status: "completed", terminal: true, runnerTrace: { traceId: "trc_trace_final", status: "completed", events: [ { seq: 1, type: "assistant_message", status: "running", message: "progress only" }, { seq: 2, type: "assistant_message", status: "completed", message: "final from trace", replyAuthority: true, final: true } ] } } as unknown as AgentChatResultResponse); assert.equal(terminal?.status, "completed"); assert.equal(terminal?.text, "final from trace"); assert.equal((terminal?.finalResponse as { text?: string } | undefined)?.text, "final from trace"); }); test("terminal failure does not synthesize completed final response from progress text", () => { const restored = { role: "agent", status: "running", text: "", traceId: "trc_failed_progress", runnerTrace: { traceId: "trc_failed_progress", status: "running" } } as ChatMessage; const terminal = terminalMessagePatchFromTurnResult(restored, { traceId: "trc_failed_progress", status: "failed", terminal: true, runnerTrace: { traceId: "trc_failed_progress", status: "failed", events: [{ seq: 1, type: "assistant_message", status: "running", message: "progress only" }] }, error: { message: "provider failed" } } as unknown as AgentChatResultResponse); assert.equal(terminal?.status, "failed"); assert.equal(terminal?.text, undefined); assert.equal(terminal?.finalResponse, undefined); assert.equal(terminal?.error?.message, "provider failed"); }); test("terminal seal blocks stale running snapshots after session restore", () => { const sealed = { role: "agent", status: "failed", text: "provider stream disconnected", traceAutoLifecycle: "terminal", runnerTrace: { traceId: "trc_1", status: "failed" }, timing: { startedAt: "2026-01-01T00:00:00.000Z", finishedAt: "2026-01-01T00:00:05.000Z", durationMs: 5000, valuesRedacted: true } } as ChatMessage; assert.deepEqual(messageStatusPatchForTerminalMerge(sealed, "running", false), { status: "failed", traceAutoLifecycle: "terminal" }); const restored = { role: "agent", status: "running", traceAutoLifecycle: "running", traceId: "trc_1", runnerTrace: { traceId: "trc_1", status: "running" }, timing: { startedAt: "2026-01-01T00:00:00.000Z", valuesRedacted: true } } as ChatMessage; const terminal = terminalMessagePatchFromTurnResult(restored, { traceId: "trc_1", status: "failed", terminal: true, startedAt: "2026-01-01T00:00:00.000Z", finishedAt: "2026-01-01T00:00:04.000Z", durationMs: 4000, error: { message: "provider stream disconnected" } } as AgentChatResultResponse); assert.equal(terminal?.status, "failed"); assert.equal(terminal?.traceAutoLifecycle, "terminal"); assert.equal(terminal?.durationMs, 4000); assert.equal(terminal?.error?.message, "provider stream disconnected"); }); test("terminal projection page seal preserves final response object", () => { const previous = { id: "msg_terminal_projection_previous", role: "agent", title: "Code Agent", status: "completed", createdAt: "2026-01-01T00:00:00.000Z", 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 = { id: "msg_terminal_projection_incoming", role: "agent", title: "Code Agent", status: "running", createdAt: "2026-01-01T00:00:00.000Z", 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); });