fix: stream cloud web trace SSE

This commit is contained in:
Code Queue Review
2026-05-24 10:19:04 +00:00
parent f9e3bf8252
commit ee5ad7530a
7 changed files with 420 additions and 68 deletions
+82 -3
View File
@@ -4383,6 +4383,9 @@ async function inspectLegacyFailureWindow(page, { beforeMessageCount }) {
contained: panel.scrollWidth <= panel.clientWidth + 2
}))
: [];
const pendingTraceEvents = latestAgent
? [...latestAgent.querySelectorAll(".message-trace-events li")].map((item) => item.textContent?.replace(/\s+/gu, " ").trim() ?? "")
: [];
const latestAgentBox = latestAgent?.getBoundingClientRect();
const pendingStillRunning =
latestAgent?.classList.contains("status-running") === true ||
@@ -4398,6 +4401,7 @@ async function inspectLegacyFailureWindow(page, { beforeMessageCount }) {
runningMessageVisible: Boolean(latestAgent?.classList.contains("status-running")),
pendingChineseVisible: /正在处理|后端处理中|不会把旧 4500ms|结构化 blocker|真实超时/u.test(pendingText),
pendingTraceVisible: /traceId\s*trc_|trace=trc_|trc_/u.test(pendingText),
pendingTraceShowsEvents: pendingTraceEvents.some((text) => /#\d+|session|prompt|tool|assistant/u.test(text)),
pendingConversationVisible: /conversation\s*cnv_|conversation=cnv_|cnv_/u.test(pendingText),
pendingSessionVisible: /session\s*(等待后端分配|cnv_|ses_)/u.test(pendingText),
pendingTraceCopyVisible: pendingRows.some((row) => /traceId/u.test(row.text) && /复制/u.test(row.text)),
@@ -4789,6 +4793,7 @@ async function runLocalAgentFixtureSmoke({
promptResults[0]?.classification?.blocker === "runner-blocked" &&
promptResults[0]?.legacyWindow?.permanentFailureAround4500ms === false &&
promptResults[0]?.legacyWindow?.pendingChineseVisible === true &&
promptResults[0]?.legacyWindow?.pendingTraceShowsEvents === true &&
(agentFixtureMode === "external-network-blocker"
? ["能力未开放", "Runner 受阻", "服务受阻"].includes(ui.agentChatStatus)
: ui.agentChatStatus === "Runner 受阻") &&
@@ -4811,6 +4816,7 @@ async function runLocalAgentFixtureSmoke({
result.legacyWindow?.runningMessageVisible === true &&
result.legacyWindow?.pendingChineseVisible === true &&
result.legacyWindow?.pendingTraceVisible === true &&
result.legacyWindow?.pendingTraceShowsEvents === true &&
result.legacyWindow?.pendingConversationVisible === true &&
result.legacyWindow?.pendingSessionVisible === true &&
result.legacyWindow?.pendingTraceCopyVisible === true &&
@@ -5017,6 +5023,7 @@ async function inspectLocalAgentPendingViewport(browser, url, { width, height, r
legacyWindow.runningMessageVisible === true &&
legacyWindow.pendingChineseVisible === true &&
legacyWindow.pendingTraceVisible === true &&
legacyWindow.pendingTraceShowsEvents === true &&
legacyWindow.pendingConversationVisible === true &&
legacyWindow.pendingSessionVisible === true &&
legacyWindow.pendingTraceCopyVisible === true &&
@@ -5543,6 +5550,7 @@ async function inspectQuickPromptGeometry(page, viewport) {
async function startStaticWebServer(options = {}) {
const rootDir = path.resolve(options.rootDir ?? webRoot);
const authFixtureSessions = new Set();
const agentTraceStore = new Map();
const server = http.createServer(async (request, response) => {
const url = new URL(request.url ?? "/", "http://127.0.0.1");
if (options.authFixture && await handleAuthFixtureApi({ request, response, url, authFixtureSessions })) {
@@ -5554,7 +5562,7 @@ async function startStaticWebServer(options = {}) {
if (options.quickPromptsFixture && await handleQuickPromptsFixtureApi({ request, response, url })) {
return;
}
if (options.agentFixture && await handleLocalAgentFixtureApi({ request, response, url, options })) {
if (options.agentFixture && await handleLocalAgentFixtureApi({ request, response, url, options, agentTraceStore })) {
return;
}
if (options.liveBuildsFixture && request.method === "GET" && url.pathname === "/v1/live-builds") {
@@ -5591,6 +5599,54 @@ async function startStaticWebServer(options = {}) {
};
}
function startLocalAgentTrace(agentTraceStore, { traceId, conversationId, delayMs }) {
if (!agentTraceStore || agentTraceStore.has(traceId)) return;
const startedAt = Date.now();
const events = [];
const append = (event) => {
const normalizedEvent = {
traceId,
...event
};
events.push(normalizedEvent);
agentTraceStore.set(traceId, localAgentTraceSnapshot({
traceId,
conversationId,
startedAt,
events,
lastEvent: normalizedEvent
}));
};
append({ seq: 1, type: "session", stage: "created", status: "completed", label: "session:created" });
const scheduledEvents = [
[250, { seq: 2, type: "prompt", stage: "sent", status: "completed", label: "prompt:sent" }],
[900, { seq: 3, type: "runner", stage: "started", status: "running", label: "runner:started" }],
[1800, { seq: 4, type: "tool_call", stage: "tool_call", status: "running", label: "tool:fixture:running", toolName: "local.source.fixture" }],
[Math.min(Math.max(delayMs - 1200, 2400), 3400), { seq: 5, type: "assistant", stage: "waiting", status: "running", label: "assistant:waiting" }]
];
for (const [waitMs, event] of scheduledEvents) {
const timer = setTimeout(() => append(event), waitMs);
timer.unref?.();
}
}
function localAgentTraceSnapshot({ traceId, conversationId = null, startedAt = Date.now(), events = [], lastEvent = null }) {
return {
traceId,
runnerKind: "codex-mcp-stdio-runner",
sessionMode: "codex-mcp-stdio-long-lived",
sessionId: conversationId,
sessionStatus: events.length > 0 ? "running" : "pending",
status: events.length > 0 ? "running" : "pending",
elapsedMs: Math.max(0, Date.now() - startedAt),
waitingFor: events.length > 0 ? "fixture-response" : "fixture-trace",
events: [...events],
lastEvent,
updatedAt: new Date().toISOString()
};
}
async function handleAuthFixtureApi({ request, response, url, authFixtureSessions }) {
if (url.pathname === "/auth/session" && request.method === "GET") {
const token = authFixtureCookie(request);
@@ -6100,7 +6156,25 @@ function liveBuildsFixturePayload() {
};
}
async function handleLocalAgentFixtureApi({ request, response, url, options = {} }) {
async function handleLocalAgentFixtureApi({ request, response, url, options = {}, agentTraceStore = null }) {
if (request.method === "GET" && url.pathname.startsWith("/v1/agent/chat/trace/")) {
const parts = url.pathname.split("/").filter(Boolean);
const traceId = decodeURIComponent(parts[4] ?? "");
if (parts[5] === "stream") {
jsonResponse(response, 503, {
status: "failed",
error: {
code: "trace_stream_unavailable_for_fixture",
message: "fixture intentionally forces EventSource fallback to polling"
},
traceId
});
return true;
}
jsonResponse(response, 200, agentTraceStore?.get(traceId) ?? localAgentTraceSnapshot({ traceId }));
return true;
}
if (request.method === "GET" && (url.pathname === "/health/live" || url.pathname === "/v1")) {
jsonResponse(response, 200, {
serviceId: "hwlab-cloud-api",
@@ -6234,10 +6308,15 @@ async function handleLocalAgentFixtureApi({ request, response, url, options = {}
}
if (request.method === "POST" && url.pathname === "/v1/agent/chat") {
const body = await readJsonBody(request);
await delay(options.agentDelayMs ?? 0);
const timestamp = "2026-05-22T00:00:00.000Z";
const conversationId = stringOrFallback(body?.conversationId, "cnv_source_fixture_browser");
const traceId = stringOrFallback(body?.traceId, "trc_source_fixture_browser");
startLocalAgentTrace(agentTraceStore, {
traceId,
conversationId,
delayMs: options.agentDelayMs ?? 0
});
await delay(options.agentDelayMs ?? 0);
const normalizedMessage = String(body?.message ?? "");
const isSkillListPrompt = /列出你能使用的所有skill/u.test(normalizedMessage);
const isExternalNetworkPrompt = /github|https?:\/\/|外网|公网|访问/u.test(normalizedMessage);