@agen-ai/agent-runtime 0.4.1 → 0.4.3

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 +176 -74
  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 +14 -0
  19. package/dist/sessionAdmission.js +67 -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 +80 -2
  28. package/dist/sessionValidation.js +348 -283
  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 package implements Agent Protocol V12 at coordinated SDK version `0.4.3`,
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
 
@@ -153,10 +157,11 @@ reopen an older request. A host must cancel human decisions when their execution
153
157
  those tasks during cleanup. Keep the session or exact opening-cleanup receipt if cleanup fails;
154
158
  disposing its registry or host resources before quiescence would lose the retained owner.
155
159
 
156
- `runtime.retrying` reports a retry already owned by the provider on the active turn. It preserves
160
+ `runtime.retrying` reports a retry already owned by the provider on the active turn or operation. It preserves
157
161
  turn ownership, pending requests, and per-output backpressure; it neither starts another execution
158
162
  nor completes the turn. It is rejected before `turn.started`, after `turn.completed`, after a
159
- `final_diff` terminal materialization, and from session-operation observations. The event's neutral
163
+ `final_diff` terminal materialization on a turn. Current source also admits it through an owned
164
+ session operation's awaited `onOutput`, with the exact `observationTurnId` and no `turn.started`. The event's neutral
160
165
  failure category and recovery hint describe the observed condition. A recovery hint never authorizes
161
166
  replaying work or changes an uncertain execution outcome. Contract failures, delegated-operation
162
167
  started evidence, and retained cleanup responsibilities remain separate from those failure values.
@@ -184,6 +189,23 @@ provider-delegated steering failures throw `AgentProviderDelegatedOperationError
184
189
  cause and explicit started-execution evidence so a host can retire the unusable session. Platform
185
190
  scheduling and provider-native queue modes are intentionally outside this SPI.
186
191
 
192
+ Current source reserves the shared execution slot for `idle_session`/`immediate` operations after
193
+ descriptor validation, through native settlement and awaited output acceptance. Caller turns and
194
+ native continuations cannot overlap that reservation. Exact cached idempotent results are returned
195
+ before reserving another execution. Close stops admission and initiates native quiescence before
196
+ joining entered operation work; failed cleanup retains its stopped owner for retry. This lifecycle
197
+ correction and operation retry observations are included in `0.4.3`, using the existing
198
+ Agent Protocol V12 and operation SPI.
199
+
200
+ Discover the operation through `session.operations` and its offered descriptor, using an exact
201
+ application-configured ID. Pass the descriptor revision as `expectedRevision`, its exact fields,
202
+ a fresh invocation ID and an independent observation correlation; the catalog revision is a
203
+ different value. Confirmation is host-owned. A completed result proves command completion, while
204
+ an accepted completed `context_compaction` item proves an observed compaction effect. Do not infer
205
+ effects from English output text, manufacture token savings, or replay an uncertain operation.
206
+ The exported repository's `examples/operations.ts` demonstrates these cases against public APIs
207
+ and is compiled and executed in the isolated packed-consumer gate.
208
+
187
209
  Configuration selection, operation invocation, and collaboration mutation use the same execution
188
210
  receipt rule: stale or malformed input fails before provider delegation, while any failure after
189
211
  the candidate reports execution start is wrapped as `AgentProviderDelegatedOperationError` and
@@ -199,28 +221,35 @@ same correlation and lifecycle validation at their respective ports.
199
221
 
200
222
  ## Session-lifetime interactions
201
223
 
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:
224
+ An adapter can offer a decision, form or plan review outside a model turn through
225
+ `sessionInteractions`. Support is separate from portable turn capabilities: absence means
226
+ `{ kind: 'unsupported' }`. Supported adapters declare exact
227
+ `{ openingOperation, requestKind }` mappings, `producerOwnership: 'exclusive'` and supported
228
+ `humanWait` modes. Create support does not imply resume or branch support. Supplying a handler
229
+ or policy for an unsupported mapping rejects before candidate opening.
230
+
231
+ Driver authors implement `AgentProviderAdapter` with candidate opening inputs
232
+ `AgentProviderCreateSessionInput`, `AgentProviderResumeSessionInput` and
233
+ `AgentProviderBranchSessionInput`. The runtime installs their producer facade before entering
234
+ that opening. Registry consumers receive `AgentValidatedProviderAdapter` in a
235
+ `ValidatedMaterializedAgentProviderInstance`; they use `AgentCreateSessionInput`,
236
+ `AgentResumeSessionInput` and `AgentBranchSessionInput`. These types separate provider offers
237
+ from application handlers. Revalidating an adapter retains its original candidate ownership.
238
+
239
+ Install `interactions.onRequest` before opening. The application callback receives a bound
240
+ `AgentSessionInteraction` containing its immutable portable request, observed deadline,
241
+ eligibility signal, terminal promise and `respond` capability. Narrow `request.kind` before
242
+ reading workspace targets, form fields or the exact plan snapshot. Responses are tagged by
243
+ kind and validate the offered choice and action; forms validate field answers, and plan
244
+ responses validate plan ID and revision. Only revision accepts required nonblank feedback.
245
+
246
+ For an already selected validated `adapter`, host-owned `openingInput` and UI-owned map:
218
247
 
219
248
  ```ts
