/** * The model-facing tool forge for the orchestration battery. * * @module @nhtio/adk/batteries/orchestration/forge * * @remarks * This file is the ONLY place the wire↔IR conversion happens, and the ONLY place a model-facing * tool is constructed for orchestration. A tool call cannot transmit a class instance, so on the * wire a reference is `{$ref: {node, select, path?, branchId?}}` and a template hole is * `{$param: {path}}` — single-key wrappers whose keys are RESERVED — and the IR uses real * {@link NodeRef}/{@link ParamRef} instances. {@link hydrateRefs} converts wire→IR on the way in * and {@link dehydrateRefs} converts IR→wire on the way out, so a model reading a plan back sees * the same `$ref`/`$param` shape it writes. Nothing else in the battery ever sees the wire form. * * Three surfaces, three threat models: * * - **Tier A — `'front'`**, what a conversational agent sees: `list_templates`, * `instantiate_plan`, `author_plan`. The from-scratch path passes the owner's request VERBATIM * and UNPARSED — pre-parsing their words into categories is what discarded * "at the Holly Springs Walgreens" in the prior art — and every return is RENDERED PROSE, never * raw JSON, because a model that re-reads its own JSON echo tends to re-plan rather than * continue. * - **Tier B — `'authoring'`**, graph mechanics, exposed only inside an authoring sub-dispatch: * `create_plan`, `add_node`, `set_node_config`, `connect_nodes`, `remove_node`, * `disconnect_edge`, `clone_plan`, `get_plan`, `validate_plan`, `freeze_plan`, * `unfreeze_plan`, `submit_plan`, `plan_status`, `raw_plan`, `raw_diff`, plus the scoped * reading pair `plan_outline` / `plan_read`. * - **Tier C is NOT a tool tier** — it is `runtime.invocable`, the allowlist of what a staged * `call` may invoke. Deliberately separate from the agent's tool surface: adding an agent tool * never adds it here. The allowlist and the registry are the SAME object — the prior art's * worst wart was ten names listed against a registry with zero callers, so author-time * validation passed and fire-time threw "not registered". There is no second list. * * Mutation tools return SCOPED PROSE, not the whole projected plan. The prior art returned * everything so the model always re-read current state; that was written for a large window and * is precisely the context problem here — 40 nodes echoed on every edit. Each mutation returns * what changed, what it now connects to, and any new issues, BOUNDED regardless of plan size. * * `submit_plan`/`freeze_plan` call {@link freezePlan} and surface each refusal with its * model-addressed message. The prior art's "the dry run produced no finding" refusal is ABSENT — * there is no dry run — and the reachability plus unedited-placeholder checks carry the weight it * used to. */ import { Tool } from "../../common"; import type { PlanStore } from "./store"; import type { InvocableTools, PredicateEvaluator } from "./types"; import type { ToolGateFn } from "../tools/_shared/index"; import type { PlanTemplate } from "./types"; /** The runtime a forge call is handed: the store, the tier-C allowlist, the wired predicate cells, and any registered templates. */ export interface ForgeOrchestrationRuntime { /** The plan store all tools read and mutate. */ store: PlanStore; /** The tier-C allowlist a staged `call` may invoke — the SAME object the tool registry is, never a second list. */ invocable: InvocableTools; /** The wired predicate cells; a needed-but-absent cell refuses at freeze. */ evaluators: PredicateEvaluator[]; /** Consumer-defined plan shapes a model can instantiate. */ templates?: PlanTemplate[]; /** The identity under which minted ops are authored. */ actorId: string; } /** Tier selection and optional per-tool overrides + a mutating-tool gate. */ export interface ForgeOrchestrationOptions { /** `'front'` for the conversational surface, `'authoring'` for the graph-mechanics surface. */ tier: 'front' | 'authoring'; /** Optional names and descriptions keyed by their default tool name. */ overrides?: Record; /** Optional gate run before any MUTATING tool executes. Throwing aborts the call. */ gate?: ToolGateFn; } /** * Forge the model-facing orchestration tools for a configured runtime and tier. * * @remarks * Returns a `Record` keyed by the (post-override) tool name. Tier A (`'front'`) * yields the three conversational tools; tier B (`'authoring'`) yields the graph-mechanics tools. * Tier C is `runtime.invocable`, not a tool tier, and is the SAME object the tool registry is. * * Where a `gate` is supplied it is run via {@link runToolGate} before any MUTATING tool executes; * read-only tools are not gated. The wire↔IR conversion ({@link hydrateRefs}/ * {@link dehydrateRefs}) happens inside this function and nowhere else. * * @param runtime - The store, tier-C allowlist, predicate cells, templates, and actor identity. * @param options - Tier, per-tool overrides, and an optional mutating-tool gate. * @returns The forged tools, keyed by name. */ export declare function forgeOrchestrationTools(runtime: ForgeOrchestrationRuntime, options: ForgeOrchestrationOptions): Record;