@agen-ai/agent-runtime 0.4.1 → 0.4.2

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 (37) hide show
  1. package/README.md +154 -71
  2. package/dist/adapterValidation.d.ts +4 -2
  3. package/dist/adapterValidation.js +108 -59
  4. package/dist/contractErrors.d.ts +1 -1
  5. package/dist/contractErrors.js +7 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +1 -0
  8. package/dist/nativeContinuations/admission.d.ts +17 -0
  9. package/dist/nativeContinuations/admission.js +250 -0
  10. package/dist/nativeContinuations/index.d.ts +2 -0
  11. package/dist/nativeContinuations/index.js +1 -0
  12. package/dist/nativeContinuations/types.d.ts +52 -0
  13. package/dist/nativeContinuations/types.js +0 -0
  14. package/dist/outputValidation.js +1 -0
  15. package/dist/providerCatalog.d.ts +1 -1
  16. package/dist/providerDriver.d.ts +4 -1
  17. package/dist/providerInstanceRegistry.d.ts +4 -4
  18. package/dist/sessionAdmission.d.ts +12 -0
  19. package/dist/sessionAdmission.js +54 -0
  20. package/dist/sessionInteractions/lifetime.d.ts +32 -3
  21. package/dist/sessionInteractions/lifetime.js +145 -13
  22. package/dist/sessionInteractions/request.d.ts +3 -2
  23. package/dist/sessionInteractions/request.js +98 -25
  24. package/dist/sessionInteractions/types.d.ts +68 -12
  25. package/dist/sessionInteractions/validation.d.ts +13 -9
  26. package/dist/sessionInteractions/validation.js +121 -60
  27. package/dist/sessionValidation.d.ts +78 -2
  28. package/dist/sessionValidation.js +213 -162
  29. package/dist/sessions.d.ts +33 -2
  30. package/dist/testing/conformance.d.ts +3 -1
  31. package/dist/testing/conformance.js +180 -85
  32. package/dist/testing/fakeProvider.js +2 -2
  33. package/dist/turnExecution/execution.d.ts +23 -6
  34. package/dist/turnExecution/execution.js +136 -32
  35. package/dist/turnExecution/requests.d.ts +15 -3
  36. package/dist/turnExecution/requests.js +93 -28
  37. package/package.json +2 -2
package/README.md CHANGED
@@ -9,10 +9,14 @@ sessions; each returned session owns its binding and conversation-local operatio
9
9
  The runtime depends only on `@agen-ai/agent-protocol`. It has no concept of tenants, SaaS
10
10
  workspaces, assigned users, database rows, persistence sequence, visibility, billing, host boots,
11
11
  distributed leases, or storage policy. A host must authorize and select an instance before calling this SPI.
12
- This source package implements Agent Protocol V11 at coordinated SDK version `0.4.1`,
13
- which remains unpublished. The published release remains `0.4.0` with V10. The source candidate
14
- also includes the additive session-interaction API.
15
- Upgrade consumers from `0.3.1` using the coordinated release and its migration guide.
12
+ This unpublished source candidate implements Agent Protocol V12 at coordinated SDK version `0.4.2`,
13
+ including tagged decision responses and exact turn request unavailability. Published `0.4.1`
14
+ retains V11 and its qualification evidence; the earlier `0.4.0` release retains V10.
15
+ The runtime also provides neutral session forms, plan reviews, staged native continuations and
16
+ explicit human-wait policies. Provider mappings require their own support and qualification.
17
+ Keep all installed SDK packages at the same version; the [0.4.2 migration
18
+ guide](../../MIGRATING-TO-0.4.2.md) covers tagged responses, continuous request output and store
19
+ contract changes. Generic contract proof and exact native qualification are separate boundaries.
16
20
  The SDK contract version is independent of private transport and product persistence versions. This
17
21
  is intentionally one coordinated tuple rather than a deployable mixed-version graph.
18
22
 
@@ -199,28 +203,35 @@ same correlation and lifecycle validation at their respective ports.
199
203
 
200
204
  ## Session-lifetime interactions
201
205
 
