@agen-ai/agent-runtime 0.4.0 → 0.4.1

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.
package/README.md CHANGED
@@ -9,8 +9,9 @@ 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.
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.
14
15
  Upgrade consumers from `0.3.1` using the coordinated release and its migration guide.
15
16
  The SDK contract version is independent of private transport and product persistence versions. This
16
17
  is intentionally one coordinated tuple rather than a deployable mixed-version graph.
@@ -19,7 +20,7 @@ is intentionally one coordinated tuple rather than a deployable mixed-version gr
19
20
 
20
21
  - `@agen-ai/agent-runtime` exports the public driver, instance, adapter, session, output,
21
22
  readiness, account-quota port validation, bounded-evidence, artifact-candidate,
22
- capability-bound interaction validation, and registry APIs.
23
+ capability-bound interaction validation, session-lifetime interaction and cleanup APIs, and registry APIs.
23
24
  - `@agen-ai/agent-runtime/environment` exports the instance discovery and session observation ports.
24
25
  - `@agen-ai/agent-runtime/testing` exports the deterministic fake provider and reusable
25
26
  conformance runner.
@@ -29,7 +30,8 @@ is intentionally one coordinated tuple rather than a deployable mixed-version gr
29
30
  1. Define an `AgentProviderDriver` with `defineAgentProviderDriver`.
30
31
  2. Give caller-owned instance definitions to `createAgentProviderRegistry`.
31
32
  3. Select a materialized instance by opaque `instanceId`.
32
- 4. Create, resume, or branch a provider session through its adapter.
33
+ 4. Create, resume, or branch a provider session through its adapter. If supported and needed,
34
+ supply the session-interaction handler on that opening input before calling the adapter.
33
35
  5. Call `startTurn` and consume its single `outputs` stream incrementally. Submit human answers with
34
36
  the same execution's `resolveRequest`, which returns an independent delivery receipt. Neutral `AgentEvent`
35
37
  output carries bounded provider source evidence atomically. A `request.opened` output may also
@@ -73,8 +75,19 @@ Explicit iterator return and normal end join native cleanup for at most 1,000 ms
73
75
  Approval continuations are refusal-first. Before delegating to the candidate adapter, the
74
76
  validated session proves that the request is still pending and unexpired and that a selected
