import type { MessageJSON } from "smoltalk"; import type { MessageThreadJSON } from "./state/messageThread.js"; import { type Interrupt } from "./interrupts.js"; import { type RunBatchResult } from "./runBatch.js"; import type { SourceLocationOpts } from "./state/checkpointStore.js"; import type { RuntimeContext } from "./state/context.js"; import { type State, type StateStack } from "./state/stateStack.js"; /** * Thrown by {@link PromptRunner.step} when a step body returns interrupts. * * Caught only at the top of `runPrompt`, which extracts the batched * `interrupts` and returns them as `runPrompt`'s result so the generated * caller can checkpoint and propagate them. NEVER propagates outside * `lib/runtime/prompt.ts`. */ export declare class PromptBailout extends Error { readonly interrupts: Interrupt[]; constructor(interrupts: Interrupt[]); } /** * Frame-backed completion tracking. Lives on `self.runnerState` so it * survives checkpoint/restore the same way `self.messagesJSON` does: * the frame's `locals` object is what gets serialized. An array (used * as a set) keeps the format JSON-safe and matches the project's * "arrays instead of sets" convention. */ export type RunnerState = { completedSteps: string[]; }; export type PromptRunnerOpts = { /** Frame-local `self` object (== the function's locals bag). */ self: any; ctx: RuntimeContext; stateStack: StateStack; /** The runPrompt frame (== `stateStack.lastFrame()`). Used by * `parallel()` as `runBatch`'s `parentFrame` for per-tool branch * lifecycle. Defaulted from `stateStack.lastFrame()` for callers that * don't pass it explicitly. */ parentFrame?: State; /** Source location of the surrounding generated call site. Used as the * base `stepPath` when stamping a checkpoint on bailout — see * `step()` for the per-key suffix. */ checkpointInfo: SourceLocationOpts | undefined; /** Callback that snapshots the current message thread JSON. Called only * on bailout so it doesn't pay the cost on the happy path. * * Returns the FULL `MessageThreadJSON` so per-message debug labels * survive the round-trip; `MessageThread.fromJSON` also still accepts * the bare `MessageJSON[]` that older checkpoints hold. */ snapshotMessages: () => MessageThreadJSON | MessageJSON[]; }; /** * Control-flow helper for `runPrompt`. Owns the idempotent-step + * checkpoint-on-interrupt machinery so the surrounding `runPrompt` * body can be a linear script. * * See `docs/superpowers/plans/2026-05-22-prompt-runner.md`. */ export declare class PromptRunner { private opts; constructor(opts: PromptRunnerOpts); /** * Run `body` as an idempotent step, identified by `key`. * * If `key` is already recorded as completed (resume case), this is a * no-op. Otherwise the body runs: * - if it returns `Interrupt[]`, snapshot messages, stamp a * checkpoint (non-pinned, matching `Runner`'s interrupt * checkpoints), attach the checkpoint id to every interrupt, and * throw `PromptBailout` so `runPrompt` can return the batch up * the stack. The key is NOT marked completed — on resume, the * step re-enters, the body re-runs, and the saved * `__interruptId_N` matches the user's response. * - if it returns nothing (the happy path), the key is marked * completed so the next resume skips this step. * * The checkpoint's `stepPath` is `${checkpointInfo.stepPath}/${key}` * so multiple steps within one `runPrompt` produce distinct * checkpoints. Without this they would all share the runPrompt-level * `stepPath` and the checkpoint store could not tell them apart on * resume. */ step(key: string, body: () => Promise): Promise; /** * Run `branchFn` concurrently for every item in `items`. Each call * receives a {@link BranchRunner} which exposes its own `step()` that * COLLECTS interrupts rather than throwing — siblings keep running * even when one halts so we can batch all interrupts into a single * shared checkpoint. * * Thin adapter over {@link runBatch} with `mode: "all"` and * `recordBranchOutcomes: false` (the caller's `branchFn` body manages * branch state itself via `stack.setResultOnBranch` / `deleteBranch`). * `runBatch` owns: per-branch abort composition, ALS-isolated invoke, * settle, shared checkpoint stamp at * `${checkpointInfo.stepPath}/${keyPrefix}` with * `intr.checkpoint`/`checkpointId` overwrite, and `popBranches` on * the no-interrupt success path. Returns a {@link RunBatchResult} * tagged union — `"interrupts"` if any branch halted (the caller * bails out of runPrompt with `result.interrupts`), `"values"` * otherwise. * * `keyFor(item, i)` MUST produce the same branch key the `branchFn` * body uses for its `stack.getOrCreateBranch(...)` — otherwise * `runBatch` would allocate a separate branch from the one the body * manages, and the leaf-checkpoint vehicle into State.toJSON's * branches walk would be lost. * * IMPORTANT: do NOT call `pr.step(...)` (which throws `PromptBailout`) * from inside a branch — use `b.step(...)` instead. A throw from * `branchFn` propagates out of `runBatch` and aborts the whole batch. */ parallel(keyPrefix: string, items: T[], keyFor: (item: T, index: number) => string, branchFn: (item: T, b: BranchRunner, index: number) => Promise): Promise>; } /** * Branch-local step runner produced by {@link PromptRunner.parallel}. * * Differs from `PromptRunner.step` in two ways: * - On interrupt, collects them on `this.interrupts` rather than * throwing. The parallel orchestrator merges across siblings. * - All subsequent `step()` calls on the same branch short-circuit * once `interrupts` is set — the branch is effectively halted. * * Completion keys live on the same `self.runnerState.completedSteps` * map as the outer runner, so callers must pick keys that are unique * per-branch (e.g. include the per-item id in the key). */ export declare class BranchRunner { private self; interrupts: Interrupt[] | null; constructor(self: any); step(key: string, body: () => Promise): Promise; }