8cc114f730
Split the oversized server AgentRun regression suite by responsibility and migrate the six stale runner-jobs mocks to command dispatch intents without changing the v0.3 baseline failure set. Co-Authored-By: Codex <noreply@openai.com>
1901 lines
101 KiB
TypeScript
1901 lines
101 KiB
TypeScript
// 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") {
|
||
assert.equal(body.dispatch?.kind, "kubernetes-job");
|
||
assert.equal(body.dispatch?.input?.commandId, undefined);
|
||
return send({
|
||
id: "cmd_evicted",
|
||
runId: "run_evicted",
|
||
state: "queued",
|
||
dispatchIntent: {
|
||
id: "dispatch_evicted",
|
||
state: "pending",
|
||
runnerJobId: "rjob_evicted",
|
||
attemptCount: 0,
|
||
durable: true
|
||
}
|
||
});
|
||
}
|
||
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);
|
||
assert.equal(body.dispatch?.kind, "kubernetes-job");
|
||
assert.equal(body.dispatch?.input?.commandId, undefined);
|
||
return send({
|
||
id: "cmd_evicted_retry",
|
||
runId: "run_evicted_retry",
|
||
state: "queued",
|
||
dispatchIntent: {
|
||
id: "dispatch_evicted_retry",
|
||
state: "pending",
|
||
runnerJobId: "rjob_evicted_retry",
|
||
attemptCount: 0,
|
||
durable: true
|
||
}
|
||
});
|
||
}
|
||
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);
|
||
assert.equal(calls.some((call) => call.path.endsWith("/runner-jobs")), false);
|
||
} finally {
|
||
await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
|
||
await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve())));
|
||
}
|
||
});
|
||
|
||
test("cloud api AgentRun adapter 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);
|
||
assert.equal(body.dispatch?.kind, "kubernetes-job");
|
||
assert.equal(body.dispatch?.input?.commandId, undefined);
|
||
return send({
|
||
id: "cmd_hwlab_minimax_m3",
|
||
runId: "run_hwlab_minimax_m3",
|
||
state: "pending",
|
||
type: "turn",
|
||
seq: 1,
|
||
dispatchIntent: {
|
||
id: "dispatch_hwlab_minimax_m3",
|
||
state: "pending",
|
||
runnerJobId: "rjob_hwlab_minimax_m3",
|
||
attemptCount: 0,
|
||
durable: true
|
||
}
|
||
});
|
||
}
|
||
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 === "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);
|
||
assert.equal(calls.some((call) => call.path.endsWith("/runner-jobs")), false);
|
||
} finally {
|
||
await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
|
||
await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve())));
|
||
}
|
||
});
|