@nhtio/adk/batteries/orchestration/forge
The model-facing tool forge for the orchestration battery.
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 NodeRef/ParamRef instances. hydrateRefs converts wire→IR on the way in and 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 pairplan_outline/plan_read. - Tier C is NOT a tool tier — it is
runtime.invocable, the allowlist of what a stagedcallmay 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 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.
Interfaces
| Interface | Description |
|---|---|
| ForgeOrchestrationOptions | Tier selection and optional per-tool overrides + a mutating-tool gate. |
| ForgeOrchestrationRuntime | The runtime a forge call is handed: the store, the tier-C allowlist, the wired predicate cells, and any registered templates. |
Functions
| Function | Description |
|---|---|
| forgeOrchestrationTools | Forge the model-facing orchestration tools for a configured runtime and tier. |