fix: separate sealed final response diagnostics
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
// SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0
|
||||
// Confirms Workbench message visibility after Vue render and browser paint.
|
||||
// SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0; PJ2026-0104010803 唯一投影 draft-2026-06-19-p2-terminal-sealed-final-response.
|
||||
// Confirms Workbench message visibility after Vue render and keeps sealed final responses separate from diagnostics.
|
||||
|
||||
import { computed, nextTick, onMounted, ref, watch } from "vue";
|
||||
import type { ChatMessage } from "@/types";
|
||||
@@ -80,7 +80,7 @@ function isAwaitingAgentBody(message: ChatMessage): boolean {
|
||||
}
|
||||
|
||||
function showMessageText(message: ChatMessage): boolean {
|
||||
return !messageDiagnosticText(message) && Boolean(visibleMessageText(message));
|
||||
return Boolean(visibleMessageText(message));
|
||||
}
|
||||
|
||||
function messageDiagnosticText(message: ChatMessage): string | null {
|
||||
@@ -132,8 +132,8 @@ function traceAutoExpanded(message: ChatMessage): boolean | null {
|
||||
</header>
|
||||
<TraceTimeline v-if="message.role === 'agent' && message.runnerTrace" :trace="message.runnerTrace" :auto-expanded="traceAutoExpanded(message)" :storage-key="traceStorageKey(message)" />
|
||||
<LoadingState v-if="isAwaitingAgentBody(message)" class="message-loading" label="思考中..." compact />
|
||||
<MessageMarkdown v-if="showMessageText(message)" class="message-text" :source="visibleMessageText(message)" />
|
||||
<p v-if="messageDiagnosticText(message)" class="message-text projection-diagnostic">{{ messageDiagnosticText(message) }}</p>
|
||||
<MessageMarkdown v-else-if="showMessageText(message)" class="message-text" :source="visibleMessageText(message)" />
|
||||
</article>
|
||||
<div v-if="!workbench.sessionDetailLoading && workbench.activeMessages.length === 0 && workbench.error" class="conversation-empty-hint conversation-error-hint">加载失败:{{ workbench.error }}</div>
|
||||
<div v-else-if="!workbench.sessionDetailLoading && workbench.activeMessages.length === 0" class="conversation-empty-hint">发起对话,或从左侧选择 session。</div>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPEC: PJ2026-010401 Web工作台 draft-2026-06-18-r1; PJ2026-0104010803 唯一投影 draft-2026-06-19-p2-terminal-sealed-final-response.
|
||||
// Responsibility: Workbench diagnostic visibility regression, including separation from sealed final response.
|
||||
|
||||
import { expect, gotoWorkbench, saveScreenshot, test } from "../fixtures/test";
|
||||
import { selectors, sessionTab } from "../fixtures/selectors";
|
||||
|
||||
@@ -42,3 +45,17 @@ test.describe("REST gap hydration errors", () => {
|
||||
await saveScreenshot(page, testInfo, "trace-hydration-timeout-visible");
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("sealed final response diagnostics separation", () => {
|
||||
test.use({ scenarioId: "sealed-final-response-diagnostics" });
|
||||
|
||||
test("keeps completed final response visible while showing diagnostics separately", async ({ page }, testInfo) => {
|
||||
await gotoWorkbench(page, "/workbench/sessions/ses_sealed_final_diag");
|
||||
const card = page.locator(`${selectors.messageCard}[data-role="agent"][data-status="completed"]`).last();
|
||||
await expect(card.locator(".projection-diagnostic")).toContainText("Trace 更新超时");
|
||||
const finalResponse = card.locator(".message-text").filter({ hasText: "已完成的 sealed final response 不应被诊断覆盖。" });
|
||||
await expect(finalResponse).toBeVisible();
|
||||
await expect(finalResponse).not.toContainText("Trace 更新超时");
|
||||
await saveScreenshot(page, testInfo, "sealed-final-response-diagnostic-separate");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user