@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.
- package/README.md +200 -9
- package/dist/adapterValidation.d.ts +4 -2
- package/dist/adapterValidation.js +140 -51
- package/dist/artifacts.js +0 -1
- package/dist/contractErrors.d.ts +2 -2
- package/dist/contractErrors.js +11 -7
- package/dist/hostBindings/opening.d.ts +2 -0
- package/dist/hostBindings/opening.js +58 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/nativeContinuations/admission.d.ts +17 -0
- package/dist/nativeContinuations/admission.js +250 -0
- package/dist/nativeContinuations/index.d.ts +2 -0
- package/dist/nativeContinuations/index.js +1 -0
- package/dist/nativeContinuations/types.d.ts +52 -0
- package/dist/nativeContinuations/types.js +0 -0
- package/dist/outputValidation.js +3 -2
- package/dist/providerCatalog.d.ts +1 -1
- package/dist/providerDriver.d.ts +4 -1
- package/dist/providerDriver.js +0 -1
- package/dist/providerInstanceRegistry.d.ts +4 -4
- package/dist/providerInstanceRegistry.js +2 -1
- package/dist/sessionAdmission.d.ts +12 -0
- package/dist/sessionAdmission.js +54 -0
- package/dist/sessionInteractions/index.d.ts +2 -0
- package/dist/sessionInteractions/index.js +1 -0
- package/dist/sessionInteractions/lifetime.d.ts +81 -0
- package/dist/sessionInteractions/lifetime.js +383 -0
- package/dist/sessionInteractions/request.d.ts +43 -0
- package/dist/sessionInteractions/request.js +381 -0
- package/dist/sessionInteractions/types.d.ts +110 -0
- package/dist/sessionInteractions/types.js +10 -0
- package/dist/sessionInteractions/validation.d.ts +25 -0
- package/dist/sessionInteractions/validation.js +180 -0
- package/dist/sessionValidation.d.ts +82 -3
- package/dist/sessionValidation.js +240 -181
- package/dist/sessions.d.ts +34 -0
- package/dist/testing/conformance.d.ts +3 -1
- package/dist/testing/conformance.js +180 -86
- package/dist/testing/fakeProvider.js +5 -6
- package/dist/turnExecution/execution.d.ts +23 -6
- package/dist/turnExecution/execution.js +136 -32
- package/dist/turnExecution/requests.d.ts +15 -3
- package/dist/turnExecution/requests.js +93 -28
- 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
|
|
13
|
-
`0.4.
|
|
14
|
-
|
|
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
|
|
77
|
-
persistence/scope mode
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
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
|
-
...
|
|
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
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
package/dist/contractErrors.d.ts
CHANGED
|
@@ -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;
|
package/dist/contractErrors.js
CHANGED
|
@@ -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) {
|