285 lines
8.9 KiB
JavaScript
285 lines
8.9 KiB
JavaScript
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"
|
|
);
|
|
});
|