/** * run-local bootstrap — the child-process entrypoint behind * `POST /api/runs/local`. * * It runs an agent entirely in-process against stub capabilities * (`runLocalFromDir` from @sapiom/agent-core) and writes the result as an * NDJSON stream to stdout: one line per {@link LocalStepTrace}, then a single * terminal summary line `{ outcome, output, error, unusedStubs, stubWarnings }`. * It needs no Sapiom account and resolves every `ctx.sapiom.*` call from stubs, * so it creates no Sapiom capability request or capability spend. The imported * definition and author step code are ordinary local code and may still perform * their own filesystem, process, environment, or network effects. * * Why a separate child process (not an in-process import): * 1. `runLocalFromDir` esbuild-bundles and dynamically `import()`s a workflow * project the harness doesn't control, so a stray top-level side effect, * infinite loop, or crash in someone else's step body is bounded to this * one child instead of taking the long-lived harness server down. * 2. It sidesteps a Vite/Vitest limitation: the dynamic `import(\`file://…\`)` * inside the loader gets intercepted by the SSR dynamic-import-vars * transform when pulled into a Vitest module graph, and mishandles the * tmpdir `file://` URL on darwin. A plain child `node` process never goes * through that transform. (Mirrors the reasoning in canvas-manifest-check.) * * Contract with the route ({@link createActionsRouter}): * - Request arrives as one JSON object on **stdin**: `{ sourceDir, input?, * stubs?, maxAttemptsPerStep? }`. * - Output is line-oriented JSON on **stdout** — the route forwards each line * through unchanged, so the shapes here ARE the wire shapes the SPA parses. * - Diagnostics go to **stderr**; the route keeps a bounded tail for failures. * - Exit 0 once the terminal line is written (even for a failed run — a failed * *run* is a successful *invocation*); exit 1 only when no run happened * (bad request, load error) after writing a terminal `error` line. */ import { type LocalStepTrace, type LocalRunOutcome, type StubFile } from "@sapiom/agent-core"; /** The request shape the route writes to this child's stdin. */ export interface RunLocalRequest { /** Absolute path to the agent project directory (contains `index.ts`). */ sourceDir: string; /** The workflow's entry-step input (optional; agent-core defaults it). */ input?: unknown; /** Explicit stub overrides. When omitted the project's committed * `.sapiom-dev/stubs.json` is used (agent-core's own precedence). */ stubs?: StubFile; /** Per-step attempt cap (optional; agent-core defaults it). */ maxAttemptsPerStep?: number; } /** * The terminal NDJSON line, written after every per-step trace line. Carries * the run-level outcome and the two stub-hygiene signals the inspector surfaces * (WB15-2): `unusedStubs` (supplied keys that matched no capability call) and * `stubWarnings` (keys that matched but carried the wrong shape). * * `kind: "summary"` discriminates it from a step-trace line so a consumer never * has to guess which line is terminal. */ export interface RunLocalSummaryLine { kind: "summary"; outcome: LocalRunOutcome; output?: unknown; error?: unknown; unusedStubs: Array<{ step: string; key: string; }>; stubWarnings: string[]; } /** Live attempt event. `started` and `settled` share one stable trace key * (`step + attempt`) so clients upsert rather than duplicate a row. */ export interface RunLocalStepLine { kind: "step"; phase: "started" | "settled"; trace: LocalStepTrace; } /** * The terminal line written when the run could not be *invoked* at all — a * malformed request, an unreadable stub file, a project that fails to load. * `outcome` is always `"failed"`; the child then exits non-zero. Distinct from * a `summary` line, which represents a run that actually executed (even if that * run's own outcome was `failed`). */ export interface RunLocalErrorLine { kind: "error"; outcome: "failed"; error: string; } /** * Parse the stdin payload into a {@link RunLocalRequest}. Throws a plain Error * with a caller-safe message (no key material, no provider names) on anything * that isn't a JSON object carrying a non-empty string `sourceDir`. */ export declare function parseRunLocalRequest(raw: string): RunLocalRequest; /** * Run the request and emit its NDJSON stream to `out`. Returns the process exit * code: 0 when a run executed (any outcome), 1 when the run could not be * invoked (a terminal `error` line is emitted first). Never throws — every * failure becomes an in-band terminal line, because a half-written stream with * a thrown stack on stderr is far harder for the route to reason about than a * clean terminal line plus an exit code. */ export declare function runBootstrap(request: RunLocalRequest, out: NodeJS.WritableStream): Promise; //# sourceMappingURL=run-local-bootstrap.d.ts.map