import assert from "node:assert/strict"; import test from "node:test"; import { codeAgentFactsFromMessage, compactHwlabApiFact } from "./code-agent-facts.mjs"; test("codex-readonly-runner is shown as partial read-only session registry", () => { const facts = codeAgentFactsFromMessage({ status: "source", provider: "codex-readonly-runner", model: "read-only-tools", backend: "hwlab-cloud-api/codex-readonly-runner", workspace: "/workspace/hwlab", sandbox: "read-only", capabilityLevel: "read-only-session-tools", sessionMode: "controlled-readonly-session-registry", implementationType: "controlled-readonly-session-registry", session: { sessionId: "ses_readonly", status: "idle", turn: 1, lastTraceId: "trc_readonly" }, sessionReuse: { reused: false, turn: 1, status: "idle" }, runner: { kind: "hwlab-readonly-runner", codexStdio: false, writeCapable: false, durableSession: false }, longLivedSessionGate: { status: "blocked", pass: false, blockers: [{ code: "controlled_readonly_not_long_lived_stdio" }] }, toolCalls: [{ name: "pwd", status: "completed" }], skills: { status: "not_requested", count: 0 }, runnerTrace: { traceId: "trc_readonly", runnerKind: "hwlab-readonly-runner", sessionMode: "controlled-readonly-session-registry", turn: 1 }, traceId: "trc_readonly" }); assert.equal(facts.kind, "controlled-readonly-session-registry"); assert.equal(facts.fullCodeAgent, false); assert.equal(facts.codexSessionGatePass, false); assert.match(facts.summary, /Codex stdio/u); assert.match(facts.rows.find((row) => row.label === "runner/provider/backend").value, /runner=hwlab-readonly-runner/u); assert.match(facts.rows.find((row) => row.label === "toolCalls").value, /1\/1 pwd\(completed\)/u); }); test("long-lived Codex stdio session fields are shown as full Code Agent session", () => { const facts = codeAgentFactsFromMessage({ status: "completed", provider: "codex-stdio", model: "gpt-5.5", backend: "hwlab-cloud-api/codex-mcp-stdio", workspace: "/workspace/hwlab", sandbox: "workspace-write", capabilityLevel: "long-lived-codex-stdio-session", sessionMode: "codex-mcp-stdio-long-lived", implementationType: "repo-owned-codex-mcp-stdio-session", session: { sessionId: "ses_stdio", status: "idle", turn: 3, idleTimeoutMs: 1800000, lastTraceId: "trc_stdio", longLivedSession: true, codexStdio: true }, runner: { kind: "codex-mcp-stdio-runner", codexStdio: true, writeCapable: true, durableSession: true }, longLivedSessionGate: { status: "pass", pass: true }, toolCalls: [{ name: "codex-reply", status: "completed" }], skills: { status: "not_requested", count: 0 }, runnerTrace: { traceId: "trc_stdio", runnerKind: "codex-mcp-stdio-runner", sessionMode: "codex-mcp-stdio-long-lived", turn: 3 }, traceId: "trc_stdio" }); assert.equal(facts.kind, "codex-stdio-long-lived"); assert.equal(facts.statusLabel, "完整 Code Agent session"); assert.equal(facts.fullCodeAgent, true); assert.equal(facts.codexSessionGatePass, true); assert.match(facts.rows.find((row) => row.label === "sandbox").value, /workspace-write/u); }); test("openai-responses fallback is explicitly text-chat-only", () => { const facts = codeAgentFactsFromMessage({ status: "source", provider: "openai-responses", model: "gpt-5.5", backend: "hwlab-cloud-api/openai-responses", capabilityLevel: "text-chat-only", sessionMode: "provider-text-request", runner: { kind: "openai-responses-fallback", codexStdio: false, writeCapable: false, durableSession: false }, longLivedSessionGate: { status: "blocked", pass: false, blockers: [{ code: "openai_responses_fallback_not_session" }] }, toolCalls: [], skills: { status: "not_requested", count: 0 }, runnerTrace: { traceId: "trc_fallback", runnerKind: "openai-responses-fallback", events: ["fallback:text-chat-only"] }, traceId: "trc_fallback" }); assert.equal(facts.kind, "text-chat-only"); assert.equal(facts.statusLabel, "openai-responses-fallback / text-chat-only"); assert.equal(facts.fullCodeAgent, false); assert.equal(facts.codexSessionGatePass, false); assert.match(facts.summary, /no workspace, tools, skills, or Codex session gate/u); }); test("stateless one-shot is not wrapped as a long-lived session", () => { const facts = codeAgentFactsFromMessage({ status: "source", provider: "codex-cli", model: "gpt-5.5", backend: "hwlab-cloud-api/codex-cli", workspace: "/workspace/hwlab", sandbox: "read-only", capabilityLevel: "text-chat-only", sessionMode: "ephemeral-one-shot", implementationType: "codex-cli-one-shot-ephemeral", session: { sessionId: "cnv_one_shot", status: "expired", idleTimeoutMs: 0, lastTraceId: "trc_one_shot" }, runner: { kind: "codex-cli-one-shot-ephemeral", codexStdio: false, writeCapable: false, durableSession: false }, longLivedSessionGate: { status: "blocked", pass: false, blockers: [{ code: "one_shot_runner_not_long_lived" }] }, traceId: "trc_one_shot" }); assert.equal(facts.kind, "stateless-one-shot"); assert.equal(facts.fullCodeAgent, false); assert.equal(facts.codexSessionGatePass, false); assert.match(facts.summary, /Ephemeral one-shot runner/u); }); test("HWLAB Skill CLI toolCall shows compact copyable /v1/m3/io facts", () => { const facts = codeAgentFactsFromMessage({ status: "source", provider: "hwlab-skill-cli", model: "controlled-m3-io", backend: "hwlab-cloud-api/hwlab-agent-runtime-skill-cli", workspace: "/workspace/hwlab", sandbox: "hwlab-api-route-only", capabilityLevel: "hwlab-api-control-ready", sessionMode: "controlled-m3-io-skill-cli", runner: { kind: "hwlab-m3-io-skill-cli", codexStdio: false, writeCapable: true, durableSession: false }, longLivedSessionGate: { status: "blocked", pass: false }, toolCalls: [ { name: "hwlab-agent-runtime.m3-io", type: "skill-cli", status: "completed", route: "/v1/m3/io", method: "POST", operationId: "op_m3_do_write_cli", traceId: "trc_m3_skill_cli", auditId: "aud_m3_do_write_cli_succeeded", evidenceId: "evd_m3_do_write_cli_succeeded", audit: { auditId: "aud_m3_do_write_cli_succeeded" }, evidence: { evidenceId: "evd_m3_do_write_cli_succeeded" }, accepted: true, readback: { status: "succeeded", value: false, resourceId: "res_boxsimu_2", port: "DI1" }, blocker: { code: "runtime_durable_not_green" }, stdout: JSON.stringify({ route: "/v1/m3/io", method: "POST", status: "succeeded", accepted: true, traceId: "trc_m3_skill_cli", operationId: "op_m3_do_write_cli", auditId: "aud_m3_do_write_cli_succeeded", evidenceId: "evd_m3_do_write_cli_succeeded", audit: { auditId: "aud_m3_do_write_cli_succeeded" }, evidence: { evidenceId: "evd_m3_do_write_cli_succeeded" }, readback: { status: "succeeded", value: false, resourceId: "res_boxsimu_2", port: "DI1" }, durable: { blocker: "runtime_durable_not_green" } }) } ], skills: { status: "used", count: 1, items: [{ name: "hwlab-agent-runtime.m3-io" }] }, runnerTrace: { traceId: "trc_m3_skill_cli", runnerKind: "hwlab-m3-io-skill-cli", sessionMode: "controlled-m3-io-skill-cli", route: "/v1/m3/io", status: "succeeded" }, traceId: "trc_m3_skill_cli" }); assert.equal(facts.kind, "hwlab-skill-cli"); assert.equal(facts.fullCodeAgent, false); assert.equal(facts.hwlabApiFacts.length, 1); assert.deepEqual(facts.hwlabApiFacts[0], { tool: "hwlab-agent-runtime.m3-io", route: "/v1/m3/io", method: "POST", operationId: "op_m3_do_write_cli", traceId: "trc_m3_skill_cli", auditId: "aud_m3_do_write_cli_succeeded", evidenceId: "evd_m3_do_write_cli_succeeded", accepted: true, readback: { status: "succeeded", value: false, resourceId: "res_boxsimu_2", port: "DI1" }, blocker: "runtime_durable_not_green", status: "completed" }); assert.equal( compactHwlabApiFact(facts.hwlabApiFacts[0]), "route=/v1/m3/io / method=POST / operationId=op_m3_do_write_cli / traceId=trc_m3_skill_cli / auditId=aud_m3_do_write_cli_succeeded / evidenceId=evd_m3_do_write_cli_succeeded / blocker=runtime_durable_not_green" ); });