@agen-ai/agent-runtime 0.4.0 → 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 (45) hide show
  1. package/README.md +200 -9
  2. package/dist/adapterValidation.d.ts +4 -2
  3. package/dist/adapterValidation.js +140 -51
  4. package/dist/artifacts.js +0 -1
  5. package/dist/contractErrors.d.ts +2 -2
  6. package/dist/contractErrors.js +11 -7
  7. package/dist/hostBindings/opening.d.ts +2 -0
  8. package/dist/hostBindings/opening.js +58 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +2 -0
  11. package/dist/nativeContinuations/admission.d.ts +17 -0
  12. package/dist/nativeContinuations/admission.js +250 -0
  13. package/dist/nativeContinuations/index.d.ts +2 -0
  14. package/dist/nativeContinuations/index.js +1 -0
  15. package/dist/nativeContinuations/types.d.ts +52 -0
  16. package/dist/nativeContinuations/types.js +0 -0
  17. package/dist/outputValidation.js +3 -2
  18. package/dist/providerCatalog.d.ts +1 -1
  19. package/dist/providerDriver.d.ts +4 -1
  20. package/dist/providerDriver.js +0 -1
  21. package/dist/providerInstanceRegistry.d.ts +4 -4
  22. package/dist/providerInstanceRegistry.js +2 -1
  23. package/dist/sessionAdmission.d.ts +12 -0
  24. package/dist/sessionAdmission.js +54 -0
  25. package/dist/sessionInteractions/index.d.ts +2 -0
  26. package/dist/sessionInteractions/index.js +1 -0
  27. package/dist/sessionInteractions/lifetime.d.ts +81 -0
  28. package/dist/sessionInteractions/lifetime.js +383 -0
  29. package/dist/sessionInteractions/request.d.ts +43 -0
  30. package/dist/sessionInteractions/request.js +381 -0
  31. package/dist/sessionInteractions/types.d.ts +110 -0
  32. package/dist/sessionInteractions/types.js +10 -0
  33. package/dist/sessionInteractions/validation.d.ts +25 -0
  34. package/dist/sessionInteractions/validation.js +180 -0
  35. package/dist/sessionValidation.d.ts +82 -3
  36. package/dist/sessionValidation.js +240 -181
  37. package/dist/sessions.d.ts +34 -0
  38. package/dist/testing/conformance.d.ts +3 -1
  39. package/dist/testing/conformance.js +180 -86
  40. package/dist/testing/fakeProvider.js +5 -6
  41. package/dist/turnExecution/execution.d.ts +23 -6
  42. package/dist/turnExecution/execution.js +136 -32
  43. package/dist/turnExecution/requests.d.ts +15 -3
  44. package/dist/turnExecution/requests.js +93 -28
  45. package/package.json +2 -2
package/README.md CHANGED
@@ -9,9 +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 V10 as part of the coordinated SDK
13
- `0.4.0` contract.
14
- 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.
15
20
  The SDK contract version is independent of private transport and product persistence versions. This
16
21
  is intentionally one coordinated tuple rather than a deployable mixed-version graph.
17
22
 
@@ -19,7 +24,7 @@ is intentionally one coordinated tuple rather than a deployable mixed-version gr
19
24
 
20
25
  - `@agen-ai/agent-runtime` exports the public driver, instance, adapter, session, output,
21
26
  readiness, account-quota port validation, bounded-evidence, artifact-candidate,
22
- capability-bound interaction validation, and registry APIs.
27
+ capability-bound interaction validation, session-lifetime interaction and cleanup APIs, and registry APIs.
23
28
  - `@agen-ai/agent-runtime/environment` exports the instance discovery and session observation ports.
24
29
  - `@agen-ai/agent-runtime/testing` exports the deterministic fake provider and reusable
25
30
  conformance runner.
@@ -29,7 +34,8 @@ is intentionally one coordinated tuple rather than a deployable mixed-version gr
29
34
  1. Define an `AgentProviderDriver` with `defineAgentProviderDriver`.
30
35
  2. Give caller-owned instance definitions to `createAgentProviderRegistry`.
31
36
  3. Select a materialized instance by opaque `instanceId`.
32
- 4. Create, resume, or branch a provider session through its adapter.
37
+ 4. Create, resume, or branch a provider session through its adapter. If supported and needed,
38
+ supply the session-interaction handler on that opening input before calling the adapter.
33
39
  5. Call `startTurn` and consume its single `outputs` stream incrementally. Submit human answers with