202
- An adapter can offer a decision outside any model turn through `sessionInteractions`.
203
- Its support declaration is separate from V11 turn capabilities: absence means
204
- `{ kind: 'unsupported' }`; supported adapters name exact `openingOperations`, declare
205
- `requestKinds: ['decision']` and require `producerOwnership: 'exclusive'`. Supplying
206
- `interactions` for an unsupported opening raises `session_interactions_unsupported` before
207
- delegating that opening. An adapter's create support does not imply resume or branch support.
208
-
209
- Install `interactions.onRequest` before opening. The callback receives a bound
210
- `AgentSessionInteraction` containing the portable request, an eligibility signal, a
211
- terminal promise and `respond({ choiceId, signal? })`. The handler can return immediately
212
- after handing the capability to an owned UI, or return the promise for its work. Opening
213
- may return while the decision remains pending; questions can also arrive after return.
214
- Do not block session creation waiting for every human decision or route these decisions
215
- through `startTurn`, `AgentEvent`, or an execution's `resolveRequest`.
216
-
217
- For an already selected `adapter`, host-owned `openingInput` and UI-owned map, register early:
206
+ An adapter can offer a decision, form or plan review outside a model turn through
207
+ `sessionInteractions`. Support is separate from portable turn capabilities: absence means
208
+ `{ kind: 'unsupported' }`. Supported adapters declare exact
209
+ `{ openingOperation, requestKind }` mappings, `producerOwnership: 'exclusive'` and supported
210
+ `humanWait` modes. Create support does not imply resume or branch support. Supplying a handler
211
+ or policy for an unsupported mapping rejects before candidate opening.
212
+
213
+ Driver authors implement `AgentProviderAdapter` with candidate opening inputs
214
+ `AgentProviderCreateSessionInput`, `AgentProviderResumeSessionInput` and
215
+ `AgentProviderBranchSessionInput`. The runtime installs their producer facade before entering
216
+ that opening. Registry consumers receive `AgentValidatedProviderAdapter` in a
217
+ `ValidatedMaterializedAgentProviderInstance`; they use `AgentCreateSessionInput`,
218
+ `AgentResumeSessionInput` and `AgentBranchSessionInput`. These types separate provider offers
219
+ from application handlers. Revalidating an adapter retains its original candidate ownership.
220
+
221
+ Install `interactions.onRequest` before opening. The application callback receives a bound
222
+ `AgentSessionInteraction` containing its immutable portable request, observed deadline,
223
+ eligibility signal, terminal promise and `respond` capability. Narrow `request.kind` before
224
+ reading workspace targets, form fields or the exact plan snapshot. Responses are tagged by
225
+ kind and validate the offered choice and action; forms validate field answers, and plan
226
+ responses validate plan ID and revision. Only revision accepts required nonblank feedback.
227
+
228
+ For an already selected validated `adapter`, host-owned `openingInput` and UI-owned map:
218
229
 
219
230
  ```ts
220
- import type { AgentProviderCreateSessionInput, AgentSessionInteraction } from '@agen-ai/agent-runtime';
231
+ import type { AgentCreateSessionInput, AgentSessionInteraction } from '@agen-ai/agent-runtime';
221
232
 
222
233
  const pending = new Map<string, AgentSessionInteraction>();
223
- const input: AgentProviderCreateSessionInput = {
234
+ const input: AgentCreateSessionInput = {
224
235
  ...openingInput,
225
236
  interactions: {
226
237
  onRequest(interaction) {
@@ -229,67 +240,124 @@ const input: AgentProviderCreateSessionInput = {
229
240
  },
230
241
  };
231
242
  const session = await adapter.createSession(input);
232
- // Present the request target and every choice consequence before selecting a choice.
233
- // Answer using that retained interaction's respond({ choiceId }) capability.
234
- // The UI retires terminal entries and observes interaction.signal.
243
+ // Narrow request.kind and present the actual offered choices before answering.
244
+ // A workspace decision uses respond({ kind: 'decision', choiceId }).
245
+ // Retire the UI entry when interaction.signal aborts; keep entered receipt ownership.
235
246
  ```
236
247
 
237
- The complete public [lifecycle example](../../examples/lifecycle-interactions.ts) uses a
238
- consumer-owned adapter, opens with a pending decision, selects a synthetic decline and joins
239
- cleanup without a model turn. It needs only the core protocol/runtime dependency closure.
240
- For an ordinary noninteractive opening, omit `interactions` from the input. The runtime
241
- does not install an automatic answer policy; a provider requiring a handler must reject that
242
- opening. Keep authentication and interactive materialization in their existing owners.
248
+ A handler can return after handing the capability to an owned UI, or return a promise whose
249
+ actual work remains tracked. Opening may return while a request remains pending. Session
250
+ requests do not become `AgentEvent` request outputs or execution `resolveRequest` calls.
251
+ The complete public [lifecycle example](../../examples/lifecycle-interactions.ts) opens with a
252
+ pending decision, selects a synthetic decline and joins cleanup without a model turn. It needs
253
+ only the core protocol/runtime dependency closure. An ordinary noninteractive opening omits
254
+ `interactions`; no automatic answer policy is installed. A provider requiring a handler must
255
+ reject that opening. Authentication retains its separate bounded owner.
243
256
 
244
- `respond` validates the choice against this exact request and allows one active response:
257
+ `respond` allows one unsettled response attempt per request:
245
258
 
246
259
  | Receipt | Meaning |
247
260
  | --- | --- |
