@agen-ai/agent-orchestration 0.0.0-stage → 0.4.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.
Files changed (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +413 -2
  3. package/dist/catalog/parse.d.ts +3 -0
  4. package/dist/catalog/parse.js +13 -0
  5. package/dist/catalog/schemas.d.ts +8 -0
  6. package/dist/catalog/schemas.js +78 -0
  7. package/dist/catalog/types.d.ts +53 -0
  8. package/dist/catalog/types.js +0 -0
  9. package/dist/coordinator/admission.d.ts +7 -0
  10. package/dist/coordinator/admission.js +189 -0
  11. package/dist/coordinator/cancel.d.ts +7 -0
  12. package/dist/coordinator/cancel.js +111 -0
  13. package/dist/coordinator/create.d.ts +3 -0
  14. package/dist/coordinator/create.js +87 -0
  15. package/dist/coordinator/parse.d.ts +10 -0
  16. package/dist/coordinator/parse.js +60 -0
  17. package/dist/coordinator/read.d.ts +4 -0
  18. package/dist/coordinator/read.js +267 -0
  19. package/dist/coordinator/recovery.d.ts +4 -0
  20. package/dist/coordinator/recovery.js +24 -0
  21. package/dist/coordinator/shutdown.d.ts +7 -0
  22. package/dist/coordinator/shutdown.js +66 -0
  23. package/dist/coordinator/types.d.ts +154 -0
  24. package/dist/coordinator/types.js +0 -0
  25. package/dist/delivery/calls.d.ts +4 -0
  26. package/dist/delivery/calls.js +34 -0
  27. package/dist/delivery/journal.d.ts +7 -0
  28. package/dist/delivery/journal.js +57 -0
  29. package/dist/delivery/parse.d.ts +4 -0
  30. package/dist/delivery/parse.js +31 -0
  31. package/dist/delivery/recovery.d.ts +3 -0
  32. package/dist/delivery/recovery.js +18 -0
  33. package/dist/delivery/scheduler.d.ts +3 -0
  34. package/dist/delivery/scheduler.js +123 -0
  35. package/dist/delivery/setup.d.ts +7 -0
  36. package/dist/delivery/setup.js +37 -0
  37. package/dist/delivery/types.d.ts +36 -0
  38. package/dist/delivery/types.js +0 -0
  39. package/dist/execution/authority.d.ts +16 -0
  40. package/dist/execution/authority.js +86 -0
  41. package/dist/execution/cleanup.d.ts +3 -0
  42. package/dist/execution/cleanup.js +75 -0
  43. package/dist/execution/control.d.ts +6 -0
  44. package/dist/execution/control.js +231 -0
  45. package/dist/execution/finish.d.ts +6 -0
  46. package/dist/execution/finish.js +49 -0
  47. package/dist/execution/journal.d.ts +19 -0
  48. package/dist/execution/journal.js +28 -0
  49. package/dist/execution/prepare.d.ts +3 -0
  50. package/dist/execution/prepare.js +153 -0
  51. package/dist/execution/scheduler.d.ts +4 -0
  52. package/dist/execution/scheduler.js +136 -0
  53. package/dist/execution/state.d.ts +107 -0
  54. package/dist/execution/state.js +120 -0
  55. package/dist/execution/stream.d.ts +9 -0
  56. package/dist/execution/stream.js +108 -0
  57. package/dist/host/types.d.ts +95 -0
  58. package/dist/host/types.js +0 -0
  59. package/dist/index.d.ts +20 -0
  60. package/dist/index.js +58 -0
  61. package/dist/mcp/bridge.d.ts +4 -0
  62. package/dist/mcp/bridge.js +222 -0
  63. package/dist/mcp/http.d.ts +11 -0
  64. package/dist/mcp/http.js +74 -0
  65. package/dist/mcp/index.d.ts +3 -0
  66. package/dist/mcp/index.js +4 -0
  67. package/dist/mcp/limits.d.ts +10 -0
  68. package/dist/mcp/limits.js +30 -0
  69. package/dist/mcp/protocol.d.ts +50 -0
  70. package/dist/mcp/protocol.js +81 -0
  71. package/dist/mcp/types.d.ts +22 -0
  72. package/dist/mcp/types.js +0 -0
  73. package/dist/policy/matching.d.ts +4 -0
  74. package/dist/policy/matching.js +18 -0
  75. package/dist/policy/parse.d.ts +3 -0
  76. package/dist/policy/parse.js +13 -0
  77. package/dist/policy/resolve.d.ts +7 -0
  78. package/dist/policy/resolve.js +67 -0
  79. package/dist/policy/schemas.d.ts +5 -0
  80. package/dist/policy/schemas.js +59 -0
  81. package/dist/policy/types.d.ts +60 -0
  82. package/dist/policy/types.js +0 -0
  83. package/dist/shared/bounds.d.ts +6 -0
  84. package/dist/shared/bounds.js +88 -0
  85. package/dist/shared/errors.d.ts +9 -0
  86. package/dist/shared/errors.js +27 -0
  87. package/dist/shared/identity.d.ts +6 -0
  88. package/dist/shared/identity.js +32 -0
  89. package/dist/shared/immutable.d.ts +2 -0
  90. package/dist/shared/immutable.js +10 -0
  91. package/dist/shared/limits.d.ts +32 -0
  92. package/dist/shared/limits.js +69 -0
  93. package/dist/shared/operational-errors.d.ts +7 -0
  94. package/dist/shared/operational-errors.js +15 -0
  95. package/dist/shared/schemas.d.ts +11 -0
  96. package/dist/shared/schemas.js +23 -0
  97. package/dist/shared/text.d.ts +2 -0
  98. package/dist/shared/text.js +15 -0
  99. package/dist/store/memory.d.ts +7 -0
  100. package/dist/store/memory.js +215 -0
  101. package/dist/store/pages.d.ts +16 -0
  102. package/dist/store/pages.js +46 -0
  103. package/dist/store/parse.d.ts +5 -0
  104. package/dist/store/parse.js +137 -0
  105. package/dist/store/types.d.ts +219 -0
  106. package/dist/store/types.js +0 -0
  107. package/dist/store-adapter/admission.d.ts +6 -0
  108. package/dist/store-adapter/admission.js +90 -0
  109. package/dist/store-adapter/context.d.ts +20 -0
  110. package/dist/store-adapter/context.js +107 -0
  111. package/dist/store-adapter/delivery.d.ts +15 -0
  112. package/dist/store-adapter/delivery.js +81 -0
  113. package/dist/store-adapter/execution.d.ts +12 -0
  114. package/dist/store-adapter/execution.js +151 -0
  115. package/dist/store-adapter/index.d.ts +7 -0
  116. package/dist/store-adapter/index.js +27 -0
  117. package/dist/store-adapter/lifecycle.d.ts +15 -0
  118. package/dist/store-adapter/lifecycle.js +80 -0
  119. package/dist/store-adapter/plan.d.ts +7 -0
  120. package/dist/store-adapter/plan.js +168 -0
  121. package/dist/store-adapter/types.d.ts +45 -0
  122. package/dist/store-adapter/types.js +0 -0
  123. package/dist/tasks/failure.d.ts +4 -0
  124. package/dist/tasks/failure.js +24 -0
  125. package/dist/tasks/parse.d.ts +18 -0
  126. package/dist/tasks/parse.js +66 -0
  127. package/dist/tasks/schemas.d.ts +47 -0
  128. package/dist/tasks/schemas.js +215 -0
  129. package/dist/tasks/types.d.ts +192 -0
  130. package/dist/tasks/types.js +0 -0
  131. package/dist/tools/create.d.ts +4 -0
  132. package/dist/tools/create.js +69 -0
  133. package/dist/tools/errors.d.ts +4 -0
  134. package/dist/tools/errors.js +35 -0
  135. package/dist/tools/guidance.d.ts +4 -0
  136. package/dist/tools/guidance.js +28 -0
  137. package/dist/tools/handlers.d.ts +4 -0
  138. package/dist/tools/handlers.js +84 -0
  139. package/dist/tools/projection.d.ts +13 -0
  140. package/dist/tools/projection.js +72 -0
  141. package/dist/tools/schemas.d.ts +6 -0
  142. package/dist/tools/schemas.js +93 -0
  143. package/dist/tools/types.d.ts +28 -0
  144. package/dist/tools/types.js +0 -0
  145. package/package.json +64 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Trevor Nichols
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,414 @@
1
- # Temporary Holding Version
1
+ # @agen-ai/agent-orchestration
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Optional managed delegation for applications using the AgenAI coding-agent SDK, at coordinated
4
+ version `0.4.0`. This package sits above
5
+ `@agen-ai/agent-runtime`. Core runtime consumers do not depend on it.
6
+
7
+ The current implementation provides bounded target catalogs, a pure developer/user policy resolver,
8
+ managed task coordination, a volatile store and host-injected completion delivery. A separately
9
+ qualified SQLite reference demonstrates durable storage and parent inbox recovery. Scoped agent tools
10
+ and an explicitly started MCP bridge make managed tasks available to a parent session. Installing or importing the package starts no agents, listeners,
11
+ processes, network connections or database work. Native harness subagents remain independent.
12
+
13
+ ## Effective choices
14
+
15
+ A target is one complete configuration certified by the host: an authorized runtime instance,
16
+ provider, model, native options, reasoning and execution-mode facts, and exact session
17
+ configuration mapping. The SDK does not invent model combinations or infer behavior from labels.
18
+
19
+ The developer can explicitly exclude a provider, model, reasoning identity, mode or combination.
20
+ Exclusions default empty. The end user enables managed delegation and selects among the remaining
21
+ targets. The main agent may choose only from those effective choices.
22
+
23
+ ```ts
24
+ import {
25
+ parseDelegationPolicySnapshot,
26
+ parseDelegationTargetCatalog,
27
+ resolveDelegationPolicy,
28
+ } from "@agen-ai/agent-orchestration";
29
+
30
+ const catalog = parseDelegationTargetCatalog(hostCertifiedCatalog);
31
+ const disabled = parseDelegationPolicySnapshot({ revision: 1 });
32
+ const enabled = parseDelegationPolicySnapshot({
33
+ revision: 2,
34
+ user: { enabled: true },
35
+ });
36
+ const choices = resolveDelegationPolicy({ catalog, policy: enabled });
37
+ // Persist user preferences in your application; pass choices.allowedTargets to admission.
38
+ ```
39
+
40
+ Omitted user policy means disabled. Enabling without a selection chooses all currently available
41
+ certified standard-mode targets, including future additions. It does not select fast mode. There
42
+ is no SDK reasoning ceiling: `max`, `xhigh`, `ultra` and future certified identities are eligible.
43
+ An explicit empty selection permits nothing and never falls back to another model.
44
+
45
+ Use `selection: { kind: "all_available", includeFast: true, unknownModeTargets: [] }` to include
46
+ certified fast targets, or `selection: { kind: "explicit", targets: [{ catalogId, targetId }] }`
47
+ for fixed selections. Unknown execution-mode behavior requires exact opt-in and a visible host
48
+ limitation. User opt-in cannot bypass a relevant developer exclusion whose compliance is unknown.
49
+
50
+ ```ts
51
+ const restricted = parseDelegationPolicySnapshot({
52
+ revision: 3,
53
+ developer: {
54
+ enabled: true,
55
+ exclusions: [
56
+ { exclusionId: "no-fast", executionMode: "fast" },
57
+ { exclusionId: "no-max", reasoning: "max" },
58
+ { exclusionId: "model-excluded", providerKey: "example", modelId: "example-model" },
59
+ ],
60
+ },
61
+ user: {
62
+ enabled: true,
63
+ selection: { kind: "explicit", targets: [{ catalogId: "example", targetId: "choice" }] },
64
+ },
65
+ });
66
+ ```
67
+
68
+ Each exclusion combines its criteria with AND; multiple exclusions combine with OR. Model and
69
+ native option criteria require a provider key. Exact target exclusions require both catalog and
70
+ target IDs. A known mismatch makes an exclusion inapplicable; otherwise unknown relevant
71
+ reasoning or speed blocks the target as unverifiable. The resolved result preserves catalog
72
+ order and exposes selected state, bounded denial reasons and missing selections for headless UI.
73
+
74
+ ## Host certification and revisions
75
+
76
+ Catalog and target references bind exact catalog, target and authorized-source revisions. A host
77
+ must advance revisions whenever configuration, availability or account/loading authority changes,
78
+ including changes that preserve display labels. IDs are opaque correlation values, not authority.
79
+ The pure resolver performs no registry, authentication or availability I/O. A host must check live
80
+ instance readiness and provider-key agreement and fence execution against current policy/source
81
+ authority immediately before dispatch.
82
+
83
+ The application mapping contains a complete `AgentSessionConfiguration` and an ordered
84
+ `beforeFirstTurn` selection list. Extra selections must be session-scoped, mutable and immediately
85
+ applicable; they must not mutate the parent or a shared instance. Certified standard mode requires
86
+ an explicit setting or immutable host attestation that rules out ambient fast mode.
87
+
88
+ ## Validation and limits
89
+
90
+ Parsers copy and freeze accepted inert data. Invalid input raises `DelegationValidationError`
91
+ with validator-neutral AgenAI issues. Ordinary declarations expose no Zod types.
92
+
93
+ Catalogs contain at most 1,000 targets and serialize to at most 1 MiB. Individual targets are at
94
+ most 32 KiB with at most 100 options and ten limitations. Opaque IDs are canonical strings of at
95
+ most 256 characters; reasoning identities are open strings of at most 128 characters. Revisions
96
+ are positive safe integers. Duplicate targets, option keys, exclusion IDs, selected identities
97
+ and inconsistent source revisions fail validation. Policy collections are bounded to 1,000
98
+ entries and 1 MiB. Oversized or malformed input cannot silently narrow into a valid configuration.
99
+
100
+ This package is ESM, MIT licensed, Node.js 22 or newer, with explicit public exports. Applications
101
+ own end-user settings, persistence, account enrollment, directories, permissions and execution
102
+ authority. No AgenAI Workspaces or private Hostd contracts are exposed.
103
+
104
+ ## Managed execution
105
+
106
+ Create an explicit store and coordinator after composing your validated runtime registry:
107
+
108
+ ```ts
109
+ import {
110
+ createDelegationCoordinator,
111
+ createInMemoryDelegationStore,
112
+ } from "@agen-ai/agent-orchestration";
113
+
114
+ const store = createInMemoryDelegationStore();
115
+ const coordinator = await createDelegationCoordinator({
116
+ host: applicationDelegationHost,
117
+ store,
118
+ limits: store.limits,
119
+ });
120
+ const client = await coordinator.bindParent({ scope: authorizedParentScope });
121
+ const choices = await client.targets();
122
+ // The caller selects an exact permitted reference; an empty catalog is not a fallback.
123
+ const selected = choices.targets.find((target) => target.reference.targetId === selectedTargetId);
124
+ if (!selected) throw new Error("The selected configuration is unavailable.");
125
+ const task = await client.spawn({
126
+ operationId: crypto.randomUUID(),
127
+ target: selected.reference,
128
+ expectedPolicyRevision: choices.policyRevision,
129
+ input: { parts: [{ type: "text", text: "Investigate the assigned task." }] },
130
+ });
131
+ const result = await client.wait({ taskId: task.taskId, timeoutMs: 30_000 });
132
+ // A timeout leaves the child running. Inspect, observe, send or explicitly cancel it later.
133
+ ```
134
+
135
+ The scoped client supports `spawn`, `inspect`, `observe`, `wait`, `send` and `cancel`. Stable task
136
+ identity is separate from each run. Retry an operation with its same ID and same input to receive
137
+ its retained receipt; changing input under an existing ID fails. Public views omit raw admitted
138
+ input. One coordinator consumes provider output and commits each bounded observation before pulling
139
+ the next output; slow observers read the retained projection and never consume the provider stream.
140
+
141
+ `send` distinguishes `steer` (supported active-turn delivery), `next_turn` (one explicitly queued
142
+ follow-up) and `follow_up` (a new run after completion). Native resume requires current host
143
+ permission and the exact retained conversation binding; unsupported continuation never falls back
144
+ to an unrelated new conversation. Uncertain steering retires the session and remains recovery
145
+ required even if a later terminal event arrives. Cancellation is a request until provider terminal
146
+ evidence confirms it; a cancellation race does not overwrite a completed result.
147
+
148
+ Managed selection changes prevent new inputs and queued starts. They do not interrupt an already
149
+ running child. The host's separately revocable execution lease controls active account/execution
150
+ authority. Inspection, cancellation and live request resolution each use their own current host
151
+ authority, so deselection need not strand a running task or pending approval. Parent turn completion
152
+ has no child cancellation side effect. Native harness delegation remains independent.
153
+
154
+ ## Host responsibilities and cleanup
155
+
156
+ `DelegationHost` supplies the registry, synchronous current policy and access assertions, an
157
+ execution lease, authorized child resources, applied-configuration verification, and request routing.
158
+ The coordinator directly owns validated session creation/resume, session-scoped configuration,
159
+ stream consumption and close. A successfully opened session is not proof that a requested model
160
+ or fast setting was applied; the host must attest the exact configuration mapping.
161
+
162
+ Acquire child resources before native opening. The directory policy must explicitly be `shared` or
163
+ `isolated`; the host provisions and retains it. An optional fresh host binding must share an
164
+ idempotent release implementation with the resource lease. Resource release covers a binding that
165
+ was never claimed; after a claimed opening, runtime cleanup finishes first. The coordinator never
166
+ calls the provider-facing binding facade's release method. After accepted interruption, the retained
167
+ execution receives one bounded control grace to publish cancellation/terminal output before its
168
+ lifetime signal is aborted. The interruption receipt owns no output. Producers
169
+ must still quiesce before runtime and host resources are released. Failed cleanup retains the exact owner
170
+ and resources rather than pretending shutdown completed. Close the coordinator before disposing
171
+ the host-owned registry. `dispose` supports bounded `drain` or `cancel`; there is no detached executor.
172
+
173
+ Approval and elicitation requests go only to the host. A process-local opaque handle grants access
174
+ to `resolveRequest`; persisted request identifiers are historical data and cannot resolve a live
175
+ request. The coordinator commits the request and its observation before starting the host routing task. That task
176
+ runs independently from the sole child output reader, so output continues while the host awaits a person.
177
+ `resolveRequest` delivers through the retained turn execution and returns `delivered`, `rejected` with
178
+ `requestState: "pending" | "unavailable"`, or `delivery_uncertain`. A delivered receipt confirms provider-owned
179
+ acceptance of that exact canonical answer or form step; it does not claim native transport acknowledgment,
180
+ turn completion or approved effect completion. Only a definitive pending rejection restores answerability;
181
+ uncertainty forbids replay. The receipt settles without waiting for a later output, request or terminal.
182
+ An unavailable rejection removes only that exact request's eligibility; it does not complete the
183
+ child or its parent. Request expiry, accepted interruption and terminality fence fresh answers.
184
+ Local malformed, stale or pre-aborted answers reject before provider entry; entered provider
185
+ exceptions and uncertain receipts retain the child output/cleanup owner and prohibit resending.
186
+ Raw request answers and native diagnostics are not stored in observations.
187
+
188
+ ## Store and capacity contract
189
+
190
+ The in-memory store is explicitly volatile. Store port contract version 3 provides owner fencing, immutable outcomes,
191
+ operation idempotency, bounded records and independent settlement/control capacity. The explicit
192
+ `@agen-ai/agent-orchestration/store-adapter` entrypoint supplies a pure transition planner and closed
193
+ row mutations for storage adapters. It does not provide a database, arbitrary transaction callback,
194
+ filesystem persistence or automatic restart execution. Store adapters own atomic commits and physical
195
+ capacity; the shared planner owns logical transitions and serialization validation.
196
+
197
+ Defaults allow four active runs, 32 queued runs, depth two, 256 retained tasks, 1,024 retained runs
198
+ and eight runs per task. A waiting approval retains its active slot. Operational count, aggregate
199
+ capacity and time budgets are finitely configurable. Serialization ceilings are 24 KiB
200
+ and 16,000 text characters per input, 8 KiB per observation/result, 2 KiB per failure, and 64 KiB per
201
+ page. Each admission reserves 32 KiB for final outcome/notification plus four 2 KiB critical control slots;
202
+ ordinary observation saturation cannot consume those reservations. Retention is explicit: records
203
+ and idempotency receipts are not automatically evicted to make room for new work.
204
+
205
+ Each descendant inherits an immutable, content-addressed ceiling of exact catalog/target/source
206
+ references. It must also pass the current origin and descendant policies. A later policy relaxation
207
+ cannot expand that ceiling; changed catalog/source revisions can require a new root task.
208
+
209
+ Settlement records a bounded completion envelope atomically with the outcome. An interrupted process
210
+ cannot restore a live executor or approval handle merely from stored records or a native conversation ID.
211
+
212
+ Failed child outcomes retain the authoritative V10 failure's `category`, `code`, bounded `message`
213
+ and `recovery` across inspection, collection, notifications and callback/MCP tools. The complete
214
+ public failure fits 2 KiB of canonical UTF-8 JSON, including escaped characters. Messages are shortened
215
+ at Unicode character boundaries as needed; codes retain a separate 128-character canonical nonblank
216
+ limit so both historical delegation codes and current protocol codes remain representable.
217
+ Child context and quota identity references are omitted because the parent visibility contract does
218
+ not qualify those identities. `wait_for_quota` retains its kind; other recovery hints retain their
219
+ safe action or exact `notBefore` data. If a retry timestamp makes the failure exceed its byte limit
220
+ even with an empty message, the projection omits that optional timestamp and retains `kind: 'retry'`
221
+ before fitting the message. It never rounds the reported instant. Hints never authorize another
222
+ child or parent-provider invocation.
223
+
224
+ A valid unsuccessful terminal without an error, such as `expired`, records a stable `unknown`
225
+ failure. V10 rejects a malformed `failed` terminal without its required error before projection.
226
+ An exception after observed dispatch remains `recovery_required` with `dispatch_uncertain`, and
227
+ unresolved cleanup or control uncertainty still overrides an apparent definitive failure.
228
+ An old store-port contract is rejected before ownership acquisition; persistence adapters must
229
+ explicitly migrate their independent record format before presenting contract version 3.
230
+
231
+ ## Completion and restart
232
+
233
+ `collect` is the default completion policy and requires no parent delivery port. `next_turn` and
234
+ `automatic` require an explicit `parentDelivery: DelegationParentDeliveryPort` on the coordinator.
235
+ A durable store rejects a volatile inbox. Reopening retained non-collect work without its usable
236
+ delivery port fails before scheduling execution. The host owns parent lifecycle and turn dispatch;
237
+ the SDK owns delivery of the bounded envelope into the host's inbox.
238
+
239
+ Inbox `accept` must deduplicate by `(storeId, notificationId)` and return its stable inbox ID. After an
240
+ uncertain acknowledgement, the coordinator calls `inspect` before attempting acceptance again. Retry
241
+ backoff is bounded from one to 30 seconds. Expiry prevents new acceptance attempts; uncertain attempts
242
+ continue inspection with bounded deadlines and backoff until the inbox confirms acceptance, rejection,
243
+ or absence. A confirmed absence after expiry marks the notification expired. One actual
244
+ inbox callback runs at a time. A timed-out callback that has not quiesced keeps its execution owner;
245
+ shutdown cannot release that owner while a callback can still commit a side effect.
246
+ Shutdown stops starting inbox deliveries and quiesces any entered callback; retained pending
247
+ notifications remain available to the next owner. Draining child execution does not imply that the
248
+ parent has consumed its completion.
249
+
250
+ Task outcome, inbox acceptance and parent-provider dispatch are independent facts. Acceptance does
251
+ not prove the model consumed a result. Delivery is conditional on retained eligible intent, current
252
+ authority and eventual destination availability. Expiry, revocation or dismissal leaves the original
253
+ task result intact. The host rechecks visibility at inspection and authorization at dispatch.
254
+
255
+ The reference inbox queues results for busy, running or waiting parents, never wakes stopped or
256
+ paused parents, and dismisses retired destinations. Automatic delivery dispatches only at an idle safe
257
+ boundary; next-turn delivery waits for an explicit host boundary. Both use the host's normal parent
258
+ turn exclusion. A committed dispatch intent without a conclusive receipt becomes `recovery_required`
259
+ after restart and is not dispatched again automatically.
260
+
261
+ Owner recovery marks potentially executed children uncertain and preserves historical requests
262
+ without making them actionable. A queued run with no preparation/dispatch intent may still start
263
+ under current authorization. A settled run can redeliver its result without invoking its provider.
264
+ Delivery retries retain only their latest claim and receipt inside the reserved final slot; retries
265
+ of superseded attempts are stale. Ordinary provider operation receipts retain their existing lifetime.
266
+
267
+ The public repository's `examples/delegation` directory contains the SQLite adapter, parent inbox and
268
+ separate-process crash tests. It is an explicit Linux/Node 24.18.0/SQLite 3.53.1 reference for local
269
+ filesystem type `0xEF53`, with a trusted owned `0700` directory,
270
+ not a required SDK dependency or a distributed production store. Other adapters must independently
271
+ qualify their physical capacity, ownership and synchronization guarantees.
272
+ Reserved capacity protects against configured budget saturation; actual volume loss or unrelated
273
+ filesystem exhaustion still requires explicit recovery and cannot be reported as a committed result.
274
+
275
+ The reference task database and its optional inbox advance together to format 3; the separate owner-lock
276
+ database remains format 1. Ordinary open does not migrate retained data. The explicit offline migration
277
+ requires exclusive ownership, stopped old producers, settled/retired runs, no claimed notifications and
278
+ no dispatch-intended inbox entries. Each predecessor migration creates a consistent synchronized
279
+ backup and retains record identities, historical receipts and settled `recovery_required` outcomes.
280
+ The 1-to-2 predecessor maps absent failure classification to unknown and refuses oversized
281
+ transformations without shortening committed content. The 2-to-3 step preserves every historical
282
+ row value and receipt; it changes only the validated format header. Neither migration restores
283
+ callbacks, invokes a provider or replays an answer.
284
+ Rollback uses the retained backup in a separately owned directory with the matched old implementation;
285
+ old code cannot open format 3 and no in-place downgrade is supported. The public repository's
286
+ `MIGRATING-TO-0.4.md` and `examples/delegation/README.md` contain the command and refusal procedure.
287
+ This public reference's Node 24.18.0/SQLite 3.53.1 qualification is independent of private Hostd's
288
+ Node 22.23.2/SQLite 3.51.3 durable runtime.
289
+
290
+ Cancellation retries must reuse the operation ID from the accepted request. A different ID after a
291
+ canonical cancellation is rejected as a conflict; it is not retained as an alias. A retry reports
292
+ current cancellation truth for the runs covered by that accepted request, without affecting a
293
+ later follow-up or repeating provider interruption. Previously queued
294
+ `next_turn` work does not resume after its predecessor is canceled or uncertain. An explicitly
295
+ requested `follow_up` remains subject to fresh authorization and exact native resume support.
296
+
297
+ Contract 3 records request-resolution `delivered` independently from subsequent output completion.
298
+ Each new resolution intent retains its exact request identity, and a rejected receipt retains authoritative
299
+ request eligibility. Receipt commits update only that request; a successor request cannot be overwritten.
300
+ Settled format-2 history retains its original `completed` meaning without being reinterpreted as delivery.
301
+ Format 1 must first migrate to 2 using the matched predecessor migration. The offline 2-to-3 step requires
302
+ settled or retired runs, no outstanding request intents and no claimed or delivery-intended inbox entries.
303
+
304
+ Distinct steering, request-resolution and interruption decisions are serialized for each live
305
+ task while output consumption proceeds independently. Steering uses the current policy revisions
306
+ for its new input and preserves the run's exact applied target and immutable inherited ceiling.
307
+ An admission-capacity rejection leaves an existing host approval request waiting and cancelable.
308
+
309
+
310
+ ## Agent tools and guidance
311
+
312
+ `createDelegationHostBinding({ client })` constructs a fresh binding for an already authorized
313
+ scoped client. It returns `undefined` while managed delegation is disabled. Enabled delegation with
314
+ no selected targets still supplies tools that explain the empty selection; it never chooses a
315
+ fallback model. The client’s `bindingSnapshot()` exposes the current enablement, completion policies
316
+ and finite tool limits without exposing host authority or credentials.
317
+
318
+ ```ts
319
+ import { createDelegationHostBinding } from "@agen-ai/agent-orchestration";
320
+
321
+ const binding = await createDelegationHostBinding({
322
+ client,
323
+ instructionPlacement: "tool_context",
324
+ });
325
+ // Pass a present binding as context.hostBinding when opening a supporting runtime session.
326
+ // A claimed binding is released by runtime session cleanup; do not release its facade directly.
327
+ ```
328
+
329
+ The binding supplies `delegation_targets`, `delegate_task`, `task_status`, `task_send` and
330
+ `task_cancel`. Target choices carry exact references and policy revisions. Mutation tools require
331
+ a UUID operation ID; retry the same operation with the same ID and arguments after a lost receipt.
332
+ `task_status` supports bounded waiting and paginated run history. Input content remains bounded by
333
+ the coordinator, and agent-visible projections stay within 24 KiB before the runtime result envelope.
334
+ No tool accepts parent identity, execution authority, policy changes, approval decisions or launch
335
+ commands. Permission requests remain host-owned.
336
+
337
+ Disabling delegation after a harness caches its tools denies new spawning, steering and follow-up
338
+ input. Existing status, result inspection and cancellation remain available under current host
339
+ visibility/control authority. Closing a tool binding stops its calls and waits for them to settle;
340
+ it does not dispose the coordinator or cancel children already admitted.
341
+
342
+ Generated instructions are internal SDK behavior. Applications select only an actual supported
343
+ instruction placement, not a prompt template. The default `tool_context` uses tool/server context
344
+ and makes no trusted system/developer-role claim. `system` and `developer` require a supporting
345
+ adapter; there is no silent downgrade or automatic user-message injection. Optional `nativeToolNames`
346
+ must name tools the session actually exposes; the guidance distinguishes them from managed tasks
347
+ without changing their availability. Tool guidance is not an authorization boundary.
348
+
349
+ ## Explicit MCP transport
350
+
351
+ Import `startDelegationMcpBridge` from `@agen-ai/agent-orchestration/mcp` to serve one binding over
352
+ Streamable HTTP using exact `@modelcontextprotocol/sdk@1.29.0`. Import alone starts no listener.
353
+ Start only when a binding exists and the provider supports the negotiated transport:
354
+
355
+ ```ts
356
+ import { startDelegationMcpBridge } from "@agen-ai/agent-orchestration/mcp";
357
+
358
+ const bridge = binding ? await startDelegationMcpBridge({ binding }) : undefined;
359
+ // Supply bridge.server to the host's session-local MCP composition.
360
+ // Once provider transport producers have stopped: await bridge?.release().
361
+ ```
362
+
363
+ Each explicit start owns an ephemeral `127.0.0.1` endpoint and fresh bearer credential. Keep the
364
+ returned headers in trusted transport configuration; never log or persist them as task data.
365
+ The bridge accepts `tool_context` instructions, validates tool inputs/results through the runtime’s
366
+ shared pure validators, and supports bounded HTTP POST responses without resumable SSE. It rejects
367
+ foreign Host/Origin values and unauthorized calls. HTTP headers and bodies have a ten-second receipt
368
+ budget; the response budget defaults to 60 seconds and is explicitly configurable with
369
+ `requestTimeoutMs`. Set it above a longer custom status-wait budget. Requests are capped at 64 KiB,
370
+ responses at 128 KiB, with 32 concurrent requests, 16 actual callback producers and 32 connections.
371
+ The separate request allowance leaves room for MCP cancellation notifications while callbacks wait.
372
+ Disconnected callers still count against the callback bound until their work settles. A binding can
373
+ have only one live bridge; successful transport release permits a fresh explicit start with a new
374
+ credential. The ordinary package graph cannot import MCP.
375
+
376
+ `release()` stops admission, aborts transport calls and waits for actual producers before closing
377
+ resources. It does not invoke `binding.release`; the runtime session owns that lease. A direct host
378
+ that never transfers its binding into a runtime opening must release the binding after bridge
379
+ quiescence. An aborted HTTP request does not roll back admitted work or authorize replay. A failed
380
+ or timed-out cleanup retains ownership for an explicit retry. No global MCP configuration, skill
381
+ installation, child process or credential discovery occurs.
382
+
383
+ The optional ACP provider accepts `sessionMcpBinding: { transport: "http", create }` to install a
384
+ fresh bridge for each bound parent opening. Its factory receives the validated binding and returns
385
+ owned MCP server entries plus a transport release callback. The provider checks negotiated support,
386
+ merges static setup under its bounds, and closes native producers before the bridge; runtime then
387
+ releases the binding. See the provider package's session-scoped binding example. Each concrete
388
+ agent/version still needs actual tool discovery/invocation qualification before a host treats its
389
+ managed delegation integration as available.
390
+
391
+ ## Runnable public examples
392
+
393
+ From the public SDK repository root, build its packages and run:
394
+
395
+ ```sh
396
+ pnpm examples:delegation
397
+ pnpm examples:delegation:acp
398
+ ```
399
+
400
+ The direct example checks disabled setup, standard/max configuration at the native invocation,
401
+ explicit fast selection, developer model/option exclusion and stale-source rejection. It exercises
402
+ progress, actual steering, queued continuation, native cancellation and a live host approval.
403
+ The credentialless ACP peer discovers and invokes the managed tools, ends its parent turn while a
404
+ child waits for approval, and consumes the completion in a later authorized parent turn. These
405
+ workflows use the same small host composition as the durable CLI.
406
+
407
+ `examples/delegation/README.md` documents the qualified SQLite setup/restart/inspect/cleanup commands
408
+ and separate-process crash cases. The packed consumer compiles and runs these sources using only
409
+ public entrypoints. Synthetic success does not qualify another installed harness, account or vendor
410
+ extension. Initialization or accepted MCP configuration alone is insufficient.
411
+
412
+ This release is SDK-first. AgenAI Workspaces preferences, account enrollment and Hostd integration
413
+ or production harness registration remain separate product follow-up work. Public package
414
+ availability does not enable those controls or grant private execution authority.
@@ -0,0 +1,3 @@
1
+ import type { DelegationTargetCatalog } from "./types.js";
2
+ export declare function parseDelegationTargetCatalog(input: unknown): DelegationTargetCatalog;
3
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1,13 @@
1
+ import { copyBoundedDelegationData } from "../shared/bounds.js";
2
+ import { delegationSchemaError } from "../shared/errors.js";
3
+ import { freezeDelegationData } from "../shared/immutable.js";
4
+ import { TargetCatalogSchema } from "./schemas.js";
5
+ function parseDelegationTargetCatalog(input) {
6
+ const captured = copyBoundedDelegationData(input);
7
+ const parsed = TargetCatalogSchema.safeParse(captured);
8
+ if (!parsed.success) return delegationSchemaError(parsed.error, captured);
9
+ return freezeDelegationData(parsed.data);
10
+ }
11
+ export {
12
+ parseDelegationTargetCatalog
13
+ };
@@ -0,0 +1,8 @@
1
+ import { z } from "zod/v4";
2
+ import type { DelegationTargetCatalog } from "./types.js";
3
+ export declare const TargetOptionSchema: z.ZodObject<{
4
+ key: z.ZodString;
5
+ optionId: z.ZodString;
6
+ }, z.core.$strict>;
7
+ export declare const TargetCatalogSchema: z.ZodType<DelegationTargetCatalog>;
8
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1,78 @@
1
+ import { AGENT_CONFIGURATION_FIELDS_MAX_LENGTH, agentProtocolSerializedJsonBytes } from "@agen-ai/agent-protocol";
2
+ import {
3
+ AgentConfigurationSelectionInputSchema,
4
+ AgentInstanceIdSchema,
5
+ AgentProviderKeySchema,
6
+ AgentSessionConfigurationSchema
7
+ } from "@agen-ai/agent-protocol/zod";
8
+ import { z } from "zod/v4";
9
+ import { DELEGATION_ENTRIES, DELEGATION_TARGET_BYTES } from "../shared/bounds.js";
10
+ import { CanonicalIdSchema, ExecutionModeSchema, ReasoningSchema, ReasonTextSchema, RevisionSchema, requireUniqueValues } from "../shared/schemas.js";
11
+ const TargetOptionSchema = z.strictObject({ key: CanonicalIdSchema, optionId: CanonicalIdSchema });
12
+ const reference = z.strictObject({
13
+ catalogId: CanonicalIdSchema,
14
+ catalogRevision: RevisionSchema,
15
+ targetId: CanonicalIdSchema,
16
+ targetRevision: RevisionSchema,
17
+ sourceId: CanonicalIdSchema,
18
+ sourceRevision: RevisionSchema
19
+ });
20
+ const target = z.strictObject({
21
+ reference,
22
+ instanceId: AgentInstanceIdSchema,
23
+ providerKey: AgentProviderKeySchema,
24
+ modelId: CanonicalIdSchema,
25
+ label: CanonicalIdSchema.max(200),
26
+ options: z.array(TargetOptionSchema).max(100),
27
+ reasoning: z.discriminatedUnion("kind", [
28
+ z.strictObject({ kind: z.literal("certified"), value: ReasoningSchema }),
29
+ z.strictObject({ kind: z.literal("unknown") })
30
+ ]),
31
+ executionMode: z.discriminatedUnion("kind", [
32
+ z.strictObject({ kind: z.literal("certified"), value: ExecutionModeSchema }),
33
+ z.strictObject({ kind: z.literal("unknown") })
34
+ ]),
35
+ certification: z.strictObject({ mappingId: CanonicalIdSchema, mappingRevision: RevisionSchema }),
36
+ availability: z.discriminatedUnion("kind", [
37
+ z.strictObject({ kind: z.literal("available") }),
38
+ z.strictObject({ kind: z.literal("unavailable"), code: CanonicalIdSchema, reason: ReasonTextSchema })
39
+ ]),
40
+ limitations: z.array(ReasonTextSchema).max(10),
41
+ application: z.strictObject({
42
+ configuration: AgentSessionConfigurationSchema,
43
+ beforeFirstTurn: z.array(AgentConfigurationSelectionInputSchema).max(AGENT_CONFIGURATION_FIELDS_MAX_LENGTH)
44
+ })
45
+ }).superRefine((value, context) => {
46
+ requireUniqueValues(value.options.map((option) => option.key), context, ["options"]);
47
+ if (value.application.configuration.kind === "managed" && value.application.beforeFirstTurn.length > 0) {
48
+ context.addIssue({ code: "custom", path: ["application", "beforeFirstTurn"], message: "Managed configurations cannot apply selectable session fields." });
49
+ }
50
+ if (value.executionMode.kind === "unknown" && value.limitations.length === 0) {
51
+ context.addIssue({ code: "custom", path: ["limitations"], message: "Unknown execution mode requires an explicit limitation." });
52
+ }
53
+ if (agentProtocolSerializedJsonBytes(value) > DELEGATION_TARGET_BYTES) {
54
+ context.addIssue({ code: "custom", path: [], message: "Delegation target exceeds 32 KiB of serialized JSON." });
55
+ }
56
+ });
57
+ const TargetCatalogSchema = z.strictObject({
58
+ catalogId: CanonicalIdSchema,
59
+ revision: RevisionSchema,
60
+ targets: z.array(target).max(DELEGATION_ENTRIES)
61
+ }).superRefine((catalog, context) => {
62
+ requireUniqueValues(catalog.targets.map((entry) => entry.reference.targetId), context, ["targets"]);
63
+ const sources = /* @__PURE__ */ new Map();
64
+ for (const [index, entry] of catalog.targets.entries()) {
65
+ if (entry.reference.catalogId !== catalog.catalogId || entry.reference.catalogRevision !== catalog.revision) {
66
+ context.addIssue({ code: "custom", path: ["targets", index, "reference"], message: "Target reference must identify this exact catalog revision." });
67
+ }
68
+ const prior = sources.get(entry.reference.sourceId);
69
+ if (prior !== void 0 && prior !== entry.reference.sourceRevision) {
70
+ context.addIssue({ code: "custom", path: ["targets", index, "reference", "sourceRevision"], message: "Targets from the same source must share its current revision." });
71
+ }
72
+ sources.set(entry.reference.sourceId, entry.reference.sourceRevision);
73
+ }
74
+ });
75
+ export {
76
+ TargetCatalogSchema,
77
+ TargetOptionSchema
78
+ };
@@ -0,0 +1,53 @@
1
+ import type { AgentConfigurationSelectionInput, AgentInstanceId, AgentProviderKey, AgentSessionConfiguration } from "@agen-ai/agent-protocol";
2
+ export interface DelegationTargetReference {
3
+ readonly catalogId: string;
4
+ readonly catalogRevision: number;
5
+ readonly targetId: string;
6
+ readonly targetRevision: number;
7
+ readonly sourceId: string;
8
+ readonly sourceRevision: number;
9
+ }
10
+ export type DelegationReasoning = string;
11
+ export type DelegationExecutionMode = "standard" | "fast";
12
+ export type DelegationSemanticFact<Value> = Readonly<{
13
+ kind: "certified";
14
+ value: Value;
15
+ }> | Readonly<{
16
+ kind: "unknown";
17
+ }>;
18
+ export interface DelegationTargetOption {
19
+ readonly key: string;
20
+ readonly optionId: string;
21
+ }
22
+ export interface DelegationTarget {
23
+ readonly reference: DelegationTargetReference;
24
+ readonly instanceId: AgentInstanceId;
25
+ readonly providerKey: AgentProviderKey;
26
+ readonly modelId: string;
27
+ readonly label: string;
28
+ readonly options: readonly DelegationTargetOption[];
29
+ readonly reasoning: DelegationSemanticFact<DelegationReasoning>;
30
+ readonly executionMode: DelegationSemanticFact<DelegationExecutionMode>;
31
+ readonly certification: Readonly<{
32
+ mappingId: string;
33
+ mappingRevision: number;
34
+ }>;
35
+ readonly availability: Readonly<{
36
+ kind: "available";
37
+ }> | Readonly<{
38
+ kind: "unavailable";
39
+ code: string;
40
+ reason: string;
41
+ }>;
42
+ readonly limitations: readonly string[];
43
+ readonly application: Readonly<{
44
+ configuration: AgentSessionConfiguration;
45
+ beforeFirstTurn: readonly AgentConfigurationSelectionInput[];
46
+ }>;
47
+ }
48
+ export interface DelegationTargetCatalog {
49
+ readonly catalogId: string;
50
+ readonly revision: number;
51
+ readonly targets: readonly DelegationTarget[];
52
+ }
53
+ //# sourceMappingURL=types.d.ts.map
File without changes
@@ -0,0 +1,7 @@
1
+ import { type CoordinatorState, type ParentBinding } from "../execution/state.js";
2
+ import type { DelegationAdmissionReceipt, DelegationSendReceipt, DelegationSendTaskInput, DelegationSpawnTaskInput } from "./types.js";
3
+ export declare function createAdmissionClient(state: CoordinatorState, binding: ParentBinding): {
4
+ spawn(raw: DelegationSpawnTaskInput): Promise<DelegationAdmissionReceipt>;
5
+ send(raw: DelegationSendTaskInput): Promise<DelegationSendReceipt>;
6
+ };
7
+ //# sourceMappingURL=admission.d.ts.map