From 6dbe201c99fc69afe73a37a0d56cfcb0d7e0e64f Mon Sep 17 00:00:00 2001 From: root Date: Thu, 2 Jul 2026 11:54:28 +0000 Subject: [PATCH] fix: seal workbench terminal turn body --- .../workbench-message-projection-runtime.test.ts | 16 ++++++++++++++++ web/hwlab-cloud-web/src/stores/workbench.ts | 6 ++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/web/hwlab-cloud-web/src/stores/workbench-message-projection-runtime.test.ts b/web/hwlab-cloud-web/src/stores/workbench-message-projection-runtime.test.ts index f89f72fd..d6e8e11d 100644 --- a/web/hwlab-cloud-web/src/stores/workbench-message-projection-runtime.test.ts +++ b/web/hwlab-cloud-web/src/stores/workbench-message-projection-runtime.test.ts @@ -1,4 +1,7 @@ import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; import { test } from "bun:test"; import type { AgentChatResultResponse, ChatMessage } from "../types"; @@ -11,6 +14,8 @@ import { turnResultStatusForMerge } from "./workbench-message-projection-runtime"; +const storeDir = path.dirname(fileURLToPath(import.meta.url)); + function agentMessage(input: Partial & Pick): ChatMessage { return { role: "agent", @@ -103,3 +108,14 @@ test("terminal response helpers treat failed body as sealed authority", () => { assert.equal(messageNeedsTerminalDiagnostics(unsealed), true); assert.equal(traceHasTerminalResponse(traceId, [unsealed]), false); }); + +test("workbench active terminal paths seal final response from turn authority", () => { + const source = fs.readFileSync(path.join(storeDir, "workbench.ts"), "utf8"); + const terminalPatchUses = source.match(/terminalMessagePatchFromTurnResult\(message, result(?: as AgentChatResultResponse)?\) \?\? \{\}/gu) ?? []; + const syncBlock = source.slice(source.indexOf("function syncTurnStatusToMessage"), source.indexOf("async function submitMessage")); + const completeBlock = source.slice(source.indexOf("function completeTrace"), source.indexOf("async function hydrateTerminalMessageDiagnostics")); + + assert.equal(terminalPatchUses.length, 2); + assert.match(syncBlock, /\.\.\.terminalPatch/u); + assert.match(completeBlock, /\.\.\.terminalPatch/u); +}); diff --git a/web/hwlab-cloud-web/src/stores/workbench.ts b/web/hwlab-cloud-web/src/stores/workbench.ts index 2c66ef0b..75827dbc 100644 --- a/web/hwlab-cloud-web/src/stores/workbench.ts +++ b/web/hwlab-cloud-web/src/stores/workbench.ts @@ -616,7 +616,8 @@ export const useWorkbenchStore = defineStore("workbench", () => { const error = resultError ?? (terminal || clearCompletedDiagnostics ? null : normalizeAgentError(runnerTrace?.error ?? message.error)); const agentRun = agentRunFromResult(result as AgentChatResultResponse, runnerTrace) ?? agentRunFromMessage(message); const projection = clearCompletedDiagnostics ? nonBlockingProjection(resultProjection) : resultProjection ?? (terminal ? null : runnerTrace.projection ?? message.projection ?? null); - return { ...message, ...messageTimingPatchForMerge(message, result), ...messageStatusPatchForTerminalMerge(message, resultStatus, terminal), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() }; + const terminalPatch = terminalMessagePatchFromTurnResult(message, result as AgentChatResultResponse) ?? {}; + return { ...message, ...messageTimingPatchForMerge(message, result), ...messageStatusPatchForTerminalMerge(message, resultStatus, terminal), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, ...terminalPatch, updatedAt: new Date().toISOString() }; }); } @@ -1281,7 +1282,8 @@ export const useWorkbenchStore = defineStore("workbench", () => { const error = resultError ?? (terminal || clearCompletedDiagnostics ? null : normalizeAgentError(runnerTrace?.error ?? message.error)); const projection = clearCompletedDiagnostics ? nonBlockingProjection(resultProjection) : resultProjection ?? (terminal ? null : runnerTrace.projection ?? message.projection ?? null); const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message); - return { ...message, ...messageTimingPatchForMerge(message, result), ...messageStatusPatchForTerminalMerge(message, resultStatus, terminal), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() }; + const terminalPatch = terminalMessagePatchFromTurnResult(message, result) ?? {}; + return { ...message, ...messageTimingPatchForMerge(message, result), ...messageStatusPatchForTerminalMerge(message, resultStatus, terminal), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, ...terminalPatch, updatedAt: new Date().toISOString() }; })); rememberTurnStatus(traceId, result); markWorkbenchTraceProjected(traceId);