34
40
  the same execution's `resolveRequest`, which returns an independent delivery receipt. Neutral `AgentEvent`
35
41
  output carries bounded provider source evidence atomically. A `request.opened` output may also
@@ -73,8 +79,19 @@ Explicit iterator return and normal end join native cleanup for at most 1,000 ms
73
79
  Approval continuations are refusal-first. Before delegating to the candidate adapter, the
74
80
  validated session proves that the request is still pending and unexpired and that a selected
75
81
  `optionId` was offered by that exact request. Provider-emitted approval requests must correlate to
76
- a live item or exact proposed-plan artifact, and every option must fit an advertised
77
- persistence/scope mode. Item identities are monotonic throughout a turn, including every request
82
+ a live item or exact proposed-plan artifact. A scoped consequence must fit an exact advertised
83
+ persistence/scope mode; a provider-managed remembrance consequence requires
84
+ `providerManagedRemembrance: true` and retains explicit unknown duration and applicability.
85
+ A delivered answer does not prove that a provider wrote a remembered rule. Exact offered IDs,
86
+ once-only response entry, and uncertain-delivery fencing remain unchanged.
87
+ Present every offered option's decision and consequence, then return the exact option chosen by
88
+ the caller; options with the same immediate decision can have different consequences. A human
89
+ handler should run independently while the original `execution.outputs` reader keeps consuming
90
+ progress. Resolve on that same execution, inspect its receipt, and retain its output and cleanup
91
+ owners through terminal settlement. A remembered answer creates no SDK rule or future automatic
92
+ answer; later requests still need their own offered choice. An uncertain answer must not be replayed.
93
+
94
+ Item identities are monotonic throughout a turn, including every request
78
95
  continuation: an adapter cannot restart an observed identity, change its kind, erase prior progress
79
96
  through an unknown-status update, regress it from in-progress to pending, or revive it after a
80
97
  terminal event or status. An `item.completed` event must carry `completed`, `failed`, or `canceled`
@@ -184,6 +201,164 @@ identity, declared bounds, forward-only lifecycle transitions, and per-output ba
184
201
  the operation result may settle. Collaboration and generated-resource observations retain the
185
202
  same correlation and lifecycle validation at their respective ports.
186
203
 