75
77
  `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
78
+ a live item or exact proposed-plan artifact. A scoped consequence must fit an exact advertised
79
+ persistence/scope mode; a provider-managed remembrance consequence requires
80
+ `providerManagedRemembrance: true` and retains explicit unknown duration and applicability.
81
+ A delivered answer does not prove that a provider wrote a remembered rule. Exact offered IDs,
82
+ once-only response entry, and uncertain-delivery fencing remain unchanged.
83
+ Present every offered option's decision and consequence, then return the exact option chosen by
84
+ the caller; options with the same immediate decision can have different consequences. A human
85
+ handler should run independently while the original `execution.outputs` reader keeps consuming
86
+ progress. Resolve on that same execution, inspect its receipt, and retain its output and cleanup
87
+ owners through terminal settlement. A remembered answer creates no SDK rule or future automatic
88
+ answer; later requests still need their own offered choice. An uncertain answer must not be replayed.
89
+
90
+ Item identities are monotonic throughout a turn, including every request
78
91
  continuation: an adapter cannot restart an observed identity, change its kind, erase prior progress
79
92
  through an unknown-status update, regress it from in-progress to pending, or revive it after a
80
93
  terminal event or status. An `item.completed` event must carry `completed`, `failed`, or `canceled`
@@ -184,6 +197,100 @@ identity, declared bounds, forward-only lifecycle transitions, and per-output ba
184
197
  the operation result may settle. Collaboration and generated-resource observations retain the
185
198
  same correlation and lifecycle validation at their respective ports.
186
199
 
200
+ ## Session-lifetime interactions
201
+
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:
218
+
219
+ ```ts
220
+ import type { AgentProviderCreateSessionInput, AgentSessionInteraction } from '@agen-ai/agent-runtime';
221
+
222
+ const pending = new Map<string, AgentSessionInteraction>();
223
+ const input: AgentProviderCreateSessionInput = {
224
+ ...openingInput,
225
+ interactions: {
226
+ onRequest(interaction) {
227
+ pending.set(interaction.request.requestId, interaction);
228
+ },
229
+ },
230
+ };
231
+ 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.
235
+ ```
236
+
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.
243
+
244
+ `respond` validates the choice against this exact request and allows one active response:
245
+
246
+ | Receipt | Meaning |
247
+ | --- | --- |
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:
267
+
268
+ | Limit | Default | Allowed override |
269
+ | --- | --- | --- |
270
+ | `maxRetainedRequests` | 8 | 1 to 32 |
271
+ | `maxRetainedBytes` | 524,288 bytes | 65,536 to 2,097,152 bytes |
272
+ | `requestTimeoutMs` | 300,000 ms | 1,000 to 1,800,000 ms |
273
+ | `responseTimeoutMs` | 30,000 ms | 1,000 to 120,000 ms |
274
+ | `cleanupTimeoutMs` | 30,000 ms | 1,000 to 120,000 ms |
275
+
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.
293
+
187
294
  ## Session-local host tools and instructions
188
295
 
189
296
  A host can pass an optional `AgentSessionHostBinding` when creating, resuming or branching a
@@ -343,12 +450,13 @@ turn/request ordering, request resolution, steering, interruption, configuration
343
450
  close, and idempotent disposal. Unsupported operations must remain explicit discriminants and
344
451
  must not expose handlers.
345
452
 
346
- The coordinated SDK version is `0.4.0` and implements Agent Protocol V10.
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.
347
455
  The earlier public `0.3.0` and `0.3.1` releases implemented V9 and remain historical.
348
456
  Keep installed packages in the seven-package SDK release family on the coordinated version; the
349
457
  orchestration and ACP packages remain optional. Every materialized instance requires `environment` and
350
458
  `accountQuota`; every session requires `environment`. The former standalone `integrations`
351
- observation port is removed. The public runtime implements Agent Protocol V10 with one current
459
+ observation port is removed. The public runtime implements Agent Protocol V11 with one current
352
460
  adapter/session surface. Package versions remain independent of private transport and product
353
461
  persistence versions.
354
462
 
@@ -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 } from "./sessionInteractions/validation.js";
19
21
  import {
20
22
  validateAgentProviderOperationResult,
21
23
  validateAgentProviderOutputForContext
@@ -62,6 +64,7 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
62
64
  );
63
65
  }
64
66
  const declaredResumption = adapter.resumption;
67
+ const sessionInteractions = validateSessionInteractionSupport(capabilities, adapter);
65
68
  let hostBinding;
66
69
  try {
67
70
  hostBinding = validateAgentSessionHostBindingSupport(adapter.hostBinding);
@@ -98,10 +101,19 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
98
101
  resumeInput.workingDirectory
99
102
  );
100
103
  const binding = parsed.binding;
104
+ const interactions = createSessionInteractionOwner({
105
+ providerKey: capabilities.providerKey,
106
+ sessionId: parsed.sessionId,
107
+ operation: "resume",
108
+ support: sessionInteractions,
109
+ ...resumeInput.interactions === void 0 ? {} : { context: resumeInput.interactions },
110
+ ...resumeInput.signal === void 0 ? {} : { signal: resumeInput.signal }
111
+ });
101
112
  const candidate = await invokeAgentSessionOpening({
102
113
  providerKey: capabilities.providerKey,
103
114
  openingInput: resumeInput,
104
115
  lease,
116
+ ...interactions === void 0 ? {} : { interactions },
105
117
  open: (context) => declaredResumption.resumeSession(context),
106
118
  candidateInput: {
107
119
  sessionId: parsed.sessionId,
@@ -109,23 +121,28 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
109
121
  binding,
110
122
  configuration: parsed.configuration,
111
123
  ...lease === void 0 ? {} : { hostBinding: lease.binding },
112
- ...resumeInput.signal === void 0 ? {} : { signal: resumeInput.signal }
124
+ ...interactions === void 0 ? {} : { interactions: interactions.facade },
125
+ ...(interactions?.openingSignal ?? resumeInput.signal) === void 0 ? {} : { signal: interactions?.openingSignal ?? resumeInput.signal }
113
126
  }
114
127
  });
115
128
  try {
116
- return validateAgentProviderSession({
129
+ const session = validateAgentProviderSession({
117
130
  capabilities,
118
131
  sessionId: parsed.sessionId,
119
132
  candidate,
120
133
  expectedBinding: binding,
134
+ ...interactions === void 0 ? {} : { sessionInteractionOwner: interactions },
121
135
  ...lease === void 0 ? {} : { hostBindingLease: lease }
122
136
  });
137
+ interactions?.adopt();
138
+ return session;
123
139
  } catch (error) {
124
140
  return closeRejectedAgentProviderSession(
125
141
  candidate,
126
142
  error,
127
143
  lease === void 0 ? void 0 : { providerKey: capabilities.providerKey, lease },
128
- { providerKey: capabilities.providerKey, input: resumeInput }
144
+ { providerKey: capabilities.providerKey, input: resumeInput },
145
+ interactions
129
146
  );
130
147
  }
131
148
  }
@@ -158,17 +175,27 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
158
175
  branchInput.workingDirectory
159
176
  );
160
177
  const sourceBinding = parsed.source.binding;
178
+ const interactions = createSessionInteractionOwner({
179
+ providerKey: capabilities.providerKey,
180
+ sessionId: parsed.sessionId,
181
+ operation: "branch",
182
+ support: sessionInteractions,
183
+ ...branchInput.interactions === void 0 ? {} : { context: branchInput.interactions },
184
+ ...branchInput.signal === void 0 ? {} : { signal: branchInput.signal }
185
+ });
161
186
  return openIdentityCreatingAgentProviderSession({
162
187
  capabilities,
163
188
  sessionId: parsed.sessionId,
164
189
  observer: branchInput.onBindingCreated,
165
190
  sourceBinding,
166
191
  openingInput: branchInput,
192
+ ...interactions === void 0 ? {} : { sessionInteractionOwner: interactions },
167
193
  ...lease === void 0 ? {} : { hostBindingLease: lease },
168
194
  open: (observer) => invokeAgentSessionOpening({
169
195
  providerKey: capabilities.providerKey,
170
196
  openingInput: branchInput,
171
197
  lease,
198
+ ...interactions === void 0 ? {} : { interactions },
172
199
  open: (context) => declaredBranching.branchSession(context),
173
200
  candidateInput: {
174
201
  sessionId: parsed.sessionId,
@@ -177,7 +204,8 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
177
204
  configuration: parsed.configuration,
178
205
  onBindingCreated: observer,
179
206
  ...lease === void 0 ? {} : { hostBinding: lease.binding },
180
- ...branchInput.signal === void 0 ? {} : { signal: branchInput.signal }
207
+ ...interactions === void 0 ? {} : { interactions: interactions.facade },
208
+ ...(interactions?.openingSignal ?? branchInput.signal) === void 0 ? {} : { signal: interactions?.openingSignal ?? branchInput.signal }
181
209
  }
182
210
  })
183
211
  });
@@ -255,6 +283,7 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
255
283
  });
256
284
  const validated = Object.freeze({
257
285
  hostBinding,
286
+ sessionInteractions,
258
287
  createSession: async (createInput) => withAgentSessionHostBinding({
259
288
  providerKey: capabilities.providerKey,
260
289
  binding: createInput.hostBinding,
@@ -274,16 +303,26 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
274
303
  const workingDirectory = parseWorkingDirectory(
275
304
  createInput.workingDirectory
276
305
  );
306
+ const interactions = createSessionInteractionOwner({
307
+ 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
+ });
277
314
  return openIdentityCreatingAgentProviderSession({
278
315
  capabilities,
279
316
  sessionId: parsed.sessionId,
280
317
  observer: createInput.onBindingCreated,
281
318
  openingInput: createInput,
319
+ ...interactions === void 0 ? {} : { sessionInteractionOwner: interactions },
282
320
  ...lease === void 0 ? {} : { hostBindingLease: lease },
283
321
  open: (observer) => invokeAgentSessionOpening({
284
322
  providerKey: capabilities.providerKey,
285
323
  openingInput: createInput,
286
324
  lease,
325
+ ...interactions === void 0 ? {} : { interactions },
287
326
  open: (context) => adapter.createSession(context),
288
327
  candidateInput: {
289
328
  sessionId: parsed.sessionId,
@@ -291,7 +330,8 @@ function validateAgentProviderAdapter(capabilityInput, adapterInput) {
291
330
  configuration: parsed.configuration,
292
331
  onBindingCreated: observer,
293
332
  ...lease === void 0 ? {} : { hostBinding: lease.binding },
294
- ...createInput.signal === void 0 ? {} : { signal: createInput.signal }
333
+ ...interactions === void 0 ? {} : { interactions: interactions.facade },
334
+ ...(interactions?.openingSignal ?? createInput.signal) === void 0 ? {} : { signal: interactions?.openingSignal ?? createInput.signal }
295
335
  }
296
336
  })
297
337
  });
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", "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"];
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;
@@ -36,17 +36,17 @@ const AGENT_PROVIDER_CONTRACT_ERROR_CODES = [
36
36
  "host_tool_name_conflict",
37
37
  "host_tool_input_invalid",
38
38
  "host_tool_result_invalid",
39
- "host_tool_capacity_exhausted"
39
+ "host_tool_capacity_exhausted",
40
+ "invalid_session_interaction",
41
+ "session_interactions_unsupported"
40
42
  ];
41
43
  class AgentProviderContractError extends Error {
42
- constructor(providerKey, code, message) {
43
- super(message);
44
+ constructor(providerKey, code, message, options) {
45
+ super(message, options);
44
46
  this.providerKey = providerKey;
45
47
  this.code = code;
46
48
  this.name = "AgentProviderContractError";
47
49
  }
48
- providerKey;
49
- code;
50
50
  }
51
51
  class AgentProviderSessionCleanupError extends AggregateError {
52
52
  providerKey;
@@ -86,8 +86,6 @@ class AgentProviderDelegatedOperationError extends Error {
86
86
  this.operation = operation;
87
87
  this.name = "AgentProviderDelegatedOperationError";
88
88
  }
89
- providerKey;
90
- operation;
91
89
  providerExecution = "started";
92
90
  }
93
91
  function throwAgentProviderContractError(providerKey, code, message) {
@@ -1,12 +1,14 @@
1
1
  import type { AgentProviderKey } from "@agen-ai/agent-protocol";
2
2
  import type { MaybePromise } from "../foundation.js";
3
3
  import type { AgentProviderSession, AgentProviderSessionContext } from "../sessions.js";
4
+ import type { SessionInteractionOwner } from "../sessionInteractions/lifetime.js";
4
5
  import { type AgentHostBindingLease } from "./lifetime.js";
5
6
  export declare function invokeAgentSessionOpening<Input extends AgentProviderSessionContext>(input: Readonly<{
6
7
  providerKey: AgentProviderKey;
7
8
  openingInput: object;
8
9
  candidateInput: Input;
9
10
  lease: AgentHostBindingLease | undefined;
11
+ interactions?: SessionInteractionOwner;
10
12
  open: (context: Input) => MaybePromise<AgentProviderSession>;
11
13
  }>): Promise<AgentProviderSession>;
12
14
  //# sourceMappingURL=opening.d.ts.map
@@ -2,6 +2,7 @@ import { AgentProviderSessionCleanupError } from "../contractErrors.js";
2
2
  import { associateAgentSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "../sessionCleanup.js";
3
3
  import { createAgentHostBindingCleanupError } from "./lifetime.js";
4
4
  async function invokeAgentSessionOpening(input) {
5
+ if (input.interactions) return invokeInteractiveSessionOpening({ ...input, interactions: input.interactions });
5
6
  try {
6
7
  return await input.open(input.candidateInput);
7
8
  } catch (error) {
@@ -19,6 +20,63 @@ async function invokeAgentSessionOpening(input) {
19
20
  throw associateAgentSessionCleanupError(retained, input.openingInput);
20
21
  }
21
22
  }
23
+ async function invokeInteractiveSessionOpening(input) {
24
+ let actual;
25
+ try {
26
+ input.interactions.requireUsable();
27
+ actual = Promise.resolve(input.open(input.candidateInput));
28
+ } catch (cause) {
29
+ actual = Promise.reject(cause);
30
+ }
31
+ void actual.catch(() => {
32
+ });
33
+ try {
34
+ const candidate = await Promise.race([actual, input.interactions.failure.then((cause) => {
35
+ throw cause;
36
+ })]);
37
+ input.interactions.requireUsable();
38
+ return candidate;
39
+ } catch (error) {
40
+ input.interactions.stop();
41
+ input.lease?.stop();
42
+ let providerClosed = false;
43
+ let nativeCleanup;
44
+ let nativeCleanupRetained = false;
45
+ const cleanup = async () => {
46
+ let candidate;
47
+ try {
48
+ candidate = await actual;
49
+ } catch (cause) {
50
+ if (isAgentProviderSessionCleanupErrorFor(cause, input.candidateInput) && cause.providerKey === input.providerKey) {
51
+ nativeCleanup = cause;
52
+ if (!nativeCleanupRetained) {
53
+ nativeCleanupRetained = true;
54
+ throw cause;
55
+ }
56
+ }
57
+ }
58
+ if (!providerClosed) {
59
+ if (nativeCleanup) await nativeCleanup.retryCleanup();
60
+ else if (candidate) await candidate.close({ reason: "contract_rejected" });
61
+ providerClosed = true;
62
+ }
63
+ await input.interactions.join();
64
+ await input.lease?.release();
65
+ };
66
+ try {
67
+ await input.interactions.close(cleanup, false);
68
+ } catch (cleanupError) {
69
+ const ownership = {
70
+ providerKey: input.providerKey,
71
+ errors: [error, cleanupError],
72
+ retryCleanup: () => input.interactions.close(cleanup, false)
73
+ };
74
+ const retained = input.lease ? createAgentHostBindingCleanupError({ ...ownership, lease: input.lease }) : new AgentProviderSessionCleanupError(ownership);
75
+ throw associateAgentSessionCleanupError(retained, input.openingInput);
76
+ }
77
+ throw error;
78
+ }
79
+ }
22
80
  export {
23
81
  invokeAgentSessionOpening
24
82
  };
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export * from "./providerDriver.js";
13
13
  export * from "./providerInstanceRegistry.js";
14
14
  export * from "./readiness.js";
15
15
  export * from "./sessions.js";
16
+ export * from "./sessionInteractions/index.js";
16
17
  export { createAgentProviderSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "./sessionCleanup.js";
17
18
  export * from "./text.js";
18
19
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ export * from "./providerDriver.js";
13
13
  export * from "./providerInstanceRegistry.js";
14
14
  export * from "./readiness.js";
15
15
  export * from "./sessions.js";
16
+ export * from "./sessionInteractions/index.js";
16
17
  import { createAgentProviderSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "./sessionCleanup.js";
17
18
  export * from "./text.js";
18
19
  export {
@@ -56,8 +56,8 @@ function isApprovalRequestCapabilitySupported(capabilities, event) {
56
56
  if (request.requestKind !== "approval") return false;
57
57
  const capability = capabilities.requests.approval;
58
58
  return capability.kind === "supported" && request.options.every(
59
- (option) => capability.modes.some(
60
- (mode) => mode.persistence === option.persistence && mode.scopeKinds.includes(option.scope.kind)
59
+ ({ consequence }) => consequence.kind === "provider_managed_remembrance" ? capability.providerManagedRemembrance : capability.modes.some(
60
+ (mode) => mode.persistence === consequence.persistence && mode.scopeKinds.includes(consequence.scope.kind)
61
61
  )
62
62
  );
63
63
  }
@@ -11,7 +11,6 @@ class AgentProviderConfigurationError extends Error {
11
11
  this.providerKey = providerKey;
12
12
  this.name = "AgentProviderConfigurationError";
13
13
  }
14
- providerKey;
15
14
  }
16
15
  function defineAgentProviderDriver(definition) {
17
16
  const providerKey = parseAgentProviderKey(definition.providerKey);
@@ -213,7 +213,8 @@ async function materializeInstances(input) {
213
213
  providerKey: definition.providerKey,
214
214
  instanceId: definition.instanceId,
215
215
  cleanupFailureInstanceIds,
216
- message: error instanceof AgentProviderConfigurationError ? `Agent provider configuration is invalid for ${definition.instanceId}.` : `Agent provider instance ${definition.instanceId} could not be materialized.`
216
+ message: error instanceof AgentProviderConfigurationError ? `Agent provider configuration is invalid for ${definition.instanceId}.` : `Agent provider instance ${definition.instanceId} could not be materialized.`,
217
+ cause: error
217
218
  });
218
219
  }
219
220
  let managed;
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ export * from "./types.js";
@@ -0,0 +1,52 @@
1
+ import type { AgentError, AgentProviderKey, AgentSessionId } from "@agen-ai/agent-protocol";
2
+ import type { MaybePromise } from "../foundation.js";
3
+ import type { AgentSessionInteractionContext, AgentSessionInteractionOpeningOperation, AgentSessionInteractionSupport, AgentSessionInteractionTerminal } from "./types.js";
4
+ import { SessionInteractionRequest } from "./request.js";
5
+ import { type ValidatedSessionInteractionContext } from "./validation.js";
6
+ export declare function sessionInteractionError(code: string, message: string, uncertain?: boolean): AgentError;
7
+ export declare function waitForInteractionWork<Result>(work: Promise<Result>, timeoutMs: number): Promise<Result>;
8
+ export declare class SessionInteractionOwner {
9
+ readonly providerKey: AgentProviderKey;
10
+ readonly sessionId: AgentSessionId;
11
+ readonly facade: AgentSessionInteractionContext;
12
+ readonly openingSignal: AbortSignal;
13
+ readonly limits: ValidatedSessionInteractionContext["limits"];
14
+ readonly failure: Promise<unknown>;
15
+ private readonly controller;
16
+ private readonly requests;
17
+ private readonly work;
18
+ private state;
19
+ private retainedBytes;
20
+ private fatal;
21
+ private fenceCause;
22
+ private resolveFailure;
23
+ private removeOpeningAbort;
24
+ private automaticClose;
25
+ private cleanupAttempt;
26
+ private cleanupDeadline;
27
+ private cleanupComplete;
28
+ private cleanupFailure;
29
+ private adopted;
30
+ private readonly context;
31
+ constructor(providerKey: AgentProviderKey, sessionId: AgentSessionId, context: AgentSessionInteractionContext, signal?: AbortSignal);
32
+ track<Result>(promise: Promise<Result>): Promise<Result>;
33
+ get accepting(): boolean;
34
+ requireUsable(): void;
35
+ private offer;
36
+ installClose(close: () => Promise<void>): void;
37
+ adopt(): void;
38
+ fence(cause: unknown, source: SessionInteractionRequest): void;
39
+ fail(cause: unknown): void;
40
+ stop(terminal?: AgentSessionInteractionTerminal): void;
41
+ join(): Promise<void>;
42
+ close(cleanup: () => MaybePromise<void>, explicit: boolean): Promise<void>;
43
+ }
44
+ export declare function createSessionInteractionOwner(input: Readonly<{
45
+ providerKey: AgentProviderKey;
46
+ sessionId: AgentSessionId;
47
+ operation: AgentSessionInteractionOpeningOperation;
48
+ support: AgentSessionInteractionSupport;
49
+ context?: AgentSessionInteractionContext;
50
+ signal?: AbortSignal;
51
+ }>): SessionInteractionOwner | undefined;
52
+ //# sourceMappingURL=lifetime.d.ts.map