fix(workbench): remove frontend trace timing compensation

This commit is contained in:
lyon
2026-06-25 00:33:21 +08:00
parent 2651a6ee55
commit f40cd324fb
8 changed files with 54 additions and 190 deletions
@@ -1,7 +1,7 @@
// 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";
import { createReadStream, existsSync, readFileSync, statSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import { extname, join, resolve, sep } from "node:path";
@@ -98,6 +98,7 @@ const terminalAssistantFinalText = [
].join("\n");
const canonicalTraceFinalText = "messageProjection 的 sealed final response 才是主消息正文。";
const traceDetailConflictText = "TRACE_DETAIL_CONFLICT_SHOULD_NOT_REPLACE_MESSAGE";
const runtimeConfigScript = `<script>\nwindow.HWLAB_CLOUD_WEB_CONFIG = {\n ...(window.HWLAB_CLOUD_WEB_CONFIG ?? {}),\n displayTime: window.HWLAB_CLOUD_WEB_CONFIG?.displayTime ?? { timeZone: "Asia/Shanghai", locale: "zh-CN", label: "北京时间" },\n workbench: {\n ...(window.HWLAB_CLOUD_WEB_CONFIG?.workbench ?? {}),\n traceTimeline: window.HWLAB_CLOUD_WEB_CONFIG?.workbench?.traceTimeline ?? { autoExpandRunning: false, autoCollapseTerminal: false }\n }\n};\n</script>`;
let state = createScenarioState("baseline");
const sseClients = new Set<ServerResponse>();
@@ -1782,6 +1783,8 @@ function sse(request: IncomingMessage, response: ServerResponse, url: URL): void
const event = (trace.events as JsonRecord[]).at(-1) ?? { status: "completed", terminal: true };
writeSse(response, "workbench.trace.event", { type: "trace.event", sessionId: "ses_running", threadId: "thr_running", traceId: "trc_running", event, snapshot: trace });
finishRunningNoFinalResponse(trace);
const message = runningAgentMessageSnapshot();
if (message) writeSse(response, "workbench.message.snapshot", { type: "message.snapshot", sessionId: "ses_running", threadId: "thr_running", traceId: "trc_running", message });
writeSse(response, "workbench.turn.snapshot", { type: "turn.snapshot", sessionId: "ses_running", threadId: "thr_running", traceId: "trc_running", turn: turnPayload("trc_running") });
return;
}
@@ -1790,8 +1793,10 @@ function sse(request: IncomingMessage, response: ServerResponse, url: URL): void
const event = { seq: 3, createdAt: new Date().toISOString(), label: "agentrun:assistant:message", type: "assistant_message", status: terminalStatus, replyAuthority: true, final: true, message: terminalText, terminal: true };
writeSse(response, "workbench.trace.event", { type: "trace.event", sessionId: "ses_running", threadId: "thr_running", traceId: "trc_running", event, snapshot: { traceId: "trc_running", sessionId: "ses_running", threadId: "thr_running", status: terminalStatus, events: [event], eventCount: 3, fullTraceLoaded: true, finalResponse: { text: terminalText, status: terminalStatus } } });
finishRunningSession(terminalStatus, terminalText);
const message = runningAgentMessageSnapshot();
if (message) writeSse(response, "workbench.message.snapshot", { type: "message.snapshot", sessionId: "ses_running", threadId: "thr_running", traceId: "trc_running", message });
writeSse(response, "workbench.turn.snapshot", { type: "turn.snapshot", sessionId: "ses_running", threadId: "thr_running", traceId: "trc_running", turn: turnPayload("trc_running") });
}, state.scenarioId === "progress-only-final-response" ? 5_000 : 350);
}, state.scenarioId === "progress-only-final-response" ? 5_000 : state.scenarioId === "event-replay" ? 1_500 : 350);
}
function broadcastSse(eventName: string, payload: JsonRecord): void {
@@ -1824,6 +1829,12 @@ function finishRunningNoFinalResponse(trace: JsonRecord = completedNoFinalTrace(
session.messages = (session.messages ?? []).map((message) => message.role === "agent" ? { ...message, text: "", status: "completed", runnerTrace: trace } : message);
}
function runningAgentMessageSnapshot(): JsonRecord | null {
const session = sessionById("ses_running");
const message = session?.messages?.find((item) => item.role === "agent" && item.traceId === "trc_running");
return message ?? null;
}
function stateSummary(): JsonRecord {
return { scenarioId: state.scenarioId, selectedSessionId: state.selectedSessionId, requestLedger: state.requestLedger, legacyRequestLedger: state.legacyRequestLedger, chatRequests: state.chatRequests, staleTraceId: state.staleTraceId, sessions: state.sessions.map((item) => ({ sessionId: item.sessionId, threadId: item.threadId, status: item.status, lastTraceId: item.lastTraceId, hidden: item.hidden === true })) };
}
@@ -1950,9 +1961,18 @@ function staticFile(response: ServerResponse, path: string): void {
const filePath = safeStaticPath(path);
const target = filePath && existsSync(filePath) && statSync(filePath).isFile() ? filePath : join(distDir, "index.html");
response.writeHead(200, { "content-type": contentType(target) });
if (extname(target) === ".html") {
response.end(injectRuntimeConfig(readFileSync(target, "utf8")));
return;
}
createReadStream(target).pipe(response);
}
function injectRuntimeConfig(html: string): string {
if (html.includes("HWLAB_CLOUD_WEB_CONFIG")) return html;
return html.replace("</head>", ` ${runtimeConfigScript}\n </head>`);
}
function safeStaticPath(path: string): string | null {
const normalized = resolve(distDir, `.${decodeURIComponent(path === "/" ? "/index.html" : path)}`);
return normalized === distDir || normalized.startsWith(`${distDir}${sep}`) ? normalized : null;