import test from "node:test"; import assert from "node:assert/strict"; import { mkdtemp } from "node:fs/promises"; import { tmpdir } from "node:os"; import { resolve } from "node:path"; import { loadConfig } from "../src/config.js"; import type { ActorExecutionInput } from "../src/agents/pipeline.js"; import { buildProviderRuntimeEnv, createProviderRunRuntime, parseActorExecutionResultFromModelOutput, resolveProviderWorkingDirectory, type ProviderRunRuntime, } from "../src/agents/provider-executor.js"; test("parseActorExecutionResultFromModelOutput parses strict JSON payload", () => { const parsed = parseActorExecutionResultFromModelOutput({ rawText: JSON.stringify({ status: "validation_fail", payload: { summary: "missing test", }, stateFlags: { needs_fix: true, }, stateMetadata: { stage: "qa", }, events: [ { type: "validation_failed", payload: { summary: "failed", }, }, ], failureKind: "soft", failureCode: "missing_test", }), }); assert.equal(parsed.status, "validation_fail"); assert.equal(parsed.payload?.summary, "missing test"); assert.equal(parsed.stateFlags?.needs_fix, true); assert.equal(parsed.stateMetadata?.stage, "qa"); assert.equal(parsed.events?.[0]?.type, "validation_failed"); assert.equal(parsed.failureKind, "soft"); assert.equal(parsed.failureCode, "missing_test"); }); test("parseActorExecutionResultFromModelOutput parses fenced JSON", () => { const parsed = parseActorExecutionResultFromModelOutput({ rawText: [ "Here is the result", "```json", JSON.stringify({ status: "success", payload: { code: "done", }, }), "```", ].join("\n"), }); assert.equal(parsed.status, "success"); assert.equal(parsed.payload?.code, "done"); }); test("parseActorExecutionResultFromModelOutput falls back when response is not JSON", () => { const parsed = parseActorExecutionResultFromModelOutput({ rawText: "Implemented update successfully.", }); assert.equal(parsed.status, "success"); assert.equal(parsed.payload?.assistantResponse, "Implemented update successfully."); }); test("parseActorExecutionResultFromModelOutput preserves status when optional fields are malformed", () => { const parsed = parseActorExecutionResultFromModelOutput({ rawText: JSON.stringify({ status: "failure", payload: { reason: "hard failure", }, stateFlags: { retryable: false, invalid_flag: "nope", }, stateMetadata: "not-an-object", events: [ { type: "validation_failed", payload: { summary: "failed", }, }, { type: 123, }, ], failureKind: "not-valid", failureCode: 403, }), }); assert.equal(parsed.status, "failure"); assert.equal(parsed.payload?.reason, "hard failure"); assert.equal(parsed.stateFlags?.retryable, false); assert.equal(parsed.stateFlags && "invalid_flag" in parsed.stateFlags, false); assert.equal(parsed.stateMetadata, undefined); assert.equal(parsed.events?.length, 1); assert.equal(parsed.events?.[0]?.type, "validation_failed"); assert.equal(parsed.failureKind, undefined); assert.equal(parsed.failureCode, undefined); }); test("resolveProviderWorkingDirectory reads cwd from actor execution context", () => { const actorInput = { executionContext: { security: { worktreePath: "/tmp/session/tasks/product-intake", }, }, } as unknown as ActorExecutionInput; assert.equal( resolveProviderWorkingDirectory(actorInput), "/tmp/session/tasks/product-intake", ); }); test("buildProviderRuntimeEnv scopes AGENT_WORKTREE_PATH to actor worktree and filters undefined auth", () => { const config = loadConfig({ CLAUDE_CODE_OAUTH_TOKEN: "oauth-token", }); const runtime = { provider: "claude", config, sharedEnv: { PATH: "/usr/bin", KEEP_ME: "1", }, claudeObservability: {} as ProviderRunRuntime["claudeObservability"], close: async () => {}, } satisfies ProviderRunRuntime; const actorInput = { executionContext: { security: { worktreePath: "/tmp/session/tasks/product-intake", }, }, } as unknown as ActorExecutionInput; const env = buildProviderRuntimeEnv({ runtime, actorInput, includeClaudeAuth: true, }); assert.equal(env.AGENT_WORKTREE_PATH, "/tmp/session/tasks/product-intake"); assert.equal(env.CLAUDE_CODE_OAUTH_TOKEN, "oauth-token"); assert.equal("ANTHROPIC_API_KEY" in env, false); assert.equal(env.KEEP_ME, "1"); }); test("createProviderRunRuntime does not require session context provisioning", async () => { const observabilityRoot = await mkdtemp(resolve(tmpdir(), "ai-ops-provider-runtime-")); const runtime = await createProviderRunRuntime({ provider: "claude", config: loadConfig({}), observabilityRootPath: observabilityRoot, baseEnv: { PATH: "/usr/bin", }, }); try { assert.equal(runtime.provider, "claude"); assert.equal(runtime.sharedEnv.PATH, "/usr/bin"); } finally { await runtime.close(); } });