// SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-20-p2-terminal-outbox-recovery; PJ2026-01060505 Workbench Performance draft-2026-06-17-p0. // Responsibility: Cloud API AgentRun session/profile continuity regression tests. import assert from "node:assert/strict"; import { createServer as createHttpServer } from "node:http"; import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { test } from "bun:test"; import { createCloudApiServer } from "./server.ts"; import { createCloudRuntimeStore } from "../db/runtime-store.ts"; import { validateCodeAgentChatSchema } from "./code-agent-chat.ts"; import { createCodexStdioSessionManager } from "./codex-stdio-session.ts"; import { createCodeAgentTraceStore } from "./code-agent-trace-store.ts"; import { submitAgentRunChatTurn } from "./code-agent-agentrun-adapter.ts"; import { codexStdioChatFixture, codexStdioReadyFixture, createFakeAppServerClient, createFakeCodexCommand, createManualAgentSession, delay, ensureTestAgentAuth, pollAgentResult, postAgent, postAgentRaw, prepareFakeCodexHome } from "./server-test-helpers.ts"; const TEST_AGENT_ACTOR = { id: "usr_agent_owner", username: "agent-owner", displayName: "Agent Owner", role: "user", status: "active" }; const TEST_AUTH_SESSION = { id: "auth_ses_agent_owner" }; function testAgentSessionRecord(input = {}) { const sessionId = input.sessionId ?? input.id; return { id: sessionId, sessionId, projectId: input.projectId ?? "pikasTech/HWLAB", agentId: input.agentId ?? "hwlab-code-agent", status: input.status ?? "idle", ownerUserId: input.ownerUserId ?? TEST_AGENT_ACTOR.id, ownerRole: input.ownerRole ?? TEST_AGENT_ACTOR.role, conversationId: input.conversationId ?? null, threadId: input.threadId ?? null, lastTraceId: input.traceId ?? input.lastTraceId ?? null, session: input.session ?? {}, updatedAt: input.updatedAt ?? "2026-06-03T00:00:00.000Z" }; } test("cloud api AgentRun adapter keeps AgentRun sessions scoped to HWLAB session across backend profile switches", async () => { const calls = []; const hwlabSessionId = "ses_server-test-profile-switch"; const minimaxSessionId = "ses_agentrun_server_test_profile_switch"; const ownerSessions = new Map([[hwlabSessionId, testAgentSessionRecord({ sessionId: hwlabSessionId, conversationId: "cnv_server-test-profile-switch", threadId: "019e8078-db67-7750-a5d9-1a99f3abd445", traceId: "trc_existing_deepseek", status: "active", session: { agentRun: { adapter: "agentrun-v01", backendProfile: "deepseek", runId: "run_existing_deepseek", commandId: "cmd_existing_deepseek", jobName: "agentrun-v01-runner-existing-deepseek", namespace: "agentrun-v01", sessionId: "ses_agentrun_server_test_profile_switch", reuseEligible: true, managerUrl: "http://127.0.0.1:1", status: "runner-job-created" } } })]]); const agentRunServer = createHttpServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); const chunks = []; for await (const chunk of request) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); const body = chunks.length ? JSON.parse(Buffer.concat(chunks).toString("utf8")) : null; calls.push({ method: request.method, path: url.pathname, body }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_profile_switch" })}\n`); }; if (request.method === "GET" && url.pathname === "/api/v1/runs/run_existing_deepseek") { response.writeHead(500, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, failureKind: "unexpected-reuse", message: "deepseek run must not be reused for minimax-m3" })}\n`); return; } if (request.method === "POST" && url.pathname === "/api/v1/runs") { assert.equal(body.backendProfile, "minimax-m3"); assert.equal(body.sessionRef.sessionId, minimaxSessionId); assert.equal(body.sessionRef.threadId, undefined); assert.equal(body.sessionRef.metadata.hwlabSessionId, hwlabSessionId); assert.equal(body.sessionRef.metadata.agentRunSessionProfile, "minimax-m3"); return send({ id: "run_hwlab_profile_switch_m3", status: "pending", backendProfile: "minimax-m3", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef }); } if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_profile_switch_m3/commands") { assert.equal(body.payload.providerProfile, "minimax-m3"); assert.equal(body.payload.sessionId, minimaxSessionId); assert.equal(body.payload.hwlabSessionId, hwlabSessionId); assert.equal(body.payload.threadId, null); assert.equal(body.dispatch?.kind, "kubernetes-job"); assert.equal(body.dispatch?.input?.commandId, undefined); return send({ id: "cmd_hwlab_profile_switch_m3", runId: "run_hwlab_profile_switch_m3", state: "pending", type: "turn", seq: 1, dispatchIntent: { id: "dispatch_hwlab_profile_switch_m3", state: "pending", runnerJobId: "rjob_hwlab_profile_switch_m3", attemptCount: 0, durable: true } }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_profile_switch_m3/commands") { return send({ items: [ { id: "cmd_hwlab_profile_switch_m3", runId: "run_hwlab_profile_switch_m3", state: "completed", type: "turn", seq: 1, idempotencyKey: "trc_server-test-agentrun-profile-switch", payload: { traceId: "trc_server-test-agentrun-profile-switch", conversationId: "cnv_server-test-profile-switch", hwlabSessionId, threadId: "019e8078-db67-7750-a5d9-1a99f3abd445", providerProfile: "minimax-m3" } } ] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_profile_switch_m3/events") { return send({ items: [ { id: "evt_profile_switch_1", runId: "run_hwlab_profile_switch_m3", seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId: "cmd_hwlab_profile_switch_m3", attemptId: "attempt_hwlab_profile_switch_m3", jobName: "agentrun-v01-runner-profile-switch-m3", namespace: "agentrun-v01" }, createdAt: "2026-06-02T00:00:00.000Z" }, { id: "evt_profile_switch_2", runId: "run_hwlab_profile_switch_m3", seq: 2, type: "assistant_message", payload: { commandId: "cmd_hwlab_profile_switch_m3", text: "AGENTRUN_HWLAB_MINIMAX_M3_OK" }, createdAt: "2026-06-02T00:00:01.000Z" }, { id: "evt_profile_switch_3", runId: "run_hwlab_profile_switch_m3", seq: 3, type: "terminal_status", payload: { commandId: "cmd_hwlab_profile_switch_m3", terminalStatus: "completed" }, createdAt: "2026-06-02T00:00:02.000Z" } ] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_profile_switch_m3/commands/cmd_hwlab_profile_switch_m3/result") { return send({ runId: "run_hwlab_profile_switch_m3", commandId: "cmd_hwlab_profile_switch_m3", attemptId: "attempt_hwlab_profile_switch_m3", runnerId: "runner_hwlab_profile_switch_m3", jobName: "agentrun-v01-runner-profile-switch-m3", namespace: "agentrun-v01", status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, messageId: "msg_profile_switch_m3", reply: "AGENTRUN_HWLAB_MINIMAX_M3_OK", lastSeq: 3, eventCount: 3, sessionRef: { sessionId: minimaxSessionId, conversationId: "cnv_server-test-profile-switch", threadId: "019e8078-db67-7750-a5d9-1a99f3abd445" } }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, failureKind: "schema-invalid", message: `unexpected ${request.method} ${url.pathname}`, traceId: "trc_fake_profile_switch" })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; const server = createCloudApiServer({ env: { HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601", HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567", HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek", HWLAB_ENVIRONMENT: "v02", HWLAB_GITOPS_PROFILE: "v02" }, accessController: { required: false, async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; }, async recordAgentSessionOwner(input) { const record = testAgentSessionRecord(input); ownerSessions.set(record.id, record); return record; }, async getAgentSession(sessionId) { return ownerSessions.get(sessionId) ?? null; } } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const traceId = "trc_server-test-agentrun-profile-switch"; const submit = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, { method: "POST", headers: { "content-type": "application/json", "x-trace-id": traceId, cookie: "hwlab_session=test-stub-session" }, body: JSON.stringify({ conversationId: "cnv_server-test-profile-switch", sessionId: hwlabSessionId, ownerUserId: "usr_agent_owner", ownerRole: "user", threadId: "019e8078-db67-7750-a5d9-1a99f3abd445", providerProfile: "minimax-m3", message: "Switch this HWLAB session to MiniMax-M3 through AgentRun" }) }); assert.equal(submit.status, 202); const payload = await pollAgentResult(port, traceId); assert.equal(payload.status, "completed"); assert.equal(payload.sessionId, hwlabSessionId); assert.equal(payload.agentRun.backendProfile, "minimax-m3"); assert.equal(payload.agentRun.runId, "run_hwlab_profile_switch_m3"); assert.equal(payload.agentRun.sessionId, minimaxSessionId); assert.equal(calls.some((call) => call.method === "GET" && call.path === "/api/v1/runs/run_existing_deepseek"), false); assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1); const stored = ownerSessions.get(hwlabSessionId); assert.equal(stored.session.providerProfile, "minimax-m3"); assert.equal(stored.session.agentRun.backendProfile, "minimax-m3"); assert.equal(stored.session.agentRun.sessionId, minimaxSessionId); assert.equal(calls.some((call) => call.path.endsWith("/runner-jobs")), false); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api AgentRun adapter starts a fresh AgentRun session after a failed stale continuation when provider changes", async () => { const calls = []; const hwlabSessionId = "ses_server-test-stale-continuation-switch"; const conversationId = "cnv_server-test-stale-continuation-switch"; const staleThreadId = "thread_stale_bad_model"; const ownerSessions = new Map([[hwlabSessionId, testAgentSessionRecord({ sessionId: hwlabSessionId, conversationId, threadId: staleThreadId, traceId: "trc_stale_bad_model", status: "thread-resume-failed", session: { providerProfile: "codex-api", codeAgentProviderProfile: "codex-api", continuation: { requiresFreshSession: true, reasonCode: "provider-stream-disconnected", staleThreadId, traceId: "trc_stale_bad_model", valuesRedacted: true }, agentRun: { adapter: "agentrun-v01", backendProfile: "codex-api", runId: "run_stale_bad_model", commandId: "cmd_stale_bad_model", sessionId: "ses_agentrun_stale_bad_model", threadId: staleThreadId, terminalStatus: "failed", reuseEligible: false } } })]]); const agentRunServer = createHttpServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); const chunks = []; for await (const chunk of request) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); const body = chunks.length ? JSON.parse(Buffer.concat(chunks).toString("utf8")) : null; calls.push({ method: request.method, path: url.pathname, body }); const send = (data, status = 200) => { response.writeHead(status, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: status < 400, data, traceId: "trc_fake_stale_continuation_switch" })}\n`); }; if (request.method === "GET" && url.pathname === "/api/v1/runs/run_stale_bad_model") { return send({ message: "stale run must not be reused after provider switch" }, 500); } if (request.method === "POST" && url.pathname === "/api/v1/sessions") { assert.match(body.sessionId, /-reset-/u); assert.equal(body.backendProfile, "dsflash-go"); return send({ sessionId: body.sessionId, backendProfile: body.backendProfile }); } if (request.method === "POST" && url.pathname === "/api/v1/runs") { assert.equal(body.backendProfile, "dsflash-go"); assert.match(body.sessionRef?.sessionId, /-reset-/u); assert.equal(body.sessionRef?.threadId, undefined); assert.equal(body.sessionRef?.metadata?.hwlabSessionId, hwlabSessionId); assert.equal(body.sessionRef?.metadata?.agentRunSessionProfile, "dsflash-go"); return send({ id: "run_stale_recovery_dsflash", status: "pending", backendProfile: "dsflash-go", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef }); } if (request.method === "POST" && url.pathname === "/api/v1/runs/run_stale_recovery_dsflash/commands") { assert.equal(body.payload.providerProfile, "dsflash-go"); assert.match(body.payload.sessionId, /-reset-/u); assert.equal(body.payload.hwlabSessionId, hwlabSessionId); assert.equal(body.payload.threadId, null); assert.equal(body.dispatch?.kind, "kubernetes-job"); assert.equal(body.dispatch?.input?.commandId, undefined); return send({ id: "cmd_stale_recovery_dsflash", runId: "run_stale_recovery_dsflash", state: "pending", type: "turn", seq: 1, dispatchIntent: { id: "dispatch_stale_recovery_dsflash", state: "pending", runnerJobId: "rjob_stale_recovery_dsflash", attemptCount: 0, durable: true } }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_stale_recovery_dsflash/commands") { return send({ items: [ { id: "cmd_stale_recovery_dsflash", runId: "run_stale_recovery_dsflash", state: "completed", type: "turn", seq: 1, idempotencyKey: "trc_server-test-stale-continuation-switch", payload: { traceId: "trc_server-test-stale-continuation-switch", conversationId, hwlabSessionId, threadId: "thread_recovered_dsflash", providerProfile: "dsflash-go" } } ] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_stale_recovery_dsflash/events") { return send({ items: [ { id: "evt_stale_recovery_1", runId: "run_stale_recovery_dsflash", seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId: "cmd_stale_recovery_dsflash" }, createdAt: "2026-06-30T00:00:00.000Z" }, { id: "evt_stale_recovery_2", runId: "run_stale_recovery_dsflash", seq: 2, type: "assistant_message", payload: { commandId: "cmd_stale_recovery_dsflash", text: "AGENTRUN_STALE_RECOVERY_OK", final: true, replyAuthority: true }, createdAt: "2026-06-30T00:00:01.000Z" }, { id: "evt_stale_recovery_3", runId: "run_stale_recovery_dsflash", seq: 3, type: "terminal_status", payload: { commandId: "cmd_stale_recovery_dsflash", terminalStatus: "completed" }, createdAt: "2026-06-30T00:00:02.000Z" } ] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_stale_recovery_dsflash/commands/cmd_stale_recovery_dsflash/result") { return send({ runId: "run_stale_recovery_dsflash", commandId: "cmd_stale_recovery_dsflash", attemptId: "attempt_stale_recovery_dsflash", runnerId: "runner_stale_recovery_dsflash", jobName: "agentrun-v01-runner-stale-recovery-dsflash", namespace: "agentrun-v01", status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, reply: "AGENTRUN_STALE_RECOVERY_OK", lastSeq: 3, eventCount: 3, sessionRef: { sessionId: calls.find((call) => call.method === "POST" && call.path === "/api/v1/runs")?.body?.sessionRef?.sessionId, conversationId, threadId: "thread_recovered_dsflash" } }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, failureKind: "schema-invalid", message: `unexpected ${request.method} ${url.pathname}`, traceId: "trc_fake_stale_continuation_switch" })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; const server = createCloudApiServer({ traceStore: createCodeAgentTraceStore(), env: { HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "JD01", HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567", HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "codex-api", HWLAB_ENVIRONMENT: "v03", HWLAB_GITOPS_PROFILE: "v03" }, accessController: { required: false, async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; }, async recordAgentSessionOwner(input) { const previous = ownerSessions.get(input.sessionId) ?? null; const record = testAgentSessionRecord({ ...(previous ?? {}), ...input, threadId: input.threadId ?? previous?.threadId ?? null, session: { ...(previous?.session ?? {}), ...(input.session ?? {}) } }); ownerSessions.set(record.id, record); return record; }, async getAgentSession(sessionId) { return ownerSessions.get(sessionId) ?? null; } } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const traceId = "trc_server-test-stale-continuation-switch"; const submit = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, { method: "POST", headers: { "content-type": "application/json", "x-trace-id": traceId, cookie: "hwlab_session=test-stub-session" }, body: JSON.stringify({ conversationId, sessionId: hwlabSessionId, ownerUserId: "usr_agent_owner", ownerRole: "user", threadId: staleThreadId, providerProfile: "dsflash-go", message: "Continue after the first model failed" }) }); assert.equal(submit.status, 202); const payload = await pollAgentResult(port, traceId); assert.equal(payload.status, "completed"); assert.equal(payload.finalResponse?.text ?? payload.reply?.content, "AGENTRUN_STALE_RECOVERY_OK"); assert.equal(payload.agentRun.backendProfile, "dsflash-go"); assert.match(payload.agentRun.sessionId, /-reset-/u); assert.equal(calls.some((call) => call.method === "GET" && call.path === "/api/v1/runs/run_stale_bad_model"), false); assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1); const commandCall = calls.find((call) => call.method === "POST" && call.path === "/api/v1/runs/run_stale_recovery_dsflash/commands"); assert.equal(commandCall.body.payload.threadId, null); const stored = ownerSessions.get(hwlabSessionId); assert.equal(stored.status, "completed"); assert.equal(stored.threadId, "thread_recovered_dsflash"); assert.equal(stored.session.providerProfile, "dsflash-go"); assert.equal(stored.session.continuation.requiresFreshSession, false); assert.equal(calls.some((call) => call.path.endsWith("/runner-jobs")), false); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api AgentRun adapter inherits session provider profile when a recovered Workbench page omits it", async () => { const calls = []; const hwlabSessionId = "ses_server-test-profile-inherit"; const agentRunSessionId = "ses_agentrun_server_test_profile_inherit"; const conversationId = "cnv_server-test-profile-inherit"; const threadId = "019e8078-db67-7750-a5d9-1a99f3abd446"; const ownerSessions = new Map([[hwlabSessionId, testAgentSessionRecord({ sessionId: hwlabSessionId, conversationId, threadId, traceId: "trc_existing_dsflash_go", status: "active", session: { providerProfile: "dsflash-go", codeAgentProviderProfile: "dsflash-go", agentRun: { adapter: "agentrun-v01", backendProfile: "dsflash-go", runId: "run_existing_dsflash_go", commandId: "cmd_existing_dsflash_go", sessionId: agentRunSessionId, terminalStatus: "completed", reuseEligible: false } } })]]); const agentRunServer = createHttpServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); const chunks = []; for await (const chunk of request) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); const body = chunks.length ? JSON.parse(Buffer.concat(chunks).toString("utf8")) : null; calls.push({ method: request.method, path: url.pathname, body }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_profile_inherit" })}\n`); }; if (request.method === "POST" && url.pathname === "/api/v1/runs") { assert.equal(body.backendProfile, "dsflash-go"); assert.equal(body.sessionRef.sessionId, agentRunSessionId); assert.equal(body.sessionRef.metadata.hwlabSessionId, hwlabSessionId); assert.equal(body.sessionRef.metadata.agentRunSessionProfile, "dsflash-go"); return send({ id: "run_hwlab_profile_inherit", status: "pending", backendProfile: "dsflash-go", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef }); } if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_profile_inherit/commands") { assert.equal(body.payload.providerProfile, "dsflash-go"); assert.equal(body.payload.sessionId, agentRunSessionId); assert.equal(body.payload.hwlabSessionId, hwlabSessionId); assert.equal(body.payload.threadId, null); assert.equal(body.dispatch?.kind, "kubernetes-job"); assert.equal(body.dispatch?.input?.commandId, undefined); return send({ id: "cmd_hwlab_profile_inherit", runId: "run_hwlab_profile_inherit", state: "pending", type: "turn", seq: 1, dispatchIntent: { id: "dispatch_hwlab_profile_inherit", state: "pending", runnerJobId: "rjob_hwlab_profile_inherit", attemptCount: 0, durable: true } }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_profile_inherit/commands") { return send({ items: [ { id: "cmd_hwlab_profile_inherit", runId: "run_hwlab_profile_inherit", state: "completed", type: "turn", seq: 1, idempotencyKey: "trc_server-test-agentrun-profile-inherit", payload: { traceId: "trc_server-test-agentrun-profile-inherit", conversationId, hwlabSessionId, threadId, providerProfile: "dsflash-go" } } ] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_profile_inherit/events") { return send({ items: [ { id: "evt_profile_inherit_1", runId: "run_hwlab_profile_inherit", seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId: "cmd_hwlab_profile_inherit", attemptId: "attempt_hwlab_profile_inherit", jobName: "agentrun-v01-runner-profile-inherit", namespace: "agentrun-v01" }, createdAt: "2026-06-02T00:00:00.000Z" }, { id: "evt_profile_inherit_2", runId: "run_hwlab_profile_inherit", seq: 2, type: "assistant_message", payload: { commandId: "cmd_hwlab_profile_inherit", text: "AGENTRUN_DSFLASH_GO_OK" }, createdAt: "2026-06-02T00:00:01.000Z" }, { id: "evt_profile_inherit_3", runId: "run_hwlab_profile_inherit", seq: 3, type: "terminal_status", payload: { commandId: "cmd_hwlab_profile_inherit", terminalStatus: "completed" }, createdAt: "2026-06-02T00:00:02.000Z" } ] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_profile_inherit/commands/cmd_hwlab_profile_inherit/result") { return send({ runId: "run_hwlab_profile_inherit", commandId: "cmd_hwlab_profile_inherit", attemptId: "attempt_hwlab_profile_inherit", runnerId: "runner_hwlab_profile_inherit", jobName: "agentrun-v01-runner-profile-inherit", namespace: "agentrun-v01", status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, messageId: "msg_profile_inherit", reply: "AGENTRUN_DSFLASH_GO_OK", lastSeq: 3, eventCount: 3, sessionRef: { sessionId: agentRunSessionId, conversationId, threadId } }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, failureKind: "schema-invalid", message: `unexpected ${request.method} ${url.pathname}`, traceId: "trc_fake_profile_inherit" })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; const server = createCloudApiServer({ env: { HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601", HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567", HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "codex", HWLAB_ENVIRONMENT: "v02", HWLAB_GITOPS_PROFILE: "v02" }, accessController: { required: false, async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; }, async recordAgentSessionOwner(input) { const previous = ownerSessions.get(input.sessionId)?.session ?? {}; const record = testAgentSessionRecord({ ...input, session: { ...previous, ...(input.session ?? {}) } }); ownerSessions.set(record.id, record); return record; }, async getAgentSession(sessionId) { return ownerSessions.get(sessionId) ?? null; } } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const traceId = "trc_server-test-agentrun-profile-inherit"; const submit = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, { method: "POST", headers: { "content-type": "application/json", "x-trace-id": traceId, cookie: "hwlab_session=test-stub-session" }, body: JSON.stringify({ conversationId, sessionId: hwlabSessionId, ownerUserId: "usr_agent_owner", ownerRole: "user", message: "Continue with the Workbench session provider profile" }) }); assert.equal(submit.status, 202); const payload = await pollAgentResult(port, traceId); assert.equal(payload.status, "completed"); assert.equal(payload.agentRun.backendProfile, "dsflash-go"); assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1); const stored = ownerSessions.get(hwlabSessionId); assert.equal(stored.session.providerProfile, "dsflash-go"); assert.equal(stored.session.agentRun.backendProfile, "dsflash-go"); assert.equal(calls.some((call) => call.path.endsWith("/runner-jobs")), false); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api AgentRun adapter rejects non-internal manager URLs by default", async () => { const server = createCloudApiServer({ env: { HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601", AGENTRUN_MGR_URL: "http://74.48.78.17:8080" } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const manualSession = await createManualAgentSession(port, { conversationId: "cnv_server-test-agentrun-public-url" }); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, { method: "POST", headers: { "content-type": "application/json", "x-trace-id": "trc_server-test-agentrun-public-url", cookie: manualSession.cookie }, body: JSON.stringify({ conversationId: manualSession.conversationId, sessionId: manualSession.sessionId, message: "must reject public manager url" }) }); assert.equal(response.status, 202); const body = await response.json(); assert.equal(body.accepted, true); const result = await pollAgentResult(port, "trc_server-test-agentrun-public-url"); assert.equal(result.status, "failed"); assert.match(JSON.stringify(result), /internal k3s Service DNS/u); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api /v1/agent/chat records authenticated owner on agent session", async () => { const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-owner-")); const codexHome = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-owner-codex-home-")); const ownerRecords = []; const ownerSessions = new Map([["ses_server-test-owner", testAgentSessionRecord({ sessionId: "ses_server-test-owner", conversationId: "cnv_server-test-owner", status: "idle" })]]); const server = createCloudApiServer({ env: { PATH: process.env.PATH, CODEX_HOME: codexHome, HWLAB_CODE_AGENT_CODEX_WORKSPACE: workspace, HWLAB_CODE_AGENT_CODEX_SANDBOX: "danger-full-access", HWLAB_CODE_AGENT_PROVIDER: "codex-stdio", HWLAB_CODE_AGENT_MODEL: "gpt-test", OPENAI_API_KEY: "test-openai-key-material", HWLAB_CODE_AGENT_OPENAI_BASE_URL: "http://127.0.0.1:65535/v1/responses" }, accessController: { required: true, async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; }, async recordAgentSessionOwner(input) { const record = testAgentSessionRecord(input); ownerRecords.push(input); ownerSessions.set(record.id, record); return record; }, async getAgentSession(sessionId) { return ownerSessions.get(sessionId) ?? null; } }, codexStdioManager: { describe() { return { ...codexStdioReadyFixture({ workspace, codexHome }), sandbox: "danger-full-access" }; }, async probe() { return { ...codexStdioReadyFixture({ workspace, codexHome }), sandbox: "danger-full-access" }; }, async chat(params = {}) { return { ...codexStdioChatFixture({ workspace, codexHome, params }), sandbox: "danger-full-access" }; }, cancel() {}, reapIdle() {} } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const payload = await postAgent(port, { conversationId: "cnv_server-test-owner", sessionId: "ses_server-test-owner", traceId: "trc_server-test-owner", message: "owner binding smoke" }); validateCodeAgentChatSchema(payload); assert.equal(payload.ownerUserId, "usr_agent_owner"); assert.equal(ownerRecords.length, 2); assert.equal(ownerRecords[0].status, "running"); const terminalOwnerRecord = ownerRecords.at(-1); assert.equal(terminalOwnerRecord.ownerUserId, "usr_agent_owner"); assert.equal(terminalOwnerRecord.sessionId, "ses_server_stdio_pwd"); assert.equal(terminalOwnerRecord.conversationId, "cnv_server-test-owner"); assert.equal(terminalOwnerRecord.traceId, "trc_server-test-owner"); assert.equal(terminalOwnerRecord.status, "completed"); assert.equal(terminalOwnerRecord.session.messageCount, 2); assert.equal(terminalOwnerRecord.session.messages.length, 2); assert.equal(terminalOwnerRecord.session.messages[0].role, "user"); assert.equal(terminalOwnerRecord.session.messages[0].text, "owner binding smoke"); assert.equal(terminalOwnerRecord.session.messages[0].status, "sent"); assert.equal(terminalOwnerRecord.session.messages[1].role, "agent"); assert.equal(terminalOwnerRecord.session.messages[1].status, "completed"); assert.equal(terminalOwnerRecord.session.messages[1].traceId, "trc_server-test-owner"); assert.equal(terminalOwnerRecord.session.firstUserMessagePreview, "owner binding smoke"); assert.equal(terminalOwnerRecord.session.valuesRedacted, true); assert.equal(JSON.stringify(ownerRecords).includes("test-openai-key-material"), false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); await rm(workspace, { recursive: true, force: true }); await rm(codexHome, { recursive: true, force: true }); } }); test("cloud api /v1/agent/chat/inspect maps conversation session and thread details to trace evidence", async () => { const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-inspect-")); const codexHome = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-inspect-codex-home-")); const server = createCloudApiServer({ env: { PATH: process.env.PATH, CODEX_HOME: codexHome, HWLAB_CODE_AGENT_CODEX_WORKSPACE: workspace, HWLAB_CODE_AGENT_CODEX_SANDBOX: "danger-full-access", HWLAB_CODE_AGENT_PROVIDER: "codex-stdio", HWLAB_CODE_AGENT_MODEL: "gpt-test", OPENAI_API_KEY: "test-openai-key-material", HWLAB_CODE_AGENT_OPENAI_BASE_URL: "http://127.0.0.1:65535/v1/responses" }, codexStdioManager: { describe() { return { ...codexStdioReadyFixture({ workspace, codexHome }), sandbox: "danger-full-access", recentSessions: [{ sessionId: "ses_server_stdio_pwd", conversationId: "cnv_server-test-inspect", threadId: "019e6c72-2373-75e3-9856-eff286db7163", status: "idle", currentTraceId: null, lastTraceId: "trc_server-test-inspect", workspace, sandbox: "danger-full-access", secretMaterialStored: false, valuesRedacted: true }] }; }, async probe() { return { ...codexStdioReadyFixture({ workspace, codexHome }), sandbox: "danger-full-access" }; }, async chat(params = {}) { const base = codexStdioChatFixture({ workspace, codexHome, params }); return { ...base, sandbox: "danger-full-access", session: { ...base.session, threadId: "019e6c72-2373-75e3-9856-eff286db7163", sandbox: "danger-full-access" }, sessionReuse: { ...base.sessionReuse, threadId: "019e6c72-2373-75e3-9856-eff286db7163" } }; }, get(sessionId) { if (sessionId !== "ses_server_stdio_pwd") return null; return { sessionId, conversationId: "cnv_server-test-inspect", threadId: "019e6c72-2373-75e3-9856-eff286db7163", status: "idle", lastTraceId: "trc_server-test-inspect", workspace, sandbox: "danger-full-access", secretMaterialStored: false, valuesRedacted: true }; }, cancel() {}, reapIdle() {} } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const traceId = "trc_server-test-inspect"; const manualSession = await createManualAgentSession(port, { conversationId: "cnv_server-test-inspect" }); const submit = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, { method: "POST", headers: { "content-type": "application/json", cookie: manualSession.cookie, "x-trace-id": traceId, "prefer": "respond-async", "x-hwlab-short-connection": "1" }, body: JSON.stringify({ conversationId: manualSession.conversationId, sessionId: manualSession.sessionId, message: "inspect mapping smoke" }) }); assert.equal(submit.status, 202); const payload = await pollAgentResult(port, traceId); assert.equal(payload.status, "completed"); const inspect = await fetch(`http://127.0.0.1:${port}/v1/agent/chat/inspect?conversationId=cnv_server-test-inspect&sessionId=${manualSession.sessionId}&threadId=019e6c72-2373-75e3-9856-eff286db7163`); assert.equal(inspect.status, 200); const body = await inspect.json(); assert.equal(body.ok, true); assert.equal(body.status, "found"); assert.equal(body.latestTraceId, traceId); assert.equal(body.traceUrl, `/v1/agent/traces/${traceId}`); assert.equal(body.resultUrl, `/v1/agent/chat/result/${traceId}`); assert.equal(body.query.sessionId, manualSession.sessionId); assert.equal(body.query.threadId, "019e6c72-2373-75e3-9856-eff286db7163"); assert.equal(body.session.sessionId, "ses_server_stdio_pwd"); assert.equal(body.session.threadId, "019e6c72-2373-75e3-9856-eff286db7163"); assert.equal(body.conversationFacts.conversationId, "cnv_server-test-inspect"); assert.equal(body.runnerTrace.traceId, traceId); assert.equal(body.valuesRedacted, true); assert.equal(body.secretMaterialStored, false); assert.equal(JSON.stringify(body).includes("test-openai-key-material"), false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); await rm(workspace, { recursive: true, force: true }); await rm(codexHome, { recursive: true, force: true }); } }); test("cloud api /v1/agent/chat/inspect returns not_found for empty conversation facts", async () => { const server = createCloudApiServer(); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat/inspect?conversationId=cnv_server-test-inspect-missing&sessionId=ses_server_test_missing&threadId=019e6c72-2373-75e3-9856-eff286db7163&traceId=trc_server_test_missing`); assert.equal(response.status, 404); const body = await response.json(); assert.equal(body.ok, false); assert.equal(body.status, "not_found"); assert.equal(body.latestTraceId, null); assert.deepEqual(body.traceIds, []); assert.equal(body.traceUrl, null); assert.equal(body.resultUrl, null); assert.equal(body.conversationFacts.turnCount, 0); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api trace reports expired projection without AgentRun read-through when live trace store has expired (#1519)", async () => { const ownerSessions = new Map(); const agentRunCalls = []; const commandFinalText = "历史 trace 已过期,但 final response 来自 AgentRun command result。"; ownerSessions.set("ses_issue842_expired", testAgentSessionRecord({ sessionId: "ses_issue842_expired", projectId: "prj_v02_code_agent", conversationId: "cnv_issue842_expired", threadId: "thread-issue-842-expired", traceId: "trc_issue842_expired", status: "active", session: { finalResponse: { text: "旧 session snapshot final 不应作为历史 trace 权威。", textChars: 30, role: "assistant", status: "completed", traceId: "trc_issue842_expired", source: "code-agent-result", valuesPrinted: false }, traceSummary: { traceId: "trc_issue842_expired", source: "stale-derived-session-evidence", sourceEventCount: 26, terminalStatus: "completed", noiseEventCount: 4, omittedNoiseCount: 4, valuesPrinted: false }, agentRun: { adapter: "agentrun-v01", runId: "run_issue842_expired", commandId: "cmd_issue842_expired", runnerId: "runner_issue842_expired", jobName: "agentrun-v01-runner-issue842-expired", namespace: "agentrun-v01", terminalStatus: "completed", valuesPrinted: false }, valuesRedacted: true } })); const agentRunServer = createHttpServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); agentRunCalls.push({ method: request.method, path: url.pathname, search: url.search }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_issue842_expired" })}\n`); }; if (request.method === "GET" && url.pathname === "/api/v1/runs/run_issue842_expired/commands") { return send({ items: [ { id: "cmd_issue842_expired", runId: "run_issue842_expired", state: "completed", type: "turn", seq: 1, idempotencyKey: "trc_issue842_expired", payload: { traceId: "trc_issue842_expired", conversationId: "cnv_issue842_expired", hwlabSessionId: "ses_issue842_expired", threadId: "thread-issue-842-expired", providerProfile: "deepseek" } } ] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_issue842_expired/events") { return send({ items: [] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_issue842_expired/commands/cmd_issue842_expired/result") { return send({ runId: "run_issue842_expired", commandId: "cmd_issue842_expired", attemptId: "attempt_issue842_expired", runnerId: "runner_issue842_expired", jobName: "agentrun-v01-runner-issue842-expired", namespace: "agentrun-v01", status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, reply: commandFinalText, lastSeq: 26, eventCount: 26, sessionRef: { sessionId: "ses_agentrun_issue842_expired", conversationId: "cnv_issue842_expired", threadId: "thread-issue-842-expired" } }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, message: `unexpected ${request.method} ${url.pathname}` })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; const traceStore = createCodeAgentTraceStore(); const server = createCloudApiServer({ traceStore, env: { HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601", HWLAB_ENVIRONMENT: "v02", HWLAB_GITOPS_PROFILE: "v02" }, accessController: { required: false, async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; }, async getAgentSessionByTraceId(traceId) { return [...ownerSessions.values()].find((session) => session.lastTraceId === traceId) ?? null; } } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/traces/trc_issue842_expired`, { headers: { cookie: "hwlab_session=test-stub-session" } }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.ok, false); assert.equal(body.status, "missing"); assert.equal(body.traceStatus, "missing"); assert.equal(body.eventCount, 0); assert.equal(body.projectionStatus, "unknown"); assert.equal(body.retention.liveTraceStore, "missing"); assert.equal(JSON.stringify(body).includes("旧 session snapshot final"), false); assert.deepEqual(agentRunCalls, []); assert.equal(JSON.stringify(body).includes("password"), false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); await new Promise((resolve, reject) => { agentRunServer.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api trace does not replay an earlier AgentRun command from GET after same-run lastSeq advances (#1519)", async () => { const calls = []; const firstTraceId = "trc_issue955_first_trace"; const codeAgentChatResults = new Map(); const runId = "run_issue955_replay"; const firstCommandId = "cmd_issue955_first"; const secondCommandId = "cmd_issue955_second"; const firstFinalText = "目前只有一个 HWPOD 可用:\n\n- d601-f103-v2 (board: D601-F103-V2 / STM32F103)\n\nAPI 返回 count=1, availableCount=1。"; const firstEvents = [ { id: "evt_issue955_first_setup", runId, seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId: firstCommandId, jobName: "agentrun-v01-runner-issue955-first", namespace: "agentrun-v01" }, createdAt: "2026-06-06T01:43:25.000Z" }, { id: "evt_issue955_first_tool", runId, seq: 18, type: "tool_call", payload: { method: "item/completed", type: "commandExecution", toolName: "commandExecution", itemId: "call_issue955_hwpod_list", command: "/bin/sh -lc 'hwpod list --available'", status: "completed", exitCode: 0, outputSummary: '{"count":1,"availableCount":1,"ids":["d601-f103-v2"]}', commandId: firstCommandId }, createdAt: "2026-06-06T01:43:42.000Z" }, { id: "evt_issue955_first_assistant", runId, seq: 27, type: "assistant_message", payload: { commandId: firstCommandId, itemId: "msg_item_0", text: firstFinalText }, createdAt: "2026-06-06T01:43:55.000Z" }, { id: "evt_issue955_first_terminal", runId, seq: 35, type: "terminal_status", payload: { commandId: firstCommandId, terminalStatus: "completed" }, createdAt: "2026-06-06T01:43:56.000Z" } ]; const secondEvents = [ { id: "evt_issue955_second_setup", runId, seq: 47, type: "backend_status", payload: { phase: "runner-claim-waiting-for-stale-lease" }, createdAt: "2026-06-06T01:45:56.000Z" }, { id: "evt_issue955_second_assistant", runId, seq: 58, type: "assistant_message", payload: { commandId: secondCommandId, itemId: "msg_item_0", text: "编译成功!来总结一下结果。" }, createdAt: "2026-06-06T01:46:24.000Z" }, { id: "evt_issue955_second_terminal", runId, seq: 66, type: "terminal_status", payload: { commandId: secondCommandId, terminalStatus: "completed" }, createdAt: "2026-06-06T01:46:28.000Z" } ]; const agentRunServer = createHttpServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); calls.push({ method: request.method, path: url.pathname, afterSeq: url.searchParams.get("afterSeq") }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_issue955_replay" })}\n`); }; if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands`) { return send({ items: [ { id: firstCommandId, runId, state: "completed", idempotencyKey: firstTraceId, createdAt: "2026-06-06T01:43:30.040Z", updatedAt: "2026-06-06T01:43:56.000Z", payload: { traceId: firstTraceId, conversationId: "cnv_issue955_replay", hwlabSessionId: "ses_issue955_replay", threadId: "thread-issue955-replay", providerProfile: "deepseek" } }, { id: secondCommandId, runId, state: "completed", idempotencyKey: "trc_issue955_second_trace", createdAt: "2026-06-06T01:46:11.000Z", updatedAt: "2026-06-06T01:46:28.000Z", payload: { traceId: "trc_issue955_second_trace", conversationId: "cnv_issue955_replay", hwlabSessionId: "ses_issue955_replay", threadId: "thread-issue955-replay", providerProfile: "deepseek" } } ] }); } if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands/${firstCommandId}/result`) { return send({ runId, commandId: firstCommandId, status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, reply: firstFinalText, lastSeq: 35, eventCount: 35, sessionRef: { sessionId: "ses_agentrun_deepseek_issue955_replay", conversationId: "cnv_issue955_replay", threadId: "thread-issue955-replay" } }); } if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) { return send({ items: [...firstEvents, ...secondEvents] }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, message: `unexpected ${request.method} ${url.pathname}` })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; codeAgentChatResults.set(firstTraceId, { accepted: true, status: "completed", shortConnection: true, traceId: firstTraceId, conversationId: "cnv_issue955_replay", sessionId: "ses_issue955_replay", threadId: "thread-issue955-replay", ownerUserId: TEST_AGENT_ACTOR.id, ownerRole: TEST_AGENT_ACTOR.role, reply: { role: "assistant", content: firstFinalText }, finalResponse: { text: firstFinalText, textChars: firstFinalText.length, role: "assistant", status: "completed", traceId: firstTraceId, valuesPrinted: false }, traceSummary: { traceId: firstTraceId, source: "code-agent-derived-session-evidence", sourceEventCount: 41, terminalStatus: "completed", agentRun: { runId, commandId: firstCommandId, lastSeq: 35, valuesPrinted: false }, valuesPrinted: false }, agentRun: { adapter: "agentrun-v01", managerUrl: `http://127.0.0.1:${agentRunPort}`, runId, commandId: firstCommandId, lastSeq: 66, terminalStatus: "completed", valuesPrinted: false }, valuesPrinted: false }); const traceStore = createCodeAgentTraceStore(); const server = createCloudApiServer({ traceStore, codeAgentChatResults, env: { HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601", HWLAB_ENVIRONMENT: "v02", HWLAB_GITOPS_PROFILE: "v02" }, accessController: { required: false, async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; } } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/traces/${firstTraceId}`, { headers: { cookie: "hwlab_session=test-stub-session" } }); assert.equal(response.status, 200); const body = await response.json(); const text = JSON.stringify(body); assert.equal(body.traceId, firstTraceId); assert.equal(body.status, "expired"); assert.equal(body.projectionStatus, "caught-up"); assert.deepEqual(calls, []); assert.equal(body.eventCount, 0); assert.deepEqual(body.events, []); assert.equal(body.finalResponse.text, firstFinalText); assert.match(text, /d601-f103-v2/u); assert.doesNotMatch(text, /编译成功/u); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); await new Promise((resolve, reject) => { agentRunServer.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api running trace reports current projection without AgentRun event refresh (#1519)", async () => { const calls = []; const traceId = "trc_issue1000_running_trace"; const runId = "run_issue1000_running_trace"; const commandId = "cmd_issue1000_running_trace"; const codeAgentChatResults = new Map(); const events = [ { id: "evt_issue1000_created", runId, seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId, jobName: "agentrun-v01-runner-issue1000", namespace: "agentrun-v01" }, createdAt: "2026-06-06T08:34:30.000Z" }, { id: "evt_issue1000_turn", runId, seq: 2, type: "backend_status", payload: { phase: "turn/started", commandId }, createdAt: "2026-06-06T08:34:31.000Z" }, { id: "evt_issue1000_tool", runId, seq: 3, type: "tool_call", payload: { method: "item/completed", type: "commandExecution", toolName: "commandExecution", itemId: "call_issue1000_hwpod", command: "hwpod list --available", status: "completed", exitCode: 0, outputSummary: "available=1", commandId }, createdAt: "2026-06-06T08:34:32.000Z" }, { id: "evt_issue1000_output", runId, seq: 4, type: "assistant_message", payload: { commandId, itemId: "msg_issue1000_running", text: "正在查看 HWPOD。" }, createdAt: "2026-06-06T08:34:33.000Z" } ]; const agentRunServer = createHttpServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); calls.push({ method: request.method, path: url.pathname, search: url.search }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_issue1000_running" })}\n`); }; if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) return send({ items: events }); if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands/${commandId}/result`) { return send({ runId, commandId, status: "running", runStatus: "running", commandState: "running", terminalStatus: null, completed: false, reply: null, lastSeq: 3, eventCount: 3 }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, message: `unexpected ${request.method} ${url.pathname}` })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; codeAgentChatResults.set(traceId, { accepted: true, status: "running", shortConnection: true, traceId, conversationId: "cnv_issue1000_running_trace", sessionId: "ses_issue1000_running_trace", threadId: "thread-issue1000-running-trace", ownerUserId: TEST_AGENT_ACTOR.id, ownerRole: TEST_AGENT_ACTOR.role, agentRun: { adapter: "agentrun-v01", managerUrl: `http://127.0.0.1:${agentRunPort}`, runId, commandId, traceId, providerTrace: { traceId }, lastSeq: 0, status: "running", commandState: "running", valuesPrinted: false }, valuesPrinted: false }); const server = createCloudApiServer({ traceStore: createCodeAgentTraceStore(), codeAgentChatResults, env: { HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601", HWLAB_ENVIRONMENT: "v02", HWLAB_GITOPS_PROFILE: "v02" }, accessController: { required: false, async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; } } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/traces/${traceId}`, { headers: { cookie: "hwlab_session=test-stub-session" } }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.status, "missing"); assert.equal(body.projectionStatus, "projecting"); assert.equal(body.eventCount, 0); assert.deepEqual(body.events, []); assert.deepEqual(calls, []); assert.equal(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands/${commandId}/result`), false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); await new Promise((resolve, reject) => { agentRunServer.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api turn status does not backfill terminal AgentRun projection on GET (#1585)", async () => { const calls = []; const traceId = "trc_issue1422_turn_status_fast_path"; const runId = "run_issue1422_turn_status_fast_path"; const commandId = "cmd_issue1422_turn_status_fast_path"; const events = [ { id: "evt_issue1422_created", runId, seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId }, createdAt: "2026-06-18T04:30:00.000Z" }, { id: "evt_issue1422_terminal", runId, seq: 2, type: "terminal_status", payload: { commandId, terminalStatus: "completed" }, createdAt: "2026-06-18T04:30:01.000Z" } ]; const agentRunServer = createHttpServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); calls.push({ method: request.method, path: url.pathname, search: url.search }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_issue1422_turn" })}\n`); }; if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) return send({ items: events }); if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands/${commandId}/result`) return send({ runId, commandId, status: "completed", runStatus: "claimed", commandState: "completed", terminalStatus: "completed", completed: true, reply: { content: "issue 1555 read-side backfill completed" }, lastSeq: 2, eventCount: 2 }); response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, message: `unexpected ${request.method} ${url.pathname}` })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; const codeAgentChatResults = new Map([[traceId, { accepted: true, status: "running", shortConnection: true, traceId, conversationId: "cnv_issue1422_turn_status", sessionId: "ses_issue1422_turn_status", threadId: "thread-issue1422-turn-status", ownerUserId: TEST_AGENT_ACTOR.id, ownerRole: TEST_AGENT_ACTOR.role, agentRun: { adapter: "agentrun-v01", managerUrl: `http://127.0.0.1:${agentRunPort}`, runId, commandId, traceId, providerTrace: { traceId }, lastSeq: 0, status: "running", commandState: "running", valuesPrinted: false }, valuesPrinted: false }]]); const server = createCloudApiServer({ traceStore: createCodeAgentTraceStore(), codeAgentChatResults, env: { HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601", HWLAB_ENVIRONMENT: "v02", HWLAB_GITOPS_PROFILE: "v02" }, accessController: { required: false, async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; } } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/turns/${traceId}`, { headers: { cookie: "hwlab_session=test-stub-session" } }); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.status, "running"); assert.equal(body.terminal, false); assert.equal(body.projectionStatus, "projecting"); assert.equal(body.agentRun.commandState, "running"); assert.equal(body.agentRun.terminalStatus ?? null, null); assert.equal(body.finalResponse, null); assert.deepEqual(calls, []); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); await new Promise((resolve, reject) => { agentRunServer.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api turn status reuses request session lookup without loading persisted AgentRun result (#1519)", async () => { const calls = []; const traceId = "trc_issue1422_turn_status_session_cache"; const runId = "run_issue1422_turn_status_session_cache"; const commandId = "cmd_issue1422_turn_status_session_cache"; const agentRunServer = createHttpServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); calls.push({ method: request.method, path: url.pathname, search: url.search }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_issue1422_session_cache" })}\n`); }; if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) { return send({ items: [ { id: "evt_issue1422_session_cache_running", runId, seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId }, createdAt: "2026-06-18T05:20:00.000Z" } ] }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, message: `unexpected ${request.method} ${url.pathname}` })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; let sessionLookupCount = 0; const session = testAgentSessionRecord({ sessionId: "ses_issue1422_turn_status_session_cache", projectId: "prj_hwpod_workbench", conversationId: "cnv_issue1422_turn_status_session_cache", threadId: "thread-issue1422-turn-status-session-cache", lastTraceId: traceId, status: "running", session: { agentRun: { adapter: "agentrun-v01", managerUrl: `http://127.0.0.1:${agentRunPort}`, runId, commandId, traceId, providerTrace: { traceId, valuesPrinted: false }, lastSeq: 0, status: "running", commandState: "running", backendProfile: "deepseek", valuesPrinted: false }, valuesRedacted: true } }); const server = createCloudApiServer({ traceStore: createCodeAgentTraceStore(), codeAgentChatResults: new Map(), env: { HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601", HWLAB_ENVIRONMENT: "v02", HWLAB_GITOPS_PROFILE: "v02" }, accessController: { required: false, async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; }, async getAgentSessionByTraceId(requestTraceId) { sessionLookupCount += 1; return requestTraceId === traceId ? session : null; } } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/agent/turns/${traceId}?projectId=prj_hwpod_workbench`, { headers: { cookie: "hwlab_session=test-stub-session" } }); assert.equal(response.status, 404); const body = await response.json(); assert.equal(body.status, "unknown"); assert.equal(body.projectionStatus, "unknown"); assert.equal(sessionLookupCount, 1); assert.equal(calls.filter((call) => call.path === `/api/v1/runs/${runId}/events`).length, 0); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); await new Promise((resolve, reject) => { agentRunServer.close((error) => (error ? reject(error) : resolve())); }); } });