Files
pikasTech-HWLAB/internal/cloud/server-agent-chat.test.ts
T
2026-06-29 16:50:25 +00:00

4630 lines
222 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 adapter and trace observability 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, syncAgentRunChatResult } 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("manual Code Agent session create writes an empty Workbench session fact", async () => {
const runtimeStore = createCloudRuntimeStore({ now: () => "2026-06-20T08:45:00.000Z" });
const ownerSessions = new Map();
const accessController = {
required: true,
async ensureBootstrap() {},
async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; },
async recordAgentSessionOwner(input = {}) {
const record = testAgentSessionRecord({ ...input, id: input.sessionId, sessionId: input.sessionId, updatedAt: input.now ?? "2026-06-20T08:45:00.000Z" });
ownerSessions.set(record.id, record);
return record;
},
async getAgentSession(sessionId) { return ownerSessions.get(sessionId) ?? null; },
store: {
async listAgentSessionsForUser() { return [...ownerSessions.values()]; },
async getAgentSession(sessionId) { return ownerSessions.get(sessionId) ?? null; }
}
};
const server = createCloudApiServer({ accessController, runtimeStore });
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const create = await fetch(`http://127.0.0.1:${port}/v1/agent/sessions`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ providerProfile: "codex-api" })
});
assert.equal(create.status, 201);
const created = await create.json();
const sessionId = created.session.sessionId;
assert.match(sessionId, /^ses_/u);
const list = await fetch(`http://127.0.0.1:${port}/v1/workbench/sessions?includeSessionId=${encodeURIComponent(sessionId)}&limit=1`);
assert.equal(list.status, 200);
const listBody = await list.json();
assert.equal(listBody.sessions[0].sessionId, sessionId);
assert.equal(listBody.sessions[0].status, "idle");
assert.equal(listBody.sessions[0].running, false);
const detail = await fetch(`http://127.0.0.1:${port}/v1/workbench/sessions/${encodeURIComponent(sessionId)}`);
assert.equal(detail.status, 200);
const detailBody = await detail.json();
assert.equal(detailBody.session.sessionId, sessionId);
assert.equal(detailBody.session.status, "idle");
assert.equal(detailBody.session.messagePageUrl, `/v1/workbench/sessions/${encodeURIComponent(sessionId)}/messages`);
const messages = await fetch(`http://127.0.0.1:${port}/v1/workbench/sessions/${encodeURIComponent(sessionId)}/messages?limit=10`);
assert.equal(messages.status, 200);
const messageBody = await messages.json();
assert.equal(messageBody.sessionId, sessionId);
assert.equal(messageBody.count, 0);
assert.deepEqual(messageBody.messages, []);
} finally {
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
}
});
test("cloud api trace resource paginates events by sinceSeq", async () => {
const traceStore = createCodeAgentTraceStore();
const traceId = "trc_trace_pagination";
for (let index = 0; index < 5; index += 1) {
traceStore.append(traceId, { type: "trace", status: "observed", label: `event:${index}` });
}
const server = createCloudApiServer({
traceStore,
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 firstPage = await fetch(`http://127.0.0.1:${port}/v1/agent/traces/${traceId}?limit=2`);
assert.equal(firstPage.status, 200);
const firstBody = await firstPage.json();
assert.deepEqual(firstBody.events.map((event) => event.label), ["event:0", "event:1"]);
assert.equal(firstBody.eventCount, 5);
assert.equal(firstBody.hasMore, true);
assert.equal(firstBody.truncated, true);
assert.equal(firstBody.fullTraceLoaded, false);
assert.equal(firstBody.range.sinceSeq, 0);
assert.equal(firstBody.range.returned, 2);
const secondPage = await fetch(`http://127.0.0.1:${port}/v1/agent/traces/${traceId}?sinceSeq=${firstBody.nextSinceSeq}&limit=2`);
assert.equal(secondPage.status, 200);
const secondBody = await secondPage.json();
assert.deepEqual(secondBody.events.map((event) => event.label), ["event:2", "event:3"]);
assert.equal(secondBody.range.sinceSeq, firstBody.nextSinceSeq);
assert.equal(secondBody.hasMore, true);
const finalPage = await fetch(`http://127.0.0.1:${port}/v1/agent/traces/${traceId}?sinceSeq=${secondBody.nextSinceSeq}&limit=2`);
assert.equal(finalPage.status, 200);
const finalBody = await finalPage.json();
assert.deepEqual(finalBody.events.map((event) => event.label), ["event:4"]);
assert.equal(finalBody.hasMore, false);
assert.equal(finalBody.truncated, false);
assert.equal(finalBody.fullTraceLoaded, true);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api turn and trace endpoints reject mismatched requested project", async () => {
const traceStore = createCodeAgentTraceStore();
const traceId = "trc_issue1429_project_scope";
traceStore.append(traceId, { type: "trace", status: "completed", label: "turn:completed", terminal: true });
const server = createCloudApiServer({
traceStore,
accessController: {
required: false,
async authenticate() {
return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION };
},
async getAgentSessionByTraceId(requestedTraceId) {
assert.equal(requestedTraceId, traceId);
return testAgentSessionRecord({ sessionId: "ses_issue1429_project_scope", projectId: "prj_v02_code_agent", traceId, status: "completed" });
}
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const wrongTurn = await fetch(`http://127.0.0.1:${port}/v1/agent/turns/${traceId}?projectId=prj_hwpod_workbench`);
assert.equal(wrongTurn.status, 404);
assert.equal((await wrongTurn.json()).error.code, "trace_project_mismatch");
const wrongTrace = await fetch(`http://127.0.0.1:${port}/v1/agent/traces/${traceId}?projectId=prj_hwpod_workbench`);
assert.equal(wrongTrace.status, 404);
assert.equal((await wrongTrace.json()).error.code, "trace_project_mismatch");
const rightTrace = await fetch(`http://127.0.0.1:${port}/v1/agent/traces/${traceId}?projectId=prj_v02_code_agent`);
assert.equal(rightTrace.status, 200);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("AgentRun adapter filters resource tools and credentials through access capabilities", async () => {
const calls = [];
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_access_tools" })}\n`);
};
if (request.method === "POST" && url.pathname === "/api/v1/sessions") return send({ ok: true });
if (request.method === "POST" && url.pathname === "/api/v1/runs") return send({ id: "run_access_tools", status: "pending", backendProfile: "deepseek", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef });
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_access_tools/commands") return send({ id: "cmd_access_tools", runId: "run_access_tools", state: "pending", type: "turn", seq: 1 });
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_access_tools/runner-jobs") return send({
action: "create-kubernetes-job",
runId: "run_access_tools",
commandId: body.commandId,
attemptId: "attempt_access_tools",
runnerId: "runner_access_tools",
namespace: "agentrun-v01",
jobName: "agentrun-v01-runner-access-tools"
});
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));
try {
const { port } = agentRunServer.address();
const traceStore = createCodeAgentTraceStore();
const result = await submitAgentRunChatTurn({
traceId: "trc_access_tool_filter",
traceStore,
params: {
message: "tool filter smoke",
ownerUserId: "usr_tool_limited",
projectId: "prj_hwpod_workbench",
conversationId: "cnv_access_tools",
sessionId: "ses_access_tools"
},
options: {
env: {
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
AGENTRUN_MGR_URL: `http://127.0.0.1:${port}`,
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_RUNTIME_API_URL: "http://hwlab-cloud-api.hwlab-v02.svc.cluster.local:6667",
HWLAB_RUNTIME_WEB_URL: "http://hwlab-cloud-web.hwlab-v02.svc.cluster.local:8080",
HWLAB_RUNTIME_NAMESPACE: "hwlab-v02",
HWLAB_RUNTIME_LANE: "v02",
HWLAB_RUNTIME_ENDPOINT_LOCKED: "1",
HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME: "1",
UNIDESK_MAIN_SERVER_IP: "http://74.48.78.17:18081"
},
accessController: {
async codeAgentToolCapabilitiesForOwner(ownerUserId) {
assert.equal(ownerUserId, "usr_tool_limited");
return {
contractVersion: "admin-access-v1",
tools: {
hwpod: { allowed: false },
unidesk_ssh: { allowed: false },
trans_cmd: { allowed: false },
github_pr: { allowed: false }
}
};
},
store: {
async findActiveDefaultApiKeyForUser(userId) {
assert.equal(userId, "usr_tool_limited");
return { displaySecret: "hwl_live_owner_secret" };
}
}
}
}
});
assert.equal(result.status, "running");
const createRun = calls.find((call) => call.method === "POST" && call.path === "/api/v1/runs");
assert.equal(createRun.body.resourceBundleRef.kind, "gitbundle");
assert.deepEqual(createRun.body.resourceBundleRef.bundles, [
{ name: "hwlab-tools", subpath: "tools", target_path: "tools" },
{ name: "hwlab-agent-skills", subpath: "skills", target_path: ".agents/skills" }
]);
assert.equal(Object.hasOwn(createRun.body.resourceBundleRef, "toolAliases"), false);
assert.equal(Object.hasOwn(createRun.body.resourceBundleRef, "skillRefs"), false);
assert.equal(Object.hasOwn(createRun.body.resourceBundleRef, "workspaceFiles"), false);
assert.deepEqual(createRun.body.executionPolicy.secretScope.toolCredentials, []);
const runnerJob = calls.find((call) => call.method === "POST" && call.path === "/api/v1/runs/run_access_tools/runner-jobs");
assert.equal(runnerJob.body.transientEnv.some((entry) => entry.name === "HWLAB_API_KEY"), false);
} finally {
await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve())));
}
});
test("cloud api /v1/agent/chat parse and params errors use structured blocker envelope", async () => {
const server = createCloudApiServer({
env: {
HWLAB_CODE_AGENT_MODEL: "gpt-test"
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const parse = await postAgentRaw(port, "{", { "x-trace-id": "trc_parse_error" });
assert.equal(parse.status, 400);
assert.equal(parse.body.status, "failed");
assert.equal(parse.body.error.code, "parse_error");
assert.equal(parse.body.error.layer, "api");
assert.equal(parse.body.error.retryable, true);
assert.equal(parse.body.error.traceId, "trc_parse_error");
assert.equal(parse.body.error.route, "/v1/agent/chat");
assert.match(parse.body.error.userMessage, /JSON/u);
assert.equal(Object.hasOwn(parse.body, "reply"), false);
assert.equal(JSON.stringify(parse.body).includes("sk-"), false);
const invalid = await postAgentRaw(port, JSON.stringify([]), { "x-trace-id": "trc_invalid_params" });
assert.equal(invalid.status, 400);
assert.equal(invalid.body.error.code, "invalid_params");
assert.equal(invalid.body.error.layer, "api");
assert.equal(invalid.body.error.retryable, true);
assert.equal(invalid.body.error.blocker.code, "invalid_params");
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api /v1/agent/chat supports short submit and result polling", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-short-"));
const codexHome = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-short-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"
};
},
async probe() {
return {
...codexStdioReadyFixture({ workspace, codexHome }),
sandbox: "danger-full-access"
};
},
async chat(params = {}) {
return {
...codexStdioChatFixture({ workspace, codexHome, params }),
sandbox: "danger-full-access",
session: {
...codexStdioChatFixture({ workspace, codexHome, params }).session,
sandbox: "danger-full-access"
}
};
},
cancel() {},
reapIdle() {}
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const traceId = "trc_server-test-short-submit";
const manualSession = await createManualAgentSession(port, {
conversationId: "cnv_server-test-short-submit"
});
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: "用pwd列出你当前的工作目录"
})
});
assert.equal(submit.status, 202);
const accepted = await submit.json();
assert.equal(accepted.accepted, true);
assert.equal(accepted.shortConnection, true);
assert.equal(accepted.traceId, traceId);
assert.equal(accepted.turnId, traceId);
assert.equal(accepted.userMessageId, "msg_server-test-short-submit_user");
assert.equal(accepted.assistantMessageId, "msg_server-test-short-submit_agent");
assert.equal(accepted.resultUrl, `/v1/agent/chat/result/${traceId}`);
const acceptedTurn = await fetch(`http://127.0.0.1:${port}/v1/agent/turns/${encodeURIComponent(traceId)}`, { headers: { cookie: manualSession.cookie } });
assert.equal(acceptedTurn.status, 200);
const acceptedTurnPayload = await acceptedTurn.json();
assert.equal(acceptedTurnPayload.turnId, traceId);
assert.equal(acceptedTurnPayload.userMessageId, accepted.userMessageId);
assert.equal(acceptedTurnPayload.assistantMessageId, accepted.assistantMessageId);
const payload = await pollAgentResult(port, traceId);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.traceId, traceId);
assert.equal(payload.turnId, traceId);
assert.equal(payload.userMessageId, accepted.userMessageId);
assert.equal(payload.assistantMessageId, accepted.assistantMessageId);
assert.equal(payload.sandbox, "danger-full-access");
assert.match(payload.reply.content, new RegExp(workspace.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")));
} 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 delegates v0.3 turns to AgentRun v0.1 over adapter", async () => {
const calls = [];
const ownerSessions = new Map();
const conversationMessages = [];
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, search: url.search, body });
const send = (data) => {
response.writeHead(200, { "content-type": "application/json" });
response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_agentrun" })}\n`);
};
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_adapter") {
return send({
id: "run_hwlab_adapter",
status: "claimed",
terminalStatus: null,
claimedBy: "runner_hwlab_adapter",
leaseExpiresAt: "2999-01-01T00:00:00.000Z",
backendProfile: "deepseek",
sessionRef: {
sessionId: "ses_agentrun_server_test_agentrun",
conversationId: "cnv_server-test-agentrun",
threadId: "019e8078-db67-7750-a5d9-1a99f3abd445",
metadata: {}
}
});
}
if (request.method === "POST" && url.pathname === "/api/v1/runs") {
assert.equal(body.tenantId, "hwlab");
assert.equal(body.projectId, "pikasTech/HWLAB");
assert.equal(body.backendProfile, "deepseek");
assert.equal(body.workspaceRef.branch, "v0.3");
assert.equal(body.workspaceRef.runtimeNamespace, "hwlab-v03");
assert.equal(body.resourceBundleRef.repoUrl, "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");
assert.equal(body.resourceBundleRef.commitId, "0123456789abcdef0123456789abcdef01234567");
assert.equal(body.resourceBundleRef.kind, "gitbundle");
assert.deepEqual(body.resourceBundleRef.bundles, [
{ name: "hwlab-tools", subpath: "tools", target_path: "tools" },
{ name: "hwlab-agent-skills", subpath: "skills", target_path: ".agents/skills" }
]);
assert.deepEqual(body.resourceBundleRef.promptRefs, [
{ name: "hwlab-v02-runtime", path: "internal/agent/prompts/hwlab-v02-runtime.md", inject: "thread-start", required: true }
]);
assert.equal(Object.hasOwn(body.resourceBundleRef, "toolAliases"), false);
assert.equal(Object.hasOwn(body.resourceBundleRef, "skillRefs"), false);
assert.equal(Object.hasOwn(body.resourceBundleRef, "workspaceFiles"), false);
const toolCredentials = body.executionPolicy.secretScope.toolCredentials;
assert.equal(toolCredentials.some((item) => item.tool === "github" && item.projection.envName === "GH_TOKEN" && item.secretRef.name === "agentrun-v01-tool-github-pr"), true);
assert.equal(toolCredentials.some((item) => item.tool === "unidesk-ssh" && item.projection.envName === "UNIDESK_SSH_CLIENT_TOKEN" && item.secretRef.name === "agentrun-v01-tool-unidesk-ssh"), true);
assert.equal(body.sessionRef.sessionId, "ses_agentrun_server_test_agentrun");
assert.equal(body.sessionRef.metadata.hwlabProjectId, "prj_hwpod_workbench");
assert.equal(body.sessionRef.metadata.hwlabSessionId, "ses_server-test-agentrun");
assert.equal(body.sessionRef.metadata.agentRunSessionProfile, "deepseek");
assert.equal(body.sessionRef.metadata.agentRunSessionPolicy, "hwlab-session-scoped");
assert.equal(body.sessionRef.metadata.threadContinuityPolicy, "hwlab-agentrun-v01-reuse-runner-thread");
assert.equal(body.sessionRef.metadata.sessionPolicy, "hwlab-agentrun-v01-session-runner-reuse");
return send({ id: "run_hwlab_adapter", status: "pending", backendProfile: "deepseek", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_adapter/commands") {
if (body.type === "steer") {
assert.equal(body.payload.targetTraceId, "trc_server-test-agentrun-adapter");
assert.equal(body.payload.targetCommandId, "cmd_hwlab_adapter");
assert.equal(body.payload.traceId, "trc_steer_server_test");
assert.match(body.payload.prompt, /STEER_MARK/u);
assert.equal(body.payload.sessionId, "ses_agentrun_server_test_agentrun");
assert.equal(body.payload.hwlabSessionId, "ses_server-test-agentrun");
assert.equal(body.payload.threadId, "019e8078-db67-7750-a5d9-1a99f3abd445");
assert.equal(body.idempotencyKey, "trc_steer_server_test");
return send({ id: "cmd_hwlab_adapter_steer", runId: "run_hwlab_adapter", state: "pending", type: "steer", seq: 7 });
}
assert.equal(body.type, "turn");
assert.match(body.payload.prompt, /AgentRun adapter/u);
assert.equal(body.payload.projectId, "prj_hwpod_workbench");
assert.equal(body.payload.sessionId, "ses_agentrun_server_test_agentrun");
assert.equal(body.payload.hwlabSessionId, "ses_server-test-agentrun");
assert.equal(body.payload.threadContinuityPolicy, "hwlab-agentrun-v01-reuse-runner-thread");
assert.equal(body.payload.sessionPolicy, "hwlab-agentrun-v01-session-runner-reuse");
const secondTurn = /第二轮/u.test(body.payload.message ?? body.payload.prompt);
if (secondTurn) {
assert.equal(body.payload.prompt, "AgentRun adapter smoke 第二轮:总结我们刚才的对话内容");
assert.equal(body.payload.message, "AgentRun adapter smoke 第二轮:总结我们刚才的对话内容");
assert.equal(Object.hasOwn(body.payload, "originalPrompt"), false);
assert.equal(Object.hasOwn(body.payload, "conversationContext"), false);
assert.equal(Object.hasOwn(body.payload, "messages"), false);
assert.equal(body.payload.prompt.includes("HWLAB 同一会话的已脱敏历史上下文"), false);
assert.equal(body.payload.prompt.includes("hwl_live_test_secret"), false);
assert.equal(body.payload.prompt.includes("sk-test-hwpod-secret"), false);
} else {
assert.equal(Object.hasOwn(body.payload, "conversationContext"), false);
}
return send({ id: secondTurn ? "cmd_hwlab_adapter_second" : "cmd_hwlab_adapter", runId: "run_hwlab_adapter", state: "pending", type: "turn", seq: secondTurn ? 2 : 1 });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_adapter/commands") {
return send({ items: [
{ id: "cmd_hwlab_adapter", runId: "run_hwlab_adapter", state: "completed", type: "turn", seq: 1, idempotencyKey: "trc_server-test-agentrun-adapter", payload: { traceId: "trc_server-test-agentrun-adapter", conversationId: "cnv_server-test-agentrun", hwlabSessionId: "ses_server-test-agentrun", threadId: "019e8078-db67-7750-a5d9-1a99f3abd445", providerProfile: "deepseek" } },
{ id: "cmd_hwlab_adapter_steer", runId: "run_hwlab_adapter", state: "completed", type: "steer", seq: 7, idempotencyKey: "trc_steer_server_test", payload: { traceId: "trc_steer_server_test", targetTraceId: "trc_server-test-agentrun-adapter", targetCommandId: "cmd_hwlab_adapter", conversationId: "cnv_server-test-agentrun", hwlabSessionId: "ses_server-test-agentrun", threadId: "019e8078-db67-7750-a5d9-1a99f3abd445", providerProfile: "deepseek" } },
{ id: "cmd_hwlab_adapter_second", runId: "run_hwlab_adapter", state: "completed", type: "turn", seq: 2, idempotencyKey: "trc_server-test-agentrun-adapter-second", payload: { traceId: "trc_server-test-agentrun-adapter-second", conversationId: "cnv_server-test-agentrun", hwlabSessionId: "ses_server-test-agentrun", threadId: "019e8078-db67-7750-a5d9-1a99f3abd445", providerProfile: "deepseek" } }
] });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_adapter/runner-jobs") {
const secondRunnerJob = body.commandId === "cmd_hwlab_adapter_second";
assert.ok(body.commandId === "cmd_hwlab_adapter" || secondRunnerJob);
const transientEnv = Object.fromEntries(body.transientEnv.map((entry) => [entry.name, entry.value]));
assert.equal(transientEnv.HWLAB_RUNTIME_API_URL, "http://hwlab-cloud-api.hwlab-v03.svc.cluster.local:6667");
assert.equal(transientEnv.HWLAB_RUNTIME_WEB_URL, "http://hwlab-cloud-web.hwlab-v03.svc.cluster.local:8080");
assert.equal(transientEnv.HWLAB_RUNTIME_NAMESPACE, "hwlab-v03");
assert.equal(transientEnv.HWLAB_RUNTIME_LANE, "v03");
assert.equal(transientEnv.HWLAB_RUNTIME_ENDPOINT_LOCKED, "1");
assert.equal(transientEnv.HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME, "1");
assert.equal(transientEnv.UNIDESK_MAIN_SERVER_IP, "http://74.48.78.17:18081");
assert.equal(transientEnv.HWLAB_CODE_AGENT_PROVIDER_PROFILE, "deepseek");
assert.equal(transientEnv.HWLAB_CODE_AGENT_PARENT_TRACE_ID, secondRunnerJob ? "trc_server-test-agentrun-adapter-second" : "trc_server-test-agentrun-adapter");
assert.ok(body.transientEnv.length >= 9);
assert.equal(Object.hasOwn(transientEnv, "UNIDESK_SSH_CLIENT_TOKEN"), false);
assert.equal(Object.hasOwn(transientEnv, "GH_TOKEN"), false);
return send({
action: "create-kubernetes-job",
runId: "run_hwlab_adapter",
commandId: body.commandId,
attemptId: secondRunnerJob ? "attempt_hwlab_adapter_second" : "attempt_hwlab_adapter",
runnerId: secondRunnerJob ? "runner_hwlab_adapter_second" : "runner_hwlab_adapter",
namespace: "agentrun-v01",
jobName: secondRunnerJob ? "agentrun-v01-runner-hwlab-adapter-second" : "agentrun-v01-runner-hwlab-adapter",
jobIdentity: { namespace: "agentrun-v01", name: secondRunnerJob ? "agentrun-v01-runner-hwlab-adapter-second" : "agentrun-v01-runner-hwlab-adapter" },
runner: { attemptId: secondRunnerJob ? "attempt_hwlab_adapter_second" : "attempt_hwlab_adapter", runnerId: secondRunnerJob ? "runner_hwlab_adapter_second" : "runner_hwlab_adapter" }
});
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_adapter/events") {
const afterSeq = Number.parseInt(url.searchParams.get("afterSeq") ?? "0", 10);
const second = afterSeq >= 5 || url.searchParams.get("afterSeq") === "3" || calls.some((call) => call.path === "/api/v1/runs/run_hwlab_adapter/commands/cmd_hwlab_adapter_second/result");
if (second) return send({ items: [
{ id: "evt_old_tail", runId: "run_hwlab_adapter", seq: 6, type: "assistant_message", payload: { commandId: "cmd_hwlab_adapter", text: "旧 command 尾部不应进入第二轮。" }, createdAt: "2026-06-01T00:00:02.500Z" },
{ id: "evt_7", runId: "run_hwlab_adapter", seq: 7, type: "backend_status", payload: { phase: "turn-started", commandId: "cmd_hwlab_adapter_second", attemptId: "attempt_hwlab_adapter_second", runnerId: "runner_hwlab_adapter_second", jobName: "agentrun-v01-runner-hwlab-adapter-second", namespace: "agentrun-v01" }, createdAt: "2026-06-01T00:00:03.000Z" },
{ id: "evt_7_prompt", runId: "run_hwlab_adapter", seq: 8, type: "backend_status", payload: { phase: "initial-prompt-assembly", commandId: "cmd_hwlab_adapter_second", initialPromptInjected: false, reason: "thread-resume", initialPrompt: { available: true, bytes: 512, sha256: "prompt-sha", promptRefCount: 1, skillCount: 3, valuesPrinted: false } }, createdAt: "2026-06-01T00:00:03.500Z" },
{ id: "evt_8", runId: "run_hwlab_adapter", seq: 8, type: "assistant_message", payload: { commandId: "cmd_hwlab_adapter_second", text: "AgentRun adapter 复用已有 runner 完成第二轮。" }, createdAt: "2026-06-01T00:00:04.000Z" },
{ id: "evt_9", runId: "run_hwlab_adapter", seq: 9, type: "terminal_status", payload: { commandId: "cmd_hwlab_adapter_second", terminalStatus: "completed" }, createdAt: "2026-06-01T00:00:05.000Z" }
] });
return send({ items: [
{ id: "evt_1", runId: "run_hwlab_adapter", seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId: "cmd_hwlab_adapter", attemptId: "attempt_hwlab_adapter", jobName: "agentrun-v01-runner-hwlab-adapter", namespace: "agentrun-v01" }, createdAt: "2026-06-01T00:00:00.000Z" },
{ id: "evt_bundle", runId: "run_hwlab_adapter", seq: 2, type: "backend_status", payload: { phase: "resource-bundle-materialized", commandId: "cmd_hwlab_adapter", kind: "gitbundle", commitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", bundles: { count: 2, names: ["hwlab-tools", "hwlab-agent-skills"], targetPaths: ["tools", ".agents/skills"], valuesPrinted: false }, tools: { count: 5, names: ["hwpod", "hwpod-ctl", "hwpod-compiler", "unidesk-ssh", "hwlab-code-agent"], valuesPrinted: false }, promptRefs: { count: 1, names: ["hwlab-v02-runtime"], valuesPrinted: false }, skillDirs: { count: 4, names: ["hwpod-cli", "hwpod-ctl", "hwlab-agent-runtime", "hwlab-code-agent"], valuesPrinted: false }, initialPrompt: { available: true, bytes: 512, sha256: "prompt-sha", promptRefCount: 1, skillCount: 4, skillNames: ["hwpod-cli", "hwpod-ctl", "hwlab-agent-runtime", "hwlab-code-agent"], valuesPrinted: false } }, createdAt: "2026-06-01T00:00:00.250Z" },
{ id: "evt_tool", runId: "run_hwlab_adapter", seq: 2, type: "tool_call", payload: { method: "item/completed", type: "commandExecution", toolName: "commandExecution", itemId: "call_agentrun_tool", command: "/bin/sh -lc 'hwpod inspect --dry-run'", status: "completed", exitCode: 0, durationMs: 708, outputSummary: '{"ok":true,"action":"hwpod-cli.plan"}', summary: { outputBytes: 42, outputTruncated: false }, commandId: "cmd_hwlab_adapter", runnerId: "runner_hwlab_adapter", attemptId: "attempt_hwlab_adapter" }, createdAt: "2026-06-01T00:00:00.500Z" },
{ id: "evt_noise", runId: "run_hwlab_adapter", seq: 3, type: "backend_status", payload: { phase: "thread/status/changed", commandId: "cmd_hwlab_adapter" }, createdAt: "2026-06-01T00:00:00.750Z" },
{ id: "evt_prompt", runId: "run_hwlab_adapter", seq: 4, type: "backend_status", payload: { phase: "initial-prompt-assembly", commandId: "cmd_hwlab_adapter", initialPromptInjected: true, reason: "thread-start", initialPrompt: { available: true, bytes: 512, sha256: "prompt-sha", promptRefCount: 1, skillCount: 4, valuesPrinted: false } }, createdAt: "2026-06-01T00:00:00.875Z" },
{ id: "evt_2", runId: "run_hwlab_adapter", seq: 4, type: "assistant_message", payload: { commandId: "cmd_hwlab_adapter", text: "AgentRun adapter 已接管 HWLAB Code Agent。" }, createdAt: "2026-06-01T00:00:01.000Z" },
{ id: "evt_3", runId: "run_hwlab_adapter", seq: 5, type: "terminal_status", payload: { commandId: "cmd_hwlab_adapter", terminalStatus: "completed" }, createdAt: "2026-06-01T00:00:02.000Z" }
] });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_adapter/commands/cmd_hwlab_adapter/result") {
return send({
runId: "run_hwlab_adapter",
commandId: "cmd_hwlab_adapter",
attemptId: "attempt_hwlab_adapter",
runnerId: "runner_hwlab_adapter",
jobName: "agentrun-v01-runner-hwlab-adapter",
namespace: "agentrun-v01",
status: "completed",
runStatus: "completed",
commandState: "completed",
terminalStatus: "completed",
completed: true,
reply: "AgentRun adapter 已接管 HWLAB Code Agent。",
lastSeq: 5,
eventCount: 5,
sessionRef: { sessionId: "ses_agentrun_server_test_agentrun", conversationId: "cnv_server-test-agentrun", threadId: "019e8078-db67-7750-a5d9-1a99f3abd445" }
});
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_adapter/commands/cmd_hwlab_adapter_second/result") {
return send({
runId: "run_hwlab_adapter",
commandId: "cmd_hwlab_adapter_second",
attemptId: "attempt_hwlab_adapter_second",
runnerId: "runner_hwlab_adapter_second",
jobName: "agentrun-v01-runner-hwlab-adapter-second",
namespace: "agentrun-v01",
status: "completed",
runStatus: "claimed",
commandState: "completed",
terminalStatus: "completed",
completed: true,
reply: "AgentRun adapter 复用已有 runner 完成第二轮。",
lastSeq: 9,
eventCount: 9,
sessionRef: { sessionId: "ses_agentrun_server_test_agentrun", conversationId: "cnv_server-test-agentrun", 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_agentrun" })}\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_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567",
HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek",
HWLAB_CODE_AGENT_DEEPSEEK_MODEL: "deepseek-chat",
HWLAB_ENVIRONMENT: "v03",
HWLAB_GITOPS_PROFILE: "v03",
HWLAB_BOOT_REF: "v0.3",
UNIDESK_MAIN_SERVER_IP: "http://74.48.78.17:18081"
},
accessController: {
required: false,
async authenticate() {
return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION };
},
async recordAgentSessionOwner(input) {
const existing = ownerSessions.get(input.sessionId ?? input.id);
const record = testAgentSessionRecord({
...existing,
...input,
session: { ...(existing?.session ?? {}), ...(input.session ?? {}) }
});
ownerSessions.set(record.id, record);
return record;
},
async getAgentSession(sessionId) {
return ownerSessions.get(sessionId) ?? null;
},
async visibleConversationForActor(actor, conversationId, projectId) {
assert.equal(actor.id, "usr_agent_owner");
assert.equal(actor.role, "user");
assert.equal(conversationId, "cnv_server-test-agentrun");
assert.equal(projectId, "prj_hwpod_workbench");
return { conversationId, messages: conversationMessages };
}
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const traceId = "trc_server-test-agentrun-adapter";
ownerSessions.set("ses_server-test-agentrun", testAgentSessionRecord({
sessionId: "ses_server-test-agentrun",
conversationId: "cnv_server-test-agentrun",
projectId: "prj_hwpod_workbench",
status: "idle",
threadId: "019e8078-db67-7750-a5d9-1a99f3abd445"
}));
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-agentrun",
projectId: "prj_hwpod_workbench",
sessionId: "ses_server-test-agentrun",
ownerUserId: "usr_agent_owner",
ownerRole: "user",
threadId: "019e8078-db67-7750-a5d9-1a99f3abd445",
retryOf: "trc_previous-agentrun-web",
message: "AgentRun adapter smoke"
})
});
assert.equal(submit.status, 202);
const accepted = await submit.json();
assert.equal(accepted.shortConnection, true);
assert.equal(accepted.turnId, traceId);
assert.equal(accepted.userMessageId, "msg_server-test-agentrun-adapter_user");
assert.equal(accepted.assistantMessageId, "msg_server-test-agentrun-adapter_agent");
for (let index = 0; index < 50 && !traceStore.snapshot(traceId).events.some((event) => event.label === "agentrun:result:completed"); index += 1) await delay(10);
const projectedTrace = traceStore.snapshot(traceId);
assert.equal(projectedTrace.status, "completed");
assert.ok(projectedTrace.events.some((event) => event.label === "agentrun:assistant:message"));
assert.ok(projectedTrace.events.some((event) => event.label === "agentrun:result:completed"));
const acceptedTurn = await fetch(`http://127.0.0.1:${port}/v1/agent/turns/${encodeURIComponent(traceId)}`, { headers: { cookie: "hwlab_session=test-stub-session" } });
assert.equal(acceptedTurn.status, 200);
const acceptedTurnPayload = await acceptedTurn.json();
assert.equal(acceptedTurnPayload.turnId, traceId);
assert.equal(acceptedTurnPayload.userMessageId, accepted.userMessageId);
assert.equal(acceptedTurnPayload.assistantMessageId, accepted.assistantMessageId);
const payload = await pollAgentResult(port, traceId);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.turnId, traceId);
assert.equal(payload.userMessageId, accepted.userMessageId);
assert.equal(payload.assistantMessageId, accepted.assistantMessageId);
assert.equal(payload.provider, "deepseek");
assert.equal(payload.backend, "agentrun-v01/deepseek");
assert.equal(payload.infrastructureBackend, "agentrun-v01/deepseek");
assert.equal(payload.capabilityLevel, "agentrun-v01-shared-code-agent-session");
assert.equal(payload.sessionMode, "agentrun-v01-durable-session");
assert.equal(payload.implementationType, "agentrun-v01-shared-execution-infra");
assert.equal(payload.runner.kind, "agentrun-v01-shared-runner");
assert.equal(payload.runner.provider, "deepseek");
assert.equal(payload.runner.codexStdio, false);
assert.equal(payload.runner.delegatedToAgentRun, true);
assert.equal(payload.providerTrace.protocol, "agentrun-v01-jsonrpc");
assert.equal(payload.providerTrace.command, "agentrun.v01.command.turn");
assert.equal(payload.providerTrace.runnerKind, "agentrun-v01-shared-runner");
assert.equal(payload.providerTrace.terminalStatus, "completed");
assert.equal(payload.longLivedSessionGate.status, "pass");
assert.equal(payload.longLivedSessionGate.provider, "deepseek");
assert.equal(payload.longLivedSessionGate.codexStdio, false);
assert.equal(payload.longLivedSessionGate.delegatedToAgentRun, true);
assert.equal(payload.agentRun.runId, "run_hwlab_adapter");
assert.equal(payload.agentRun.commandId, "cmd_hwlab_adapter");
assert.equal(payload.agentRun.jobName, "agentrun-v01-runner-hwlab-adapter");
const serializedPayload = JSON.stringify(payload);
assert.equal(serializedPayload.includes("repo-owned-codex"), false);
assert.equal(serializedPayload.includes("codex-app-server-stdio"), false);
assert.equal(serializedPayload.includes("\"provider\":\"codex-stdio\""), false);
assert.equal(serializedPayload.includes("\"codexStdio\":true"), false);
assert.match(payload.reply.content, /AgentRun adapter/u);
assert.ok(payload.runnerTrace.events.some((event) => event.label === "agentrun:backend:runner-job-created"));
const bundleEvent = payload.runnerTrace.events.find((event) => event.label === "agentrun:backend:resource-bundle-materialized");
assert.equal(bundleEvent?.details?.kind, "gitbundle");
assert.deepEqual(bundleEvent?.details?.bundles?.names, ["hwlab-tools", "hwlab-agent-skills"]);
assert.deepEqual(bundleEvent?.details?.tools?.names, ["hwpod", "hwpod-ctl", "hwpod-compiler", "unidesk-ssh", "hwlab-code-agent"]);
assert.deepEqual(bundleEvent?.details?.promptRefs?.names, ["hwlab-v02-runtime"]);
assert.deepEqual(bundleEvent?.details?.skillDirs?.names, ["hwpod-cli", "hwpod-ctl", "hwlab-agent-runtime", "hwlab-code-agent"]);
const promptEvent = payload.runnerTrace.events.find((event) => event.label === "agentrun:backend:initial-prompt-assembly");
assert.equal(promptEvent?.details?.initialPromptInjected, true);
assert.equal(promptEvent?.details?.reason, "thread-start");
assert.equal(promptEvent?.details?.initialPrompt?.promptRefCount, 1);
assert.equal(promptEvent?.details?.initialPrompt?.skillCount, 4);
assert.ok(calls.some((call) => call.path === "/api/v1/runs/run_hwlab_adapter/runner-jobs"));
const inspectByTrace = await fetch(`http://127.0.0.1:${port}/v1/agent/chat/inspect?traceId=${traceId}`);
assert.equal(inspectByTrace.status, 200);
const inspectByTraceBody = await inspectByTrace.json();
assert.equal(inspectByTraceBody.ok, true);
assert.equal(inspectByTraceBody.latestTraceId, traceId);
assert.equal(inspectByTraceBody.session.sessionId, "ses_server-test-agentrun");
assert.equal(inspectByTraceBody.session.conversationId, "cnv_server-test-agentrun");
assert.equal(inspectByTraceBody.session.threadId, "019e8078-db67-7750-a5d9-1a99f3abd445");
assert.equal(inspectByTraceBody.conversationFacts.conversationId, "cnv_server-test-agentrun");
assert.equal(inspectByTraceBody.conversationFacts.threadId, "019e8078-db67-7750-a5d9-1a99f3abd445");
assert.equal(inspectByTraceBody.runnerTrace.traceId, traceId);
assert.equal(inspectByTraceBody.valuesRedacted, true);
assert.equal(JSON.stringify(inspectByTraceBody).includes("hwl_live_test_secret"), false);
const trace = await fetch(`http://127.0.0.1:${port}/v1/agent/traces/${traceId}`);
assert.equal(trace.status, 200);
const traceBody = await trace.json();
const requestTraceEvent = traceBody.events.find((event) => event.label === "agentrun:request:accepted");
assert.equal(requestTraceEvent.eventType, "request");
assert.equal(requestTraceEvent.backend, "agentrun-v01/deepseek");
const runnerJobTraceEvent = traceBody.events.find((event) => event.label === "agentrun:runner-job:created");
assert.equal(runnerJobTraceEvent.eventType, "backend");
assert.equal(runnerJobTraceEvent.backend, "agentrun-v01/deepseek");
assert.ok(traceBody.events.some((event) => event.runId === "run_hwlab_adapter"));
assert.ok(traceBody.events.some((event) => event.label === "agentrun:assistant:message"));
assert.equal(traceBody.events.some((event) => event.details?.initialPromptInjected === true), true);
const commandTraceEvent = traceBody.events.find((event) => event.label === "item/commandExecution:completed");
assert.equal(commandTraceEvent.eventType, "tool_call");
assert.equal(commandTraceEvent.backend, "agentrun-v01/deepseek");
assert.ok(Date.parse(commandTraceEvent.appendedAt) >= Date.parse(commandTraceEvent.createdAt));
assert.equal(commandTraceEvent.toolName, "commandExecution");
assert.equal(commandTraceEvent.createdAt, "2026-06-01T00:00:00.500Z");
assert.match(commandTraceEvent.command, /hwpod inspect --dry-run/u);
assert.match(commandTraceEvent.stdoutSummary, /hwpod-cli\.plan/u);
conversationMessages.push(
{
id: "msg_693_user",
role: "user",
text: "看看 HWPOD 可用性?",
status: "sent",
traceId,
conversationId: "cnv_server-test-agentrun",
sessionId: "ses_server-test-agentrun",
threadId: "019e8078-db67-7750-a5d9-1a99f3abd445",
createdAt: "2026-06-02T00:00:00.000Z"
},
{
id: "msg_693_agent",
role: "agent",
text: "v0.2 HWPOD 可用性快照:可用;apiKey=hwl_live_test_secret sk-test-hwpod-secret-000000",
status: "completed",
traceId,
conversationId: "cnv_server-test-agentrun",
sessionId: "ses_server-test-agentrun",
threadId: "019e8078-db67-7750-a5d9-1a99f3abd445",
createdAt: "2026-06-02T00:00:01.000Z"
}
);
const steerRequest = fetch(`http://127.0.0.1:${port}/v1/agent/chat/steer`, {
method: "POST",
headers: { "content-type": "application/json", "x-trace-id": traceId, cookie: "hwlab_session=test-stub-session" },
body: JSON.stringify({
traceId,
steerTraceId: "trc_steer_server_test",
conversationId: "cnv_server-test-agentrun",
projectId: "prj_hwpod_workbench",
sessionId: "ses_server-test-agentrun",
threadId: "019e8078-db67-7750-a5d9-1a99f3abd445",
message: "请按 STEER_MARK 调整最终回复"
})
});
const steer = await Promise.race([
steerRequest,
delay(100).then(() => null)
]);
assert.ok(steer, "terminal steer rejection must return as a short response");
assert.equal(steer.status, 409);
const steerBody = await steer.json();
assert.equal(steerBody.accepted, false);
assert.equal(steerBody.error.code, "steer_trace_terminal");
assert.equal(steerBody.route, "/v1/agent/chat/steer");
assert.equal(steerBody.traceId, traceId);
assert.equal(steerBody.agentRun.runId, "run_hwlab_adapter");
assert.equal(calls.some((call) => call.method === "POST" && call.path === "/api/v1/runs/run_hwlab_adapter/commands" && call.body?.type === "steer"), false);
const secondTraceId = "trc_server-test-agentrun-adapter-second";
const second = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
method: "POST",
headers: { "content-type": "application/json", "x-trace-id": secondTraceId, cookie: "hwlab_session=test-stub-session" },
body: JSON.stringify({
conversationId: "cnv_server-test-agentrun",
projectId: "prj_hwpod_workbench",
sessionId: "ses_server-test-agentrun",
ownerUserId: "usr_agent_owner",
ownerRole: "user",
threadId: "019e8078-db67-7750-a5d9-1a99f3abd445",
message: "AgentRun adapter smoke 第二轮:总结我们刚才的对话内容"
})
});
assert.equal(second.status, 202);
const secondPayload = await pollAgentResult(port, secondTraceId);
validateCodeAgentChatSchema(secondPayload);
assert.equal(secondPayload.status, "completed");
assert.equal(secondPayload.provider, "deepseek");
assert.equal(secondPayload.backend, "agentrun-v01/deepseek");
assert.equal(secondPayload.runner.kind, "agentrun-v01-shared-runner");
assert.equal(secondPayload.runner.codexStdio, false);
assert.equal(secondPayload.agentRun.runId, "run_hwlab_adapter");
assert.equal(secondPayload.agentRun.commandId, "cmd_hwlab_adapter_second");
assert.equal(secondPayload.providerTrace.commandId, "cmd_hwlab_adapter_second");
assert.equal(secondPayload.providerTrace.traceId, secondTraceId);
assert.equal(secondPayload.agentRun.providerTrace.commandId, "cmd_hwlab_adapter_second");
assert.equal(secondPayload.agentRun.providerTrace.traceId, secondTraceId);
assert.equal(secondPayload.agentRun.jobName, "agentrun-v01-runner-hwlab-adapter-second");
assert.equal(secondPayload.sessionReuse.reused, true);
assert.equal(secondPayload.agentRun.runnerJobCount, 1);
assert.match(secondPayload.reply.content, /复用已有 runner/u);
assert.ok(secondPayload.runnerTrace.events.some((event) => event.label === "agentrun:run:reused"));
assert.ok(secondPayload.runnerTrace.events.some((event) => event.label === "agentrun:runner-job:ensured"));
assert.equal(secondPayload.runnerTrace.events.some((event) => String(event.text ?? event.message ?? "").includes("旧 command 尾部")), false);
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1);
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs/run_hwlab_adapter/runner-jobs").length, 2);
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs/run_hwlab_adapter/commands").length, 2);
} 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 keeps admitted user message when billing preflight fails before AgentRun dispatch (#1619)", async () => {
const traceId = "trc_issue1619_billing_after_admission";
const sessionId = "ses_issue1619_billing_after_admission";
const conversationId = "cnv_issue1619_billing_after_admission";
const ownerSessions = new Map([[sessionId, testAgentSessionRecord({ sessionId, conversationId, projectId: "prj_hwpod_workbench", status: "idle" })]]);
const ownerWrites = [];
const billingCalls = [];
const agentRunCalls = [];
const agentRunServer = createHttpServer(async (request, response) => {
agentRunCalls.push({ method: request.method, url: request.url });
response.writeHead(500, { "content-type": "application/json" });
response.end(`${JSON.stringify({ ok: false, message: "AgentRun must not be called when billing preflight failed" })}\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_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567",
HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek",
HWLAB_ENVIRONMENT: "v03",
HWLAB_GITOPS_PROFILE: "v03",
HWLAB_USER_BILLING_CODE_AGENT_ENABLED: "1"
},
userBillingAuth: { active: true },
userBillingClient: {
configured: true,
async billingPreflight(body) {
billingCalls.push(body);
assert.ok(ownerWrites.some((write) => write.traceId === traceId && write.status === "running"), "durable admission must be recorded before billing preflight");
return { ok: false, status: 503, error: { code: "billing_preflight_unavailable", message: "billing preflight unavailable" } };
}
},
accessController: {
required: false,
async authenticate() {
return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION, userBilling: { active: true } };
},
async recordAgentSessionOwner(input) {
ownerWrites.push(input);
const existing = ownerSessions.get(input.sessionId ?? input.id);
const record = testAgentSessionRecord({
...existing,
...input,
session: { ...(existing?.session ?? {}), ...(input.session ?? {}) }
});
ownerSessions.set(record.id, record);
return record;
},
async getAgentSession(requestedSessionId) {
return ownerSessions.get(requestedSessionId) ?? null;
}
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
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,
projectId: "prj_hwpod_workbench",
sessionId,
message: "测试一下和 cpython 对比的性能"
})
});
assert.equal(submit.status, 202);
const accepted = await submit.json();
assert.equal(accepted.accepted, true);
assert.equal(accepted.traceId, traceId);
const payload = await pollAgentResult(port, traceId);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "failed");
assert.equal(payload.error.code, "billing_preflight_unavailable");
assert.match(payload.finalResponse.text, /billing preflight unavailable/u);
assert.equal(billingCalls.length, 1);
assert.equal(agentRunCalls.length, 0);
assert.ok(ownerWrites.some((write) => write.traceId === traceId && write.status === "running"));
assert.ok(ownerWrites.some((write) => write.traceId === traceId && write.status === "failed"));
const stored = ownerSessions.get(sessionId);
const userMessage = stored?.session?.messages?.find((message) => message.role === "user");
assert.match(userMessage?.text ?? "", /cpython/u);
const turn = await fetch(`http://127.0.0.1:${port}/v1/agent/turns/${traceId}`, { headers: { cookie: "hwlab_session=test-stub-session" } });
assert.equal(turn.status, 200);
const turnBody = await turn.json();
assert.equal(turnBody.status, "failed");
assert.equal(turnBody.error.code, "billing_preflight_unavailable");
} 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("AgentRun sync converts terminal command result even when run remains claimed (#1555)", async () => {
const calls = [];
const traceId = "trc_issue1555_terminal_command";
const runId = "run_issue1555_claimed";
const commandId = "cmd_issue1555_completed";
const finalText = [
"已新增并实际运行:",
"",
"- Go`go test ./...` 保留两个空格",
"- Rust`cargo test --release`",
"",
"```sh",
"go test ./...",
"cargo test --release",
"```",
"",
"| benchmark | Go | Rust |",
"|---|---:|---:|",
"| fib | 1.2ms | 1.0ms |"
].join("\n");
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_issue1555" })}\n`);
};
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) {
return send({ items: [
{ id: "evt_issue1555_assistant", runId, seq: 138, type: "assistant_message", payload: { commandId, text: finalText, final: true, replyAuthority: true }, createdAt: "2026-06-18T00:00:00.000Z" },
{ id: "evt_issue1555_terminal", runId, seq: 139, type: "terminal_status", payload: { commandId, terminalStatus: "completed" }, createdAt: "2026-06-18T00:00:00.000Z" }
] });
}
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands`) {
return send({ items: [
{ id: commandId, runId, state: "completed", type: "turn", seq: 1, idempotencyKey: traceId, payload: { traceId, conversationId: "cnv_issue1555", hwlabSessionId: "ses_issue1555", threadId: "thread_issue1555" } }
] });
}
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands/${commandId}/result`) {
return send({
runId,
commandId,
attemptId: "attempt_issue1555",
runnerId: "runner_issue1555",
jobName: "agentrun-v01-runner-issue1555",
namespace: "agentrun-v02",
status: "completed",
runStatus: "claimed",
commandState: "completed",
terminalStatus: "completed",
completed: true,
reply: finalText,
lastSeq: 139,
eventCount: 139,
sessionRef: { sessionId: "ses_issue1555", conversationId: "cnv_issue1555", threadId: "thread_issue1555" }
});
}
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();
try {
const currentResult = {
ok: true,
accepted: true,
shortConnection: true,
status: "running",
traceId,
conversationId: "cnv_issue1555",
sessionId: "ses_issue1555",
threadId: "thread_issue1555",
agentRun: {
adapter: "agentrun-v01",
managerUrl: `http://127.0.0.1:${agentRunPort}`,
runId,
commandId,
status: "pending",
runStatus: "claimed",
commandState: "pending",
terminalStatus: null,
lastSeq: 0,
valuesPrinted: false
},
valuesPrinted: false
};
const synced = await syncAgentRunChatResult({
traceId,
currentResult,
traceStore,
options: {
env: {
AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`,
HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1"
}
}
});
assert.equal(synced.resultSynced, true);
assert.equal(synced.result.status, "completed");
assert.equal(synced.result.agentRun.runStatus, "claimed");
assert.equal(synced.result.agentRun.commandState, "completed");
assert.equal(synced.result.agentRun.terminalStatus, "completed");
assert.equal(synced.result.finalResponse.text, finalText);
assert.equal(synced.result.reply.content, finalText);
assert.ok((synced.result.finalResponse.text.match(/\n/gu) ?? []).length > 8);
assert.ok(synced.result.finalResponse.text.includes("`go test ./...` 保留两个空格"));
const assistantEvent = traceStore.snapshot(traceId).events.find((event) => event.label === "agentrun:assistant:message");
assert.equal(assistantEvent?.message, finalText);
assert.deepEqual(calls.map((call) => call.path), [
`/api/v1/runs/${runId}/commands`,
`/api/v1/runs/${runId}/events`,
`/api/v1/runs/${runId}/commands/${commandId}/result`
]);
assert.ok(traceStore.snapshot(traceId).events.some((event) => event.label === "agentrun:result:completed"));
} finally {
await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve())));
}
});
test("AgentRun sync seals completed final response from authoritative terminal assistant trace event (#1629)", async () => {
const calls = [];
const traceId = "trc_issue1629_terminal_assistant_final";
const runId = "run_issue1629_trace_final";
const commandId = "cmd_issue1629_trace_final";
const finalText = [
"全部六份数据到手!下面是完整的六语言终极性能对比:",
"",
"| language | runtime | status |",
"|---|---|---|",
"| Lua | LuaJIT | pass |"
].join("\n");
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_issue1629" })}\n`);
};
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands`) {
return send({ items: [
{ id: commandId, runId, state: "completed", type: "turn", seq: 1, idempotencyKey: traceId, payload: { traceId, conversationId: "cnv_issue1629", hwlabSessionId: "ses_issue1629", threadId: "thread_issue1629" } }
] });
}
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) {
return send({ items: [
{ id: "evt_issue1629_progress", runId, seq: 21, type: "assistant_message", payload: { commandId, text: "正在补 Lua 基准测试。" }, createdAt: "2026-06-19T15:47:13.000Z" },
{ id: "evt_issue1629_final", runId, seq: 28, type: "assistant_message", payload: { commandId, text: finalText, final: true, replyAuthority: true }, createdAt: "2026-06-19T15:47:28.000Z" },
{ id: "evt_issue1629_terminal", runId, seq: 29, type: "terminal_status", payload: { commandId, terminalStatus: "completed" }, createdAt: "2026-06-19T15:47:29.000Z" }
] });
}
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands/${commandId}/result`) {
return send({
runId,
commandId,
attemptId: "attempt_issue1629",
runnerId: "runner_issue1629",
jobName: "agentrun-v01-runner-issue1629",
namespace: "agentrun-v01",
status: "completed",
runStatus: "completed",
commandState: "completed",
terminalStatus: "completed",
completed: true,
reply: null,
finalResponse: null,
lastSeq: 29,
eventCount: 29,
sessionRef: { sessionId: "ses_issue1629", conversationId: "cnv_issue1629", threadId: "thread_issue1629" }
});
}
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();
try {
const currentResult = {
ok: true,
accepted: true,
shortConnection: true,
status: "running",
traceId,
conversationId: "cnv_issue1629",
sessionId: "ses_issue1629",
threadId: "thread_issue1629",
agentRun: {
adapter: "agentrun-v01",
managerUrl: `http://127.0.0.1:${agentRunPort}`,
runId,
commandId,
status: "running",
runStatus: "running",
commandState: "running",
terminalStatus: null,
lastSeq: 0,
valuesPrinted: false
},
valuesPrinted: false
};
const synced = await syncAgentRunChatResult({
traceId,
currentResult,
traceStore,
options: {
env: {
AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`,
HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1"
}
}
});
assert.equal(synced.resultSynced, true);
assert.equal(synced.result.status, "completed");
assert.equal(synced.result.finalResponse.text, finalText);
assert.equal(synced.result.assistantText ?? synced.result.finalResponse.text, finalText);
assert.equal(synced.result.reply.content, finalText);
assert.equal(synced.result.traceSummary.finalAssistantRow.textChars, finalText.length);
const assistantEvent = synced.result.runnerTrace.events.find((event) => event.label === "agentrun:assistant:message" && event.message === finalText);
assert.equal(assistantEvent?.message, finalText);
assert.deepEqual(calls.map((call) => call.path), [
`/api/v1/runs/${runId}/commands`,
`/api/v1/runs/${runId}/events`,
`/api/v1/runs/${runId}/commands/${commandId}/result`
]);
} finally {
await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve())));
}
});
test("cloud api AgentRun adapter reports persistent thread resume when a completed run needs a new runner", async () => {
const calls = [];
const hwlabSessionId = "ses_server-test-thread-resume";
const agentRunSessionId = "ses_agentrun_deepseek_server_test_thread_resume";
const conversationId = "cnv_server-test-thread-resume";
const threadId = "019e90e0-9535-7130-a894-47ef4e127206";
const ownerSessions = new Map([[hwlabSessionId, testAgentSessionRecord({
sessionId: hwlabSessionId,
conversationId,
threadId,
traceId: "trc_previous_thread_resume",
status: "idle",
session: {
agentRun: {
adapter: "agentrun-v01",
backendProfile: "deepseek",
managerUrl: "http://127.0.0.1:1",
runId: "run_issue812_previous",
commandId: "cmd_issue812_previous",
jobName: "agentrun-v01-runner-issue812-previous",
namespace: "agentrun-v01",
sessionId: agentRunSessionId,
conversationId,
threadId,
terminalStatus: "completed",
runStatus: "completed",
reuseEligible: true,
reused: 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_issue812_resume" })}\n`);
};
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_issue812_previous") {
return send({
id: "run_issue812_previous",
status: "completed",
terminalStatus: "completed",
backendProfile: "deepseek",
sessionRef: { sessionId: agentRunSessionId, conversationId, threadId, metadata: {} }
});
}
if (request.method === "POST" && url.pathname === "/api/v1/sessions") {
assert.equal(body.sessionId, agentRunSessionId);
assert.equal(body.backendProfile, "deepseek");
return send({ sessionId: body.sessionId, backendProfile: body.backendProfile });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs") {
assert.equal(body.sessionRef.sessionId, agentRunSessionId);
assert.equal(body.sessionRef.conversationId, conversationId);
assert.equal(body.sessionRef.metadata.hwlabSessionId, hwlabSessionId);
assert.equal(body.sessionRef.metadata.threadContinuityPolicy, "hwlab-agentrun-v01-reuse-runner-thread");
return send({ id: "run_issue812_resume", status: "pending", backendProfile: "deepseek", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_issue812_resume/commands") {
assert.equal(body.type, "turn");
assert.equal(body.payload.sessionId, agentRunSessionId);
assert.equal(body.payload.hwlabSessionId, hwlabSessionId);
assert.equal(body.payload.threadId, null);
assert.match(body.payload.prompt, /ISSUE812_RESUME/u);
return send({ id: "cmd_issue812_resume", runId: "run_issue812_resume", state: "pending", type: "turn", seq: 1 });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_issue812_resume/commands") {
return send({ items: [
{ id: "cmd_issue812_resume", runId: "run_issue812_resume", state: "completed", type: "turn", seq: 1, idempotencyKey: "trc_server-test-issue812-resume", payload: { traceId: "trc_server-test-issue812-resume", conversationId, hwlabSessionId, threadId, providerProfile: "deepseek" } }
] });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_issue812_resume/runner-jobs") {
assert.equal(body.commandId, "cmd_issue812_resume");
return send({
action: "create-kubernetes-job",
runId: "run_issue812_resume",
commandId: "cmd_issue812_resume",
attemptId: "attempt_issue812_resume",
runnerId: "runner_issue812_resume",
namespace: "agentrun-v01",
jobName: "agentrun-v01-runner-issue812-resume",
jobIdentity: { namespace: "agentrun-v01", name: "agentrun-v01-runner-issue812-resume" },
runner: { attemptId: "attempt_issue812_resume", runnerId: "runner_issue812_resume" }
});
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_issue812_resume/events") {
return send({ items: [
{ id: "evt_issue812_1", runId: "run_issue812_resume", seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId: "cmd_issue812_resume", attemptId: "attempt_issue812_resume", jobName: "agentrun-v01-runner-issue812-resume", namespace: "agentrun-v01" }, createdAt: "2026-06-04T00:00:00.000Z" },
{ id: "evt_issue812_2", runId: "run_issue812_resume", seq: 2, type: "backend_status", payload: { phase: "initial-prompt-assembly", commandId: "cmd_issue812_resume", initialPromptInjected: false, reason: "thread-resume" }, createdAt: "2026-06-04T00:00:00.500Z" },
{ id: "evt_issue812_3", runId: "run_issue812_resume", seq: 3, type: "assistant_message", payload: { commandId: "cmd_issue812_resume", text: "ISSUE812_RESUME_OK" }, createdAt: "2026-06-04T00:00:01.000Z" },
{ id: "evt_issue812_4", runId: "run_issue812_resume", seq: 4, type: "terminal_status", payload: { commandId: "cmd_issue812_resume", terminalStatus: "completed" }, createdAt: "2026-06-04T00:00:02.000Z" }
] });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_issue812_resume/commands/cmd_issue812_resume/result") {
return send({
runId: "run_issue812_resume",
commandId: "cmd_issue812_resume",
attemptId: "attempt_issue812_resume",
runnerId: "runner_issue812_resume",
jobName: "agentrun-v01-runner-issue812-resume",
namespace: "agentrun-v01",
status: "completed",
runStatus: "completed",
commandState: "completed",
terminalStatus: "completed",
completed: true,
reply: "ISSUE812_RESUME_OK",
lastSeq: 4,
eventCount: 4,
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_issue812_resume" })}\n`);
});
await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve));
const agentRunPort = agentRunServer.address().port;
for (const record of ownerSessions.values()) {
record.session.agentRun.managerUrl = `http://127.0.0.1:${agentRunPort}`;
}
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_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 existing = ownerSessions.get(input.sessionId ?? input.id);
const record = testAgentSessionRecord({
...existing,
...input,
session: { ...(existing?.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-issue812-resume";
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,
message: "ISSUE812_RESUME after completed AgentRun run"
})
});
assert.equal(submit.status, 202);
const payload = await pollAgentResult(port, traceId);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.sessionId, hwlabSessionId);
assert.equal(payload.threadId, threadId);
assert.equal(payload.agentRun.runId, "run_issue812_resume");
assert.equal(payload.agentRun.reused, false);
assert.equal(payload.agentRun.runnerReused, false);
assert.equal(payload.agentRun.threadReused, false);
assert.equal(payload.agentRun.persistentResume, false);
assert.equal(payload.sessionReuse.threadId, threadId);
assert.equal(payload.sessionReuse.reused, true);
assert.equal(payload.sessionReuse.status, "thread-resumed");
assert.equal(payload.sessionReuse.runnerReused, false);
assert.equal(payload.sessionReuse.threadReused, true);
assert.equal(payload.sessionReuse.persistentResume, true);
assert.equal(payload.reply.content, "ISSUE812_RESUME_OK");
assert.equal(calls.some((call) => call.method === "GET" && call.path === "/api/v1/runs/run_issue812_previous"), true);
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1);
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs/run_issue812_resume/runner-jobs").length, 1);
} 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 exposes invalid tool-call attribution in result payload", async () => {
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;
const send = (data) => {
response.writeHead(200, { "content-type": "application/json" });
response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_agentrun_invalid_tool" })}\n`);
};
if (request.method === "POST" && url.pathname === "/api/v1/runs") {
return send({ id: "run_invalid_tool", status: "pending", backendProfile: "deepseek", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_invalid_tool/commands") {
return send({ id: "cmd_invalid_tool", runId: "run_invalid_tool", state: "pending", type: "turn", seq: 1 });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_invalid_tool/commands") {
return send({ items: [
{ id: "cmd_invalid_tool", runId: "run_invalid_tool", state: "failed", type: "turn", seq: 1, idempotencyKey: "trc_server-test-agentrun-invalid-tool", payload: { traceId: "trc_server-test-agentrun-invalid-tool", conversationId: "cnv_invalid_tool", hwlabSessionId: "ses_server-test-invalid-tool", threadId: "thread_invalid_tool", providerProfile: "deepseek" } }
] });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_invalid_tool/runner-jobs") {
return send({
action: "create-kubernetes-job",
runId: "run_invalid_tool",
commandId: "cmd_invalid_tool",
attemptId: "attempt_invalid_tool",
runnerId: "runner_invalid_tool",
namespace: "agentrun-v01",
jobName: "agentrun-v01-runner-invalid-tool",
runner: { attemptId: "attempt_invalid_tool", runnerId: "runner_invalid_tool" }
});
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_invalid_tool/events") {
return send({ items: [
{ id: "evt_invalid_tool", runId: "run_invalid_tool", seq: 1, type: "error", payload: { commandId: "cmd_invalid_tool", failureKind: "provider-invalid-tool-call", message: "invalid params, invalid function arguments json string, tool_call_id: call_function_selftest_2 (2013)" }, createdAt: "2026-06-02T00:00:00.000Z" },
{ id: "evt_invalid_tool_terminal", runId: "run_invalid_tool", seq: 2, type: "terminal_status", payload: { commandId: "cmd_invalid_tool", terminalStatus: "failed", failureKind: "provider-invalid-tool-call" }, createdAt: "2026-06-02T00:00:01.000Z" }
] });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_invalid_tool/commands/cmd_invalid_tool/result") {
return send({
runId: "run_invalid_tool",
commandId: "cmd_invalid_tool",
attemptId: "attempt_invalid_tool",
runnerId: "runner_invalid_tool",
jobName: "agentrun-v01-runner-invalid-tool",
namespace: "agentrun-v01",
status: "failed",
runStatus: "failed",
commandState: "failed",
terminalStatus: "failed",
failureKind: "provider-invalid-tool-call",
failureMessage: "invalid params, invalid function arguments json string, tool_call_id: call_function_selftest_2 (2013)",
completed: false,
lastSeq: 2,
eventCount: 2,
sessionRef: { sessionId: "ses_agentrun_invalid_tool", conversationId: "cnv_invalid_tool", threadId: "thread_invalid_tool" }
});
}
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_agentrun_invalid_tool" })}\n`);
});
await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve));
const agentRunPort = agentRunServer.address().port;
const ownerSessions = new Map([["ses_server-test-invalid-tool", testAgentSessionRecord({
sessionId: "ses_server-test-invalid-tool",
conversationId: "cnv_invalid_tool",
status: "idle"
})]]);
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-invalid-tool";
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_invalid_tool", sessionId: "ses_server-test-invalid-tool", message: "invalid tool-call attribution" })
});
assert.equal(submit.status, 202);
const payload = await pollAgentResult(port, traceId);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "failed");
assert.equal(payload.providerTrace.failureKind, "provider-invalid-tool-call");
assert.equal(payload.error.code, "provider-invalid-tool-call");
assert.equal(payload.error.category, "provider_invalid_tool_call");
assert.match(payload.error.userMessage, /无效 tool-call arguments JSON/u);
assert.match(payload.error.userMessage, /AgentRun\/provider/u);
assert.match(payload.error.userMessage, /不是 HWPOD/u);
assert.equal(payload.finalResponse.status, "failed");
assert.match(payload.finalResponse.text, /invalid function arguments json string/u);
assert.equal(payload.traceSummary.terminalStatus, "failed");
assert.match(payload.traceSummary.finalAssistantRow.textPreview, /invalid function arguments json string/u);
assert.equal(payload.blocker?.category ?? payload.error.category, "provider_invalid_tool_call");
assert.match(payload.blocker?.summary ?? payload.error.userMessage, /invalid function arguments json string/u);
assert.equal(payload.session.status, "failed");
assert.equal(payload.session.lifecycle.requiresNewSession, false);
assert.equal(payload.sessionReuse.threadId, "thread_invalid_tool");
assert.equal(payload.sessionReuse.status, "failed-resumable");
assert.equal(payload.agentRun.reuseEligible, true);
assert.equal(payload.reuseEligible, true);
const serializedPayload = JSON.stringify(payload);
assert.equal(serializedPayload.includes("repo-owned-codex"), false);
assert.equal(serializedPayload.includes("codex-app-server-stdio"), false);
assert.equal(serializedPayload.includes("\"provider\":\"codex-stdio\""), 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 retries evicted session storage with a fresh session", async () => {
const calls = [];
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_agentrun_evicted" })}\n`);
};
if (request.method === "POST" && url.pathname === "/api/v1/sessions") {
return send({ sessionId: body.sessionId, backendProfile: body.backendProfile });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs") {
const retry = String(body.sessionRef?.sessionId ?? "").includes("-reset-");
return send({ id: retry ? "run_evicted_retry" : "run_evicted", status: "pending", backendProfile: "deepseek", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_evicted/commands") {
return send({ id: "cmd_evicted", runId: "run_evicted", state: "queued" });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_evicted_retry/commands") {
assert.equal(body.type, "turn");
assert.equal(body.payload.threadId, null);
assert.match(body.payload.prompt, /resume evicted session/u);
assert.match(body.payload.sessionId, /-reset-/u);
return send({ id: "cmd_evicted_retry", runId: "run_evicted_retry", state: "queued" });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_evicted/runner-jobs") {
return send({
action: "create-kubernetes-job",
runId: "run_evicted",
commandId: "cmd_evicted",
attemptId: "attempt_evicted",
runnerId: "runner_evicted",
namespace: "agentrun-v01",
jobName: "agentrun-v01-runner-evicted",
runner: { attemptId: "attempt_evicted", runnerId: "runner_evicted" }
});
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_evicted_retry/runner-jobs") {
return send({
action: "create-kubernetes-job",
runId: "run_evicted_retry",
commandId: "cmd_evicted_retry",
attemptId: "attempt_evicted_retry",
runnerId: "runner_evicted_retry",
namespace: "agentrun-v01",
jobName: "agentrun-v01-runner-evicted-retry",
runner: { attemptId: "attempt_evicted_retry", runnerId: "runner_evicted_retry" }
});
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_evicted/events") {
return send({ items: [
{ id: "evt_evicted", runId: "run_evicted", seq: 1, type: "error", payload: { commandId: "cmd_evicted", failureKind: "session-store-evicted", message: "codex app-server thread/resume reported no rollout found for PVC-backed session; session storage was likely evicted" }, createdAt: "2026-06-02T00:00:00.000Z" },
{ id: "evt_evicted_terminal", runId: "run_evicted", seq: 2, type: "terminal_status", payload: { commandId: "cmd_evicted", terminalStatus: "failed", failureKind: "session-store-evicted" }, createdAt: "2026-06-02T00:00:01.000Z" }
] });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_evicted_retry/events") {
return send({ items: [
{ id: "evt_evicted_retry_1", runId: "run_evicted_retry", seq: 1, type: "backend_status", payload: { commandId: "cmd_evicted_retry", phase: "runner-job-created" }, createdAt: "2026-06-02T00:00:02.000Z" },
{ id: "evt_evicted_retry_2", runId: "run_evicted_retry", seq: 2, type: "assistant_message", payload: { commandId: "cmd_evicted_retry", text: "EVICTED_RETRY_OK", final: true, replyAuthority: true }, createdAt: "2026-06-02T00:00:03.000Z" },
{ id: "evt_evicted_retry_terminal", runId: "run_evicted_retry", seq: 3, type: "terminal_status", payload: { commandId: "cmd_evicted_retry", terminalStatus: "completed" }, createdAt: "2026-06-02T00:00:04.000Z" }
] });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_evicted/commands") {
return send({ items: [{ id: "cmd_evicted", runId: "run_evicted", state: "failed", terminalStatus: "failed", idempotencyKey: "trc_server-test-agentrun-evicted", payload: { traceId: "trc_server-test-agentrun-evicted" } }] });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_evicted_retry/commands") {
return send({ items: [{ id: "cmd_evicted_retry", runId: "run_evicted_retry", state: "completed", terminalStatus: "completed", idempotencyKey: "trc_server-test-agentrun-evicted", payload: { traceId: "trc_server-test-agentrun-evicted" } }] });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_evicted/commands/cmd_evicted/result") {
return send({
runId: "run_evicted",
commandId: "cmd_evicted",
attemptId: "attempt_evicted",
runnerId: "runner_evicted",
jobName: "agentrun-v01-runner-evicted",
namespace: "agentrun-v01",
status: "failed",
runStatus: "failed",
commandState: "failed",
terminalStatus: "failed",
failureKind: "session-store-evicted",
failureMessage: "codex app-server thread/resume reported no rollout found for PVC-backed session; session storage was likely evicted",
completed: false,
lastSeq: 2,
eventCount: 2,
sessionRef: { sessionId: "ses_agentrun_evicted", conversationId: "cnv_evicted", threadId: "thread_evicted" }
});
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_evicted_retry/commands/cmd_evicted_retry/result") {
return send({
runId: "run_evicted_retry",
commandId: "cmd_evicted_retry",
attemptId: "attempt_evicted_retry",
runnerId: "runner_evicted_retry",
jobName: "agentrun-v01-runner-evicted-retry",
namespace: "agentrun-v01",
status: "completed",
runStatus: "completed",
commandState: "completed",
terminalStatus: "completed",
completed: true,
reply: "EVICTED_RETRY_OK",
lastSeq: 3,
eventCount: 3,
sessionRef: { sessionId: "ses_agentrun_evicted-reset-trcservertes", conversationId: "cnv_evicted", threadId: "thread_evicted_retry" }
});
}
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_agentrun_evicted" })}\n`);
});
await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve));
const agentRunPort = agentRunServer.address().port;
const ownerSessions = new Map([["ses_server-test-evicted", testAgentSessionRecord({
sessionId: "ses_server-test-evicted",
conversationId: "cnv_evicted",
threadId: "thread_evicted",
status: "idle"
})]]);
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-evicted";
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_evicted", sessionId: "ses_server-test-evicted", threadId: "thread_evicted", message: "resume evicted session" })
});
assert.equal(submit.status, 202);
const payload = await pollAgentResult(port, traceId);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.reply.content, "EVICTED_RETRY_OK");
assert.equal(payload.agentRun.runId, "run_evicted_retry");
assert.equal(payload.agentRun.commandId, "cmd_evicted_retry");
assert.equal(payload.agentRun.freshSessionRetryOf.runId, "run_evicted");
assert.equal(payload.agentRun.freshSessionRetryOf.commandId, "cmd_evicted");
assert.match(payload.agentRun.sessionId, /-reset-/u);
assert.equal(payload.session.threadId, "thread_evicted_retry");
assert.equal(payload.sessionReuse.status, "thread-resumed");
assert.equal(payload.error, undefined);
assert.equal(ownerSessions.get("ses_server-test-evicted")?.status, "completed");
assert.equal(calls.some((call) => call.method === "POST" && call.path === "/api/v1/runs/run_evicted_retry/commands"), true);
} 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 maps minimax-m3 provider profile to AgentRun backend", async () => {
const calls = [];
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_agentrun_minimax_m3" })}\n`);
};
if (request.method === "POST" && url.pathname === "/api/v1/runs") {
assert.equal(body.backendProfile, "minimax-m3");
assert.equal(body.sessionRef.sessionId, "ses_agentrun_server_test_minimax_m3");
assert.equal(body.sessionRef.metadata.hwlabSessionId, "ses_server-test-minimax-m3");
assert.equal(body.sessionRef.metadata.agentRunSessionProfile, "minimax-m3");
assert.equal(body.sessionRef.metadata.agentRunSessionPolicy, "hwlab-session-scoped");
assert.equal(body.executionPolicy.secretScope.providerCredentials[0].profile, "minimax-m3");
assert.equal(body.executionPolicy.secretScope.providerCredentials[0].secretRef.name, "agentrun-v01-provider-minimax-m3");
assert.equal(body.executionPolicy.secretScope.providerCredentials[0].secretRef.namespace, "agentrun-v01");
return send({ id: "run_hwlab_minimax_m3", status: "pending", backendProfile: "minimax-m3", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_minimax_m3/commands") {
assert.equal(body.payload.providerProfile, "minimax-m3");
assert.equal(body.payload.sessionId, "ses_agentrun_server_test_minimax_m3");
assert.equal(body.payload.hwlabSessionId, "ses_server-test-minimax-m3");
assert.match(body.payload.prompt, /MiniMax-M3/u);
return send({ id: "cmd_hwlab_minimax_m3", runId: "run_hwlab_minimax_m3", state: "pending", type: "turn", seq: 1 });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_minimax_m3/commands") {
return send({ items: [
{ id: "cmd_hwlab_minimax_m3", runId: "run_hwlab_minimax_m3", state: "completed", type: "turn", seq: 1, idempotencyKey: "trc_server-test-agentrun-minimax-m3", payload: { traceId: "trc_server-test-agentrun-minimax-m3", conversationId: "cnv_server-test-minimax-m3", hwlabSessionId: "ses_server-test-minimax-m3", threadId: null, providerProfile: "minimax-m3" } }
] });
}
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_minimax_m3/runner-jobs") {
assert.equal(body.commandId, "cmd_hwlab_minimax_m3");
return send({
action: "create-kubernetes-job",
runId: "run_hwlab_minimax_m3",
commandId: "cmd_hwlab_minimax_m3",
attemptId: "attempt_hwlab_minimax_m3",
runnerId: "runner_hwlab_minimax_m3",
namespace: "agentrun-v01",
jobName: "agentrun-v01-runner-hwlab-minimax-m3",
jobIdentity: { namespace: "agentrun-v01", name: "agentrun-v01-runner-hwlab-minimax-m3" },
runner: { attemptId: "attempt_hwlab_minimax_m3", runnerId: "runner_hwlab_minimax_m3" }
});
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_minimax_m3/events") {
return send({ items: [
{ id: "evt_minimax_1", runId: "run_hwlab_minimax_m3", seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId: "cmd_hwlab_minimax_m3", attemptId: "attempt_hwlab_minimax_m3", jobName: "agentrun-v01-runner-hwlab-minimax-m3", namespace: "agentrun-v01" }, createdAt: "2026-06-02T00:00:00.000Z" },
{ id: "evt_minimax_2", runId: "run_hwlab_minimax_m3", seq: 2, type: "assistant_message", payload: { commandId: "cmd_hwlab_minimax_m3", text: "AGENTRUN_MINIMAX_M3_OK" }, createdAt: "2026-06-02T00:00:01.000Z" },
{ id: "evt_minimax_3", runId: "run_hwlab_minimax_m3", seq: 3, type: "terminal_status", payload: { commandId: "cmd_hwlab_minimax_m3", terminalStatus: "completed" }, createdAt: "2026-06-02T00:00:02.000Z" }
] });
}
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_minimax_m3/commands/cmd_hwlab_minimax_m3/result") {
return send({
runId: "run_hwlab_minimax_m3",
commandId: "cmd_hwlab_minimax_m3",
attemptId: "attempt_hwlab_minimax_m3",
runnerId: "runner_hwlab_minimax_m3",
jobName: "agentrun-v01-runner-hwlab-minimax-m3",
namespace: "agentrun-v01",
status: "completed",
runStatus: "completed",
commandState: "completed",
terminalStatus: "completed",
completed: true,
reply: "AGENTRUN_MINIMAX_M3_OK",
lastSeq: 3,
eventCount: 3,
sessionRef: { sessionId: "ses_agentrun_server_test_minimax_m3", conversationId: "cnv_server-test-minimax-m3", threadId: null }
});
}
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_agentrun_minimax_m3" })}\n`);
});
await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve));
const agentRunPort = agentRunServer.address().port;
const minimaxOwnerSessions = new Map([["ses_server-test-minimax-m3", testAgentSessionRecord({
sessionId: "ses_server-test-minimax-m3",
conversationId: "cnv_server-test-minimax-m3",
status: "idle"
})]]);
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);
minimaxOwnerSessions.set(record.id, record);
return record;
},
async getAgentSession(sessionId) {
return minimaxOwnerSessions.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-minimax-m3";
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-minimax-m3",
sessionId: "ses_server-test-minimax-m3",
providerProfile: "minimax-m3",
message: "MiniMax-M3 AgentRun adapter smoke"
})
});
assert.equal(submit.status, 202);
const accepted = await submit.json();
assert.equal(accepted.shortConnection, true);
const payload = await pollAgentResult(port, traceId);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.provider, "minimax-m3");
assert.equal(payload.backend, "agentrun-v01/minimax-m3");
assert.equal(payload.infrastructureBackend, "agentrun-v01/minimax-m3");
assert.equal(payload.runner.kind, "agentrun-v01-shared-runner");
assert.equal(payload.runner.provider, "minimax-m3");
assert.equal(payload.runner.codexStdio, false);
assert.equal(payload.agentRun.backendProfile, "minimax-m3");
assert.equal(payload.sessionId, "ses_server-test-minimax-m3");
assert.equal(payload.agentRun.sessionId, "ses_agentrun_server_test_minimax_m3");
assert.equal(payload.providerTrace.backendProfile, "minimax-m3");
assert.equal(payload.agentRun.jobName, "agentrun-v01-runner-hwlab-minimax-m3");
assert.equal(payload.reply.content, "AGENTRUN_MINIMAX_M3_OK");
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1);
} 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 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);
return send({ id: "cmd_hwlab_profile_switch_m3", runId: "run_hwlab_profile_switch_m3", state: "pending", type: "turn", seq: 1 });
}
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 === "POST" && url.pathname === "/api/v1/runs/run_hwlab_profile_switch_m3/runner-jobs") {
assert.equal(body.commandId, "cmd_hwlab_profile_switch_m3");
return send({
action: "create-kubernetes-job",
runId: "run_hwlab_profile_switch_m3",
commandId: "cmd_hwlab_profile_switch_m3",
attemptId: "attempt_hwlab_profile_switch_m3",
runnerId: "runner_hwlab_profile_switch_m3",
namespace: "agentrun-v01",
jobName: "agentrun-v01-runner-profile-switch-m3",
jobIdentity: { namespace: "agentrun-v01", name: "agentrun-v01-runner-profile-switch-m3" },
runner: { attemptId: "attempt_hwlab_profile_switch_m3", runnerId: "runner_hwlab_profile_switch_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,
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);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.sessionId, hwlabSessionId);
assert.equal(payload.infrastructureBackend, "agentrun-v01/minimax-m3");
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(payload.reply.content, "AGENTRUN_HWLAB_MINIMAX_M3_OK");
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.agentRun.backendProfile, "minimax-m3");
assert.equal(stored.session.agentRun.sessionId, minimaxSessionId);
} 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, "projecting");
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()));
});
}
});
test("cloud api resumes AgentRun projection from durable state after restart", async () => {
const calls = [];
const durableEvents = [];
const projectionStates = new Map();
const ownerWrites = [];
const traceId = "trc_projection_resume_restart";
const runId = "run_projection_resume_restart";
const commandId = "cmd_projection_resume_restart";
const sessionId = "ses_projection_resume_restart";
const conversationId = "cnv_projection_resume_restart";
const finalText = "OK from resumed AgentRun projection.";
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, afterSeq: url.searchParams.get("afterSeq") });
const send = (body, status = 200) => {
response.writeHead(status, { "content-type": "application/json" });
response.end(`${JSON.stringify(body)}\n`);
};
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) {
assert.equal(url.searchParams.get("afterSeq"), "21");
return send({ items: [
{ id: "evt_projection_resume_22", runId, seq: 22, type: "assistant_message", payload: { commandId, text: finalText }, createdAt: "2026-06-18T16:31:11.000Z" },
{ id: "evt_projection_resume_23", runId, seq: 23, type: "terminal_status", payload: { commandId, terminalStatus: "completed" }, createdAt: "2026-06-18T16:31:12.000Z" }
] });
}
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands/${commandId}/result`) {
return send({
runId,
commandId,
status: "completed",
runStatus: "completed",
commandState: "completed",
terminalStatus: "completed",
completed: true,
reply: finalText,
lastSeq: 23,
eventCount: 23,
sessionRef: { sessionId, conversationId, threadId: "thread-projection-resume-restart" }
});
}
return send({ ok: false, message: `unexpected ${request.method} ${url.pathname}` }, 404);
});
await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve));
const agentRunPort = agentRunServer.address().port;
const session = testAgentSessionRecord({
sessionId,
conversationId,
projectId: "prj_hwpod_workbench",
status: "running",
traceId,
updatedAt: "2026-06-18T16:31:04.000Z",
session: {
traceResults: {
[traceId]: {
traceId,
status: "running",
sessionId,
conversationId,
updatedAt: "2026-06-18T16:31:04.000Z",
agentRun: {
adapter: "agentrun-v01",
managerUrl: `http://127.0.0.1:${agentRunPort}`,
runId,
commandId,
traceId,
lastSeq: 0,
status: "running",
commandState: "running",
backendProfile: "codex",
valuesPrinted: false
},
valuesRedacted: true
}
}
}
});
durableEvents.push({
traceId,
seq: 27,
source: "agentrun",
sourceSeq: 21,
type: "backend",
status: "running",
label: "agentrun:backend:mcpServer/startupStatus/updated",
commandId,
runId,
createdAt: "2026-06-18T16:31:04.200Z",
valuesPrinted: false
});
projectionStates.set(traceId, {
traceId,
sessionId,
conversationId,
threadId: "thread-projection-resume-restart",
ownerUserId: TEST_AGENT_ACTOR.id,
ownerRole: TEST_AGENT_ACTOR.role,
runId,
commandId,
sourceRunId: runId,
sourceCommandId: commandId,
managerUrl: `http://127.0.0.1:${agentRunPort}`,
backendProfile: "codex",
lastSourceSeq: 21,
lastAgentRunSeq: 21,
lastProjectedSeq: 27,
sourceLatestSeq: 21,
upstreamLatestSeq: 21,
projectionStatus: "projecting",
projectionHealth: "healthy",
resultSyncState: "not_started",
createdAt: "2026-06-18T16:31:04.000Z",
updatedAt: "2026-06-18T16:31:04.000Z",
valuesPrinted: false
});
const runtimeStore = {
async queryAgentTraceEvents(params = {}) {
return { events: durableEvents.filter((event) => event.traceId === params.traceId), count: durableEvents.length };
},
async writeAgentTraceEvent(params = {}) {
const event = params.event ?? params.traceEvent ?? params;
durableEvents.push(event);
return { written: true, traceEvent: event };
},
async getWorkbenchProjectionState(params = {}) {
const projectionState = projectionStates.get(params.traceId) ?? null;
return { projectionState, found: Boolean(projectionState) };
},
async queryWorkbenchProjectionStates() {
return { states: [...projectionStates.values()], count: projectionStates.size };
},
async writeWorkbenchProjectionState(params = {}) {
const projectionState = params.projectionState ?? params.state ?? params;
projectionStates.set(projectionState.traceId, projectionState);
return { written: true, projectionState };
}
};
const accessController = {
required: false,
store: {
async listAgentSessionsForUser() { return [session]; },
async getAgentSessionByTraceId(requestTraceId) { return requestTraceId === traceId ? session : null; }
},
async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; },
async recordAgentSessionOwner(input = {}) {
ownerWrites.push(input);
return { ...session, status: input.status ?? session.status, session: input.session ?? session.session };
}
};
const server = createCloudApiServer({
accessController,
runtimeStore,
codeAgentChatResults: new Map(),
env: {
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1",
HWLAB_CODE_AGENT_AGENTRUN_PROJECTION_RESUME_ENABLED: "1",
HWLAB_CODE_AGENT_AGENTRUN_PROJECTION_RESUME_INITIAL_DELAY_MS: "1",
HWLAB_CODE_AGENT_AGENTRUN_PROJECTION_RESUME_INTERVAL_MS: "0",
HWLAB_CODE_AGENT_AGENTRUN_PROJECTION_RESUME_MIN_AGE_MS: "0",
HWLAB_CODE_AGENT_AGENTRUN_PROJECTION_RESUME_BATCH_SIZE: "10"
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
for (let index = 0; index < 80 && projectionStates.get(traceId)?.resultSyncState !== "synced"; index += 1) await delay(10);
assert.ok(calls.some((call) => call.path === `/api/v1/runs/${runId}/events` && call.afterSeq === "21"));
assert.equal(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands/${commandId}/result`), true);
assert.equal(projectionStates.get(traceId)?.lastSourceSeq, 23);
assert.equal(projectionStates.get(traceId)?.resultSyncState, "synced");
assert.equal(ownerWrites.some((write) => write.status === "completed"), true);
assert.ok(durableEvents.some((event) => event.sourceSeq === 22));
} 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 skips AgentRun refresh when terminal snapshot is already complete (#1422)", async () => {
const calls = [];
const traceId = "trc_issue1422_trace_terminal_snapshot";
const runId = "run_issue1422_trace_terminal_snapshot";
const commandId = "cmd_issue1422_trace_terminal_snapshot";
const finalText = "trace endpoint should reuse the local terminal snapshot.";
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 });
response.writeHead(500, { "content-type": "application/json" });
response.end(`${JSON.stringify({ ok: false, message: `unexpected refresh ${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();
traceStore.append(traceId, {
type: "backend",
status: "running",
label: "agentrun:backend:runner-job-created",
source: "agentrun",
sourceSeq: 1,
runId,
commandId,
valuesPrinted: false
});
traceStore.append(traceId, {
type: "result",
status: "completed",
label: "agentrun:terminal:completed",
source: "agentrun",
sourceSeq: 3,
runId,
commandId,
terminal: true,
valuesPrinted: false
});
const codeAgentChatResults = new Map([[traceId, {
accepted: true,
status: "completed",
shortConnection: true,
traceId,
conversationId: "cnv_issue1422_trace_terminal_snapshot",
sessionId: "ses_issue1422_trace_terminal_snapshot",
threadId: "thread-issue1422-trace-terminal-snapshot",
ownerUserId: TEST_AGENT_ACTOR.id,
ownerRole: TEST_AGENT_ACTOR.role,
finalResponse: { text: finalText, textChars: finalText.length, role: "assistant", status: "completed", traceId, valuesPrinted: false },
traceSummary: {
traceId,
source: "agentrun-command-result",
sourceEventCount: 3,
terminalStatus: "completed",
agentRun: { runId, commandId, lastSeq: 3, valuesPrinted: false },
valuesPrinted: false
},
agentRun: {
adapter: "agentrun-v01",
managerUrl: `http://127.0.0.1:${agentRunPort}`,
runId,
commandId,
traceId,
lastSeq: 3,
status: "completed",
commandState: "completed",
terminalStatus: "completed",
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", valuesPrinted: false },
valuesPrinted: false
},
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", valuesPrinted: false },
valuesPrinted: false
}]]);
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/${traceId}?limit=1`, {
headers: { cookie: "hwlab_session=test-stub-session" }
});
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.status, "completed");
assert.equal(body.eventCount, 2);
assert.equal(body.agentRun.commandId, commandId);
assert.equal(body.finalResponse.text, finalText);
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 trace does not schedule terminal side effects from GET reads (#1519)", async () => {
const ownerCalls = [];
const factCalls = [];
let releaseOwnerWrite;
let ownerWriteReleased = false;
const ownerWrite = new Promise((resolve) => {
releaseOwnerWrite = () => {
if (ownerWriteReleased) return;
ownerWriteReleased = true;
resolve();
};
});
const traceId = "trc_issue1422_trace_effects_async";
const runId = "run_issue1422_trace_effects_async";
const commandId = "cmd_issue1422_trace_effects_async";
const finalText = "trace reads should not wait for terminal side effects.";
const traceStore = createCodeAgentTraceStore();
traceStore.append(traceId, {
type: "backend",
status: "running",
label: "agentrun:backend:run-created",
source: "agentrun",
sourceSeq: 1,
runId,
commandId,
valuesPrinted: false
});
traceStore.append(traceId, {
type: "result",
status: "completed",
label: "agentrun:terminal:completed",
source: "agentrun",
sourceSeq: 3,
runId,
commandId,
terminal: true,
valuesPrinted: false
});
const codeAgentChatResults = new Map([[traceId, {
accepted: true,
status: "completed",
shortConnection: true,
traceId,
conversationId: "cnv_issue1422_trace_effects_async",
sessionId: "ses_issue1422_trace_effects_async",
threadId: "thread-issue1422-trace-effects-async",
ownerUserId: TEST_AGENT_ACTOR.id,
ownerRole: TEST_AGENT_ACTOR.role,
finalResponse: { text: finalText, textChars: finalText.length, role: "assistant", status: "completed", traceId, valuesPrinted: false },
traceSummary: {
traceId,
source: "agentrun-command-result",
sourceEventCount: 3,
terminalStatus: "completed",
finalAssistantRow: { role: "assistant", status: "completed", textChars: finalText.length, textPreview: finalText, messageId: `msg_${traceId.slice(4)}`, valuesPrinted: false },
agentRun: { runId, commandId, lastSeq: 3, valuesPrinted: false },
valuesPrinted: false
},
agentRun: {
adapter: "agentrun-v01",
managerUrl: "http://127.0.0.1:1",
runId,
commandId,
traceId,
lastSeq: 3,
status: "completed",
commandState: "completed",
terminalStatus: "completed",
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", valuesPrinted: false },
valuesPrinted: false
},
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", valuesPrinted: false },
valuesPrinted: false
}]]);
const server = createCloudApiServer({
traceStore,
codeAgentChatResults,
env: {
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601",
HWLAB_ENVIRONMENT: "v02",
HWLAB_GITOPS_PROFILE: "v02"
},
sessionRegistry: {
recordFact(conversationId, fact) {
factCalls.push({ conversationId, fact });
return { ok: true };
}
},
accessController: {
required: false,
async authenticate() {
return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION };
},
async recordAgentSessionOwner(input) {
ownerCalls.push(input);
await ownerWrite;
return { ok: true, sessionId: input.sessionId };
}
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const fetchTrace = async () => {
const response = await Promise.race([
fetch(`http://127.0.0.1:${port}/v1/agent/traces/${traceId}?limit=1`, {
headers: { cookie: "hwlab_session=test-stub-session" }
}),
delay(250).then(() => null)
]);
assert.ok(response, "trace read should not wait for terminal side effects to finish");
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.status, "completed");
assert.equal(body.eventCount, 2);
assert.equal(body.finalResponse.text, finalText);
};
for (let i = 0; i < 2; i += 1) {
await fetchTrace();
}
for (let i = 0; i < 20 && ownerCalls.length < 1; i += 1) await delay(10);
assert.equal(ownerCalls.length, 0);
assert.equal(factCalls.length, 0);
assert.equal(codeAgentChatResults.get(traceId).turnStatusTerminalEffects, undefined);
releaseOwnerWrite();
assert.equal(codeAgentChatResults.get(traceId).turnStatusTerminalEffects, undefined);
} finally {
releaseOwnerWrite?.();
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api turn status skips AgentRun refresh for complete terminal evidence (#1422)", async () => {
const calls = [];
const traceId = "trc_issue1422_terminal_fast_path";
const runId = "run_issue1422_terminal_fast_path";
const commandId = "cmd_issue1422_terminal_fast_path";
const finalText = "终态结果已经由 AgentRun command result 投影完成。";
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 });
response.writeHead(500, { "content-type": "application/json" });
response.end(`${JSON.stringify({ ok: false, message: `unexpected refresh ${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: "completed",
shortConnection: true,
traceId,
conversationId: "cnv_issue1422_terminal_fast_path",
sessionId: "ses_issue1422_terminal_fast_path",
threadId: "thread-issue1422-terminal-fast-path",
ownerUserId: TEST_AGENT_ACTOR.id,
ownerRole: TEST_AGENT_ACTOR.role,
finalResponse: { text: finalText, textChars: finalText.length, role: "assistant", status: "completed", traceId, valuesPrinted: false },
traceSummary: {
traceId,
source: "agentrun-command-result",
sourceEventCount: 3,
terminalStatus: "completed",
finalAssistantRow: { role: "assistant", status: "completed", textChars: finalText.length, textPreview: finalText, messageId: `msg_${traceId.slice(4)}`, valuesPrinted: false },
agentRun: { runId, commandId, lastSeq: 3, valuesPrinted: false },
valuesPrinted: false
},
agentRun: {
adapter: "agentrun-v01",
managerUrl: `http://127.0.0.1:${agentRunPort}`,
runId,
commandId,
traceId,
lastSeq: 3,
status: "completed",
commandState: "completed",
terminalStatus: "completed",
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", valuesPrinted: false },
valuesPrinted: false
},
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", 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, "completed");
assert.equal(body.terminal, true);
assert.equal(body.finalResponse.text, finalText);
assert.equal(body.traceSummary.agentRun.commandId, commandId);
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 does not record terminal side effects from GET reads (#1519)", async () => {
const ownerCalls = [];
const factCalls = [];
let releaseOwnerWrite;
let ownerWriteReleased = false;
const ownerWrite = new Promise((resolve) => {
releaseOwnerWrite = () => {
if (ownerWriteReleased) return;
ownerWriteReleased = true;
resolve();
};
});
const traceId = "trc_issue1422_terminal_effects_once";
const runId = "run_issue1422_terminal_effects_once";
const commandId = "cmd_issue1422_terminal_effects_once";
const finalText = "终态副作用只需要收敛一次。";
const codeAgentChatResults = new Map([[traceId, {
accepted: true,
status: "completed",
shortConnection: true,
traceId,
conversationId: "cnv_issue1422_terminal_effects_once",
sessionId: "ses_issue1422_terminal_effects_once",
threadId: "thread-issue1422-terminal-effects-once",
ownerUserId: TEST_AGENT_ACTOR.id,
ownerRole: TEST_AGENT_ACTOR.role,
finalResponse: { text: finalText, textChars: finalText.length, role: "assistant", status: "completed", traceId, valuesPrinted: false },
traceSummary: {
traceId,
source: "agentrun-command-result",
sourceEventCount: 3,
terminalStatus: "completed",
finalAssistantRow: { role: "assistant", status: "completed", textChars: finalText.length, textPreview: finalText, messageId: `msg_${traceId.slice(4)}`, valuesPrinted: false },
agentRun: { runId, commandId, lastSeq: 3, valuesPrinted: false },
valuesPrinted: false
},
agentRun: {
adapter: "agentrun-v01",
managerUrl: "http://127.0.0.1:1",
runId,
commandId,
traceId,
lastSeq: 3,
status: "completed",
commandState: "completed",
terminalStatus: "completed",
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", valuesPrinted: false },
valuesPrinted: false
},
providerTrace: { traceId, runId, commandId, terminalStatus: "completed", valuesPrinted: false },
valuesPrinted: false
}]]);
const server = createCloudApiServer({
traceStore: createCodeAgentTraceStore(),
codeAgentChatResults,
env: {
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601",
HWLAB_ENVIRONMENT: "v02",
HWLAB_GITOPS_PROFILE: "v02"
},
sessionRegistry: {
recordFact(conversationId, fact) {
factCalls.push({ conversationId, fact });
return { ok: true };
}
},
accessController: {
required: false,
async authenticate() {
return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION };
},
async recordAgentSessionOwner(input) {
ownerCalls.push(input);
await ownerWrite;
return { ok: true, sessionId: input.sessionId };
}
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const fetchTurnStatus = async () => {
const response = await Promise.race([
fetch(`http://127.0.0.1:${port}/v1/agent/turns/${traceId}`, {
headers: { cookie: "hwlab_session=test-stub-session" }
}),
delay(250).then(() => null)
]);
assert.ok(response, "turn status should not wait for terminal side effects to finish");
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.status, "completed");
assert.equal(body.finalResponse.text, finalText);
};
for (let i = 0; i < 2; i += 1) {
await fetchTurnStatus();
}
for (let i = 0; i < 20 && ownerCalls.length < 1; i += 1) await delay(10);
assert.equal(ownerCalls.length, 0);
assert.equal(factCalls.length, 0);
assert.equal(codeAgentChatResults.get(traceId).turnStatusTerminalEffects, undefined);
releaseOwnerWrite();
assert.equal(codeAgentChatResults.get(traceId).turnStatusTerminalEffects, undefined);
} finally {
releaseOwnerWrite?.();
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api legacy trace read does not repair historical AgentRun trace after lastTraceId advances (#1519)", async () => {
const calls = [];
const ownerSessions = new Map();
const runId = "run_issue955_historical";
const firstTraceId = "trc_issue955_historical_first";
const secondTraceId = "trc_issue955_historical_second";
const firstCommandId = "cmd_issue955_historical_first";
const secondCommandId = "cmd_issue955_historical_second";
const firstFinalText = "目前只有一个 HWPOD 可用:\n\n- d601-f103-v2 (board: D601-F103-V2 / STM32F103)\n\nAPI 返回 count=1, availableCount=1。";
const secondFinalText = "编译成功!hwpod build completed。";
const events = [
{ id: "evt_issue955_hist_first_created", runId, seq: 1, type: "command_created", payload: { commandId: firstCommandId }, createdAt: "2026-06-06T01:43:23.000Z" },
{ id: "evt_issue955_hist_first_tool", runId, seq: 25, 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}', commandId: firstCommandId }, createdAt: "2026-06-06T01:43:46.000Z" },
{ id: "evt_issue955_hist_first_message", runId, seq: 34, type: "assistant_message", payload: { commandId: firstCommandId, itemId: "msg_issue955_first", text: firstFinalText }, createdAt: "2026-06-06T01:46:19.000Z" },
{ id: "evt_issue955_hist_first_done", runId, seq: 35, type: "terminal_status", payload: { commandId: firstCommandId, terminalStatus: "completed" }, createdAt: "2026-06-06T01:46:19.000Z" },
{ id: "evt_issue955_hist_second_created", runId, seq: 36, type: "command_created", payload: { commandId: secondCommandId }, createdAt: "2026-06-06T01:46:11.000Z" },
{ id: "evt_issue955_hist_second_message", runId, seq: 58, type: "assistant_message", payload: { commandId: secondCommandId, itemId: "msg_issue955_second", text: secondFinalText }, createdAt: "2026-06-06T01:46:28.000Z" },
{ id: "evt_issue955_hist_second_done", runId, seq: 67, 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, search: url.search });
const send = (data) => {
response.writeHead(200, { "content-type": "application/json" });
response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_issue955_historical" })}\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:46.360Z", payload: { traceId: firstTraceId, conversationId: "cnv_issue955_historical", hwlabSessionId: "ses_issue955_historical", threadId: "thread-issue955-historical", providerProfile: "deepseek" } },
{ id: secondCommandId, runId, state: "completed", idempotencyKey: secondTraceId, createdAt: "2026-06-06T01:46:11.000Z", updatedAt: "2026-06-06T01:46:28.000Z", payload: { traceId: secondTraceId, conversationId: "cnv_issue955_historical", hwlabSessionId: "ses_issue955_historical", threadId: "thread-issue955-historical", 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", conversationId: "cnv_issue955_historical", threadId: "thread-issue955-historical" } });
}
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands/${secondCommandId}/result`) {
return send({ runId, commandId: secondCommandId, status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, reply: secondFinalText, lastSeq: 67, eventCount: 67, sessionRef: { sessionId: "ses_agentrun_deepseek_issue955", conversationId: "cnv_issue955_historical", threadId: "thread-issue955-historical" } });
}
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) {
return send({ items: events });
}
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;
ownerSessions.set("ses_issue955_historical", testAgentSessionRecord({
sessionId: "ses_issue955_historical",
projectId: "prj_v02_code_agent",
conversationId: "cnv_issue955_historical",
threadId: "thread-issue955-historical",
lastTraceId: secondTraceId,
status: "active",
session: {
messages: [
{ id: "msg_issue955_user_first", role: "user", text: "看看有几个hwpod可用", traceId: firstTraceId, status: "submitted" },
{ id: "msg_issue955_user_second", role: "user", text: "试一下编译 d601-f103-v2", traceId: secondTraceId, status: "submitted" },
{ id: "msg_issue955_second", role: "agent", text: firstFinalText, traceId: secondTraceId, status: "idle" }
],
agentRun: { adapter: "agentrun-v01", managerUrl: `http://127.0.0.1:${agentRunPort}`, runId, commandId: firstCommandId, backendProfile: "deepseek", terminalStatus: "completed", lastSeq: 35, valuesPrinted: false },
finalResponse: { text: firstFinalText, textChars: firstFinalText.length, role: "assistant", status: "completed", traceId: secondTraceId, valuesPrinted: false },
traceSummary: { traceId: secondTraceId, source: "code-agent-derived-session-evidence", sourceEventCount: 36, terminalStatus: "completed", agentRun: { runId, commandId: firstCommandId, lastSeq: 35, valuesPrinted: false }, valuesPrinted: false },
traceResults: {
[secondTraceId]: {
traceId: secondTraceId,
status: "completed",
conversationId: "cnv_issue955_historical",
sessionId: "ses_issue955_historical",
threadId: "thread-issue955-historical",
finalResponse: { text: firstFinalText, textChars: firstFinalText.length, role: "assistant", status: "completed", traceId: secondTraceId, valuesPrinted: false },
traceSummary: { traceId: secondTraceId, source: "agent-session-trace-repair", sourceEventCount: 4, 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, backendProfile: "deepseek", terminalStatus: "completed", lastSeq: 35, valuesPrinted: false },
valuesRedacted: true
}
},
valuesRedacted: true
}
}));
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: "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 || session.session?.messages?.some((message) => message.traceId === traceId)) ?? null;
},
async recordAgentSessionOwner(input) {
const existing = ownerSessions.get(input.sessionId) ?? testAgentSessionRecord(input);
const nextSession = { ...(existing.session ?? {}), ...(input.session ?? {}) };
nextSession.traceResults = { ...(existing.session?.traceResults ?? {}), ...(input.session?.traceResults ?? {}) };
const record = testAgentSessionRecord({ ...existing, ...input, session: nextSession });
ownerSessions.set(record.id, record);
return record;
}
}
});
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();
assert.equal(body.traceId, firstTraceId);
assert.equal(body.status, "missing");
assert.equal(body.projectionStatus, "unknown");
assert.equal(body.eventCount, 0);
assert.deepEqual(calls, []);
assert.equal(ownerSessions.get("ses_issue955_historical").session.traceResults[firstTraceId], undefined);
const secondResultResponse = await fetch(`http://127.0.0.1:${port}/v1/agent/chat/result/${secondTraceId}`, {
headers: { cookie: "hwlab_session=test-stub-session" }
});
assert.equal(secondResultResponse.status, 200);
const secondResult = await secondResultResponse.json();
assert.equal(secondResult.traceId, secondTraceId);
assert.equal(secondResult.projectionStatus, "projecting");
assert.equal(secondResult.agentRun.commandId, firstCommandId);
assert.deepEqual(calls, []);
assert.equal(ownerSessions.get("ses_issue955_historical").session.traceResults[secondTraceId].agentRun.commandId, firstCommandId);
} 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 result polling does not repair polluted completed AgentRun memory cache (#1519)", async () => {
const calls = [];
const runId = "run_issue955_live_cache";
const firstTraceId = "trc_issue955_live_cache_first";
const secondTraceId = "trc_issue955_live_cache_second";
const firstCommandId = "cmd_issue955_live_cache_first";
const secondCommandId = "cmd_issue955_live_cache_second";
const firstFinalText = "目前只有一个 HWPOD 可用:d601-f103-v2。";
const secondFinalText = "编译成功!hwpod build completedKeil MDK USART atk_f103.hex queued。";
const events = [
{ id: "evt_issue955_live_first_message", runId, seq: 10, type: "assistant_message", payload: { commandId: firstCommandId, itemId: "msg_issue955_live_first", text: firstFinalText }, createdAt: "2026-06-06T01:46:19.000Z" },
{ id: "evt_issue955_live_first_done", runId, seq: 11, type: "terminal_status", payload: { commandId: firstCommandId, terminalStatus: "completed" }, createdAt: "2026-06-06T01:46:19.000Z" },
{ id: "evt_issue955_live_second_message", runId, seq: 20, type: "assistant_message", payload: { commandId: secondCommandId, itemId: "msg_issue955_live_second", text: secondFinalText }, createdAt: "2026-06-06T01:46:28.000Z" },
{ id: "evt_issue955_live_second_done", runId, seq: 21, 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, search: url.search });
const send = (data) => {
response.writeHead(200, { "content-type": "application/json" });
response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_issue955_live_cache" })}\n`);
};
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands`) {
return send({ items: [
{ id: firstCommandId, runId, state: "completed", idempotencyKey: firstTraceId, payload: { traceId: firstTraceId, conversationId: "cnv_issue955_live_cache", hwlabSessionId: "ses_issue955_live_cache", threadId: "thread-issue955-live-cache" } },
{ id: secondCommandId, runId, state: "completed", idempotencyKey: secondTraceId, payload: { traceId: secondTraceId, conversationId: "cnv_issue955_live_cache", hwlabSessionId: "ses_issue955_live_cache", threadId: "thread-issue955-live-cache" } }
] });
}
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: 11, eventCount: 11 });
}
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands/${secondCommandId}/result`) {
return send({ runId, commandId: secondCommandId, status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, reply: secondFinalText, lastSeq: 21, eventCount: 21 });
}
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) {
return send({ items: events });
}
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();
codeAgentChatResults.set(secondTraceId, {
accepted: true,
status: "completed",
shortConnection: true,
traceId: secondTraceId,
conversationId: "cnv_issue955_live_cache",
sessionId: "ses_issue955_live_cache",
threadId: "thread-issue955-live-cache",
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: secondTraceId, valuesPrinted: false },
traceSummary: { traceId: secondTraceId, source: "code-agent-derived-session-evidence", sourceEventCount: 11, terminalStatus: "completed", agentRun: { runId, commandId: firstCommandId, lastSeq: 11, valuesPrinted: false }, valuesPrinted: false },
agentRun: {
adapter: "agentrun-v01",
managerUrl: `http://127.0.0.1:${agentRunPort}`,
runId,
commandId: firstCommandId,
traceId: secondTraceId,
lastSeq: 11,
terminalStatus: "completed",
providerTrace: { traceId: secondTraceId, runId, commandId: firstCommandId, terminalStatus: "completed", valuesPrinted: false },
valuesPrinted: false
},
providerTrace: { traceId: secondTraceId, runId, commandId: firstCommandId, terminalStatus: "completed", 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/chat/result/${secondTraceId}`, {
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, secondTraceId);
assert.equal(body.status, "completed");
assert.equal(body.projectionStatus, "projecting");
assert.equal(body.agentRun.commandId, firstCommandId);
assert.equal(body.agentRun.traceId, secondTraceId);
assert.equal(body.terminalEvidence.available, true);
assert.equal(body.terminalEvidence.agentRun.commandId, firstCommandId);
assert.equal(body.terminalEvidence.finalResponse.traceId, secondTraceId);
assert.equal(body.reply.content, firstFinalText);
assert.match(text, /目前只有一个 HWPOD 可用/u);
assert.doesNotMatch(text, /编译成功/u);
assert.equal(text.includes('"fallback"'), false);
assert.deepEqual(calls, []);
assert.equal(codeAgentChatResults.get(secondTraceId).agentRun.commandId, firstCommandId);
} 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 result polling does not query AgentRun registry when projection is stale (#1519)", async () => {
const calls = [];
const runId = "run_issue955_registry_missing";
const traceId = "trc_issue955_registry_missing";
const staleCommandId = "cmd_issue955_registry_stale";
const staleFinalText = "目前只有一个 HWPOD 可用:d601-f103-v2。";
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 });
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands`) {
response.writeHead(200, { "content-type": "application/json" });
response.end(`${JSON.stringify({ ok: true, data: { items: [] }, traceId: "trc_fake_issue955_missing" })}\n`);
return;
}
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();
codeAgentChatResults.set(traceId, {
accepted: true,
status: "completed",
shortConnection: true,
traceId,
conversationId: "cnv_issue955_registry_missing",
sessionId: "ses_issue955_registry_missing",
threadId: "thread-issue955-registry-missing",
ownerUserId: TEST_AGENT_ACTOR.id,
ownerRole: TEST_AGENT_ACTOR.role,
reply: { role: "assistant", content: staleFinalText },
finalResponse: { text: staleFinalText, textChars: staleFinalText.length, role: "assistant", status: "completed", traceId, valuesPrinted: false },
agentRun: { adapter: "agentrun-v01", managerUrl: `http://127.0.0.1:${agentRunPort}`, runId, commandId: staleCommandId, traceId, terminalStatus: "completed", 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/chat/result/${traceId}`, {
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, traceId);
assert.equal(body.status, "completed");
assert.equal(body.projectionStatus, "projecting");
assert.equal(body.agentRun.commandId, staleCommandId);
assert.match(text, /目前只有一个 HWPOD 可用/u);
assert.deepEqual(calls, []);
assert.equal(calls.some((call) => call.path.includes("/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 result polling compacts large runnerTrace while preserving providerTrace", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-result-compact-"));
const codexHome = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-result-compact-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",
HWLAB_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT: "32",
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"
};
},
async probe() {
return {
...codexStdioReadyFixture({ workspace, codexHome }),
sandbox: "danger-full-access"
};
},
async chat(params = {}) {
const base = codexStdioChatFixture({ workspace, codexHome, params });
const events = Array.from({ length: 160 }, (_, index) => ({
seq: index + 1,
traceId: params.traceId,
type: "assistant_message",
status: "chunk",
label: index === 159 ? "assistant:completed" : "assistant:chunk",
createdAt: "2026-05-23T00:00:00.000Z",
waitingFor: index === 159 ? null : "turn/completed",
valuesPrinted: false
}));
return {
...base,
sandbox: "danger-full-access",
session: {
...base.session,
sandbox: "danger-full-access"
},
runnerTrace: {
...base.runnerTrace,
events,
eventLabels: events.map((event) => event.label),
eventCount: events.length,
lastEvent: events.at(-1)
}
};
},
cancel() {},
reapIdle() {}
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const traceId = "trc_server-test-result-compact";
const manualSession = await createManualAgentSession(port, {
conversationId: "cnv_server-test-result-compact"
});
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: "生成大量 trace 后返回"
})
});
assert.equal(submit.status, 202);
const payload = await pollAgentResult(port, traceId);
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.traceId, traceId);
assert.equal(payload.providerTrace.protocol, "codex-app-server-jsonrpc-stdio");
assert.equal(payload.providerTrace.command, "codex app-server --listen stdio://");
assert.equal(payload.runnerTrace.eventCount, 160);
assert.equal(payload.runnerTrace.eventsCompacted, true);
assert.equal(payload.runnerTrace.events.length, 32);
assert.equal(payload.runnerTrace.events.some((event) => event.label === "trace:compacted"), true);
assert.equal(payload.runnerTrace.lastEvent.label, "assistant:completed");
assert.equal(payload.runnerTrace.eventLabels.includes("trace:compacted"), true);
} 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 answers skills prompt through real Codex stdio and retains trace", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-stdio-skills-"));
const codexHome = path.join(workspace, "codex-home");
const skillsDir = path.join(workspace, "skills");
const fakeCodex = await createFakeCodexCommand();
await mkdir(path.join(skillsDir, "hwlab-test-skill"), { recursive: true });
await prepareFakeCodexHome(codexHome);
await writeFile(path.join(skillsDir, "hwlab-test-skill", "SKILL.md"), [
"---",
"name: hwlab-test-skill",
"description: Test skill mounted for Codex stdio discovery.",
"version: v-test",
"---",
"",
"# HWLAB Test Skill"
].join("\n"));
const server = createCloudApiServer({
env: {
PATH: process.env.PATH,
OPENAI_API_KEY: "test-openai-key-material",
CODEX_HOME: codexHome,
HWLAB_CODE_AGENT_PROVIDER: "codex-stdio",
HWLAB_CODE_AGENT_MODEL: "gpt-test",
HWLAB_CODE_AGENT_CODEX_COMMAND: fakeCodex.command,
HWLAB_CODE_AGENT_CODEX_STDIO_ENABLED: "1",
HWLAB_CODE_AGENT_CODEX_STDIO_SUPERVISOR: "repo-owned",
HWLAB_CODE_AGENT_CODEX_WORKSPACE: workspace,
HWLAB_CODE_AGENT_SKILLS_DIRS: skillsDir,
HWLAB_CODE_AGENT_SKILLS_STRICT: "1",
HWLAB_CODE_AGENT_OPENAI_BASE_URL: "http://127.0.0.1:65535/v1/responses"
},
codexStdioManager: createCodexStdioSessionManager({
idFactory: () => "ses_server_stdio_skills",
createRpcClient: async () => createFakeAppServerClient({
text: "模型泛化回答不应成为 skills 最终回复。"
})
})
});
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_stdio_skills",
traceId: "trc_server_stdio_skills",
message: "列出你可用的skills"
});
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "completed");
assert.equal(payload.provider, "codex-stdio");
assert.equal(payload.backend, "hwlab-cloud-api/codex-app-server-stdio");
assert.equal(payload.capabilityLevel, "long-lived-codex-stdio-session");
assert.equal(payload.providerTrace.sidecarOnly, undefined);
assert.equal(payload.providerTrace.toolName, "codex-app-server.thread/start+turn/start");
assert.equal(payload.skills.status, "ready");
const discovered = payload.skills.items.find((skill) => skill.name === "hwlab-test-skill");
assert.ok(discovered);
assert.equal(discovered.source, path.join(skillsDir, "hwlab-test-skill", "SKILL.md"));
assert.equal(discovered.manifest.path, path.join(skillsDir, "hwlab-test-skill", "SKILL.md"));
assert.equal(discovered.version, "v-test");
assert.ok(payload.toolCalls.some((toolCall) => toolCall.name === "skills.discover" && toolCall.status === "completed"));
assert.match(payload.reply.content, /hwlab-test-skill/u);
assert.match(payload.reply.content, /真实 skill manifest 清单/u);
assert.doesNotMatch(payload.reply.content, /模型泛化回答/u);
assert.ok(payload.runnerTrace.events.some((event) => event.label === "prompt:sent"));
assert.ok(payload.runnerTrace.events.some((event) => event.label === "tool:codex-app-server.thread/start+turn/start:started"));
assert.equal(JSON.stringify(payload).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(fakeCodex.root, { recursive: true, force: true });
}
});
test("cloud api /v1/agent/chat returns structured skills blocker without local skills manifest", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-stdio-skills-missing-"));
const codexHome = path.join(workspace, "codex-home");
const fakeCodex = await createFakeCodexCommand();
await prepareFakeCodexHome(codexHome);
const server = createCloudApiServer({
env: {
PATH: process.env.PATH,
OPENAI_API_KEY: "test-openai-key-material",
CODEX_HOME: codexHome,
HWLAB_CODE_AGENT_PROVIDER: "codex-stdio",
HWLAB_CODE_AGENT_MODEL: "gpt-test",
HWLAB_CODE_AGENT_CODEX_COMMAND: fakeCodex.command,
HWLAB_CODE_AGENT_CODEX_STDIO_ENABLED: "1",
HWLAB_CODE_AGENT_CODEX_STDIO_SUPERVISOR: "repo-owned",
HWLAB_CODE_AGENT_CODEX_WORKSPACE: workspace,
HWLAB_CODE_AGENT_SKILLS_DIRS: path.join(workspace, "missing-skills"),
HWLAB_CODE_AGENT_SKILLS_STRICT: "1",
HWLAB_CODE_AGENT_OPENAI_BASE_URL: "http://127.0.0.1:65535/v1/responses"
},
codexStdioManager: createCodexStdioSessionManager({
idFactory: () => "ses_server_stdio_skills_missing",
createRpcClient: async () => createFakeAppServerClient({
text: "模型泛化回答:alpha-skill"
})
})
});
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_stdio_skills_missing",
traceId: "trc_server_stdio_skills_missing",
message: "列出你可用的skills"
});
validateCodeAgentChatSchema(payload);
assert.equal(payload.status, "failed");
assert.equal(payload.provider, "codex-stdio");
assert.equal(payload.backend, "hwlab-cloud-api/codex-app-server-stdio");
assert.equal(payload.capabilityLevel, "blocked");
assert.equal(payload.error.code, "skills_unavailable");
assert.equal(payload.skills.status, "blocked");
assert.equal(payload.skills.blockers[0].code, "skills_unavailable");
assert.deepEqual(payload.skills.sourceIssues, ["pikasTech/HWLAB#136", "pikasTech/HWLAB#237"]);
assert.ok(payload.toolCalls.some((toolCall) => toolCall.name === "skills.discover" && toolCall.status === "blocked"));
assert.equal(payload.toolCalls.some((toolCall) => toolCall.name === "codex-app-server.thread/start+turn/start"), false);
assert.equal(payload.session.status, "idle");
assert.equal(Object.hasOwn(payload, "reply"), false);
assert.equal(JSON.stringify(payload).includes("alpha-skill"), false);
assert.equal(JSON.stringify(payload).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(fakeCodex.root, { recursive: true, force: true });
}
});
test("cloud api /v1/agent/chat blocks forbidden file paths without leaking values", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-file-block-"));
const server = createCloudApiServer({
env: {
PATH: process.env.PATH,
HWLAB_CODE_AGENT_WORKSPACE: workspace
}
});
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-security-block",
traceId: "trc_server-test-security-block",
message: "cat ../outside.txt"
});
assert.equal(payload.status, "failed");
assert.match(payload.error.code, /^(codex_cli_binary_missing|stdio_protocol_not_wired)$/u);
assert.deepEqual(payload.toolCalls, []);
assert.equal(payload.capabilityLevel, "blocked");
assert.ok(payload.runnerTrace.events.some((event) => event.label === "codex-stdio:blocked"));
assert.equal(Object.hasOwn(payload, "reply"), false);
assert.equal(JSON.stringify(payload).includes("sk-"), false);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api /v1/agent/chat/cancel reports unsupported lifecycle degradation", async () => {
const traceStore = createCodeAgentTraceStore();
traceStore.append("trc_cancel_unsupported", {
type: "request",
status: "running",
label: "request:running",
sessionId: "ses_cancel_unsupported",
sessionStatus: "busy",
sessionLifecycleStatus: "busy"
});
const server = createCloudApiServer({
traceStore,
codexStdioManager: {
get() {
return null;
}
},
env: {
PATH: process.env.PATH,
HWLAB_CODE_AGENT_PROVIDER: "codex-stdio"
}
});
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/cancel`, {
method: "POST",
headers: {
"content-type": "application/json",
"x-trace-id": "trc_cancel_unsupported"
},
body: JSON.stringify({
conversationId: "cnv_cancel_unsupported",
sessionId: "ses_cancel_unsupported",
traceId: "trc_cancel_unsupported"
})
});
assert.equal(response.status, 501);
const payload = await response.json();
assert.equal(payload.status, "degraded");
assert.equal(payload.unsupported, true);
assert.equal(payload.degraded, true);
assert.equal(payload.error.code, "cancel_unsupported");
assert.equal(payload.sessionLifecycleStatus, "failed");
assert.equal(payload.sessionSummary.unsupported, true);
assert.match(payload.sessionSummary.userMessage, /unsupported\/degraded/u);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api /v1/agent/chat/cancel rejects AgentRun trace scope mismatch", async () => {
const traceId = "trc_cancel_scope_mismatch";
const codeAgentChatResults = new Map();
const traceStore = createCodeAgentTraceStore();
codeAgentChatResults.set(traceId, {
traceId,
status: "running",
conversationId: "cnv_cancel_scope",
sessionId: "ses_cancel_scope",
threadId: "thread-cancel-scope",
agentRun: {
runId: "run_cancel_scope",
commandId: "cmd_cancel_scope",
sessionId: "ses_agentrun_cancel_scope",
conversationId: "cnv_cancel_scope",
threadId: "thread-cancel-scope",
managerUrl: "http://127.0.0.1:65535",
status: "running",
commandState: "running"
}
});
const server = createCloudApiServer({
traceStore,
codeAgentChatResults,
env: {
PATH: process.env.PATH,
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
HWLAB_CODE_AGENT_PROVIDER: "agentrun-v01",
HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1"
}
});
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/cancel`, {
method: "POST",
headers: {
"content-type": "application/json",
"x-trace-id": traceId
},
body: JSON.stringify({
conversationId: "cnv_cancel_scope_other",
sessionId: "ses_cancel_scope_other",
threadId: "thread-cancel-scope-other",
traceId
})
});
assert.equal(response.status, 409);
const payload = await response.json();
assert.equal(payload.status, "blocked");
assert.equal(payload.canceled, false);
assert.equal(payload.error.code, "cancel_scope_mismatch");
assert.equal(codeAgentChatResults.get(traceId)?.status, "running");
assert.ok(traceStore.snapshot(traceId)?.events?.some((event) => event.label === "agentrun:cancel:scope_mismatch"));
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api /v1/agent/chat/cancel keeps already terminal AgentRun result instead of overwriting", async () => {
const traceId = "trc_cancel_already_terminal";
const codeAgentChatResults = new Map();
const traceStore = createCodeAgentTraceStore();
codeAgentChatResults.set(traceId, {
traceId,
status: "completed",
finalResponse: "OK",
conversationId: "cnv_cancel_already_terminal",
sessionId: "ses_cancel_already_terminal",
threadId: "thread-cancel-already-terminal",
agentRun: {
runId: "run_cancel_already_terminal",
commandId: "cmd_cancel_already_terminal",
sessionId: "ses_agentrun_cancel_already_terminal",
conversationId: "cnv_cancel_already_terminal",
threadId: "thread-cancel-already-terminal",
status: "completed",
commandState: "completed",
terminalStatus: "completed"
}
});
const accessController = {
required: false,
async authenticate() { return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION }; }
};
const server = createCloudApiServer({
traceStore,
codeAgentChatResults,
accessController,
env: {
PATH: process.env.PATH,
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
HWLAB_CODE_AGENT_PROVIDER: "agentrun-v01"
}
});
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/cancel`, {
method: "POST",
headers: {
"content-type": "application/json",
"x-trace-id": traceId
},
body: JSON.stringify({
conversationId: "cnv_cancel_already_terminal",
sessionId: "ses_cancel_already_terminal",
threadId: "thread-cancel-already-terminal",
traceId
})
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.status, "completed");
assert.equal(payload.finalResponse, "OK");
assert.equal(payload.cancelDisposition.status, "already_terminal");
assert.equal(payload.cancelDisposition.forwarded, false);
assert.equal(codeAgentChatResults.get(traceId).status, "completed");
assert.ok(traceStore.snapshot(traceId)?.events?.some((event) => event.label === "agentrun:cancel:already-terminal"));
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api /v1/agent/chat/cancel returns already-terminal when AgentRun result completed", async () => {
const calls = [];
const traceId = "trc_cancel_terminal_result_authority";
const runId = "run_cancel_terminal_result_authority";
const commandId = "cmd_cancel_terminal_result_authority";
const finalText = "Cancel did not overwrite the completed AgentRun result.";
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 });
const send = (body, status = 200) => {
response.writeHead(status, { "content-type": "application/json" });
response.end(`${JSON.stringify(body)}\n`);
};
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/events`) return send({ items: [] });
if (request.method === "GET" && url.pathname === `/api/v1/runs/${runId}/commands/${commandId}/result`) {
return send({ runId, commandId, status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, reply: finalText, lastSeq: 7, eventCount: 7 });
}
if (request.method === "POST" && url.pathname === `/api/v1/commands/${commandId}/cancel`) return send({ ok: true, canceled: true });
return send({ ok: false, message: `unexpected ${request.method} ${url.pathname}` }, 404);
});
await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve));
const agentRunPort = agentRunServer.address().port;
const traceStore = createCodeAgentTraceStore();
const codeAgentChatResults = new Map();
codeAgentChatResults.set(traceId, {
traceId,
status: "running",
conversationId: "cnv_cancel_terminal_result_authority",
sessionId: "ses_cancel_terminal_result_authority",
threadId: "thread-cancel-terminal-result-authority",
agentRun: {
adapter: "agentrun-v01",
managerUrl: `http://127.0.0.1:${agentRunPort}`,
runId,
commandId,
traceId,
providerTrace: { traceId, runId, commandId, valuesPrinted: false },
lastSeq: 6,
status: "running",
commandState: "running",
valuesPrinted: false
},
valuesRedacted: true
});
const server = createCloudApiServer({
traceStore,
codeAgentChatResults,
env: {
PATH: process.env.PATH,
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
HWLAB_CODE_AGENT_PROVIDER: "agentrun-v01",
HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1"
}
});
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/cancel`, {
method: "POST",
headers: { "content-type": "application/json", "x-trace-id": traceId },
body: JSON.stringify({ traceId, conversationId: "cnv_cancel_terminal_result_authority", sessionId: "ses_cancel_terminal_result_authority", threadId: "thread-cancel-terminal-result-authority" })
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.status, "completed");
assert.equal(payload.cancelDisposition.status, "already_terminal");
assert.equal(payload.cancelDisposition.forwarded, false);
assert.equal(payload.finalResponse.text, finalText);
assert.equal(calls.some((call) => call.method === "POST" && call.path === `/api/v1/commands/${commandId}/cancel`), false);
assert.equal(codeAgentChatResults.get(traceId)?.status, "completed");
} 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 /v1/agent/chat reports Codex stdio blocker instead of structured skills fallback", async () => {
const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-no-skills-"));
const skillsDir = path.join(root, "missing-skills");
const server = createCloudApiServer({
env: {
PATH: process.env.PATH,
HWLAB_CODE_AGENT_WORKSPACE: root
},
skillsDirs: [skillsDir],
skillsDirsExact: true
});
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-skills-missing"
});
const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
method: "POST",
headers: {
"content-type": "application/json",
cookie: manualSession.cookie
},
body: JSON.stringify({
conversationId: manualSession.conversationId,
sessionId: manualSession.sessionId,
message: "列出你可用的skills"
})
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.status, "failed");
assert.equal(payload.provider, "codex-stdio");
assert.notEqual(payload.error.code, "skills_unavailable");
assert.equal(payload.capabilityLevel, "blocked");
assert.equal(payload.skills.status, "not_requested");
assert.deepEqual(payload.toolCalls, []);
assert.ok(payload.runnerLimitations.includes("no-controlled-readonly-fallback"));
assert.equal(Object.hasOwn(payload, "reply"), false);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api /v1/agent/chat does not run unsupported local grep fallback", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "hwlab-agent-tool-blocked-"));
const server = createCloudApiServer({
env: {
PATH: process.env.PATH,
HWLAB_CODE_AGENT_WORKSPACE: workspace
}
});
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-tool-unavailable"
});
const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
method: "POST",
headers: {
"content-type": "application/json",
cookie: manualSession.cookie
},
body: JSON.stringify({
conversationId: manualSession.conversationId,
sessionId: manualSession.sessionId,
message: "请用grep搜索 package.json"
})
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.status, "failed");
assert.notEqual(payload.error.code, "tool_unavailable");
assert.equal(payload.provider, "codex-stdio");
assert.equal(payload.capabilityLevel, "blocked");
assert.deepEqual(payload.toolCalls, []);
assert.ok(payload.runnerLimitations.includes("no-controlled-readonly-fallback"));
assert.equal(Object.hasOwn(payload, "reply"), false);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api /v1/agent/chat does not call delayed provider fallback beyond legacy 4500ms UI timeout", async () => {
let providerCalled = false;
const server = createCloudApiServer({
callCodeAgentProvider: async ({ providerPlan, message, traceId }) => {
providerCalled = true;
await delay(4700);
return {
provider: providerPlan.provider,
model: providerPlan.model,
backend: providerPlan.backend,
content: `延迟真实 provider stub: ${message} / ${traceId}`,
usage: null,
providerTrace: {
source: "delayed-test-provider"
}
};
}
});
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-agent-chat-delayed"
});
const startedAt = Date.now();
const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
method: "POST",
headers: {
"content-type": "application/json",
cookie: manualSession.cookie,
"x-trace-id": "trc_server-test-agent-chat-delayed"
},
body: JSON.stringify({
conversationId: manualSession.conversationId,
sessionId: manualSession.sessionId,
message: "请用一句话说明当前 HWLAB 工作台可以做什么,稍后回答"
})
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(Date.now() - startedAt < 4500, true);
assert.equal(payload.status, "failed");
assert.equal(payload.conversationId, "cnv_server-test-agent-chat-delayed");
assert.equal(payload.traceId, "trc_server-test-agent-chat-delayed");
assert.equal(payload.provider, "codex-stdio");
assert.equal(payload.capabilityLevel, "blocked");
assert.equal(providerCalled, false);
assert.equal(Object.hasOwn(payload, "reply"), false);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});
test("cloud api /v1/agent/chat does not call OpenAI provider 502/503 fallback", async () => {
for (const status of [502, 503]) {
const providerServer = createHttpServer((request, response) => {
request.resume();
response.writeHead(status, { "content-type": "application/json" });
response.end(JSON.stringify({
error: {
message: `upstream ${status}`
}
}));
});
await new Promise((resolve) => providerServer.listen(0, "127.0.0.1", resolve));
const providerPort = providerServer.address().port;
const server = createCloudApiServer({
env: {
OPENAI_API_KEY: "test-openai-key-material",
HWLAB_CODE_AGENT_PROVIDER: "openai",
HWLAB_CODE_AGENT_ALLOW_TEXT_FALLBACK: "true",
HWLAB_CODE_AGENT_MODEL: "gpt-test",
HWLAB_CODE_AGENT_OPENAI_BASE_URL: `http://127.0.0.1:${providerPort}/v1/responses`
}
});
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-agent-chat-provider-${status}`
});
const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
method: "POST",
headers: {
"content-type": "application/json",
cookie: manualSession.cookie,
"x-trace-id": `trc_server-test-agent-chat-provider-${status}`
},
body: JSON.stringify({
conversationId: manualSession.conversationId,
sessionId: manualSession.sessionId,
message: `provider ${status}`
})
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.status, "failed");
assert.equal(payload.traceId, `trc_server-test-agent-chat-provider-${status}`);
assert.match(payload.error.code, /^(codex_cli_binary_missing|codex_cli_native_dependency_missing|codex_stdio_supervisor_disabled|stdio_protocol_not_wired)$/u);
assert.match(payload.error.layer, /^(runner|api)$/u);
assert.equal(typeof payload.error.retryable, "boolean");
assert.equal(payload.error.blocker.traceId, `trc_server-test-agent-chat-provider-${status}`);
assert.equal(typeof payload.error.blocker.retryable, "boolean");
assert.equal(payload.error.blocker.capabilityLevel, "blocked");
assert.equal(payload.provider, "codex-stdio");
assert.equal(Object.hasOwn(payload, "reply"), false);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
await new Promise((resolve, reject) => {
providerServer.close((error) => (error ? reject(error) : resolve()));
});
}
}
});
test("cloud api /v1/agent/chat does not call empty provider text fallback", async () => {
let providerCalled = false;
const server = createCloudApiServer({
callCodeAgentProvider: async () => {
providerCalled = true;
throw new Error("empty provider fallback must not be used");
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
try {
const { port } = server.address();
const manualSession = await createManualAgentSession(port, {
conversationId: "cnv_empty-provider-text"
});
const response = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
method: "POST",
headers: {
"content-type": "application/json",
cookie: manualSession.cookie
},
body: JSON.stringify({
conversationId: manualSession.conversationId,
sessionId: manualSession.sessionId,
message: "你好"
})
});
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.conversationId, "cnv_empty-provider-text");
assert.equal(payload.status, "failed");
assert.match(payload.error.code, /^(codex_cli_binary_missing|stdio_protocol_not_wired)$/u);
assert.equal(payload.provider, "codex-stdio");
assert.equal(Object.hasOwn(payload, "reply"), false);
assert.equal(providerCalled, false);
} finally {
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}
});