204
+ ## Session-lifetime interactions
205
+
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:
229
+
230
+ ```ts
231
+ import type { AgentCreateSessionInput, AgentSessionInteraction } from '@agen-ai/agent-runtime';
232
+
233
+ const pending = new Map<string, AgentSessionInteraction>();
234
+ const input: AgentCreateSessionInput = {
235
+ ...openingInput,
236
+ interactions: {
237
+ onRequest(interaction) {
238
+ pending.set(interaction.request.requestId, interaction);
239
+ },
240
+ },
241
+ };
242
+ const session = await adapter.createSession(input);
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.
246
+ ```
247
+
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.
256
+
257
+ `respond` allows one unsettled response attempt per request:
258
+
259
+ | Receipt | Meaning |
260
+ | --- | --- |
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.
292
+
293
+ | Limit | Default | Allowed override |
294
+ | --- | --- | --- |
295
+ | `maxRetainedRequests` | 8 | 1 to 32 |
296
+ | `maxRetainedBytes` | 524,288 bytes | 65,536 to 2,097,152 bytes |
297
+ | `requestTimeoutMs` in bounded mode | 300,000 ms | 1,000 to 1,800,000 ms |
298
+ | `responseTimeoutMs` | 30,000 ms | 1,000 to 120,000 ms |
299
+ | `cleanupTimeoutMs` | 30,000 ms | 1,000 to 120,000 ms |
300
+
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 })`.
361
+
187
362
  ## Session-local host tools and instructions
188
363
 
189
364
  A host can pass an optional `AgentSessionHostBinding` when creating, resuming or branching a
@@ -343,12 +518,28 @@ turn/request ordering, request resolution, steering, interruption, configuration
343
518
  close, and idempotent disposal. Unsupported operations must remain explicit discriminants and
344
519
  must not expose handlers.
345
520
 
346
- The coordinated SDK version is `0.4.0` and implements Agent Protocol 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.
347
538
  The earlier public `0.3.0` and `0.3.1` releases implemented V9 and remain historical.
348
539
  Keep installed packages in the seven-package SDK release family on the coordinated version; the
349
540
  orchestration and ACP packages remain optional. Every materialized instance requires `environment` and
350
541
  `accountQuota`; every session requires `environment`. The former standalone `integrations`
351
- observation port is removed. The public runtime implements Agent Protocol V10 with one current
542
+ observation port is removed. The public runtime implements Agent Protocol V11 with one current
352
543
  adapter/session surface. Package versions remain independent of private transport and product
353
544
  persistence versions.
354
545
 
@@ -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
@@ -16,6 +16,8 @@ import { containsAgentControlCharacter } from "./internal/controlCharacters.js";
16
16
  import { withAgentSessionHostBinding } from "./hostBindings/lifetime.js";
17
17
  import { invokeAgentSessionOpening } from "./hostBindings/opening.js";
18
18
  import { validateAgentSessionHostBindingSupport } from "./hostBindings/validation.js";
19
+ import { createSessionInteractionOwner } from "./sessionInteractions/lifetime.js";
20
+ import { validateSessionInteractionSupport, validateNativeContinuationSupport } from "./sessionInteractions/validation.js";
19
21
  import {
20
22
  validateAgentProviderOperationResult,
21
23
  validateAgentProviderOutputForContext
@@ -23,9 +25,13 @@ import {
23
25
  import {
24
26
  closeRejectedAgentProviderSession,
25
27
  openIdentityCreatingAgentProviderSession,
26
- validateAgentProviderSession
28
+ validateAgentProviderSession,
29
+ createAgentSessionValidationOwner
27
30
  } from "./sessionValidation.js";
28
31
  const validatedAdapters = /* @__PURE__ */ new WeakMap();
32
+ function isCandidateAdapter(adapter) {
33
+ return !validatedAdapters.has(adapter);
34
+ }
29
35
  function parseWorkingDirectory(value) {
30
36
  if (value.length < 1 || value.length > 4096 || !path.isAbsolute(value) || path.normalize(value) !== value || containsAgentControlCharacter(value)) {
31
37
  throw new TypeError(
@@ -46,7 +52,15 @@ function assertAuthenticationOutputKind(providerKey, output) {
46
52
  }
47
53
  function validateAgentProviderAdapter(capabilityInput, adapterInput) {
48
54
  const capabilities = parseAgentCapabilities(capabilityInput);
49
- 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
+ };
50
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")) {
51
65
  throwAgentProviderContractError(
52
66
  capabilities.providerKey,
@@ -62,6 +76,8 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
62
76
  );
63
77
  }
64
78
  const declaredResumption = adapter.resumption;
79
+ const sessionInteractions = validateSessionInteractionSupport(capabilities, adapter);
80
+ const nativeContinuations = validateNativeContinuationSupport(capabilities, adapter, sessionInteractions);
65
81
  let hostBinding;
66
82
  try {
67
83
  hostBinding = validateAgentSessionHostBindingSupport(adapter.hostBinding);
@@ -74,7 +90,7 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
74
90
  }
75
91
  const resumption = declaredResumption.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
76
92
  kind: "supported",
77
- resumeSession: async (resumeInput) => withAgentSessionHostBinding({
93
+ resumeSession: async (resumeInput, invocation = {}) => withAgentSessionHostBinding({
78
94
  providerKey: capabilities.providerKey,
79
95
  binding: resumeInput.hostBinding,
80
96
  openingInput: resumeInput,
@@ -98,10 +114,25 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
98
114
  resumeInput.workingDirectory
99
115
  );
100
116
  const binding = parsed.binding;
117
+ const validationOwner = createAgentSessionValidationOwner(capabilities, parsed.sessionId);
118
+ const interactions = createSessionInteractionOwner({
119
+ providerKey: capabilities.providerKey,
120
+ sessionId: parsed.sessionId,
121
+ operation: "resume",
122
+ support: sessionInteractions,
123
+ nativeSupport: nativeContinuations,
124
+ validationOwner,
125
+ ...resumeInput.nativeContinuations === void 0 ? {} : { continuations: resumeInput.nativeContinuations },
126
+ ...resumeInput.interactions === void 0 ? {} : { context: resumeInput.interactions },
127
+ ...resumeInput.signal === void 0 ? {} : { signal: resumeInput.signal }
128
+ });
129
+ if (interactions) invocation.owner = interactions;
130
+ interactions?.bind();
101
131
  const candidate = await invokeAgentSessionOpening({
102
132
  providerKey: capabilities.providerKey,
103
133
  openingInput: resumeInput,
104
134
  lease,
135
+ ...interactions === void 0 ? {} : { interactions },
105
136
  open: (context) => declaredResumption.resumeSession(context),
106
137
  candidateInput: {
107
138
  sessionId: parsed.sessionId,
@@ -109,23 +140,29 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
109
140
  binding,
110
141
  configuration: parsed.configuration,
111
142
  ...lease === void 0 ? {} : { hostBinding: lease.binding },
112
- ...resumeInput.signal === void 0 ? {} : { signal: resumeInput.signal }
143
+ ...interactions === void 0 ? {} : { interactions: interactions.facade },
144
+ ...(interactions?.openingSignal ?? resumeInput.signal) === void 0 ? {} : { signal: interactions?.openingSignal ?? resumeInput.signal }
113
145
  }
114
146
  });
115
147
  try {
116
- return validateAgentProviderSession({
148
+ const session = validateAgentProviderSession({
117
149
  capabilities,
150
+ validationOwner,
118
151
  sessionId: parsed.sessionId,
119
152
  candidate,
120
153
  expectedBinding: binding,
154
+ ...interactions === void 0 ? {} : { sessionInteractionOwner: interactions },
121
155
  ...lease === void 0 ? {} : { hostBindingLease: lease }
122
156
  });
157
+ interactions?.adopt();
158
+ return session;
123
159
  } catch (error) {
124
160
  return closeRejectedAgentProviderSession(
125
161
  candidate,
126
162
  error,
127
163
  lease === void 0 ? void 0 : { providerKey: capabilities.providerKey, lease },
128
- { providerKey: capabilities.providerKey, input: resumeInput }
164
+ { providerKey: capabilities.providerKey, input: resumeInput },
165
+ interactions
129
166
  );
130
167
  }
131
168
  }
@@ -134,7 +171,7 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
134
171
  const declaredBranching = adapter.branching;
135
172
  const branching = declaredBranching.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
136
173
  kind: "through_turn",
137
- branchSession: async (branchInput) => withAgentSessionHostBinding({
174
+ branchSession: async (branchInput, invocation = {}) => withAgentSessionHostBinding({
138
175
  providerKey: capabilities.providerKey,
139
176
  binding: branchInput.hostBinding,
140
177
  openingInput: branchInput,
@@ -158,17 +195,33 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
158
195
  branchInput.workingDirectory
159
196
  );
160
197
  const sourceBinding = parsed.source.binding;
198
+ const validationOwner = createAgentSessionValidationOwner(capabilities, parsed.sessionId);
199
+ const interactions = createSessionInteractionOwner({
200
+ providerKey: capabilities.providerKey,
201
+ sessionId: parsed.sessionId,
202
+ operation: "branch",
203
+ support: sessionInteractions,
204
+ nativeSupport: nativeContinuations,
205
+ validationOwner,
206
+ ...branchInput.nativeContinuations === void 0 ? {} : { continuations: branchInput.nativeContinuations },
207
+ ...branchInput.interactions === void 0 ? {} : { context: branchInput.interactions },
208
+ ...branchInput.signal === void 0 ? {} : { signal: branchInput.signal }
209
+ });
210
+ if (interactions) invocation.owner = interactions;
161
211
  return openIdentityCreatingAgentProviderSession({
162
212
  capabilities,
213
+ validationOwner,
163
214
  sessionId: parsed.sessionId,
164
215
  observer: branchInput.onBindingCreated,
165
216
  sourceBinding,
166
217
  openingInput: branchInput,
218
+ ...interactions === void 0 ? {} : { sessionInteractionOwner: interactions },
167
219
  ...lease === void 0 ? {} : { hostBindingLease: lease },
168
220
  open: (observer) => invokeAgentSessionOpening({
169
221
  providerKey: capabilities.providerKey,
170
222
  openingInput: branchInput,
171
223
  lease,
224
+ ...interactions === void 0 ? {} : { interactions },
172
225
  open: (context) => declaredBranching.branchSession(context),
173
226
  candidateInput: {
174
227
  sessionId: parsed.sessionId,
@@ -177,7 +230,8 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
177
230
  configuration: parsed.configuration,
178
231
  onBindingCreated: observer,
179
232
  ...lease === void 0 ? {} : { hostBinding: lease.binding },
180
- ...branchInput.signal === void 0 ? {} : { signal: branchInput.signal }
233
+ ...interactions === void 0 ? {} : { interactions: interactions.facade },
234
+ ...(interactions?.openingSignal ?? branchInput.signal) === void 0 ? {} : { signal: interactions?.openingSignal ?? branchInput.signal }
181
235
  }
182
236
  })
183
237
  });
@@ -253,54 +307,89 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
253
307
  return result;
254
308
  }
255
309
  });
256
- const validated = Object.freeze({
257
- hostBinding,
258
- createSession: async (createInput) => withAgentSessionHostBinding({
259
- providerKey: capabilities.providerKey,
260
- binding: createInput.hostBinding,
261
- openingInput: createInput,
262
- support: hostBinding,
263
- open: async (lease) => {
264
- throwIfAgentOperationAborted(createInput.signal);
265
- const parsed = parseAgentSessionOpenInput({
266
- operation: "create",
267
- sessionId: createInput.sessionId,
268
- configuration: createInput.configuration
269
- });
270
- assertAgentSessionConfigurationSupported(
271
- capabilities,
272
- parsed.configuration
273
- );
274
- const workingDirectory = parseWorkingDirectory(
275
- createInput.workingDirectory
276
- );
277
- return openIdentityCreatingAgentProviderSession({
278
- capabilities,
279
- sessionId: parsed.sessionId,
280
- observer: createInput.onBindingCreated,
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({
351
+ providerKey: capabilities.providerKey,
281
352
  openingInput: createInput,
282
- ...lease === void 0 ? {} : { hostBindingLease: lease },
283
- open: (observer) => invokeAgentSessionOpening({
284
- providerKey: capabilities.providerKey,
285
- openingInput: createInput,
286
- lease,
287
- open: (context) => adapter.createSession(context),
288
- candidateInput: {
289
- sessionId: parsed.sessionId,
290
- workingDirectory,
291
- configuration: parsed.configuration,
292
- onBindingCreated: observer,
293
- ...lease === void 0 ? {} : { hostBinding: lease.binding },
294
- ...createInput.signal === void 0 ? {} : { signal: createInput.signal }
295
- }
296
- })
297
- });
298
- }
299
- }),
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,
300
374
  resumption,
301
375
  branching,
302
376
  authentication
303
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
+ });
304
393
  validatedAdapters.set(validated, adapter);
305
394
  return validated;
306
395
  }
package/dist/artifacts.js CHANGED
@@ -18,7 +18,6 @@ class AgentArtifactCandidateError extends TypeError {
18
18
  this.code = code;
19
19
  this.name = "AgentArtifactCandidateError";
20
20
  }
21
- code;
22
21
  }
23
22
  function candidateError(code) {
24
23
  throw new AgentArtifactCandidateError(code);
@@ -1,12 +1,12 @@
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"];
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 {
7
7
  readonly providerKey: AgentProviderKey;
8
8
  readonly code: AgentProviderContractErrorCode;
9
- constructor(providerKey: AgentProviderKey, code: AgentProviderContractErrorCode, message: string);
9
+ constructor(providerKey: AgentProviderKey, code: AgentProviderContractErrorCode, message: string, options?: ErrorOptions);
10
10
  }
11
11
  export declare class AgentProviderSessionCleanupError extends AggregateError {
12
12
  readonly providerKey: AgentProviderKey;
@@ -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",
@@ -36,17 +37,22 @@ const AGENT_PROVIDER_CONTRACT_ERROR_CODES = [
36
37
  "host_tool_name_conflict",
37
38
  "host_tool_input_invalid",
38
39
  "host_tool_result_invalid",
39
- "host_tool_capacity_exhausted"
40
+ "host_tool_capacity_exhausted",
41
+ "invalid_session_interaction",
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"
40
48
  ];
41
49
  class AgentProviderContractError extends Error {
42
- constructor(providerKey, code, message) {
43
- super(message);
50
+ constructor(providerKey, code, message, options) {
51
+ super(message, options);
44
52
  this.providerKey = providerKey;
45
53
  this.code = code;
46
54
  this.name = "AgentProviderContractError";
47
55
  }
48
- providerKey;
49
- code;
50
56
  }
51
57
  class AgentProviderSessionCleanupError extends AggregateError {
52
58
  providerKey;
@@ -86,8 +92,6 @@ class AgentProviderDelegatedOperationError extends Error {
86
92
  this.operation = operation;
87
93
  this.name = "AgentProviderDelegatedOperationError";
88
94
  }
89
- providerKey;
90
- operation;
91
95
  providerExecution = "started";
92
96
  }
93
97
  function throwAgentProviderContractError(providerKey, code, message) {