248
- | `delivered` | The provider-owned handler accepted the exact choice. The interaction becomes `answered`; this does not prove a wire write, native consumption, effect completion or persistent storage. |
249
- | `rejected` with `requestState: 'pending'` | Definitive refusal preserves this request's eligibility. A second response while one is active also receives pending refusal; it cannot start overlapping native delivery. |
250
- | `rejected` with `requestState: 'unavailable'` | The request has retired and the capability cannot answer it again. |
251
- | `delivery_uncertain` | Entered delivery did not establish acceptance or refusal. The session is fenced against further mutation; close it without replaying the choice. |
252
-
253
- Malformed choices and pre-aborted response inputs preserve their original errors before provider
254
- entry. An entered response that throws or exceeds its response deadline returns uncertainty,
255
- even if the provider later settles. Closing or aborting does not make an entered answer safe
256
- to repeat. Successful cleanup never resolves that uncertainty or authorizes replay.
257
-
258
- Terminal variants are `answered`, `rejected`, `delivery_uncertain`, `withdrawn`, `expired`,
259
- `session_closed`, and `failed`. `interaction.signal` controls answer eligibility and UI
260
- presentation; terminal settlement records the decision lifecycle. Neither means the native
261
- producer, entered response or callback has stopped. Close first fences admission and starts
262
- native shutdown, then joins the actual response, terminal and callback work. A callback that
263
- ignores abort remains owned. Request expiry or handler failure fails the interaction owner;
264
- cleanup and its original failure remain observable through the owning session.
265
-
266
- The runtime validates integer overrides once on the opening context:
261
+ | `delivered` | The provider-owned handler accepted the exact response. This does not prove model consumption, continuation start, successful effects or persistence. |
262
+ | `rejected` with `requestState: 'pending'` | Definitive refusal permits a later attempt only if the request remains live. A concurrent response also receives pending refusal without entering overlapping delivery. |
263
+ | `rejected` with `requestState: 'unavailable'` | The request cannot be answered again. |
264
+ | `delivery_uncertain` | Entered delivery did not establish acceptance or refusal. Mutation is fenced; retain cleanup and do not replay the response. |
265
+
266
+ Terminals are `answered`, `rejected`, `delivery_uncertain`, `withdrawn`, `expired`,
267
+ `session_closed` and `failed`. `answered.response` retains accepted kind, choice and action,
268
+ plus plan ID/revision where applicable; it does not duplicate form values or revision feedback.
269
+ `expired.source` distinguishes provider expiry from host policy. A terminal ends answer
270
+ eligibility, independently of an entered attempt's receipt. A delivered or rejected/pending
271
+ receipt racing withdrawal remains unchanged and cannot restore the retired request.
272
+
273
+ Local parse and pre-abort errors remain precise before provider entry. Entered response
274
+ exceptions or observation timeout return uncertainty. Successful cleanup cannot establish
275
+ whether an uncertain answer was accepted or authorize replay. Eligibility abortion, terminal
276
+ settlement and promise observation are not proof that producers or callbacks have joined.
277
+
278
+ ### Human waiting and retention
279
+
280
+ Omitting `humanWait`, or selecting `{ kind: 'bounded' }`, retains the finite default.
281
+ The only numeric human timeout is `limits.requestTimeoutMs`. An explicitly supported
282
+ `humanWait: { kind: 'until_withdrawn_or_closed' }` installs no SDK human timer. Supplying a
283
+ `requestTimeoutMs` property in that mode rejects before opening, even when it equals the
284
+ default. The producer facade omits that property in unbounded mode.
285
+
286
+ The descriptor's optional `expiresAt` means actual provider expiry. At validated offer time,
287
+ the runtime fixes `interaction.deadline` to the earlier provider expiry or bounded host policy;
288
+ provider expiry wins a tie. No provider expiry and no host timer yields `{ kind: 'no_deadline' }`.
289
+ Presentation delay and queue residence do not restart this deadline. Expiry never fabricates an
290
+ answer. Without independently qualified provider-local survival semantics, expiry initiates
291
+ owned shutdown. Exact withdrawal remains distinct from expiry or general transport failure.
267
292
 
268
293
  | Limit | Default | Allowed override |
269
294
  | --- | --- | --- |
270
295
  | `maxRetainedRequests` | 8 | 1 to 32 |
271
296
  | `maxRetainedBytes` | 524,288 bytes | 65,536 to 2,097,152 bytes |
272
- | `requestTimeoutMs` | 300,000 ms | 1,000 to 1,800,000 ms |
297
+ | `requestTimeoutMs` in bounded mode | 300,000 ms | 1,000 to 1,800,000 ms |
273
298
  | `responseTimeoutMs` | 30,000 ms | 1,000 to 120,000 ms |
274
299
  | `cleanupTimeoutMs` | 30,000 ms | 1,000 to 120,000 ms |
275
300
 
