@warble/claude-agent-sdk 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -108,6 +108,12 @@ interface LlmCall {
108
108
  * prompt text instead.
109
109
  */
110
110
  when: WhenGuard | null;
111
+ /** Same-profile component aliases this exact step may invoke. */
112
+ component_calls: ComponentCall[];
113
+ }
114
+ interface ComponentCall {
115
+ alias: string;
116
+ component: string;
111
117
  }
112
118
  /**
113
119
  * A closed-vocabulary guard on a conditional `llm_call`: `guard` is one of `on_failure` /
@@ -187,8 +193,21 @@ interface SlotDecl {
187
193
  /** Present only when the author declared a condition. Carried verbatim; not interpreted here. */
188
194
  present_when?: unknown;
189
195
  }
196
+ /**
197
+ * One file a component declares it needs, by identity (IR 0.7).
198
+ *
199
+ * Content is deliberately not carried — the deliberate contrast with a slot variant, whose text is.
200
+ * `hash` is `sha256:<hex>` over the bytes and neither field is authorable; both are computed at
201
+ * compile, so an author-written value cannot rot into a claim nothing keeps true.
202
+ */
203
+ interface AssetDecl {
204
+ path: string;
205
+ hash: string;
206
+ bytes: number;
207
+ }
190
208
  interface ComponentNode {
191
209
  id: string;
210
+ entrypoint: boolean;
192
211
  verb: string;
193
212
  type: ComponentType;
194
213
  realization_kind: RealizationKind;
@@ -211,6 +230,8 @@ interface ComponentNode {
211
230
  /** Slots belonging to THIS component's own prompt text (its steps and `brief`). Profile-level
212
231
  * slots live on the IR root instead and are deliberately not copied here. */
213
232
  slots?: SlotDecl[];
233
+ /** Files this component declares it needs, by identity. Absent when it declares none. */
234
+ assets?: AssetDecl[];
214
235
  }
215
236
  interface WarbleIr {
216
237
  warble_ir_version: string;
@@ -237,74 +258,108 @@ declare function parseIr(json: string): WarbleIr;
237
258
  /** Distinct tier names across a node's `llm_calls`, order-preserving. */
238
259
  declare function distinctTiers(calls: readonly LlmCall[]): string[];
239
260
 
261
+ /** One statically-authorized caller-step edge in the materialized profile. */
262
+ interface ComponentDependency {
263
+ caller: string;
264
+ step: string;
265
+ alias: string;
266
+ component: string;
267
+ }
268
+ /** The deterministic transitive closure for one selectable root. */
269
+ interface EntryClosure {
270
+ root: string;
271
+ components: readonly string[];
272
+ }
240
273
  /**
241
- * Target capability profiles — the declarative side of the capability model
242
- * (`docs/spec/capability-model.md`), owned by THIS back-end in TypeScript.
274
+ * Preparation selection before target legalization. `roots` are the only nodes an operation may
275
+ * start; `callees` are addressable only through an edge in `dependencies`.
276
+ */
277
+ interface ComponentClosurePlan {
278
+ roots: readonly ComponentNode[];
279
+ callees: readonly ComponentNode[];
280
+ components: readonly ComponentNode[];
281
+ entries: readonly EntryClosure[];
282
+ dependencies: readonly ComponentDependency[];
283
+ }
284
+ /** Return all declared edges in profile/step/declaration order. */
285
+ declare function componentDependencies(ir: WarbleIr): readonly ComponentDependency[];
286
+ /**
287
+ * Resolve exactly the requested roots and their transitive callees. The reader rechecks the graph
288
+ * rather than trusting compiler provenance so a forged object IR cannot widen or cycle authority.
289
+ */
290
+ declare function resolveComponentClosure(ir: WarbleIr, requestedRoots?: readonly string[]): ComponentClosurePlan;
291
+
292
+ /**
293
+ * What a host says about each slot, keyed by slot name:
243
294
  *
244
- * A runtime target is `engine × mode`. This back-end declares one target, `claude-agent-sdk:local`:
245
- * the local `@anthropic-ai/claude-agent-sdk` `query()` loop (subscription login, compute on the
246
- * user's machine). The shared thing across back-ends is the IR + the capability-model
247
- * *semantics* (native / realize-via / degrade / fail, criticality, provided_by); the profile *data*
248
- * is target-specific and each back-end writes its own. So this file is the TS sibling of
249
- * the Rust file target's `targets.rs`, not a shared table.
295
+ * - a string — render this variant;
296
+ * - `null` — the slot's `present_when` does not hold, so remove it entirely;
297
+ * - absent — the host has no opinion, so render the declared `default`.
250
298
  *
251
- * How `local` differs from the Rust file target's `claude-code:headless` (the point of the second
252
- * back-end):
253
- * - `llm:per_step_tier` native (in-loop per-call model) ← headless: realize-via(subagents)
254
- * - `structured_output_capture` native (message stream) ← headless: native(stream-json)
255
- * - `render_contract` realize-via(warble-render) ← headless: realize-via(html-file)
256
- * The rest match headless, including the safety-critical loud-fails (human_approval, blast_radius).
299
+ * A plain table rather than a callback, so what the model was told is decided before the run rather
300
+ * than during it: it can be recorded, compared and fingerprinted. A callback could not be.
257
301
  */
258
- /** One of the four resolution outcomes a capability can take on a target. */
259
- type CapabilityOutcome = "native" | "realize-via" | "degrade" | "fail";
260
- /** Who supplies a resolved capability. */
261
- type ProvidedBy = "runtime" | "warble" | "none";
302
+ type SlotSupply = Readonly<Record<string, string | null>>;
262
303
  /**
263
- * safety-critical capabilities must never silently degrade — unsupported means the resolution pass
264
- * aborts. required/best-effort may degrade with a warning recorded in the report.
304
+ * What to do with a conditional slot the host did not answer.
305
+ *
306
+ * `"fail"` on any path whose text reaches a model: `default` covers "no opinion on the wording", and
307
+ * it cannot cover "no opinion on whether this exists at all" — defaulting there ships instructions
308
+ * for a capability that may have been withheld, which is the failure `present_when` exists to
309
+ * prevent.
310
+ *
311
+ * `"default"` on a display path. The rule above protects a model, not a reader, and a structural
312
+ * view that refuses to render a valid profile is worse than one showing the default wording. Note
313
+ * this is a real difference in meaning, not a relaxation for convenience: a display says "this is
314
+ * what the default binding would say", never "this is what will be sent".
265
315
  */
266
- type Criticality = "safety-critical" | "required" | "best-effort";
267
- interface CapabilityEntry {
268
- outcome: CapabilityOutcome;
269
- via: string | null;
270
- provided_by: ProvidedBy;
271
- criticality: Criticality;
272
- note: string | null;
273
- }
274
- type CapabilityProfile = Record<string, CapabilityEntry>;
275
- /** The one target this back-end declares (engine × mode). */
276
- type TargetId = "claude-agent-sdk:local";
277
- declare const DEFAULT_TARGET: TargetId;
278
- declare function isKnownTarget(value: string): value is TargetId;
279
- declare function knownTargetNames(): readonly string[];
280
- /** Capability profile for `claude-agent-sdk:local`. */
281
- declare function localProfile(): CapabilityProfile;
282
- /** Resolve a target id to its capability profile, or `null` if unknown. */
283
- declare function profileFor(targetId: string): CapabilityProfile | null;
284
-
285
- interface ResolvedCapability {
286
- capability: string;
287
- outcome: CapabilityOutcome;
288
- provided_by: ProvidedBy;
289
- criticality: Criticality;
290
- note?: string;
291
- }
292
- type ResolutionReport = ResolvedCapability[];
293
- /** Union of declared + implied required capabilities, de-duplicated, order-preserving. */
294
- declare function collectRequiredCapabilities(node: ComponentNode): string[];
295
- declare function resolveCapabilities(node: ComponentNode, targetId: string, profile: CapabilityProfile): ResolutionReport;
316
+ type UnansweredCondition = "fail" | "default";
296
317
  /**
297
- * Resolve one node's required capabilities against `targetId`, erroring on any `fail` outcome
298
- * (no silent degradation). Callers must not dispatch when this throws.
318
+ * Resolve each declared slot to the text that will replace its references, or to `null` when the
319
+ * host said it is not present.
320
+ *
321
+ * A slot's chosen variant may itself reference other slots — the compiler collects references from
322
+ * variant text and validates them, so this is authored-legal — hence the recursion, with a visiting
323
+ * set so a cycle is reported rather than spun on.
324
+ *
325
+ * @param decls every slot in scope for the text being resolved. That is the profile's slots PLUS
326
+ * the component's, together: compile checks the two layers separately, but it also folds the
327
+ * profile's `system_prompt` into each component's `brief` (`render_brief`), so by the time a
328
+ * consumer sees the IR one string carries references from both. Names are unique project-wide —
329
+ * compile refuses a collision — which is what makes one combined table unambiguous.
330
+ * @param supply the host's table.
331
+ * @param scope names the scope in errors ("component 'x'").
299
332
  */
300
- declare function resolveNodeCapabilities(node: ComponentNode, targetId: string): ResolutionReport;
333
+ declare function resolveSlots(decls: readonly SlotDecl[], supply: SlotSupply, scope: string, unanswered?: UnansweredCondition): ReadonlyMap<string, string | null>;
301
334
  /**
302
- * Read-only inspection counterpart to `resolveNodeCapabilities`. It returns
303
- * the same report, including a failed entry, but does not legalize execution.
304
- * Only display-only callers may use this; dispatch/emit/chat retain the loud
305
- * failure above.
335
+ * Apply a resolved scope to one piece of prompt text.
336
+ *
337
+ * A slot resolved to `null` leaves nothing behind — deliberately the empty string rather than any
338
+ * variant, since the point of a condition that does not hold is that the wording must not appear.
306
339
  */
307
- declare function inspectNodeCapabilities(node: ComponentNode, targetId: string): ResolutionReport;
340
+ declare function applySlots(raw: string, resolved: ReadonlyMap<string, string | null>, scope: string): string;
341
+ /**
342
+ * Refuse prompt text that still carries a slot reference.
343
+ *
344
+ * The guard, not a formality. Every other failure in this file is loud on its own; this one catches
345
+ * the case where a surface was never routed through {@link applySlots} at all — a new prompt-carrying
346
+ * field, a path that assembles text somewhere unexpected — which is exactly how the original defect
347
+ * arrived: the IR grew slots and nothing downstream was taught to consume them.
348
+ */
349
+ declare function assertNoSlotReferences(raw: string, owner: string): void;
350
+ /**
351
+ * Parse repeated `--slot name=variant` / `--slot name=` flags into a {@link SlotSupply}.
352
+ *
353
+ * Lives here rather than in the CLI so it is reachable by a test and by any host that wants the same
354
+ * surface; the CLI turns the thrown error into its own usage failure. Mirrors the Rust CLI's flag of
355
+ * the same name — two user-facing surfaces that disagreed about what `name=` means would be worse
356
+ * than either one alone.
357
+ *
358
+ * An empty value is **not** an empty variant name: it is the host saying the slot's condition does
359
+ * not hold and the wording must not appear at all. That distinction is why the flag takes a trailing
360
+ * `=` rather than requiring a variant.
361
+ */
362
+ declare function parseSlotFlags(flags: readonly string[]): SlotSupply;
308
363
 
309
364
  /**
310
365
  * Which provider serves a tier's model — an **open string**, opaque to warble (mirrors how the IR
@@ -378,6 +433,76 @@ declare class ModelConfig {
378
433
  validate(ir: WarbleIr): void;
379
434
  }
380
435
 
436
+ /**
437
+ * Target capability profiles — the declarative side of the capability model
438
+ * (`docs/spec/capability-model.md`), owned by THIS back-end in TypeScript.
439
+ *
440
+ * A runtime target is `engine × mode`. This back-end declares one target, `claude-agent-sdk:local`:
441
+ * the local `@anthropic-ai/claude-agent-sdk` `query()` loop (subscription login, compute on the
442
+ * user's machine). The shared thing across back-ends is the IR + the capability-model
443
+ * *semantics* (native / realize-via / degrade / fail, criticality, provided_by); the profile *data*
444
+ * is target-specific and each back-end writes its own. So this file is the TS sibling of
445
+ * the Rust file target's `targets.rs`, not a shared table.
446
+ *
447
+ * How `local` differs from the Rust file target's `claude-code:headless` (the point of the second
448
+ * back-end):
449
+ * - `llm:per_step_tier` native (in-loop per-call model) ← headless: realize-via(subagents)
450
+ * - `structured_output_capture` native (message stream) ← headless: native(stream-json)
451
+ * - `render_contract` realize-via(warble-render) ← headless: realize-via(html-file)
452
+ * The rest match headless, including the safety-critical loud-fails (human_approval, blast_radius).
453
+ */
454
+ /** One of the four resolution outcomes a capability can take on a target. */
455
+ type CapabilityOutcome = "native" | "realize-via" | "degrade" | "fail";
456
+ /** Who supplies a resolved capability. */
457
+ type ProvidedBy = "runtime" | "warble" | "none";
458
+ /**
459
+ * safety-critical capabilities must never silently degrade — unsupported means the resolution pass
460
+ * aborts. required/best-effort may degrade with a warning recorded in the report.
461
+ */
462
+ type Criticality = "safety-critical" | "required" | "best-effort";
463
+ interface CapabilityEntry {
464
+ outcome: CapabilityOutcome;
465
+ via: string | null;
466
+ provided_by: ProvidedBy;
467
+ criticality: Criticality;
468
+ note: string | null;
469
+ }
470
+ type CapabilityProfile = Record<string, CapabilityEntry>;
471
+ /** The one target this back-end declares (engine × mode). */
472
+ type TargetId = "claude-agent-sdk:local";
473
+ declare const DEFAULT_TARGET: TargetId;
474
+ declare function isKnownTarget(value: string): value is TargetId;
475
+ declare function knownTargetNames(): readonly string[];
476
+ /** Capability profile for `claude-agent-sdk:local`. */
477
+ declare function localProfile(): CapabilityProfile;
478
+ /** Resolve a target id to its capability profile, or `null` if unknown. */
479
+ declare function profileFor(targetId: string): CapabilityProfile | null;
480
+
481
+ interface ResolvedCapability {
482
+ capability: string;
483
+ outcome: CapabilityOutcome;
484
+ via?: string;
485
+ provided_by: ProvidedBy;
486
+ criticality: Criticality;
487
+ note?: string;
488
+ }
489
+ type ResolutionReport = ResolvedCapability[];
490
+ /** Union of declared + implied required capabilities, de-duplicated, order-preserving. */
491
+ declare function collectRequiredCapabilities(node: ComponentNode): string[];
492
+ declare function resolveCapabilities(node: ComponentNode, targetId: string, profile: CapabilityProfile): ResolutionReport;
493
+ /**
494
+ * Resolve one node's required capabilities against `targetId`, erroring on any `fail` outcome
495
+ * (no silent degradation). Callers must not dispatch when this throws.
496
+ */
497
+ declare function resolveNodeCapabilities(node: ComponentNode, targetId: string): ResolutionReport;
498
+ /**
499
+ * Read-only inspection counterpart to `resolveNodeCapabilities`. It returns
500
+ * the same report, including a failed entry, but does not legalize execution.
501
+ * Only display-only callers may use this; dispatch/emit/chat retain the loud
502
+ * failure above.
503
+ */
504
+ declare function inspectNodeCapabilities(node: ComponentNode, targetId: string): ResolutionReport;
505
+
381
506
  /**
382
507
  * Per-step provider routing — the hybrid-LLM core (see docs/spec/capability-model.md §7.2).
383
508
  *
@@ -455,47 +580,6 @@ interface StepMessage {
455
580
  */
456
581
  declare function buildStepMessages(step: StagedStep, question: string, artifacts: Readonly<Record<string, string>>): StepMessage[];
457
582
 
458
- /**
459
- * Minimal OpenAI-compatible chat client for the hybrid-staged path (see docs/spec/capability-model.md §7.2).
460
- *
461
- * A local step (provider `openai_compat`, e.g. ollama's `http://localhost:11434/v1`) is executed by
462
- * calling `POST {endpoint}/chat/completions` directly — NOT through the Claude SDK, whose `agents[].model`
463
- * is a restricted alias union that loud-fails on a local model id (SDK-NOTES.md #1). ollama speaks the
464
- * OpenAI Chat Completions shape, not the Anthropic Messages shape, so this is a distinct, deliberately
465
- * tiny client — no streaming, no tools, no retries. It is the "third provider-aware back-end" embryo:
466
- * enough to prove a per-step local model can be marshaled into a cloud run, not a production LLM client.
467
- *
468
- * Live-gated: exercised only when an ollama (or other OpenAI-compat) endpoint is reachable. The request
469
- * SHAPING is unit-tested via {@link buildChatRequest} with no network.
470
- */
471
-
472
- interface ChatRequest {
473
- model: string;
474
- messages: StepMessage[];
475
- stream: false;
476
- /** Deterministic-leaning default; a local step is a bounded transform, not open-ended generation. */
477
- temperature: number;
478
- }
479
- /** Build the JSON body for an OpenAI-compatible `/chat/completions` call. Pure (no network). */
480
- declare function buildChatRequest(model: string, messages: StepMessage[]): ChatRequest;
481
- /** Extract the assistant text from an OpenAI-compatible completion response. Pure. */
482
- declare function extractCompletionText(body: unknown): string;
483
- interface CallLocalOptions {
484
- endpoint: string;
485
- model: string;
486
- messages: StepMessage[];
487
- /** Optional bearer token (ollama ignores it; other OpenAI-compat servers may require it). */
488
- apiKey?: string;
489
- /** Injectable for tests; defaults to global fetch. */
490
- fetchImpl?: typeof fetch;
491
- }
492
- /**
493
- * Call an OpenAI-compatible chat endpoint and return the assistant text. Live-gated (needs a reachable
494
- * endpoint); the request/response shaping is covered by {@link buildChatRequest} /
495
- * {@link extractCompletionText} tests, and a stubbed `fetchImpl` can drive this end-to-end offline.
496
- */
497
- declare function callOpenAiCompat(opts: CallLocalOptions): Promise<string>;
498
-
499
583
  /**
500
584
  * IR enum → `query({options})` mapping — the core of this back-end, the TS analogue of the
501
585
  * file target's `emit.rs`. Keyed on the **three orthogonal IR enums** (`realization_kind`,
@@ -575,6 +659,8 @@ interface DispatchMeta {
575
659
  * beyond `wren` and Write/Edit are scoped to this root, instead of denied outright. `null` on the
576
660
  * hybrid-staged path (out of scope — see buildHybridStagedPlan). */
577
661
  setupScope: string | null;
662
+ /** True when this plan requires the prepared-registry component runtime, not plain runDispatch. */
663
+ componentInvocation?: boolean;
578
664
  }
579
665
  interface DispatchPlan {
580
666
  /** The user question (assembled prompt for `query()`). */
@@ -837,6 +923,8 @@ interface Trace {
837
923
  /** Per assistant turn (per-step granularity the headless file target can't produce). */
838
924
  steps: StepUsage[];
839
925
  denials: Denial[];
926
+ /** Redacted parent/child attempt telemetry. Payloads and provider errors are never included. */
927
+ componentCalls?: ComponentCallTrace[];
840
928
  }
841
929
  /** Pure: fold the captured message stream + guardrail denials into a trace. */
842
930
  declare function aggregateTrace(messages: readonly SDKMessage[], meta: {
@@ -886,6 +974,19 @@ interface RunConfig {
886
974
  * changed across the run, which is itself the finding.
887
975
  */
888
976
  onPromptFingerprint?: (fingerprint: PromptFingerprint) => void;
977
+ /**
978
+ * The IR file this plan came from, and the IR itself — supplied together to land the assets its
979
+ * components declare into the working directory before the agent runs.
980
+ *
981
+ * Optional because a caller that built a plan without an IR file on disk (a test, a programmatic
982
+ * IR) has nothing to land from. Omitting it on a plan whose components DO declare assets means the
983
+ * agent runs without them, which is the pre-existing behaviour this closes — so a host that can
984
+ * supply it should.
985
+ */
986
+ assets?: {
987
+ ir: WarbleIr;
988
+ irPath: string;
989
+ };
889
990
  }
890
991
  /**
891
992
  * A dispatch failure that still carries the SDK's session id, when the result message had one —
@@ -904,89 +1005,385 @@ declare class DispatchSessionError extends DispatchError {
904
1005
  */
905
1006
  declare function runDispatch(plan: DispatchPlan, cfg: RunConfig): Promise<RunResult>;
906
1007
 
1008
+ interface DispatchInput {
1009
+ /** A parsed IR or a raw JSON string. */
1010
+ ir: WarbleIr | string;
1011
+ /**
1012
+ * What this host says about each declared slot: a variant name to render, `null` to remove the
1013
+ * slot because its `present_when` does not hold, or nothing at all to take the declared default.
1014
+ *
1015
+ * The IR carries every variant and picks none — picking is the host's job, and so is answering a
1016
+ * condition. Omit this entirely for an IR that declares no slots; supplying it then is harmless
1017
+ * but pointless. An IR that DOES declare a conditional slot and gets no answer for it is a loud
1018
+ * failure rather than a silent default (see `resolveSlots`).
1019
+ */
1020
+ slots?: SlotSupply;
1021
+ /** The data question to answer (the `query()` prompt). Optional for prepare-only (dry-run/emit). */
1022
+ question?: string;
1023
+ target?: string;
1024
+ flavor?: RenderFlavor;
1025
+ models?: ModelConfig;
1026
+ maxTurns?: number;
1027
+ /** Explicit bound-project cwd (absolute or cwd-relative). Overrides `irPath`-based resolution. */
1028
+ project?: string;
1029
+ /** Resolve each node's relative `context_binding.project` against this IR file's directory. */
1030
+ irPath?: string;
1031
+ /**
1032
+ * Scope preparation to this root id and its complete transitive component-call closure. An
1033
+ * unreachable sibling is left untouched, so its model, slots, assets, capabilities, guardrails,
1034
+ * effect and trigger never enter this dispatch's preflight. Use this for `chat`, which starts one
1035
+ * root per process.
1036
+ *
1037
+ * Omit (the default) to prepare every component in the IR — the shape `manifest`, `emit`, and
1038
+ * the whole-profile `dispatch` subcommand need, since each of those actually reads or runs
1039
+ * every component and must know every component's resolution, not just one's.
1040
+ *
1041
+ * This narrows *which closure's* requirements gate a call; it does not weaken a failed
1042
+ * capability. Reachable callees are fully planned under their own authority and stored in the
1043
+ * prepared-callee registry, never appended to the root plan list or started automatically.
1044
+ */
1045
+ componentId?: string;
1046
+ }
1047
+ interface PreparedComponent {
1048
+ id: string;
1049
+ node: ComponentNode;
1050
+ report: ResolutionReport;
1051
+ plan: DispatchPlan;
1052
+ /** Every IR step with its own runtime model/provider binding, retained for trusted step execution. */
1053
+ steps: readonly StagedStep[];
1054
+ role: "entry" | "callee";
1055
+ }
1056
+ interface PreparedDispatch {
1057
+ target: string;
1058
+ profile: string;
1059
+ /** Backwards-compatible root plan list. Reachable callees never appear here merely by reachability. */
1060
+ components: PreparedComponent[];
1061
+ /** Immutable records addressable only through the authorized dependency table. */
1062
+ preparedCallees: readonly PreparedComponent[];
1063
+ entries: readonly EntryClosure[];
1064
+ dependencies: readonly ComponentDependency[];
1065
+ }
1066
+ /** Stable redacted status for a component the configured target cannot run. */
1067
+ declare const UNAVAILABLE_COMPONENT_REASON = "component is unavailable on the configured runtime";
1068
+ interface UnavailableDisplayComponent {
1069
+ id: string;
1070
+ node: ComponentNode;
1071
+ /** Read-only diagnostic resolution; never an executable capability grant. */
1072
+ inspection: ResolutionReport;
1073
+ availability: {
1074
+ status: "unavailable";
1075
+ reason: typeof UNAVAILABLE_COMPONENT_REASON;
1076
+ };
1077
+ }
907
1078
  /**
908
- * The orchestrator's system prompt — PROVIDER-AGNOSTIC. Lists the steps in order by name and the
909
- * produces→consumes marshaling; it never says which step is local vs cloud (that is the handler's job,
910
- * from the binding). So the same prompt shape is emitted whether the binding is all-cloud or hybrid.
911
- */
912
- declare function buildToolDriverPrompt(steps: readonly StagedStep[]): string;
913
- /**
914
- * Run the hybrid-tool path: one orchestrator query() + a `dispatch_step` tool that routes each step to
915
- * its bound provider. Mirrors {@link runHybridStaged}'s outputs (result.txt / trace.json / RunResult).
916
- */
917
- declare function runHybridTool(plan: DispatchPlan, cfg: RunConfig): Promise<RunResult>;
918
-
919
- /**
920
- * What a host says about each slot, keyed by slot name:
1079
+ * A component in a display manifest that the target CAN run.
921
1080
  *
922
- * - a string — render this variant;
923
- * - `null` — the slot's `present_when` does not hold, so remove it entirely;
924
- * - absent — the host has no opinion, so render the declared `default`.
1081
+ * Deliberately **not** a {@link PreparedComponent}: it carries no `plan`. A plan built for a display
1082
+ * is resolved under the lenient unanswered-condition policy, so handing one out would let a consumer
1083
+ * do the natural "preview it, then dispatch what was previewed" — `runDispatch(component.plan, cfg)`
1084
+ * type-checks with no cast — and send a model wording for a condition nobody ever answered. That is
1085
+ * exactly the failure the strict policy exists to prevent, arriving through the display door.
925
1086
  *
926
- * A plain table rather than a callback, so what the model was told is decided before the run rather
927
- * than during it: it can be recorded, compared and fingerprinted. A callback could not be.
1087
+ * A plan is still built during preparation, because that is what surfaces an unsupported enum as a
1088
+ * wall-hit; it is discarded rather than returned. Nothing in the manifest builders reads it.
928
1089
  */
929
- type SlotSupply = Readonly<Record<string, string | null>>;
1090
+ interface AvailableDisplayComponent {
1091
+ id: string;
1092
+ node: ComponentNode;
1093
+ report: ResolutionReport;
1094
+ }
1095
+ type DisplayComponent = AvailableDisplayComponent | UnavailableDisplayComponent;
1096
+ interface PreparedDisplayManifest {
1097
+ target: string;
1098
+ profile: string;
1099
+ components: readonly DisplayComponent[];
1100
+ entries: readonly EntryClosure[];
1101
+ dependencies: readonly ComponentDependency[];
1102
+ }
930
1103
  /**
931
- * What to do with a conditional slot the host did not answer.
932
- *
933
- * `"fail"` on any path whose text reaches a model: `default` covers "no opinion on the wording", and
934
- * it cannot cover "no opinion on whether this exists at all" — defaulting there ships instructions
935
- * for a capability that may have been withheld, which is the failure `present_when` exists to
936
- * prevent.
937
- *
938
- * `"default"` on a display path. The rule above protects a model, not a reader, and a structural
939
- * view that refuses to render a valid profile is worse than one showing the default wording. Note
940
- * this is a real difference in meaning, not a relaxation for convenience: a display says "this is
941
- * what the default binding would say", never "this is what will be sent".
1104
+ * Resolve a node's bound wren project to an absolute cwd. Relative `context_binding.project` paths
1105
+ * resolve against the IR file's directory (`irPath`) when given, else the current working directory;
1106
+ * an explicit `project` always wins.
942
1107
  */
943
- type UnansweredCondition = "fail" | "default";
1108
+ declare function resolveProjectCwd(node: ComponentNode, opts: {
1109
+ project?: string;
1110
+ irPath?: string;
1111
+ }): string;
944
1112
  /**
945
- * Resolve each declared slot to the text that will replace its references, or to `null` when the
946
- * host said it is not present.
947
- *
948
- * A slot's chosen variant may itself reference other slots — the compiler collects references from
949
- * variant text and validates them, so this is authored-legal — hence the recursion, with a visiting
950
- * set so a cycle is reported rather than spun on.
1113
+ * Parse + resolve + build every requested component's `query({options})`, without calling the SDK.
951
1114
  *
952
- * @param decls every slot in scope for the text being resolved. That is the profile's slots PLUS
953
- * the component's, together: compile checks the two layers separately, but it also folds the
954
- * profile's `system_prompt` into each component's `brief` (`render_brief`), so by the time a
955
- * consumer sees the IR one string carries references from both. Names are unique project-wide —
956
- * compile refuses a collision — which is what makes one combined table unambiguous.
957
- * @param supply the host's table.
958
- * @param scope names the scope in errors ("component 'x'").
1115
+ * By default this prepares every advertised entry and the union of their closures. Pass
1116
+ * `input.componentId` to scope preparation to one advertised root and its transitive callees;
1117
+ * components outside that closure are never consulted. See {@link DispatchInput.componentId}.
959
1118
  */
960
- declare function resolveSlots(decls: readonly SlotDecl[], supply: SlotSupply, scope: string, unanswered?: UnansweredCondition): ReadonlyMap<string, string | null>;
1119
+ declare function prepareDispatch(input: DispatchInput): PreparedDispatch;
961
1120
  /**
962
- * Apply a resolved scope to one piece of prompt text.
963
- *
964
- * A slot resolved to `null` leaves nothing behind — deliberately the empty string rather than any
965
- * variant, since the point of a condition that does not hold is that the wording must not appear.
1121
+ * Prepare a display-only whole-profile manifest. Unsupported components are
1122
+ * represented by a closed unavailable marker; no executable plan is built
1123
+ * for them. This must never be used by emit, dispatch, or chat.
966
1124
  */
967
- declare function applySlots(raw: string, resolved: ReadonlyMap<string, string | null>, scope: string): string;
1125
+ declare function prepareDisplayManifest(input: Omit<DispatchInput, "componentId" | "question">): PreparedDisplayManifest;
1126
+ interface DispatchRunConfig {
1127
+ outDir: string;
1128
+ warbleBin?: string;
1129
+ title?: string;
1130
+ /** Host-lowered root invocation limits. Defaults are fixed by the composition specification. */
1131
+ componentLimits?: ComponentInvocationLimits;
1132
+ /** Root cancellation propagated into all active child query() calls. */
1133
+ signal?: AbortSignal;
1134
+ /** Deterministic host/test seam; production omits this and uses fresh Agent SDK query() calls. */
1135
+ componentStepRunner?: ComponentStepRunner;
1136
+ /** Embedder enforcement runs before each component's own immutable guardrail floor. */
1137
+ hostCanUseTool?: Options["canUseTool"];
1138
+ hostHooks?: Options["hooks"];
1139
+ }
1140
+ interface ComponentOutcome {
1141
+ id: string;
1142
+ report: ResolutionReport;
1143
+ plan: DispatchPlan;
1144
+ result: RunResult;
1145
+ }
1146
+ interface DispatchOutcome {
1147
+ target: string;
1148
+ components: ComponentOutcome[];
1149
+ }
968
1150
  /**
969
- * Refuse prompt text that still carries a slot reference.
970
- *
971
- * The guard, not a formality. Every other failure in this file is loud on its own; this one catches
972
- * the case where a surface was never routed through {@link applySlots} at all — a new prompt-carrying
973
- * field, a path that assembles text somewhere unexpected — which is exactly how the original defect
974
- * arrived: the IR grew slots and nothing downstream was taught to consume them.
1151
+ * Verify every reachable mount's travelling assets without writing them. This is intentionally a
1152
+ * separate executable preflight from pure graph preparation: it reads the filesystem, but still
1153
+ * completes before a model, session, output directory, or asset target is created.
975
1154
  */
976
- declare function assertNoSlotReferences(raw: string, owner: string): void;
1155
+ declare function preflightDispatchAssets(input: DispatchInput, prepared: PreparedDispatch): void;
977
1156
  /**
978
- * Parse repeated `--slot name=variant` / `--slot name=` flags into a {@link SlotSupply}.
1157
+ * Prepare then RUN each component against the live Agent SDK loop. Requires `input.question`.
1158
+ * Writes each run's artifacts under `runCfg.outDir` (see {@link runDispatch}).
1159
+ */
1160
+ declare function dispatch(input: DispatchInput, runCfg: DispatchRunConfig): Promise<DispatchOutcome>;
1161
+
1162
+ declare const DEFAULT_COMPONENT_INVOCATION_LIMITS: Readonly<{
1163
+ maxDepth: 8;
1164
+ maxAttempts: 32;
1165
+ maxRequestBytes: 65536;
1166
+ maxResultBytes: 1048576;
1167
+ maxTurns: 40;
1168
+ maxTurnsPerChild: 12;
1169
+ }>;
1170
+ interface ComponentInvocationLimits {
1171
+ maxDepth?: number;
1172
+ maxAttempts?: number;
1173
+ maxRequestBytes?: number;
1174
+ maxResultBytes?: number;
1175
+ maxTurns?: number;
1176
+ maxTurnsPerChild?: number;
1177
+ /** Rejected by the Agent SDK composition preflight until provider spend is reservable. */
1178
+ maxCostUsd?: number;
1179
+ deadlineAt?: number;
1180
+ }
1181
+ interface ComponentInvocationRequest {
1182
+ request: string;
1183
+ input?: Record<string, unknown>;
1184
+ }
1185
+ interface ComponentProvenance {
1186
+ verified?: boolean;
1187
+ definition?: unknown;
1188
+ }
1189
+ type ComponentInvocationResult = {
1190
+ status: "ok";
1191
+ output: {
1192
+ kind: "value";
1193
+ value: unknown;
1194
+ } | {
1195
+ kind: "render";
1196
+ blocks: Record<string, unknown>[];
1197
+ summary?: string;
1198
+ };
1199
+ provenance?: ComponentProvenance;
1200
+ } | {
1201
+ status: "refused";
1202
+ code: "callee_refused";
1203
+ message: string;
1204
+ } | {
1205
+ status: "error";
1206
+ code: "unsupported_callee" | "invalid_request" | "invalid_result" | "budget_exhausted" | "cancelled" | "transient_transport" | "callee_failed";
1207
+ message: string;
1208
+ retryable: boolean;
1209
+ };
1210
+ type ComponentCallStatus = "ok" | "refused" | "error" | "cancelled" | "late_discarded";
1211
+ interface ComponentCallTrace {
1212
+ call_id: string;
1213
+ parent_call_id: string | null;
1214
+ caller_mount: string;
1215
+ trusted_step_id: string;
1216
+ alias: string;
1217
+ callee_mount: string;
1218
+ attempt: number;
1219
+ depth: number;
1220
+ admitted_at: number;
1221
+ completed_at: number;
1222
+ status: ComponentCallStatus;
1223
+ request_bytes: number;
1224
+ result_bytes: number;
1225
+ model_turns: number;
1226
+ total_cost_usd: number;
1227
+ token_usage: {
1228
+ input_tokens: number;
1229
+ output_tokens: number;
1230
+ cache_creation_input_tokens: number;
1231
+ cache_read_input_tokens: number;
1232
+ } | null;
1233
+ degradation: string | null;
1234
+ }
1235
+ interface ComponentStepUsage {
1236
+ model: string;
1237
+ usage: unknown;
1238
+ }
1239
+ interface ComponentStepResult {
1240
+ text: string;
1241
+ turns: number;
1242
+ totalCostUsd: number;
1243
+ usage?: ComponentStepUsage[];
1244
+ degradation?: string | null;
1245
+ }
1246
+ /** A step failed after the provider exposed billable telemetry. The runtime must charge it. */
1247
+ declare class ComponentStepExecutionError extends DispatchError {
1248
+ readonly telemetry: ComponentStepResult;
1249
+ readonly retryable: boolean;
1250
+ constructor(message: string, telemetry: ComponentStepResult, retryable: boolean);
1251
+ }
1252
+ interface ComponentStepRun {
1253
+ rootRunId: string;
1254
+ callId: string | null;
1255
+ parentCallId: string | null;
1256
+ component: PreparedComponent;
1257
+ step: StagedStep;
1258
+ request: Required<ComponentInvocationRequest>;
1259
+ artifacts: Readonly<Record<string, string>>;
1260
+ aliases: readonly string[];
1261
+ signal: AbortSignal;
1262
+ maxTurns: number;
1263
+ invoke(alias: string, request: unknown): Promise<ComponentInvocationResult>;
1264
+ }
1265
+ type ComponentStepRunner = (run: ComponentStepRun) => Promise<ComponentStepResult>;
1266
+ interface ComponentRuntimeOptions {
1267
+ prepared: PreparedDispatch;
1268
+ runStep: ComponentStepRunner;
1269
+ limits?: ComponentInvocationLimits;
1270
+ signal?: AbortSignal;
1271
+ now?: () => number;
1272
+ newId?: () => string;
1273
+ }
1274
+ interface ComponentRootResult {
1275
+ finalText: string;
1276
+ turns: number;
1277
+ totalCostUsd: number;
1278
+ usage: ComponentStepUsage[];
1279
+ componentCalls: ComponentCallTrace[];
1280
+ }
1281
+ declare function normalizeComponentRequest(value: unknown, maxBytes?: number): {
1282
+ value: Required<ComponentInvocationRequest>;
1283
+ bytes: number;
1284
+ } | {
1285
+ error: ComponentInvocationResult;
1286
+ };
1287
+ declare function normalizeComponentResult(finalText: string, renderContract: readonly RenderBlock[], maxBytes?: number): {
1288
+ value: ComponentInvocationResult;
1289
+ bytes: number;
1290
+ };
1291
+ declare function assertEligibleComponentCallee(component: PreparedComponent): void;
1292
+ /** A root caller may own the final render path, but its model step stays read-only and SDK-bound. */
1293
+ declare function assertEligibleComponentCaller(component: PreparedComponent): void;
1294
+ declare class ComponentInvocationRuntime {
1295
+ private readonly options;
1296
+ readonly rootRunId: string;
1297
+ readonly componentCalls: ComponentCallTrace[];
1298
+ private readonly limits;
1299
+ private readonly abortController;
1300
+ private readonly now;
1301
+ private readonly newId;
1302
+ private readonly byId;
1303
+ private readonly edgeByStep;
1304
+ private readonly descendantDepth;
1305
+ private attempts;
1306
+ private availableTurns;
1307
+ private totalTurnsObserved;
1308
+ private totalCostUsd;
1309
+ private readonly allUsage;
1310
+ private cancelledAt;
1311
+ private deadlineTimer;
1312
+ constructor(options: ComponentRuntimeOptions);
1313
+ get signal(): AbortSignal;
1314
+ cancel(): void;
1315
+ runRoot(componentId: string, request: ComponentInvocationRequest): Promise<ComponentRootResult>;
1316
+ private throwIfCancelled;
1317
+ private aliasesFor;
1318
+ private availableFor;
1319
+ private charge;
1320
+ private maxDescendantDepth;
1321
+ private descendantDepthForStep;
1322
+ /** Reserve an active root SDK query before its synchronous child handlers may be admitted. */
1323
+ private reserveRootStep;
1324
+ private executeComponent;
1325
+ private enqueueInvocation;
1326
+ private invoke;
1327
+ }
1328
+
1329
+ /**
1330
+ * Minimal OpenAI-compatible chat client for the hybrid-staged path (see docs/spec/capability-model.md §7.2).
979
1331
  *
980
- * Lives here rather than in the CLI so it is reachable by a test and by any host that wants the same
981
- * surface; the CLI turns the thrown error into its own usage failure. Mirrors the Rust CLI's flag of
982
- * the same name — two user-facing surfaces that disagreed about what `name=` means would be worse
983
- * than either one alone.
1332
+ * A local step (provider `openai_compat`, e.g. ollama's `http://localhost:11434/v1`) is executed by
1333
+ * calling `POST {endpoint}/chat/completions` directly — NOT through the Claude SDK, whose `agents[].model`
1334
+ * is a restricted alias union that loud-fails on a local model id (SDK-NOTES.md #1). ollama speaks the
1335
+ * OpenAI Chat Completions shape, not the Anthropic Messages shape, so this is a distinct, deliberately
1336
+ * tiny client — no streaming, no tools, no retries. It is the "third provider-aware back-end" embryo:
1337
+ * enough to prove a per-step local model can be marshaled into a cloud run, not a production LLM client.
984
1338
  *
985
- * An empty value is **not** an empty variant name: it is the host saying the slot's condition does
986
- * not hold and the wording must not appear at all. That distinction is why the flag takes a trailing
987
- * `=` rather than requiring a variant.
1339
+ * Live-gated: exercised only when an ollama (or other OpenAI-compat) endpoint is reachable. The request
1340
+ * SHAPING is unit-tested via {@link buildChatRequest} with no network.
988
1341
  */
989
- declare function parseSlotFlags(flags: readonly string[]): SlotSupply;
1342
+
1343
+ interface ChatRequest {
1344
+ model: string;
1345
+ messages: StepMessage[];
1346
+ stream: false;
1347
+ /** Deterministic-leaning default; a local step is a bounded transform, not open-ended generation. */
1348
+ temperature: number;
1349
+ }
1350
+ /** Build the JSON body for an OpenAI-compatible `/chat/completions` call. Pure (no network). */
1351
+ declare function buildChatRequest(model: string, messages: StepMessage[]): ChatRequest;
1352
+ /** Extract the assistant text from an OpenAI-compatible completion response. Pure. */
1353
+ declare function extractCompletionText(body: unknown): string;
1354
+ interface CallLocalOptions {
1355
+ endpoint: string;
1356
+ model: string;
1357
+ messages: StepMessage[];
1358
+ /** Optional bearer token (ollama ignores it; other OpenAI-compat servers may require it). */
1359
+ apiKey?: string;
1360
+ /** Injectable for tests; defaults to global fetch. */
1361
+ fetchImpl?: typeof fetch;
1362
+ }
1363
+ /**
1364
+ * Call an OpenAI-compatible chat endpoint and return the assistant text. Live-gated (needs a reachable
1365
+ * endpoint); the request/response shaping is covered by {@link buildChatRequest} /
1366
+ * {@link extractCompletionText} tests, and a stubbed `fetchImpl` can drive this end-to-end offline.
1367
+ */
1368
+ declare function callOpenAiCompat(opts: CallLocalOptions): Promise<string>;
1369
+
1370
+ /**
1371
+ * The orchestrator's system prompt — PROVIDER-AGNOSTIC. Lists the steps in order by name and the
1372
+ * produces→consumes marshaling; it never says which step is local vs cloud (that is the handler's job,
1373
+ * from the binding). So the same prompt shape is emitted whether the binding is all-cloud or hybrid.
1374
+ */
1375
+ declare function buildToolDriverPrompt(steps: readonly StagedStep[]): string;
1376
+ /**
1377
+ * Run the hybrid-tool path: one orchestrator query() + a `dispatch_step` tool that routes each step to
1378
+ * its bound provider. Mirrors {@link runHybridStaged}'s outputs (result.txt / trace.json / RunResult).
1379
+ */
1380
+ declare function runHybridTool(plan: DispatchPlan, cfg: RunConfig): Promise<RunResult>;
1381
+
1382
+ /** The directory an IR's assets travel in, derived from the IR's own path. Mirrors the Rust CLI's. */
1383
+ declare function assetDirForIr(irPath: string): string;
1384
+ /** Executable-preflight probe: verify every asset before model/session/output work begins. */
1385
+ declare function validateAssets(ir: WarbleIr, irPath: string, cwd: string): void;
1386
+ declare function landAssets(ir: WarbleIr, irPath: string, cwd: string): string[];
990
1387
 
991
1388
  interface RenderResult {
992
1389
  outPath: string;
@@ -1003,6 +1400,22 @@ declare function renderEnvelope(finalText: string, outPath: string, opts: {
1003
1400
  title?: string;
1004
1401
  }): RenderResult;
1005
1402
 
1403
+ declare function createSdkComponentStepRunner(cfg?: Pick<ComposedRunConfig, "onPromptFingerprint" | "hostCanUseTool" | "hostHooks">): {
1404
+ runStep: ComponentStepRunner;
1405
+ denials: Denial[];
1406
+ };
1407
+ interface ComposedRunConfig extends RunConfig {
1408
+ prepared: PreparedDispatch;
1409
+ root: PreparedComponent;
1410
+ limits?: ComponentInvocationLimits;
1411
+ signal?: AbortSignal;
1412
+ runStep?: ComponentStepRunner;
1413
+ hostCanUseTool?: Options["canUseTool"];
1414
+ hostHooks?: Options["hooks"];
1415
+ }
1416
+ /** Execute one prepared root and its authorized descendants. Only this root path persists output. */
1417
+ declare function runComposedDispatch(plan: DispatchPlan, cfg: ComposedRunConfig): Promise<RunResult>;
1418
+
1006
1419
  /**
1007
1420
  * A minimal structured snapshot of what a turn resolved — filters/dimensions/measures/grain — used
1008
1421
  * only to thread context into the NEXT turn's prompt. Nothing here is inferred by this module: a
@@ -1108,138 +1521,6 @@ declare class ChatSession {
1108
1521
  }
1109
1522
  declare function createChatSession(plan: DispatchPlan, runCfg: RunConfig, initialResumeSessionId?: string): ChatSession;
1110
1523
 
1111
- interface DispatchInput {
1112
- /** A parsed IR or a raw JSON string. */
1113
- ir: WarbleIr | string;
1114
- /**
1115
- * What this host says about each declared slot: a variant name to render, `null` to remove the
1116
- * slot because its `present_when` does not hold, or nothing at all to take the declared default.
1117
- *
1118
- * The IR carries every variant and picks none — picking is the host's job, and so is answering a
1119
- * condition. Omit this entirely for an IR that declares no slots; supplying it then is harmless
1120
- * but pointless. An IR that DOES declare a conditional slot and gets no answer for it is a loud
1121
- * failure rather than a silent default (see `resolveSlots`).
1122
- */
1123
- slots?: SlotSupply;
1124
- /** The data question to answer (the `query()` prompt). Optional for prepare-only (dry-run/emit). */
1125
- question?: string;
1126
- target?: string;
1127
- flavor?: RenderFlavor;
1128
- models?: ModelConfig;
1129
- maxTurns?: number;
1130
- /** Explicit bound-project cwd (absolute or cwd-relative). Overrides `irPath`-based resolution. */
1131
- project?: string;
1132
- /** Resolve each node's relative `context_binding.project` against this IR file's directory. */
1133
- irPath?: string;
1134
- /**
1135
- * Scope preparation to exactly this component id: only its capabilities are resolved and only
1136
- * its plan is built — every *other* component in the IR is left untouched, so its
1137
- * `required_capabilities` never enter this dispatch's preflight. Use this for `chat`, which
1138
- * only ever runs one component per process.
1139
- *
1140
- * Omit (the default) to prepare every component in the IR — the shape `manifest`, `emit`, and
1141
- * the whole-profile `dispatch` subcommand need, since each of those actually reads or runs
1142
- * every component and must know every component's resolution, not just one's.
1143
- *
1144
- * This narrows *which* component's requirements gate a given `prepareDispatch` call — it does
1145
- * not change what happens when a gated capability is unmet (still a loud throw, same message,
1146
- * same named capability; see `resolveNodeCapabilities`). A component that can itself invoke
1147
- * another IR component at runtime would need that callee's requirements folded in here too, but
1148
- * no such reachability exists yet: `borrowed_actions` names external runtime actions (notify,
1149
- * ticket, …), never another component, and the one mechanism shaped for it — `orchestrating` /
1150
- * `effect.outcome.kind: "dispatch"` with `routable_scope` — is parsed but not consumed by any
1151
- * back-end (`docs/spec/authoring.md` marks `orchestrating` "scaffolded", not realized). If that
1152
- * ever lands, this scoping must fold in the callee's requirements too.
1153
- */
1154
- componentId?: string;
1155
- }
1156
- interface PreparedComponent {
1157
- id: string;
1158
- node: ComponentNode;
1159
- report: ResolutionReport;
1160
- plan: DispatchPlan;
1161
- }
1162
- interface PreparedDispatch {
1163
- target: string;
1164
- components: PreparedComponent[];
1165
- }
1166
- /** Stable redacted status for a component the configured target cannot run. */
1167
- declare const UNAVAILABLE_COMPONENT_REASON = "component is unavailable on the configured runtime";
1168
- interface UnavailableDisplayComponent {
1169
- id: string;
1170
- node: ComponentNode;
1171
- availability: {
1172
- status: "unavailable";
1173
- reason: typeof UNAVAILABLE_COMPONENT_REASON;
1174
- };
1175
- }
1176
- /**
1177
- * A component in a display manifest that the target CAN run.
1178
- *
1179
- * Deliberately **not** a {@link PreparedComponent}: it carries no `plan`. A plan built for a display
1180
- * is resolved under the lenient unanswered-condition policy, so handing one out would let a consumer
1181
- * do the natural "preview it, then dispatch what was previewed" — `runDispatch(component.plan, cfg)`
1182
- * type-checks with no cast — and send a model wording for a condition nobody ever answered. That is
1183
- * exactly the failure the strict policy exists to prevent, arriving through the display door.
1184
- *
1185
- * A plan is still built during preparation, because that is what surfaces an unsupported enum as a
1186
- * wall-hit; it is discarded rather than returned. Nothing in the manifest builders reads it.
1187
- */
1188
- interface AvailableDisplayComponent {
1189
- id: string;
1190
- node: ComponentNode;
1191
- report: ResolutionReport;
1192
- }
1193
- type DisplayComponent = AvailableDisplayComponent | UnavailableDisplayComponent;
1194
- interface PreparedDisplayManifest {
1195
- target: string;
1196
- components: DisplayComponent[];
1197
- }
1198
- /**
1199
- * Resolve a node's bound wren project to an absolute cwd. Relative `context_binding.project` paths
1200
- * resolve against the IR file's directory (`irPath`) when given, else the current working directory;
1201
- * an explicit `project` always wins.
1202
- */
1203
- declare function resolveProjectCwd(node: ComponentNode, opts: {
1204
- project?: string;
1205
- irPath?: string;
1206
- }): string;
1207
- /**
1208
- * Parse + resolve + build every requested component's `query({options})`, without calling the SDK.
1209
- *
1210
- * By default this prepares every component in the IR. Pass `input.componentId` to scope
1211
- * preparation — and therefore capability resolution — to exactly that one component; every other
1212
- * component's `required_capabilities` are never consulted, so a component that isn't being
1213
- * dispatched can't wall-hit a dispatch it has nothing to do with. See {@link DispatchInput.componentId}.
1214
- */
1215
- declare function prepareDispatch(input: DispatchInput): PreparedDispatch;
1216
- /**
1217
- * Prepare a display-only whole-profile manifest. Unsupported components are
1218
- * represented by a closed unavailable marker; no executable plan is built
1219
- * for them. This must never be used by emit, dispatch, or chat.
1220
- */
1221
- declare function prepareDisplayManifest(input: Omit<DispatchInput, "componentId" | "question">): PreparedDisplayManifest;
1222
- interface DispatchRunConfig {
1223
- outDir: string;
1224
- warbleBin?: string;
1225
- title?: string;
1226
- }
1227
- interface ComponentOutcome {
1228
- id: string;
1229
- report: ResolutionReport;
1230
- plan: DispatchPlan;
1231
- result: RunResult;
1232
- }
1233
- interface DispatchOutcome {
1234
- target: string;
1235
- components: ComponentOutcome[];
1236
- }
1237
- /**
1238
- * Prepare then RUN each component against the live Agent SDK loop. Requires `input.question`.
1239
- * Writes each run's artifacts under `runCfg.outDir` (see {@link runDispatch}).
1240
- */
1241
- declare function dispatch(input: DispatchInput, runCfg: DispatchRunConfig): Promise<DispatchOutcome>;
1242
-
1243
1524
  /**
1244
1525
  * Codegen (`emit`) — freeze a prepared dispatch into an importable TS agent module.
1245
1526
  *
@@ -1319,6 +1600,7 @@ interface GuardrailManifest {
1319
1600
  }
1320
1601
  interface AvailableAgentManifest {
1321
1602
  id: string;
1603
+ entrypoint: boolean;
1322
1604
  verb: string;
1323
1605
  component_type: ComponentNode["type"];
1324
1606
  realization_kind: ComponentNode["realization_kind"];
@@ -1329,11 +1611,13 @@ interface AvailableAgentManifest {
1329
1611
  tools: ToolRef[];
1330
1612
  output_schema: unknown;
1331
1613
  capabilities: ResolutionReport;
1614
+ dependencies: ComponentDependencyManifest[];
1332
1615
  brief?: string;
1333
1616
  }
1334
1617
  /** A display-only declaration of a component that remains unavailable to this target. */
1335
1618
  interface UnavailableAgentManifest {
1336
1619
  id: string;
1620
+ entrypoint: boolean;
1337
1621
  verb: string;
1338
1622
  component_type: ComponentNode["type"];
1339
1623
  realization_kind: ComponentNode["realization_kind"];
@@ -1345,17 +1629,34 @@ interface UnavailableAgentManifest {
1345
1629
  tools: [];
1346
1630
  output_schema: Record<string, never>;
1347
1631
  capabilities: [];
1632
+ capability_inspection: ResolutionReport;
1633
+ dependencies: ComponentDependencyManifest[];
1348
1634
  availability: {
1349
1635
  status: "unavailable";
1350
1636
  reason: string;
1351
1637
  };
1352
1638
  }
1353
1639
  type AgentManifest = AvailableAgentManifest | UnavailableAgentManifest;
1640
+ type ComponentDependencyManifest = Omit<ComponentDependency, "caller">;
1641
+ interface EntryManifest {
1642
+ id: string;
1643
+ closure: string[];
1644
+ availability: {
1645
+ status: "available";
1646
+ } | {
1647
+ status: "unavailable";
1648
+ reason: string;
1649
+ };
1650
+ /** Null when the entry's closure declares no component invocation. */
1651
+ invocation_realization: ResolvedCapability | null;
1652
+ }
1354
1653
  interface Manifest {
1355
1654
  manifest_version: string;
1356
1655
  compat: CompatibilityPolicy;
1357
1656
  profile: string;
1358
1657
  target: string;
1658
+ /** Selectable roots only. Internal mounts remain visible under `agents`, never here. */
1659
+ entries: EntryManifest[];
1359
1660
  agents: AgentManifest[];
1360
1661
  }
1361
1662
  /** Port of `emit.rs::build_agent_bundle`, minus the tool-map parameter (this back-end's is fixed,
@@ -1365,12 +1666,12 @@ interface Manifest {
1365
1666
  * it satisfies this structurally — but nothing here reads a plan, so nothing here should require
1366
1667
  * one; see `AvailableDisplayComponent` for why a display must not carry it.
1367
1668
  */
1368
- declare function buildAgentManifest(component: AvailableDisplayComponent): AvailableAgentManifest;
1669
+ declare function buildAgentManifest(component: AvailableDisplayComponent, dependencies?: readonly ComponentDependency[]): AvailableAgentManifest;
1369
1670
  /** Never derives a plan, tool, or capability grant for an unavailable component. */
1370
- declare function buildUnavailableAgentManifest(component: UnavailableDisplayComponent): UnavailableAgentManifest;
1671
+ declare function buildUnavailableAgentManifest(component: UnavailableDisplayComponent, dependencies?: readonly ComponentDependency[]): UnavailableAgentManifest;
1371
1672
  /** Build the full display manifest for a `prepareDispatch` result. `raw` is the same IR the
1372
1673
  * dispatch was prepared from — re-parsed here (a second, cheap, pure parse) just to read
1373
1674
  * `profile`, which `PreparedDispatch` does not itself carry. */
1374
1675
  declare function buildManifest(prepared: PreparedDispatch | PreparedDisplayManifest, raw: string): Manifest;
1375
1676
 
1376
- export { type AgentManifest, type AskOptions, type AvailableAgentManifest, type AvailableDisplayComponent, type BuildConfig, COMPONENT_TYPES, type CallLocalOptions, type CapabilityEntry, type CapabilityOutcome, type CapabilityProfile, type ChatRequest, ChatSession, type ClarifyOutcome, type CompatibilityPolicy, type ComponentNode, type ComponentOutcome, type ComponentType, type ContextBinding, type Criticality, DEFAULT_CLARIFY_THRESHOLD, DEFAULT_RENDER_FLAVOR, DEFAULT_TARGET, DESTRUCTIVE_BASH_DENY, type Denial, type DiscoverClaudeModelsOptions, DispatchError, type DispatchInput, type DispatchMeta, type DispatchOutcome, type DispatchPlan, type DispatchRunConfig, DispatchSessionError, type DisplayComponent, type Effect, type EmitOptions, type GateFailureMode, type GateKind, type GuardConfig, type Guardrail, type GuardrailManifest, type IrConfig, type LlmCall, MODEL_CATALOG_VERSION, type Manifest, type ModelCatalogModel, type ModelCatalogResult, type ModelCatalogUnavailableCode, ModelConfig, OUTCOME_KINDS, type Outcome, type OutcomeKind, type PreconditionResult, type PreparedComponent, type PreparedDispatch, type PreparedDisplayManifest, type PromptFingerprint, type ProvidedBy, type Provider, REALIZATION_KINDS, type RealizationKind, type RenderBlock, type RenderFlavor, type RenderGate, type RenderResult, type ResolutionReport, type ResolvedCapability, type ResolvedIntent, type RoutingMode, type RoutingPlan, type RunConfig, type RunResult, SUPPORTED_IR_VERSIONS, type SessionState, type SlotSupply, type StagedStep, type StepManifest, type StepMessage, type StepRealization, type StepUsage, TRIGGER_KINDS, type TargetId, type TierBinding, type ToolPlan, type ToolRef, type Trace, type Trigger, type TriggerKind, type Turn, type TurnResult, UNAVAILABLE_COMPONENT_REASON, type UnansweredCondition, type UnavailableAgentManifest, type UnavailableDisplayComponent, type WarbleIr, type WhenGuardOut, aggregateTrace, appendTurn, applySlots, assertNoSlotReferences, buildAgentManifest, buildChatRequest, buildDispatchPlan, buildManifest, buildStepMessages, buildToolDriverPrompt, buildTurnPrompt, buildUnavailableAgentManifest, callOpenAiCompat, collectRequiredCapabilities, composeCanUseTool, composeHooks, createChatSession, createSessionState, decideClarify, discoverClaudeModels, dispatch, distillFollowup, distinctProviders, distinctTiers, emitAgentModule, extractCompletionText, fingerprintPrompts, fingerprintSurfaces, inspectNodeCapabilities, isKnownTarget, knownTargetNames, lastResolvedIntent, lastSessionId, localProfile, makeReadOnlyGuard, parseIr, parseRenderFlavor, parseSlotFlags, planProviderRouting, prepareDispatch, prepareDisplayManifest, profileFor, promptSurfaces, promptSurfacesOf, renderEnvelope, resolveCapabilities, resolveNodeCapabilities, resolveProjectCwd, resolveSlots, resolveStagedSteps, runDispatch, runHybridTool, shouldSplitPerStepTier, usesLocalProvider };
1677
+ export { type AgentManifest, type AskOptions, type AvailableAgentManifest, type AvailableDisplayComponent, type BuildConfig, COMPONENT_TYPES, type CallLocalOptions, type CapabilityEntry, type CapabilityOutcome, type CapabilityProfile, type ChatRequest, ChatSession, type ClarifyOutcome, type CompatibilityPolicy, type ComponentCall, type ComponentCallTrace, type ComponentClosurePlan, type ComponentDependency, type ComponentDependencyManifest, type ComponentInvocationLimits, type ComponentInvocationRequest, type ComponentInvocationResult, ComponentInvocationRuntime, type ComponentNode, type ComponentOutcome, type ComponentRootResult, ComponentStepExecutionError, type ComponentStepResult, type ComponentStepRun, type ComponentStepRunner, type ComponentType, type ComposedRunConfig, type ContextBinding, type Criticality, DEFAULT_CLARIFY_THRESHOLD, DEFAULT_COMPONENT_INVOCATION_LIMITS, DEFAULT_RENDER_FLAVOR, DEFAULT_TARGET, DESTRUCTIVE_BASH_DENY, type Denial, type DiscoverClaudeModelsOptions, DispatchError, type DispatchInput, type DispatchMeta, type DispatchOutcome, type DispatchPlan, type DispatchRunConfig, DispatchSessionError, type DisplayComponent, type Effect, type EmitOptions, type EntryClosure, type EntryManifest, type GateFailureMode, type GateKind, type GuardConfig, type Guardrail, type GuardrailManifest, type IrConfig, type LlmCall, MODEL_CATALOG_VERSION, type Manifest, type ModelCatalogModel, type ModelCatalogResult, type ModelCatalogUnavailableCode, ModelConfig, OUTCOME_KINDS, type Outcome, type OutcomeKind, type PreconditionResult, type PreparedComponent, type PreparedDispatch, type PreparedDisplayManifest, type PromptFingerprint, type ProvidedBy, type Provider, REALIZATION_KINDS, type RealizationKind, type RenderBlock, type RenderFlavor, type RenderGate, type RenderResult, type ResolutionReport, type ResolvedCapability, type ResolvedIntent, type RoutingMode, type RoutingPlan, type RunConfig, type RunResult, SUPPORTED_IR_VERSIONS, type SessionState, type SlotSupply, type StagedStep, type StepManifest, type StepMessage, type StepRealization, type StepUsage, TRIGGER_KINDS, type TargetId, type TierBinding, type ToolPlan, type ToolRef, type Trace, type Trigger, type TriggerKind, type Turn, type TurnResult, UNAVAILABLE_COMPONENT_REASON, type UnansweredCondition, type UnavailableAgentManifest, type UnavailableDisplayComponent, type WarbleIr, type WhenGuardOut, aggregateTrace, appendTurn, applySlots, assertEligibleComponentCallee, assertEligibleComponentCaller, assertNoSlotReferences, assetDirForIr, buildAgentManifest, buildChatRequest, buildDispatchPlan, buildManifest, buildStepMessages, buildToolDriverPrompt, buildTurnPrompt, buildUnavailableAgentManifest, callOpenAiCompat, collectRequiredCapabilities, componentDependencies, composeCanUseTool, composeHooks, createChatSession, createSdkComponentStepRunner, createSessionState, decideClarify, discoverClaudeModels, dispatch, distillFollowup, distinctProviders, distinctTiers, emitAgentModule, extractCompletionText, fingerprintPrompts, fingerprintSurfaces, inspectNodeCapabilities, isKnownTarget, knownTargetNames, landAssets, lastResolvedIntent, lastSessionId, localProfile, makeReadOnlyGuard, normalizeComponentRequest, normalizeComponentResult, parseIr, parseRenderFlavor, parseSlotFlags, planProviderRouting, preflightDispatchAssets, prepareDispatch, prepareDisplayManifest, profileFor, promptSurfaces, promptSurfacesOf, renderEnvelope, resolveCapabilities, resolveComponentClosure, resolveNodeCapabilities, resolveProjectCwd, resolveSlots, resolveStagedSteps, runComposedDispatch, runDispatch, runHybridTool, shouldSplitPerStepTier, usesLocalProvider, validateAssets };