From fa924a64c8107396270e07fda6ef36e01f55f091 Mon Sep 17 00:00:00 2001 From: lyon Date: Fri, 19 Jun 2026 21:22:21 +0800 Subject: [PATCH] fix: separate sealed final response diagnostics --- .../scripts/workbench-e2e-server.ts | 48 ++++++++++++++++++- .../workbench/ConversationPanel.vue | 8 ++-- .../specs/projection-diagnostics.spec.ts | 17 +++++++ 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/web/hwlab-cloud-web/scripts/workbench-e2e-server.ts b/web/hwlab-cloud-web/scripts/workbench-e2e-server.ts index f0120ffd..a1a8ae35 100644 --- a/web/hwlab-cloud-web/scripts/workbench-e2e-server.ts +++ b/web/hwlab-cloud-web/scripts/workbench-e2e-server.ts @@ -1,4 +1,4 @@ -// SPEC: PJ2026-010401 Web工作台 draft-2026-06-18-r1; PJ2026-0104010803 唯一投影 draft-2026-06-19-p0-projector-resume; PJ2026-010403 API契约 draft-2026-06-18-r1. +// SPEC: PJ2026-010401 Web工作台 draft-2026-06-18-r1; PJ2026-0104010803 唯一投影 draft-2026-06-19-p2-terminal-sealed-final-response; PJ2026-010403 API契约 draft-2026-06-18-r1. // Responsibility: Same-origin fake Workbench API and static server for Playwright browser regression tests. import { createReadStream, existsSync, statSync } from "node:fs"; @@ -139,6 +139,7 @@ async function handleRequest(request: IncomingMessage, response: ServerResponse) const traceId = decodeURIComponent(traceMatch[1] ?? ""); if (state.scenarioId === "completed-replay-detail-404" && traceId === "trc_completed") return json(response, 404, { ok: false, status: 404, error: { code: "trace_replay_unavailable" } }); if (state.scenarioId === "trace-hydration-timeout-diagnostic" && traceId === "trc_trace_hydration_timeout") return json(response, 504, { ok: false, status: 504, error: { code: "trace_hydration_timeout", message: "Trace 更新超时,运行记录暂不可见。" } }); + if (state.scenarioId === "sealed-final-response-diagnostics" && traceId === "trc_sealed_final_diag") return json(response, 504, { ok: false, status: 504, error: { code: "trace_hydration_timeout", message: "Trace 更新超时,运行记录暂不可见。" } }); if (traceId === state.staleTraceId) return json(response, 502, { ok: false, status: 502, error: { code: "upstream_unavailable", message: "stale trace is unavailable" } }); return json(response, 200, workbenchTracePayload(traceId, url)); } @@ -386,6 +387,10 @@ function createScenarioState(scenarioId: string): ScenarioState { sessions.unshift(traceHydrationTimeoutSession()); traces.trc_trace_hydration_timeout = traceHydrationTimeoutTrace(); } + if (id === "sealed-final-response-diagnostics") { + sessions.unshift(sealedFinalResponseDiagnosticSession()); + traces.trc_sealed_final_diag = sealedFinalResponseDiagnosticTrace(); + } if (id === "scroll-follow-long-trace") { const session = scrollFollowSession(); sessions.unshift(session); @@ -425,6 +430,8 @@ function createScenarioState(scenarioId: string): ScenarioState { ? "ses_projection_sse_error" : id === "trace-hydration-timeout-diagnostic" ? "ses_trace_hydration_timeout" + : id === "sealed-final-response-diagnostics" + ? "ses_sealed_final_diag" : id === "create-while-route-loading" ? "ses_running" : base.selectedSessionId; @@ -706,6 +713,45 @@ function traceHydrationTimeoutSession(): SessionRecord { }; } +function sealedFinalResponseDiagnosticTrace(): JsonRecord { + const now = new Date().toISOString(); + const finalText = "已完成的 sealed final response 不应被诊断覆盖。"; + return { + traceId: "trc_sealed_final_diag", + status: "completed", + sessionId: "ses_sealed_final_diag", + threadId: "thr_sealed_final_diag", + turnId: "turn_sealed_final_diag", + events: [], + eventCount: 1, + fullTraceLoaded: false, + hasMore: true, + finalResponse: { text: finalText, status: "completed" }, + terminalEvidence: { terminal: true, status: "completed" }, + sealedAt: now + }; +} + +function sealedFinalResponseDiagnosticSession(): SessionRecord { + const now = new Date().toISOString(); + const finalText = "已完成的 sealed final response 不应被诊断覆盖。"; + const trace = sealedFinalResponseDiagnosticTrace(); + return { + sessionId: "ses_sealed_final_diag", + threadId: "thr_sealed_final_diag", + status: "completed", + lastTraceId: "trc_sealed_final_diag", + updatedAt: now, + messageCount: 2, + firstUserMessagePreview: "sealed final response diagnostic separation", + turnSummary: { traceId: "trc_sealed_final_diag", status: "completed", running: false, terminal: true, sealedAt: now }, + messages: [ + { id: "msg_sealed_final_user", messageId: "msg_sealed_final_user", role: "user", title: "用户", text: "sealed final response diagnostic separation", status: "sent", createdAt: now, sessionId: "ses_sealed_final_diag", threadId: "thr_sealed_final_diag", turnId: "turn_sealed_final_diag" }, + { id: "msg_sealed_final_agent", messageId: "msg_sealed_final_agent", role: "agent", title: "Code Agent", text: finalText, status: "completed", createdAt: now, updatedAt: now, sessionId: "ses_sealed_final_diag", threadId: "thr_sealed_final_diag", traceId: "trc_sealed_final_diag", turnId: "turn_sealed_final_diag", sealedAt: now, runnerTrace: trace, finalResponse: { text: finalText, status: "completed" } } + ] + }; +} + function noFinalRunningTrace(): JsonRecord { const createdAt = new Date().toISOString(); return { diff --git a/web/hwlab-cloud-web/src/components/workbench/ConversationPanel.vue b/web/hwlab-cloud-web/src/components/workbench/ConversationPanel.vue index 5a4e0882..ded92454 100644 --- a/web/hwlab-cloud-web/src/components/workbench/ConversationPanel.vue +++ b/web/hwlab-cloud-web/src/components/workbench/ConversationPanel.vue @@ -1,6 +1,6 @@