276
- Request eligibility uses the earlier provider expiry or runtime request deadline. Retained
277
- capacity includes terminal requests whose native terminal, response or callback has not drained.
278
- Unknown fields, fractional values and values outside the bounds reject the opening context.
279
- The opening signal applies until the session is adopted; later cancellation belongs to the
280
- returned session or a specific response rather than the completed opening call.
281
-
282
- If an opening fails with incomplete cleanup, use
283
- `isAgentProviderSessionCleanupErrorFor(error, openingInput)` with the exact input object
284
- passed to the adapter, such as `input` above.
285
- Retain that `AgentProviderSessionCleanupError` and its bound `retryCleanup()`; keep the
286
- registry, instance, host resources and installation references owned until it succeeds.
287
- A returned session with failed close likewise retains cleanup through the session and any
288
- returned cleanup error. An observation timeout does not detach the actual work. Retry joins
289
- the same owner and never opens another session or resends a choice. Serialized errors and
290
- request IDs cannot restore callable cleanup or response authority. Adapter authors use
291
- `createAgentProviderSessionCleanupError({ openingInput, providerKey, errors, retryCleanup })`
292
- to preserve a failed opening's exact cleanup owner.
301
+ Retained descriptor bytes include each immutable plan snapshot once. Separately bounded
302
+ response material remains charged to its single unsettled attempt. Terminal requests stay
303
+ retained until actual native terminal, response and presentation callback work drain.
304
+ Unknown fields, fractional values and out-of-range overrides reject. Human-wait policy does
305
+ not remove response, preparation, cleanup, authentication, transport or capacity bounds.
306
+ All turn and session request IDs share the fixed session-generation census of 4,096 IDs and
307
+ 1 MiB of UTF-8 identity bytes; tombstones are not evicted to grant reuse.
308
+
309
+ ### Native continuations
310
+
311
+ A response that can start native work uses a provider `prepared` interaction. Effect-free
312
+ `prepareResponse` receives the exact parsed response and runtime-owned attempt identity.
313
+ It returns either `no_continuation` or `native_continuation`, with once-only `dispatch` and
314
+ idempotent predispatch `cancel` operations. Direct interactions may only deliver responses
315
+ that cannot start execution. A known no-work action does not reserve an execution.
316
+
317
+ The application installs `nativeContinuations.onExecution` before opening. Exact support
318
+ includes the opening operation, request kind and timing; it does not imply other combinations.
319
+ When preparation selects a continuation, the runtime synchronously reserves the same slot
320
+ used by caller `startTurn`, allocates a turn identity and adopts the candidate through ordinary
321
+ output validation. It never creates a synthetic caller prompt or calls candidate `startTurn`.
322
+
323
+ The host must actually call `next()` on the supplied execution's sole output iterator. Merely
324
+ retaining the handle or acquiring an iterator is insufficient. The parked wrapper pull proves
325
+ reader readiness without touching the candidate iterator before dispatch. A handler may await
326
+ that first output: dispatch does not wait for callback fulfillment. After readiness, runtime
327
+ rechecks exact request, attempt, opening, deadline and slot ownership before entering dispatch.
328
+ Output produced during dispatch stays on that original reader.
329
+
330
+ One fixed 30-second window covers preparation and reader readiness from response claim.
331
+ Predispatch failure cancels and joins allocated source/callback ownership before releasing the
332
+ slot. Failed cleanup retains the stopped owner and its retry handle. A started execution uses
333
+ normal session quiescence and output cleanup. The independent continuation `outcome` reports
334
+ `started` or `not_started`; a delivery receipt proves neither. Missing outcome cannot free a
335
+ slot or rewrite a delivered receipt. One dispatch-anchored response deadline observes both
336
+ receipt and outcome; uncertainty fences mutation without dropping either owner.
337
+
338
+ Opening timing is explicit: `after_open_return`, `before_open_return`, or `before_binding`.
339
+ The opening owner exists before candidate entry and transfers atomically after exact binding
340
+ validation. Exceptional data awaiting binding is limited to 16 envelopes, 1 MiB and 30 seconds.
341
+ It cannot invent a binding or route output by working directory. Core timing support does not
342
+ qualify a native provider mapping; select only that provider's explicitly supported tuple.
343
+
344
+ ### Close and retained cleanup
345
+
346
+ Close fences opening/session admission and starts native quiescence before joining preparation,
347
+ responses, output and callbacks. A callback ignoring abort remains owned. Host binding and
348
+ installation resources are released only after actual native cleanup and joins succeed.
349
+ The opening signal applies until adoption; later cancellation belongs to the returned session
350
+ or response owner.
351
+
352
+ If opening fails with incomplete cleanup, call
353
+ `isAgentProviderSessionCleanupErrorFor(error, openingInput)` with the exact original object.
354
+ Retain the matching `AgentProviderSessionCleanupError` and its bound `retryCleanup()`; keep
355
+ registry, instance, host resources and installation references until it succeeds. A returned
356
+ session with failed close likewise retains cleanup through that session and its cleanup error.
357
+ An observation timeout cannot detach actual work. Retry joins the same owner without opening
358
+ another session or resending a response. Serialized errors and request IDs cannot restore
359
+ callable cleanup or response authority. Adapter authors preserve failed opening ownership with
360
+ `createAgentProviderSessionCleanupError({ openingInput, providerKey, errors, retryCleanup })`.
293
361
 
294
362
  ## Session-local host tools and instructions
295
363
 
@@ -450,8 +518,23 @@ turn/request ordering, request resolution, steering, interruption, configuration
450
518
  close, and idempotent disposal. Unsupported operations must remain explicit discriminants and
451
519
  must not expose handlers.
452
520
 
