/** * LLM calls for workflow steps via Claude Code subprocess. * * Spawns `claude --print` as a child process with the resolved prompt. * Claude Code handles its own OAuth authentication internally — no * credential reading, token refresh, or API endpoint logic needed here. * This is the same mechanism the admin chat uses (claude-agent.ts). * * Two spawn modes: * * ONE-SHOT (no mcpServers) * ──────────────────────── * VALIDATE PROMPT * │ * ├─ empty → throw (no silent fallback) * │ * v * SPAWN claude --print --model --max-turns 1 * │ * ├─ ENOENT (not in PATH) → throw * ├─ non-zero exit → throw with stderr + stdout * ├─ timeout → SIGTERM → throw * ├─ empty stdout → throw * └─ stdout text → return * * AGENTIC (mcpServers declared) * ───────────────────────────── * VALIDATE PROMPT * │ * v * WRITE TEMP MCP CONFIG (mkdtemp + writeFile) * │ * ├─ disk error → throw (cleanup skipped — nothing to clean) * │ * v * SPAWN claude --print --model --max-turns * --mcp-config --strict-mcp-config * --permission-mode dontAsk * │ * ├─ same error paths as one-shot * │ * v * CLEANUP TEMP CONFIG (finally — always runs) * │ * ├─ ENOENT → swallow (already cleaned or never written) * └─ other error → log, don't throw (cleanup must not mask spawn error) * * probeLlmHealth() shares the same spawn path with a trivial "pong" * prompt and a shorter timeout. Used as the pre-flight check before * executing workflow LLM steps (Task 74 — graceful degradation). */ import type { McpServerConfig } from "./step-resolver.js"; interface SpawnedChildLike { stdout: { on(event: "data", cb: (chunk: Buffer) => void): void; }; stderr: { on(event: "data", cb: (chunk: Buffer) => void): void; }; on(event: "close", cb: (code: number | null) => void): void; on(event: "error", cb: (err: Error) => void): void; kill(signal?: string): void; } type SpawnFn = (cmd: string, args: string[], opts: unknown) => SpawnedChildLike; export interface LlmCallParams { prompt: string; model: string; maxTokens?: number; timeoutMs: number; /** MCP server bindings for agentic steps. */ mcpServers?: Record; /** Max agent turns (default 1, or 10 when mcpServers present). */ maxTurns?: number; /** * Optional spawn function override — used only by tests to inject a * fake child process. Production callers must omit this. */ _spawn?: SpawnFn; } export interface ProbeLlmHealthParams { model: string; /** Probe timeout — defaults to 10 seconds. */ timeoutMs?: number; /** Test-only spawn injection. */ _spawn?: SpawnFn; } export interface ProbeResult { ok: boolean; latencyMs: number; /** Set when ok is false. One-line error summary suitable for logs and * substitution into fallback template `{{_error}}`. */ error?: string; } /** * Why readAdminModel returns a tuple instead of string | null: * the caller persists the result into WorkflowRun.error, which the admin * agent reads. A silent null collapsed six distinct failure modes (ENOENT, * EACCES, parse error, missing field, wrong type, empty string) into a * single error string asserting "adminModel both unset" — which was often * a lie (the file existed and was correct; something else had failed). * The tuple surfaces the exact reason so the persisted error and the * stderr log agree, and the admin agent knows whether to inspect the * file, the permissions, the path, or a concurrent-write race. */ export type AdminModelReason = "ok" | "enoent" | "eacces" | "fs_error" | "parse_error" | "field_missing" | "field_wrong_type" | "field_empty"; export interface AdminModelResolution { /** Model ID when reason is "ok"; null for every failure mode. */ model: string | null; reason: AdminModelReason; /** Absolute path attempted — included in failure logs so the operator * can inspect the exact file that was read. */ path: string; /** Underlying error message for fs_error and parse_error. */ error?: string; } /** * Read the account's adminModel from account.json. * Returns {model, reason, path, error?} — callers branch on .model (null * means fail) and may surface .reason/.path into user-visible errors. */ export declare function readAdminModel(platformRoot: string, accountId: string): AdminModelResolution; /** * Execute an LLM call by spawning Claude Code. * * When mcpServers is provided, spawns in agentic mode: writes a temp * config file, adds --mcp-config and --strict-mcp-config flags, and * uses the step's maxTurns (or default 10) instead of 1. * * Returns the model's text response. The caller is responsible for * parsing the response (JSON or raw text) via parseStepOutput(). */ export declare function executeLlmCall(params: LlmCallParams): Promise; /** * Lightweight pre-flight probe for LLM availability. * * Spawns `claude --print` with a trivial prompt ("pong") and a short * timeout. Any failure mode that would cause a real LLM step to fail * (missing binary, dead OAuth token, Anthropic outage, network partition) * also fails this probe — it is the strongest available signal short * of running the real workflow. * * Called at most once per workflow run, only when the workflow contains * at least one LLM step. Per-step spawn failures mid-run are handled * separately by the workflow-execute convergence arm. * * SUCCESS → { ok: true, latencyMs } * FAILURE → { ok: false, latencyMs, error: one-line summary } */ export declare function probeLlmHealth(params: ProbeLlmHealthParams): Promise; export {}; //# sourceMappingURL=llm-call.d.ts.map