220
- import type { AgentProviderCreateSessionInput, AgentSessionInteraction } from '@agen-ai/agent-runtime';
249
+ import type { AgentCreateSessionInput, AgentSessionInteraction } from '@agen-ai/agent-runtime';
221
250
 
222
251
  const pending = new Map<string, AgentSessionInteraction>();
223
- const input: AgentProviderCreateSessionInput = {
252
+ const input: AgentCreateSessionInput = {
224
253
  ...openingInput,
225
254
  interactions: {
226
255
  onRequest(interaction) {
@@ -229,67 +258,124 @@ const input: AgentProviderCreateSessionInput = {
229
258
  },
230
259
  };
231
260
  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.
261
+ // Narrow request.kind and present the actual offered choices before answering.
262
+ // A workspace decision uses respond({ kind: 'decision', choiceId }).
263
+ // Retire the UI entry when interaction.signal aborts; keep entered receipt ownership.
235
264
  ```
236
265
 
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.
266
+ A handler can return after handing the capability to an owned UI, or return a promise whose
267
+ actual work remains tracked. Opening may return while a request remains pending. Session
268
+ requests do not become `AgentEvent` request outputs or execution `resolveRequest` calls.
269
+ The complete public [lifecycle example](../../examples/lifecycle-interactions.ts) opens with a
270
+ pending decision, selects a synthetic decline and joins cleanup without a model turn. It needs
271
+ only the core protocol/runtime dependency closure. An ordinary noninteractive opening omits
272
+ `interactions`; no automatic answer policy is installed. A provider requiring a handler must
273
+ reject that opening. Authentication retains its separate bounded owner.
243
274
 
244
- `respond` validates the choice against this exact request and allows one active response:
275
+ `respond` allows one unsettled response attempt per request:
245
276
 
246
277
  | Receipt | Meaning |
247
278
  | --- | --- |
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:
279
+ | `delivered` | The provider-owned handler accepted the exact response. This does not prove model consumption, continuation start, successful effects or persistence. |
280
+ | `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. |
281
+ | `rejected` with `requestState: 'unavailable'` | The request cannot be answered again. |
282
+ | `delivery_uncertain` | Entered delivery did not establish acceptance or refusal. Mutation is fenced; retain cleanup and do not replay the response. |
283
+
284
+ Terminals are `answered`, `rejected`, `delivery_uncertain`, `withdrawn`, `expired`,
285
+ `session_closed` and `failed`. `answered.response` retains accepted kind, choice and action,
286
+ plus plan ID/revision where applicable; it does not duplicate form values or revision feedback.
287
+ `expired.source` distinguishes provider expiry from host policy. A terminal ends answer
288
+ eligibility, independently of an entered attempt's receipt. A delivered or rejected/pending
289
+ receipt racing withdrawal remains unchanged and cannot restore the retired request.
290
+
291
+ Local parse and pre-abort errors remain precise before provider entry. Entered response
292
+ exceptions or observation timeout return uncertainty. Successful cleanup cannot establish
293
+ whether an uncertain answer was accepted or authorize replay. Eligibility abortion, terminal
294
+ settlement and promise observation are not proof that producers or callbacks have joined.
295
+
296
+ ### Human waiting and retention
297
+
298
+ Omitting `humanWait`, or selecting `{ kind: 'bounded' }`, retains the finite default.
299
+ The only numeric human timeout is `limits.requestTimeoutMs`. An explicitly supported
300
+ `humanWait: { kind: 'until_withdrawn_or_closed' }` installs no SDK human timer. Supplying a
301
+ `requestTimeoutMs` property in that mode rejects before opening, even when it equals the
302
+ default. The producer facade omits that property in unbounded mode.
303
+
304
+ The descriptor's optional `expiresAt` means actual provider expiry. At validated offer time,
305
+ the runtime fixes `interaction.deadline` to the earlier provider expiry or bounded host policy;
306
+ provider expiry wins a tie. No provider expiry and no host timer yields `{ kind: 'no_deadline' }`.
307
+ Presentation delay and queue residence do not restart this deadline. Expiry never fabricates an
308
+ answer. Without independently qualified provider-local survival semantics, expiry initiates
309
+ owned shutdown. Exact withdrawal remains distinct from expiry or general transport failure.
267
310
 
268
311
  | Limit | Default | Allowed override |
269
312
  | --- | --- | --- |
270
313
  | `maxRetainedRequests` | 8 | 1 to 32 |
271
314
  | `maxRetainedBytes` | 524,288 bytes | 65,536 to 2,097,152 bytes |
272
- | `requestTimeoutMs` | 300,000 ms | 1,000 to 1,800,000 ms |
315
+ | `requestTimeoutMs` in bounded mode | 300,000 ms | 1,000 to 1,800,000 ms |
273
316
  | `responseTimeoutMs` | 30,000 ms | 1,000 to 120,000 ms |
274
317
  | `cleanupTimeoutMs` | 30,000 ms | 1,000 to 120,000 ms |
275
318
 
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.
319
+ Retained descriptor bytes include each immutable plan snapshot once. Separately bounded
320
+ response material remains charged to its single unsettled attempt. Terminal requests stay
321
+ retained until actual native terminal, response and presentation callback work drain.
322
+ Unknown fields, fractional values and out-of-range overrides reject. Human-wait policy does
323
+ not remove response, preparation, cleanup, authentication, transport or capacity bounds.
324
+ All turn and session request IDs share the fixed session-generation census of 4,096 IDs and
325
+ 1 MiB of UTF-8 identity bytes; tombstones are not evicted to grant reuse.
326
+
327
+ ### Native continuations
328
+
329
+ A response that can start native work uses a provider `prepared` interaction. Effect-free
330
+ `prepareResponse` receives the exact parsed response and runtime-owned attempt identity.
331
+ It returns either `no_continuation` or `native_continuation`, with once-only `dispatch` and
332
+ idempotent predispatch `cancel` operations. Direct interactions may only deliver responses
333
+ that cannot start execution. A known no-work action does not reserve an execution.
334
+
335
+ The application installs `nativeContinuations.onExecution` before opening. Exact support
336
+ includes the opening operation, request kind and timing; it does not imply other combinations.
337
+ When preparation selects a continuation, the runtime synchronously reserves the same slot
338
+ used by caller `startTurn`, allocates a turn identity and adopts the candidate through ordinary
339
+ output validation. It never creates a synthetic caller prompt or calls candidate `startTurn`.
340
+
341
+ The host must actually call `next()` on the supplied execution's sole output iterator. Merely
342
+ retaining the handle or acquiring an iterator is insufficient. The parked wrapper pull proves
343
+ reader readiness without touching the candidate iterator before dispatch. A handler may await
344
+ that first output: dispatch does not wait for callback fulfillment. After readiness, runtime
345
+ rechecks exact request, attempt, opening, deadline and slot ownership before entering dispatch.
346
+ Output produced during dispatch stays on that original reader.
347
+
348
+ One fixed 30-second window covers preparation and reader readiness from response claim.
349
+ Predispatch failure cancels and joins allocated source/callback ownership before releasing the
350
+ slot. Failed cleanup retains the stopped owner and its retry handle. A started execution uses
351
+ normal session quiescence and output cleanup. The independent continuation `outcome` reports
352
+ `started` or `not_started`; a delivery receipt proves neither. Missing outcome cannot free a
353
+ slot or rewrite a delivered receipt. One dispatch-anchored response deadline observes both
354
+ receipt and outcome; uncertainty fences mutation without dropping either owner.
355
+
356
+ Opening timing is explicit: `after_open_return`, `before_open_return`, or `before_binding`.
357
+ The opening owner exists before candidate entry and transfers atomically after exact binding
358
+ validation. Exceptional data awaiting binding is limited to 16 envelopes, 1 MiB and 30 seconds.
359
+ It cannot invent a binding or route output by working directory. Core timing support does not
360
+ qualify a native provider mapping; select only that provider's explicitly supported tuple.
361
+
362
+ ### Close and retained cleanup
363
+
364
+ Close fences opening/session admission and starts native quiescence before joining preparation,
365
+ responses, output and callbacks. A callback ignoring abort remains owned. Host binding and
366
+ installation resources are released only after actual native cleanup and joins succeed.
367
+ The opening signal applies until adoption; later cancellation belongs to the returned session
368
+ or response owner.
369
+
370
+ If opening fails with incomplete cleanup, call
371
+ `isAgentProviderSessionCleanupErrorFor(error, openingInput)` with the exact original object.
372
+ Retain the matching `AgentProviderSessionCleanupError` and its bound `retryCleanup()`; keep
373
+ registry, instance, host resources and installation references until it succeeds. A returned
374
+ session with failed close likewise retains cleanup through that session and its cleanup error.
375
+ An observation timeout cannot detach actual work. Retry joins the same owner without opening
376
+ another session or resending a response. Serialized errors and request IDs cannot restore
377
+ callable cleanup or response authority. Adapter authors preserve failed opening ownership with
378
+ `createAgentProviderSessionCleanupError({ openingInput, providerKey, errors, retryCleanup })`.
293
379
 
294
380
  ## Session-local host tools and instructions
295
381
 
@@ -450,13 +536,29 @@ turn/request ordering, request resolution, steering, interruption, configuration
450
536
  close, and idempotent disposal. Unsupported operations must remain explicit discriminants and
451
537
  must not expose handlers.
452
538
 
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.
539
+ `resolutionFor(request, { signal })` accepts the existing synchronous resolution callback,
540
+ an asynchronous callback, or `undefined` to deliberately leave a request unanswered. Its
541
+ request-scoped signal aborts when the original output stream reports that exact request
542
+ unavailable, when the turn completes, or when conformance cleanup begins. Honor the signal
543
+ to dismiss pending human input; do not fabricate a denial or retry an answer after retirement.
544
+ The runner continuously reads the same execution while callbacks and delivery receipts are
545
+ pending. A returned answer is checked against the exact offered request before delivery.
546
+ `request_resolution` appears in the report only after a delivered receipt;
547
+ `request_unavailability` records consumption of a native retirement event. Opening a request
548
+ alone proves neither. The runner checks session controls before its main turn and inspects
549
+ turn-produced resources after terminal output while the session remains open. It then starts session
550
+ close before joining its reader and actual callback tasks; failure also starts close before joins. Original
551
+ failures and cleanup failures remain observable. A callback that ignores cancellation
552
+ remains owned until its actual promise settles.
553
+
554
+ The coordinated SDK release is `0.4.3` and implements Agent Protocol V12.
555
+ The earlier `0.4.1` release retains V11.
556
+ The earlier `0.4.0` release retains V10.
455
557
  The earlier public `0.3.0` and `0.3.1` releases implemented V9 and remain historical.
456
558
  Keep installed packages in the seven-package SDK release family on the coordinated version; the
457
559
  orchestration and ACP packages remain optional. Every materialized instance requires `environment` and
458
560
  `accountQuota`; every session requires `environment`. The former standalone `integrations`
459
- observation port is removed. The public runtime implements Agent Protocol V11 with one current
561
+ observation port is removed. The public runtime implements Agent Protocol V12 with one current
460
562
  adapter/session surface. Package versions remain independent of private transport and product
461
563
  persistence versions.
462
564
 
@@ -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 {