453
- The coordinated source SDK version is `0.4.1`, remains unpublished and implements Agent Protocol V11.
454
- The published coordinated release remains `0.4.0` with V10.
521
+ `resolutionFor(request, { signal })` accepts the existing synchronous resolution callback,
522
+ an asynchronous callback, or `undefined` to deliberately leave a request unanswered. Its
523
+ request-scoped signal aborts when the original output stream reports that exact request
524
+ unavailable, when the turn completes, or when conformance cleanup begins. Honor the signal
525
+ to dismiss pending human input; do not fabricate a denial or retry an answer after retirement.
526
+ The runner continuously reads the same execution while callbacks and delivery receipts are
527
+ pending. A returned answer is checked against the exact offered request before delivery.
528
+ `request_resolution` appears in the report only after a delivered receipt;
529
+ `request_unavailability` records consumption of a native retirement event. Opening a request
530
+ alone proves neither. The runner checks session controls before its main turn and inspects
531
+ turn-produced resources after terminal output while the session remains open. It then starts session
532
+ close before joining its reader and actual callback tasks; failure also starts close before joins. Original
533
+ failures and cleanup failures remain observable. A callback that ignores cancellation
534
+ remains owned until its actual promise settles.
535
+
536
+ The coordinated SDK release is `0.4.1` and implements Agent Protocol V11.
537
+ The earlier `0.4.0` release retains V10.
455
538
  The earlier public `0.3.0` and `0.3.1` releases implemented V9 and remain historical.
456
539
  Keep installed packages in the seven-package SDK release family on the coordinated version; the
457
540
  orchestration and ACP packages remain optional. Every materialized instance requires `environment` and
@@ -1,4 +1,6 @@
1
1
  import { type AgentCapabilities } from "@agen-ai/agent-protocol";
2
- import type { AgentProviderAdapter } from "./sessions.js";
3
- export declare function validateAgentProviderAdapter(capabilityInput: AgentCapabilities, adapterInput: AgentProviderAdapter): AgentProviderAdapter;
2
+ import type { AgentProviderAdapter, AgentValidatedProviderAdapter } from "./sessions.js";
3
+ export declare function validateAgentProviderAdapter(capabilities: AgentCapabilities, candidate: AgentProviderAdapter): AgentValidatedProviderAdapter;
4
+ export declare function validateAgentProviderAdapter(capabilities: AgentCapabilities, candidate: AgentValidatedProviderAdapter): AgentValidatedProviderAdapter;
5
+ export declare function validateAgentProviderAdapter(capabilities: AgentCapabilities, candidate: AgentProviderAdapter | AgentValidatedProviderAdapter): AgentValidatedProviderAdapter;
4
6
  //# sourceMappingURL=adapterValidation.d.ts.map
@@ -17,7 +17,7 @@ import { withAgentSessionHostBinding } from "./hostBindings/lifetime.js";
17
17
  import { invokeAgentSessionOpening } from "./hostBindings/opening.js";
18
18
  import { validateAgentSessionHostBindingSupport } from "./hostBindings/validation.js";
19
19
  import { createSessionInteractionOwner } from "./sessionInteractions/lifetime.js";
