export { CANVAS_EXTENSIONS, CANVAS_STOPHOOK_PATH, CANVAS_INBOX_WATCHER_PATH, CANVAS_RECAP_PATH, CANVAS_GOAL_CAPTURE_PATH, CANVAS_PASSIVE_CONTEXT_PATH, CANVAS_CONTEXT_INTRO_PATH, CANVAS_DOC_SUBSTRATE_PATH, CANVAS_STRUCTURED_OUTPUT_PATH, CANVAS_BASH_VALVE_PATH, } from './canvas-extensions.js'; import type { NodeMeta, LaunchSpec, Mode, Lifecycle } from '../canvas/index.js'; import type { ModelProvider, ModelStrength, ScopeConfig } from '../../types.js'; export { OPENAI_PROVIDER_ENV, configuredLadderProviders, inferPortableModelSelection } from './model-selection.js'; export type { PortableModelSelection } from './model-selection.js'; export interface EquivalentProviderModel { fromProvider: ModelProvider; toProvider: ModelProvider; strength: ModelStrength; model: string; } /** Map a concrete ladder model back to its provider×strength cell, then return * the other configured provider's model at the same strength. `failedProviders` * is per-turn state: once a provider has produced a retryable outage error, do * not bounce back to it or keep reattempting the same failed provider. */ export declare function equivalentOtherProviderModel(currentModel: string, failedProviders?: ReadonlySet): EquivalentProviderModel | null; /** True when an explicit model token is QUALIFIED — a `/` * or `/` raw token — the structural marker of a concrete exact * pick. Deliberately provider-agnostic: a real registry provider namespace * (`anthropic`, `openai-codex`, a user's own `amazon-bedrock`, …) * qualifies identically, so the rule never depends on a fixed provider * allowlist and never needs updating as providers are added. A bare tier * (`ultra`) or bare family alias (`opus`/`sonnet`/`haiku`) has no `/` and * stays unpinned. */ export declare function isProviderPinnedModelToken(model: string): boolean; /** True when `model` is a PORTABLE ladder token — a bare strength/alias * (`ultra`, `normal`, `opus`, …) or a `anthropic|openai / strength` pair — * i.e. a form `normalizeModel` resolves through the ladders rather than * passing through as a concrete id. Portable tokens are safe to PERSIST as * defaults (`persistDefaultKindModel`): they re-resolve against whatever * ladder the running build ships, so they never freeze a stale concrete * model id to disk. Mirrors `normalizeModel`'s branch structure exactly. */ export declare function isPortableModelToken(model: string): boolean; export declare function normalizeModel(model: string, ladders?: ScopeConfig['modelLadders']): string; /** The interleaved model-ladder cycle Alt+M / Alt+Shift+M walks: for each strength tier * from strongest to weakest, each offered provider before descending — anthropic/ultra, * openai/ultra, anthropic/strong, openai/strong, anthropic/medium, … Returns the * concrete `provider/id:thinking` ladder cells in that order. `providers` narrows the * cycle to the signed-in columns (`configuredLadderProviders`); an empty subset falls * back to every provider so the cycle can never go rungless. */ export declare function interleavedLadderCycle(providers?: readonly ModelProvider[]): string[]; /** The next model in the interleaved ladder cycle, starting from `currentSpec` * (a `provider/id:thinking` spec — pass `formatModelSpec(session.model, * session.thinkingLevel)`). `direction` is +1 (forward) or -1 (backward). * * Rungs are matched on bare id AND thinking level first, so ladder cells that * share a bare model id (e.g. anthropic ultra=…:high vs strong=…:medium) don't * collapse onto each other; a bare-id-only match is the fallback. An off-ladder * current model — including a rung on a provider `providers` excludes — starts * the cycle at its top (the strongest offered rung). */ export declare function nextLadderModel(currentSpec: string, direction?: 1 | -1, providers?: readonly ModelProvider[]): string; /** Compose a node's full pi launch recipe from the kind registry. The system * prompt is now a minimal static scaffold (`LAUNCH_SYSTEM_PROMPT`) — runtime/ * spine/lifecycle/kind prose is spliced per-turn from gated builtin memory docs, * not baked in here. Launch KNOBS (model/tools/extensions) still come from the * kind registry (`resolveKindConfig`), keyed on the FULL kind string (top-level * or sub-kind). Model precedence, strongest to weakest: * 1. Caller-supplied `opts.model` (durable on `meta.model_override`, re-passed * on every polymorph). * 2. The kind's registered model — mode-aware: orchestrator mode prefers * `orchestratorModel`, falling back to `model`; base mode always uses * `model`. * 3. Unset → pi default. * Callers pass the authoritative lifecycle + hasManager (`parent !== null`) so * a polymorph/flip rebuilds the recipe faithfully; `lifecycle` is returned * as-given — it is no longer persona-frontmatter-derived. The two canvas * extensions are always first; kind-declared extensions follow. */ export declare function buildLaunchSpec(kind: string, mode: Mode, opts: { lifecycle: Lifecycle; hasManager: boolean; extraEnv?: Record; model?: string; /** The TARGET node's cwd — anchors the project-scope stack for kind/ladder * resolution. Defaults to this process's own cwd, which is correct only * when the caller IS resolving for itself; spawn/polymorph paths must * pass the node's own cwd (same rule as `readMergedLaunchConfig`). */ cwd?: string; /** The TARGET node's profile id — REQUIRED for a profile's kind/ladder * overrides (e.g. a `/model` default persisted by * `persistDefaultKindModel`) to be honored. Defaults to the ambient * `CRTR_PROFILE_ID`, which is EMPTY for a front-door shell invocation — * exactly the path that creates the user's sessions — so callers that * know the node's profile must pass it explicitly (null = no profile). */ profileId?: string | null; /** Carry an EXISTING node's pin verbatim through a rebuild that supplies * no NEW `--model` override (a polymorph/lifecycle/kind change, or a * fresh-root relaunch). When set (including `false`), this wins over the * token-shape derivation below — the pin is decided exactly once, at the * moment of an explicit user model change, from that change's RAW token; * every other rebuild must preserve it rather than re-infer it from * `opts.model`, which may be a portablized `model_override` whose shape * no longer reflects the original raw pick (e.g. a concrete pin that * happens to byte-equal a ladder tier cell). Leave undefined for genuine * first-spawn and for an explicit new `--model` override on * promote/config, where `opts.model`'s shape IS the raw token and * legitimately re-decides the pin. */ modelProviderPinned?: boolean; /** Exact-consult launch: pass the captured `provider/id:thinking` spec * verbatim as `launch.model`, skipping `floorReviewModel`/`normalizeModel` * entirely (avoids any tier-collision reinterpretation). `opts.model` MUST * be defined when this is true. Sets `launch.modelExact = true`, which the * broker's launch-time/turn-time resolution paths must bypass every * fallback for and fail closed on unavailability. */ modelExact?: boolean; }): { launch: LaunchSpec; lifecycle: 'terminal' | 'resident'; }; export interface PiInvocation { /** argv after the `pi` binary. */ argv: string[]; /** env to merge into the process. */ env: Record; } /** Construct the pi invocation for a node. * - fresh start: pass `prompt` (the node's first user message), no resume. * - fork start: pass `forkFrom` (absolute .jsonl path or partial uuid) to `--fork` * — pi COPIES that conversation into a NEW session for this node, then `prompt` * is delivered as the next message. One-shot at birth: the node thereafter * captures its OWN pi_session_file and revives by `--session` like any other. * - revive idle/done: pass `resumeSessionPath` (absolute .jsonl path) to * `--session` (keeps conversation). `resumeSessionId` (a bare uuid) is * accepted here too, but revive.ts's `resumeArgs` never produces one — * `headlessBrokerHost`'s preflight (host.ts) fatally rejects a bare * `--session` id before ever spawning the broker, so a real revive only * ever resumes by path (or falls through to fresh when no path exists). * - refresh-yield: same `.jsonl` (`resumeSessionPath`) PLUS `newCycle` — * emits the valueless `--crtr-new-cycle` token alongside `--session`. The * broker resets the session-tree leaf and roots the new cycle as a fresh * sibling branch instead of starting a new file; the node re-reads its * roadmap exactly as a fresh launch would. */ export declare function buildPiArgv(meta: NodeMeta, opts?: { prompt?: string; resumeSessionId?: string; resumeSessionPath?: string; forkFrom?: string; newCycle?: boolean; }): PiInvocation; /** The pi-SDK launch config the headless broker drives an in-process engine * with — the structural inverse of `buildPiArgv`'s flag vocabulary. Each field * maps one of buildPiArgv's emitted flags back to its SDK option: * `-e`→`extensionPaths`, `-n`→`editorName`, `--fork`→`forkFrom`, * `--session`→`resumeSessionPath|resumeSessionId`, `--crtr-new-cycle`→ * `newCycle`, `--model`→`model`, `--crtr-model-provider-pinned`→ * `modelProviderPinned`, `--crtr-model-exact`→`modelExact`, `--tools`→`tools`, * `--append-system-prompt`→`appendSystemPromptPath`, the trailing * positional→`firstPrompt`. */ export interface BrokerSdkConfig { /** The node's pinned working dir (from `CRTR_NODE_CWD`, else `process.cwd()`). */ cwd: string; /** Absolute `.js` extension paths (the canvas extensions). */ extensionPaths: string[]; /** Session label (`-n`). */ editorName?: string; /** Spawn-time fork source (`--fork `). */ forkFrom?: string; /** Resume by absolute `.jsonl` path (preferred — `SessionManager.open`). */ resumeSessionPath?: string; /** Resume by bare session uuid. Accepted by the generic builder, but no live * caller sets it — revive resumes by absolute path only, and the broker * preflight rejects a bare `--session`. */ resumeSessionId?: string; /** A refresh-yield's marker: reset the session-tree leaf and root a fresh * cycle as a new branch of the SAME resumed `.jsonl`, instead of starting a * new session file (`--crtr-new-cycle`). Only meaningful alongside * `resumeSessionPath`; never set together with `forkFrom`. */ newCycle?: boolean; /** Model spec, e.g. `anthropic/sonnet` (`--model`). */ model?: string; /** A caller explicitly pinned `--model` to a provider; only a managed launch cooldown permits fallback. */ modelProviderPinned?: boolean; /** Exact-consult launch (`--crtr-model-exact`): `model` must run verbatim — * every launch-time/turn-time fallback is bypassed and unavailability must * fail rather than substitute. */ modelExact?: boolean; /** Tool allowlist (`--tools a,b,c` → `['a','b','c']`). */ tools?: string[]; /** `--append-system-prompt` arg — a file path in practice (pi's loader * resolves a path or literal text identically). */ appendSystemPromptPath?: string; /** The fresh-start kickoff message (the trailing positional). */ firstPrompt?: string; } /** Translate a `PiInvocation` (the recipe `buildPiArgv` produced) into the SDK * config the broker hosts an engine with. This is the EXACT inverse of * `buildPiArgv` and must track its flag set 1:1 — co-located here so the two * never drift. Safe because `buildPiArgv` is the SOLE producer of these flags * (we own both ends). SIDE EFFECT: merges `inv.env` into `process.env` so the * in-process engine + the bound canvas extensions see the same env a forked pi * would (CRTR_NODE_ID, CRTR_SUBTREE, …). */ export declare function piInvocationToSdkConfig(inv: PiInvocation): BrokerSdkConfig;