import test from "node:test"; import assert from "node:assert/strict"; import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk"; import { loadConfig } from "../src/config.js"; import { parseCodexFinalResponse, runCodexPrompt, } from "../src/examples/codex.js"; import { readClaudeResult, runClaudePrompt, } from "../src/examples/claude.js"; import type { SessionContext } from "../src/examples/session-context.js"; type ClaudeQueryFunction = (typeof import("@anthropic-ai/claude-agent-sdk"))["query"]; function createMessageStream( messages: SDKMessage[], ): AsyncIterable { return { async *[Symbol.asyncIterator]() { for (const message of messages) { yield message; } }, }; } test("runCodexPrompt wires client options and parses final output", async () => { const config = loadConfig({ CODEX_API_KEY: "codex-token", OPENAI_BASE_URL: "https://api.example.com/v1", }); let capturedClientInput: Record | undefined; let capturedThreadInput: | { workingDirectory: string; skipGitRepoCheck: boolean; } | undefined; let capturedPrompt = ""; let closed = false; let output = ""; const sessionContext: SessionContext = { provider: "codex", sessionId: "session-codex", mcp: { codexConfig: { mcp_servers: { local: { command: "node", args: ["server.js"], }, }, }, }, promptWithContext: "prompt with context", runtimeInjection: { workingDirectory: "/tmp/worktree", env: { PATH: "/usr/bin", }, discoveryFilePath: "/tmp/worktree/.agent-context/resources.json", }, runInSession: async (run: () => Promise) => run(), close: async () => { closed = true; }, }; await runCodexPrompt("ignored", { config, createSessionContextFn: async () => sessionContext, createCodexClient: (input) => { capturedClientInput = input as Record; return { startThread: (threadInput) => { capturedThreadInput = threadInput; return { run: async (prompt) => { capturedPrompt = prompt; return { finalResponse: " completed ", }; }, }; }, }; }, writeOutput: (line) => { output = line; }, }); assert.equal(capturedClientInput?.["apiKey"], "codex-token"); assert.equal(capturedClientInput?.["baseUrl"], "https://api.example.com/v1"); assert.deepEqual(capturedClientInput?.["config"], sessionContext.mcp.codexConfig); assert.deepEqual(capturedClientInput?.["env"], sessionContext.runtimeInjection.env); assert.deepEqual(capturedThreadInput, { workingDirectory: "/tmp/worktree", skipGitRepoCheck: true, }); assert.equal(capturedPrompt, "prompt with context"); assert.equal(output, "completed"); assert.equal(closed, true); }); test("runClaudePrompt wires auth env, stream parsing, and output", async () => { const config = loadConfig({ CLAUDE_CODE_OAUTH_TOKEN: "oauth-token", ANTHROPIC_API_KEY: "legacy-api-key", CLAUDE_MODEL: "claude-sonnet-4-6", CLAUDE_CODE_PATH: "/usr/local/bin/claude", }); let closed = false; let output = ""; let queryInput: | { prompt: string; options?: Record; } | undefined; const sessionContext: SessionContext = { provider: "claude", sessionId: "session-claude", mcp: { claudeMcpServers: { local: { type: "stdio", command: "node", args: ["server.js"], }, }, }, promptWithContext: "augmented prompt", runtimeInjection: { workingDirectory: "/tmp/claude-worktree", env: { PATH: "/usr/bin", ANTHROPIC_API_KEY: "ambient-api-key", }, discoveryFilePath: "/tmp/claude-worktree/.agent-context/resources.json", }, runInSession: async (run: () => Promise) => run(), close: async () => { closed = true; }, }; const queryFn: ClaudeQueryFunction = ((input: { prompt: string; options?: Record; }) => { queryInput = input; const stream = createMessageStream([ { type: "result", subtype: "success", result: " response text ", } as SDKMessage, ]); return { ...stream, close: () => {}, } as ReturnType; }) as ClaudeQueryFunction; await runClaudePrompt("ignored", { config, createSessionContextFn: async () => sessionContext, queryFn, writeOutput: (line) => { output = line; }, }); assert.equal(queryInput?.prompt, "augmented prompt"); assert.equal(queryInput?.options?.model, "claude-sonnet-4-6"); assert.equal(queryInput?.options?.pathToClaudeCodeExecutable, "/usr/local/bin/claude"); assert.equal(queryInput?.options?.cwd, "/tmp/claude-worktree"); assert.equal(queryInput?.options?.authToken, "oauth-token"); assert.deepEqual(queryInput?.options?.mcpServers, sessionContext.mcp.claudeMcpServers); const env = queryInput?.options?.env as Record | undefined; assert.equal(env?.CLAUDE_CODE_OAUTH_TOKEN, "oauth-token"); assert.equal(env?.ANTHROPIC_API_KEY, undefined); assert.equal(output, "response text"); assert.equal(closed, true); }); test("readClaudeResult throws on non-success result events", async () => { const stream = createMessageStream([ { type: "result", subtype: "error_during_execution", errors: ["first", "second"], } as SDKMessage, ]); await assert.rejects( () => readClaudeResult(stream), /Claude query failed \(error_during_execution\): first; second/, ); }); test("parseCodexFinalResponse keeps fallback text for empty responses", () => { const response = parseCodexFinalResponse({ finalResponse: " ", }); assert.equal(response, "(No response text returned)"); });