20
- import { validateSessionInteractionSupport } from "./sessionInteractions/validation.js";
20
+ import { validateSessionInteractionSupport, validateNativeContinuationSupport } from "./sessionInteractions/validation.js";
21
21
  import {
22
22
  validateAgentProviderOperationResult,
23
23
  validateAgentProviderOutputForContext
@@ -25,9 +25,13 @@ import {
25
25
  import {
26
26
  closeRejectedAgentProviderSession,
27
27
  openIdentityCreatingAgentProviderSession,
28
- validateAgentProviderSession
28
+ validateAgentProviderSession,
29
+ createAgentSessionValidationOwner
29
30
  } from "./sessionValidation.js";
30
31
  const validatedAdapters = /* @__PURE__ */ new WeakMap();
32
+ function isCandidateAdapter(adapter) {
33
+ return !validatedAdapters.has(adapter);
34
+ }
31
35
  function parseWorkingDirectory(value) {
32
36
  if (value.length < 1 || value.length > 4096 || !path.isAbsolute(value) || path.normalize(value) !== value || containsAgentControlCharacter(value)) {
33
37
  throw new TypeError(
@@ -48,7 +52,15 @@ function assertAuthenticationOutputKind(providerKey, output) {
48
52
  }
49
53
  function validateAgentProviderAdapter(capabilityInput, adapterInput) {
50
54
  const capabilities = parseAgentCapabilities(capabilityInput);
51
- const adapter = validatedAdapters.get(adapterInput) ?? adapterInput;
55
+ if (!isCandidateAdapter(adapterInput)) return validateAgentProviderAdapter(capabilities, validatedAdapters.get(adapterInput));
56
+ const adapter = adapterInput;
57
+ const returnedOpening = (promise, opening) => {
58
+ void promise.then(() => {
59
+ queueMicrotask(() => opening.owner?.markReturned());
60
+ }, () => {
61
+ });
62
+ return promise;
63
+ };
52
64
  if (adapter === null || typeof adapter !== "object" || typeof adapter.createSession !== "function" || adapter.resumption === null || typeof adapter.resumption !== "object" || !["supported", "unsupported"].includes(adapter.resumption.kind) || adapter.resumption.kind === "supported" && typeof adapter.resumption.resumeSession !== "function" || adapter.branching === null || typeof adapter.branching !== "object" || !["through_turn", "unsupported"].includes(adapter.branching.kind) || adapter.branching.kind === "through_turn" && typeof adapter.branching.branchSession !== "function" || adapter.authentication === null || typeof adapter.authentication !== "object" || !["supported", "unsupported"].includes(adapter.authentication.kind) || adapter.authentication.kind === "supported" && (typeof adapter.authentication.start !== "function" || typeof adapter.authentication.cancel !== "function")) {
53
65
  throwAgentProviderContractError(
54
66
  capabilities.providerKey,
@@ -65,6 +77,7 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
65
77
  }
66
78
  const declaredResumption = adapter.resumption;
67
79
  const sessionInteractions = validateSessionInteractionSupport(capabilities, adapter);
80
+ const nativeContinuations = validateNativeContinuationSupport(capabilities, adapter, sessionInteractions);
68
81
  let hostBinding;
69
82
  try {
70
83
  hostBinding = validateAgentSessionHostBindingSupport(adapter.hostBinding);
@@ -77,7 +90,7 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
77
90
  }
78
91
  const resumption = declaredResumption.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
79
92
  kind: "supported",
80
- resumeSession: async (resumeInput) => withAgentSessionHostBinding({
93
+ resumeSession: async (resumeInput, invocation = {}) => withAgentSessionHostBinding({
81
94
  providerKey: capabilities.providerKey,
82
95
  binding: resumeInput.hostBinding,
83
96
  openingInput: resumeInput,
@@ -101,14 +114,20 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
101
114
  resumeInput.workingDirectory
102
115
  );
103
116
  const binding = parsed.binding;
117
+ const validationOwner = createAgentSessionValidationOwner(capabilities, parsed.sessionId);
104
118
  const interactions = createSessionInteractionOwner({
105
119
  providerKey: capabilities.providerKey,
106
120
  sessionId: parsed.sessionId,
107
121
  operation: "resume",
108
122
  support: sessionInteractions,
123
+ nativeSupport: nativeContinuations,
124
+ validationOwner,
125
+ ...resumeInput.nativeContinuations === void 0 ? {} : { continuations: resumeInput.nativeContinuations },
109
126
  ...resumeInput.interactions === void 0 ? {} : { context: resumeInput.interactions },
110
127
  ...resumeInput.signal === void 0 ? {} : { signal: resumeInput.signal }
111
128
  });
129
+ if (interactions) invocation.owner = interactions;
130
+ interactions?.bind();
112
131
  const candidate = await invokeAgentSessionOpening({
113
132
  providerKey: capabilities.providerKey,
114
133
  openingInput: resumeInput,
@@ -128,6 +147,7 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
128
147
  try {
129
148
  const session = validateAgentProviderSession({
130
149
  capabilities,
150
+ validationOwner,
131
151
  sessionId: parsed.sessionId,
132
152
  candidate,
133
153
  expectedBinding: binding,
@@ -151,7 +171,7 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
151
171
  const declaredBranching = adapter.branching;
152
172
  const branching = declaredBranching.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
153
173
  kind: "through_turn",
154
- branchSession: async (branchInput) => withAgentSessionHostBinding({
174
+ branchSession: async (branchInput, invocation = {}) => withAgentSessionHostBinding({
155
175
  providerKey: capabilities.providerKey,
156
176
  binding: branchInput.hostBinding,
157
177
  openingInput: branchInput,
@@ -175,16 +195,22 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
175
195
  branchInput.workingDirectory
176
196
  );
177
197
  const sourceBinding = parsed.source.binding;
198
+ const validationOwner = createAgentSessionValidationOwner(capabilities, parsed.sessionId);
178
199
  const interactions = createSessionInteractionOwner({
179
200
  providerKey: capabilities.providerKey,
180
201
  sessionId: parsed.sessionId,
181
202
  operation: "branch",
182
203
  support: sessionInteractions,
204
+ nativeSupport: nativeContinuations,
205
+ validationOwner,
206
+ ...branchInput.nativeContinuations === void 0 ? {} : { continuations: branchInput.nativeContinuations },
183
207
  ...branchInput.interactions === void 0 ? {} : { context: branchInput.interactions },
184
208
  ...branchInput.signal === void 0 ? {} : { signal: branchInput.signal }
185
209
  });
210
+ if (interactions) invocation.owner = interactions;
186
211
  return openIdentityCreatingAgentProviderSession({
187
212
  capabilities,
213
+ validationOwner,
188
214
  sessionId: parsed.sessionId,
189
215
  observer: branchInput.onBindingCreated,
190
216
  sourceBinding,
@@ -281,66 +307,89 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
281
307
  return result;
282
308
  }
283
309
  });
284
- const validated = Object.freeze({
285
- hostBinding,
286
- sessionInteractions,
287
- createSession: async (createInput) => withAgentSessionHostBinding({
288
- providerKey: capabilities.providerKey,
289
- binding: createInput.hostBinding,
290
- openingInput: createInput,
291
- support: hostBinding,
292
- open: async (lease) => {
293
- throwIfAgentOperationAborted(createInput.signal);
294
- const parsed = parseAgentSessionOpenInput({
295
- operation: "create",
296
- sessionId: createInput.sessionId,
297
- configuration: createInput.configuration
298
- });
299
- assertAgentSessionConfigurationSupported(
300
- capabilities,
301
- parsed.configuration
302
- );
303
- const workingDirectory = parseWorkingDirectory(
304
- createInput.workingDirectory
305
- );
306
- const interactions = createSessionInteractionOwner({
310
+ const createSession = async (createInput, invocation = {}) => withAgentSessionHostBinding({
311
+ providerKey: capabilities.providerKey,
312
+ binding: createInput.hostBinding,
313
+ openingInput: createInput,
314
+ support: hostBinding,
315
+ open: async (lease) => {
316
+ throwIfAgentOperationAborted(createInput.signal);
317
+ const parsed = parseAgentSessionOpenInput({
318
+ operation: "create",
319
+ sessionId: createInput.sessionId,
320
+ configuration: createInput.configuration
321
+ });
322
+ assertAgentSessionConfigurationSupported(
323
+ capabilities,
324
+ parsed.configuration
325
+ );
326
+ const workingDirectory = parseWorkingDirectory(
327
+ createInput.workingDirectory
328
+ );
329
+ const validationOwner = createAgentSessionValidationOwner(capabilities, parsed.sessionId);
330
+ const interactions = createSessionInteractionOwner({
331
+ providerKey: capabilities.providerKey,
332
+ sessionId: parsed.sessionId,
333
+ operation: "create",
334
+ support: sessionInteractions,
335
+ nativeSupport: nativeContinuations,
336
+ validationOwner,
337
+ ...createInput.nativeContinuations === void 0 ? {} : { continuations: createInput.nativeContinuations },
338
+ ...createInput.interactions === void 0 ? {} : { context: createInput.interactions },
339
+ ...createInput.signal === void 0 ? {} : { signal: createInput.signal }
340
+ });
341
+ if (interactions) invocation.owner = interactions;
342
+ return openIdentityCreatingAgentProviderSession({
343
+ capabilities,
344
+ validationOwner,
345
+ sessionId: parsed.sessionId,
346
+ observer: createInput.onBindingCreated,
347
+ openingInput: createInput,
348
+ ...interactions === void 0 ? {} : { sessionInteractionOwner: interactions },
349
+ ...lease === void 0 ? {} : { hostBindingLease: lease },
350
+ open: (observer) => invokeAgentSessionOpening({
307
351
  providerKey: capabilities.providerKey,
308
- sessionId: parsed.sessionId,
309
- operation: "create",
310
- support: sessionInteractions,
311
- ...createInput.interactions === void 0 ? {} : { context: createInput.interactions },
312
- ...createInput.signal === void 0 ? {} : { signal: createInput.signal }
313
- });
314
- return openIdentityCreatingAgentProviderSession({
315
- capabilities,
316
- sessionId: parsed.sessionId,
317
- observer: createInput.onBindingCreated,
318
352
  openingInput: createInput,
319
- ...interactions === void 0 ? {} : { sessionInteractionOwner: interactions },
320
- ...lease === void 0 ? {} : { hostBindingLease: lease },
321
- open: (observer) => invokeAgentSessionOpening({
322
- providerKey: capabilities.providerKey,
323
- openingInput: createInput,
324
- lease,
325
- ...interactions === void 0 ? {} : { interactions },
326
- open: (context) => adapter.createSession(context),
327
- candidateInput: {
328
- sessionId: parsed.sessionId,
329
- workingDirectory,
330
- configuration: parsed.configuration,
331
- onBindingCreated: observer,
332
- ...lease === void 0 ? {} : { hostBinding: lease.binding },
333
- ...interactions === void 0 ? {} : { interactions: interactions.facade },
334
- ...(interactions?.openingSignal ?? createInput.signal) === void 0 ? {} : { signal: interactions?.openingSignal ?? createInput.signal }
335
- }
336
- })
337
- });
338
- }
339
- }),
353
+ lease,
354
+ ...interactions === void 0 ? {} : { interactions },
355
+ open: (context) => adapter.createSession(context),
356
+ candidateInput: {
357
+ sessionId: parsed.sessionId,
358
+ workingDirectory,
359
+ configuration: parsed.configuration,
360
+ onBindingCreated: observer,
361
+ ...lease === void 0 ? {} : { hostBinding: lease.binding },
362
+ ...interactions === void 0 ? {} : { interactions: interactions.facade },
363
+ ...(interactions?.openingSignal ?? createInput.signal) === void 0 ? {} : { signal: interactions?.openingSignal ?? createInput.signal }
364
+ }
365
+ })
366
+ });
367
+ }
368
+ });
369
+ const openingAdapter = Object.freeze({
370
+ hostBinding,
371
+ sessionInteractions,
372
+ nativeContinuations,
373
+ createSession,
340
374
  resumption,
341
375
  branching,
342
376
  authentication
343
377
  });
378
+ const validated = Object.freeze({
379
+ ...openingAdapter,
380
+ createSession: (input) => {
381
+ const invocation = {};
382
+ return returnedOpening(createSession(input, invocation), invocation);
383
+ },
384
+ resumption: resumption.kind === "unsupported" ? resumption : Object.freeze({ kind: "supported", resumeSession: (input) => {
385
+ const invocation = {};
386
+ return returnedOpening(resumption.resumeSession(input, invocation), invocation);
387
+ } }),
388
+ branching: branching.kind === "unsupported" ? branching : Object.freeze({ kind: "through_turn", branchSession: (input) => {
389
+ const invocation = {};
390
+ return returnedOpening(branching.branchSession(input, invocation), invocation);
391
+ } })
392
+ });
344
393
  validatedAdapters.set(validated, adapter);
345
394
  return validated;
346
395
  }
@@ -1,6 +1,6 @@
1
1
  import type { AgentProviderKey } from "@agen-ai/agent-protocol";
2
2
  import type { MaybePromise } from "./foundation.js";
3
- export declare const AGENT_PROVIDER_CONTRACT_ERROR_CODES: readonly ["capability_port_mismatch", "invalid_adapter", "invalid_session", "invalid_binding", "binding_callback_missing", "binding_callback_repeated", "binding_callback_mismatch", "resume_binding_mismatch", "branch_binding_reused", "session_closed", "session_unusable", "concurrent_turn", "active_turn_mismatch", "invalid_operation_result", "invalid_inventory", "invalid_collaboration_transition", "input_operation_mismatch", "output_session_mismatch", "output_turn_mismatch", "output_authentication_attempt_mismatch", "output_operation_mismatch", "output_collaboration_mismatch", "output_resource_mismatch", "output_capability_mismatch", "input_capability_mismatch", "invalid_turn_sequence", "request_resolution_mismatch", "configuration_key_unsupported", "configuration_value_unsupported", "request_pending", "invalid_host_binding", "host_binding_unsupported", "host_binding_reused", "host_binding_closed", "host_tool_name_conflict", "host_tool_input_invalid", "host_tool_result_invalid", "host_tool_capacity_exhausted", "invalid_session_interaction", "session_interactions_unsupported"];
3
+ export declare const AGENT_PROVIDER_CONTRACT_ERROR_CODES: readonly ["capability_port_mismatch", "invalid_adapter", "invalid_session", "invalid_binding", "binding_callback_missing", "binding_callback_repeated", "binding_callback_mismatch", "resume_binding_mismatch", "branch_binding_reused", "session_closed", "session_unusable", "concurrent_turn", "active_turn_mismatch", "invalid_operation_result", "invalid_inventory", "invalid_collaboration_transition", "input_operation_mismatch", "output_session_mismatch", "output_turn_mismatch", "output_authentication_attempt_mismatch", "output_operation_mismatch", "output_collaboration_mismatch", "output_resource_mismatch", "output_capability_mismatch", "input_capability_mismatch", "invalid_turn_sequence", "request_resolution_mismatch", "request_identity_limit_exceeded", "configuration_key_unsupported", "configuration_value_unsupported", "request_pending", "invalid_host_binding", "host_binding_unsupported", "host_binding_reused", "host_binding_closed", "host_tool_name_conflict", "host_tool_input_invalid", "host_tool_result_invalid", "host_tool_capacity_exhausted", "invalid_session_interaction", "session_interactions_unsupported", "native_continuations_unsupported", "native_continuation_preparation_failed", "native_continuation_reader_not_ready", "native_continuation_limit_exceeded", "invalid_native_continuation"];
4
4
  export type AgentProviderContractErrorCode = (typeof AGENT_PROVIDER_CONTRACT_ERROR_CODES)[number];
5
5
  export type AgentProviderDelegatedOperation = "resolve_request" | "steer_turn" | "invoke_operation" | "apply_configuration" | "spawn_collaboration" | "control_collaboration";
6
6
  export declare class AgentProviderContractError extends Error {
@@ -26,6 +26,7 @@ const AGENT_PROVIDER_CONTRACT_ERROR_CODES = [
26
26
  "input_capability_mismatch",
27
27
  "invalid_turn_sequence",
28
28
  "request_resolution_mismatch",
29
+ "request_identity_limit_exceeded",
29
30
  "configuration_key_unsupported",
30
31
  "configuration_value_unsupported",
31
32
  "request_pending",
@@ -38,7 +39,12 @@ const AGENT_PROVIDER_CONTRACT_ERROR_CODES = [
38
39
  "host_tool_result_invalid",
39
40
  "host_tool_capacity_exhausted",
40
41
  "invalid_session_interaction",
41
- "session_interactions_unsupported"
42
+ "session_interactions_unsupported",
43
+ "native_continuations_unsupported",
44
+ "native_continuation_preparation_failed",
45
+ "native_continuation_reader_not_ready",
46
+ "native_continuation_limit_exceeded",
47
+ "invalid_native_continuation"
42
48
  ];
43
49
  class AgentProviderContractError extends Error {
44
50
  constructor(providerKey, code, message, options) {
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export * from "./providerInstanceRegistry.js";
14
14
  export * from "./readiness.js";
15
15
  export * from "./sessions.js";
16
16
  export * from "./sessionInteractions/index.js";
17
+ export * from "./nativeContinuations/index.js";
17
18
  export { createAgentProviderSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "./sessionCleanup.js";
18
19
  export * from "./text.js";
19
20
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ export * from "./providerInstanceRegistry.js";
14
14
  export * from "./readiness.js";
15
15
  export * from "./sessions.js";
16
16
  export * from "./sessionInteractions/index.js";
17
+ export * from "./nativeContinuations/index.js";
17
18
  import { createAgentProviderSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "./sessionCleanup.js";
18
19
  export * from "./text.js";
19
20
  export {