diff --git a/internal/cloud/server.test.mjs b/internal/cloud/server.test.mjs index f40f8334..46a687aa 100644 --- a/internal/cloud/server.test.mjs +++ b/internal/cloud/server.test.mjs @@ -18,6 +18,16 @@ import { } from "../db/runtime-store.mjs"; import { HWLAB_M3_IO_CAPABILITY_LEVELS } from "../../skills/hwlab-agent-runtime/scripts/src/m3-io-skill-client.mjs"; +const CODEX_STDIO_FEASIBILITY_BLOCKERS = new Set([ + "codex_cli_binary_missing", + "codex_stdio_supervisor_disabled", + "codex_stdio_egress_boundary" +]); + +function assertCodexStdioFeasibilityBlocker(value, label = "codex stdio blocker") { + assert.equal(CODEX_STDIO_FEASIBILITY_BLOCKERS.has(value), true, `${label}: ${value}`); +} + test("cloud api exposes /health, /health/live, and /live probes", async () => { const originalUrl = process.env.HWLAB_CLOUD_DB_URL; const originalSslMode = process.env.HWLAB_CLOUD_DB_SSL_MODE; @@ -81,16 +91,16 @@ test("cloud api exposes /health, /health/live, and /live probes", async () => { assert.equal(healthPayload.readiness.sessionRunner.durableSession, false); assert.equal(healthPayload.readiness.codexStdio.status, "blocked"); assert.equal(healthPayload.readiness.codexStdio.ready, false); - assert.equal(healthPayload.readiness.codexStdio.blocker, "codex_stdio_supervisor_disabled"); + assertCodexStdioFeasibilityBlocker(healthPayload.readiness.codexStdio.blocker); assert.equal(healthPayload.readiness.codeAgent.providerReady, false); assert.equal(healthPayload.readiness.codeAgent.durableDbReady, false); assert.equal(healthPayload.readiness.codeAgent.sessionRunnerReady, true); assert.equal(healthPayload.readiness.codeAgent.codexStdioFeasible, false); - assert.deepEqual(healthPayload.readiness.codeAgent.currentBlockers.slice(0, 3), [ + assert.deepEqual(healthPayload.readiness.codeAgent.currentBlockers.slice(0, 2), [ "provider_config_blocked", - "runtime_durable_adapter_missing", - "codex_stdio_supervisor_disabled" + "runtime_durable_adapter_missing" ]); + assertCodexStdioFeasibilityBlocker(healthPayload.readiness.codeAgent.currentBlockers[2], "readiness current blocker"); assert.equal(healthPayload.readiness.codeAgent.secretMaterialRead, false); assert.equal(healthPayload.codeAgent.status, "partial"); assert.equal(healthPayload.codeAgent.agentKind, "controlled-readonly-session-registry"); @@ -993,7 +1003,7 @@ test("cloud api /v1 describes Code Agent egress blocker without leaking API key" const payload = await response.json(); assert.equal(payload.codeAgent.status, "partial"); assert.equal(payload.codeAgent.egress.directPublicOpenAi, true); - assert.match(payload.codeAgent.blocker, /Codex stdio|long-lived/u); + assert.match(payload.codeAgent.blocker, /Codex stdio|long-lived|Codex CLI command/u); assert.equal(payload.codeAgent.runner.ready, true); assert.equal(payload.codeAgent.safety.secretMaterialRead, false); assert.equal(JSON.stringify(payload).includes("test-openai-key-material"), false); @@ -1057,8 +1067,8 @@ test("cloud api health reports provider and durable DB ready while codex stdio r assert.equal(payload.readiness.sessionRunner.status, "read_only_ready"); assert.equal(payload.readiness.sessionRunner.ready, true); assert.equal(payload.readiness.codexStdio.status, "blocked"); - assert.equal(payload.readiness.codexStdio.blocker, "codex_stdio_supervisor_disabled"); - assert.deepEqual(payload.readiness.codeAgent.currentBlockers, ["codex_stdio_supervisor_disabled"]); + assertCodexStdioFeasibilityBlocker(payload.readiness.codexStdio.blocker); + assert.deepEqual(payload.readiness.codeAgent.currentBlockers, [payload.readiness.codexStdio.blocker]); assert.equal(payload.readiness.codeAgent.providerReady, true); assert.equal(payload.readiness.codeAgent.durableDbReady, true); assert.equal(payload.readiness.codeAgent.sessionRunnerReady, true); diff --git a/scripts/m1-contract-smoke.mjs b/scripts/m1-contract-smoke.mjs index be7ac9d3..c773137a 100644 --- a/scripts/m1-contract-smoke.mjs +++ b/scripts/m1-contract-smoke.mjs @@ -13,11 +13,20 @@ import { runCli } from "../tools/hwlab-cli/lib/cli.mjs"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const children = []; +const CODEX_STDIO_FEASIBILITY_BLOCKERS = new Set([ + "codex_cli_binary_missing", + "codex_stdio_supervisor_disabled", + "codex_stdio_egress_boundary" +]); function logOk(name) { process.stdout.write(`[m1-smoke] ok ${name}\n`); } +function assertCodexStdioFeasibilityBlocker(value, label = "codex stdio blocker") { + assert.equal(CODEX_STDIO_FEASIBILITY_BLOCKERS.has(value), true, `${label}: ${value}`); +} + function freePort() { return new Promise((resolve, reject) => { const server = net.createServer(); @@ -205,16 +214,16 @@ async function smokeCloudApi() { assert.equal(health.body.db.secretRefs[0].secretName, "hwlab-cloud-api-dev-db"); assert.equal(health.body.db.secretRefs[0].redacted, true); assert.equal(health.body.codeAgent.status, "partial"); - assert.match(health.body.codeAgent.blocker, /Codex stdio|long-lived/u); - assert.equal(health.body.codeAgent.reason, "codex_stdio_supervisor_disabled"); + assert.match(health.body.codeAgent.blocker, /Codex stdio|long-lived|Codex CLI command/u); + assertCodexStdioFeasibilityBlocker(health.body.codeAgent.reason, "health codeAgent reason"); assert.equal(health.body.readiness.provider.status, "blocked"); assert.equal(health.body.readiness.provider.blocker, "provider_config_blocked"); assert.equal(health.body.readiness.sessionRunner.status, "read_only_ready"); assert.equal(health.body.readiness.sessionRunner.ready, true); assert.equal(health.body.readiness.codexStdio.status, "blocked"); - assert.equal(health.body.readiness.codexStdio.blocker, "codex_stdio_supervisor_disabled"); + assertCodexStdioFeasibilityBlocker(health.body.readiness.codexStdio.blocker, "health readiness codexStdio blocker"); assert.equal(health.body.readiness.codeAgent.currentBlockers.includes("provider_config_blocked"), true); - assert.equal(health.body.readiness.codeAgent.currentBlockers.includes("codex_stdio_supervisor_disabled"), true); + assert.equal(health.body.readiness.codeAgent.currentBlockers.includes(health.body.readiness.codexStdio.blocker), true); assert.ok(health.body.codeAgent.missingEnv.includes("OPENAI_API_KEY")); assert.equal(health.body.codeAgent.secretRefs[0].secretName, "hwlab-code-agent-provider"); assert.equal(health.body.codeAgent.secretRefs[0].secretKey, "openai-api-key"); @@ -236,7 +245,7 @@ async function smokeCloudApi() { assert.ok(adapter.body.methods.includes("evidence.record.query")); assert.equal(adapter.body.codeAgent.endpoint, "POST /v1/agent/chat"); assert.equal(adapter.body.codeAgent.status, "partial"); - assert.match(adapter.body.codeAgent.blocker, /Codex stdio|long-lived/u); + assert.match(adapter.body.codeAgent.blocker, /Codex stdio|long-lived|Codex CLI command/u); assert.equal(adapter.body.codeAgent.provider, "codex-cli"); assert.equal(adapter.body.codeAgent.model, "gpt-m1-local"); assert.equal(adapter.body.codeAgent.backend, "hwlab-cloud-api/codex-cli"); @@ -271,13 +280,13 @@ async function smokeCloudApi() { assert.equal(agentChat.body.provider, "codex-cli"); assert.equal(agentChat.body.model, "gpt-m1-local"); assert.equal(agentChat.body.backend, "hwlab-cloud-api/codex-cli"); - assert.equal(agentChat.body.error.code, "provider_unavailable"); + assert.equal(agentChat.body.error.code, "codex_cli_binary_missing"); assert.match(agentChat.body.error.message, /Codex CLI command is not available/); assert.deepEqual(agentChat.body.error.missingCommands, ["codex"]); assert.ok(agentChat.body.error.missingEnv.includes("OPENAI_API_KEY")); assert.equal(agentChat.body.availability.status, "partial"); - assert.match(agentChat.body.availability.blocker, /Codex stdio|long-lived/u); - assert.equal(agentChat.body.availability.reason, "codex_stdio_supervisor_disabled"); + assert.match(agentChat.body.availability.blocker, /Codex stdio|long-lived|Codex CLI command/u); + assertCodexStdioFeasibilityBlocker(agentChat.body.availability.reason, "agent chat availability reason"); assert.equal(agentChat.body.availability.fallback.status, "blocked"); assert.equal(agentChat.body.availability.fallback.reason, "provider_unavailable"); assert.equal(agentChat.body.availability.secretRefs[0].secretName, "hwlab-code-agent-provider");