@agen-ai/agent-runtime 0.3.1 → 0.4.0
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 +176 -25
- package/dist/adapterValidation.d.ts +1 -1
- package/dist/adapterValidation.js +162 -95
- package/dist/contractErrors.d.ts +15 -2
- package/dist/contractErrors.js +39 -1
- package/dist/hostBindings/index.d.ts +4 -0
- package/dist/hostBindings/index.js +9 -0
- package/dist/hostBindings/lifetime.d.ts +25 -0
- package/dist/hostBindings/lifetime.js +147 -0
- package/dist/hostBindings/opening.d.ts +12 -0
- package/dist/hostBindings/opening.js +24 -0
- package/dist/hostBindings/schema.d.ts +9 -0
- package/dist/hostBindings/schema.js +169 -0
- package/dist/hostBindings/types.d.ts +71 -0
- package/dist/hostBindings/types.js +0 -0
- package/dist/hostBindings/validation.d.ts +14 -0
- package/dist/hostBindings/validation.js +107 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/internal/objectShape.d.ts +2 -0
- package/dist/internal/objectShape.js +7 -0
- package/dist/outputValidation.js +1 -0
- package/dist/sessionCleanup.d.ts +9 -0
- package/dist/sessionCleanup.js +17 -0
- package/dist/sessionValidation.d.ts +12 -2
- package/dist/sessionValidation.js +134 -305
- package/dist/sessions.d.ts +28 -4
- package/dist/testing/conformance.d.ts +3 -3
- package/dist/testing/conformance.js +24 -30
- package/dist/testing/fakeProvider.js +56 -45
- package/dist/turnExecution/execution.d.ts +26 -0
- package/dist/turnExecution/execution.js +214 -0
- package/dist/turnExecution/receipts.d.ts +6 -0
- package/dist/turnExecution/receipts.js +39 -0
- package/dist/turnExecution/requests.d.ts +19 -0
- package/dist/turnExecution/requests.js +56 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,8 +8,10 @@ sessions; each returned session owns its binding and conversation-local operatio
|
|
|
8
8
|
|
|
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
|
-
leases, or storage policy. A host must authorize and select an instance before calling this SPI.
|
|
12
|
-
This source package implements Agent Protocol
|
|
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.
|
|
13
15
|
The SDK contract version is independent of private transport and product persistence versions. This
|
|
14
16
|
is intentionally one coordinated tuple rather than a deployable mixed-version graph.
|
|
15
17
|
|
|
@@ -28,7 +30,8 @@ is intentionally one coordinated tuple rather than a deployable mixed-version gr
|
|
|
28
30
|
2. Give caller-owned instance definitions to `createAgentProviderRegistry`.
|
|
29
31
|
3. Select a materialized instance by opaque `instanceId`.
|
|
30
32
|
4. Create, resume, or branch a provider session through its adapter.
|
|
31
|
-
5.
|
|
33
|
+
5. Call `startTurn` and consume its single `outputs` stream incrementally. Submit human answers with
|
|
34
|
+
the same execution's `resolveRequest`, which returns an independent delivery receipt. Neutral `AgentEvent`
|
|
32
35
|
output carries bounded provider source evidence atomically. A `request.opened` output may also
|
|
33
36
|
carry separately bounded, non-truncated provider request context for continuation; lifecycle,
|
|
34
37
|
authentication, artifact, and standalone diagnostic evidence remain separate output variants.
|
|
@@ -77,26 +80,73 @@ through an unknown-status update, regress it from in-progress to pending, or rev
|
|
|
77
80
|
terminal event or status. An `item.completed` event must carry `completed`, `failed`, or `canceled`
|
|
78
81
|
status. Completion-only snapshots remain valid when no earlier lifecycle event was observed.
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
A session reserves one turn execution synchronously at `startTurn`. Native execution stays lazy until
|
|
84
|
+
its first output pull; an unused handle has no provider effects. Separate sessions run concurrently.
|
|
85
|
+
The execution's `outputs` has exactly one consumer and one outstanding pull. Its ownership continues
|
|
86
|
+
across human requests until native terminal output, entered response/control settlement, and cleanup.
|
|
82
87
|
|
|
83
88
|
Materialization-scoped context usage remains monotonic only within one native provider process. A
|
|
84
89
|
provider that replaces that process emits a `process.started` lifecycle output as the explicit
|
|
85
90
|
boundary before reporting usage from the replacement. Validation then clears only the
|
|
86
91
|
`materialization` occupancy, cumulative counters, and compaction allowance; logical `session`
|
|
87
92
|
measurements remain continuous.
|
|
88
|
-
`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
`startTurn(input)` returns `{ turnId, outputs, resolveRequest }`. `outputs` preserves per-output
|
|
94
|
+
backpressure and stays open during `waiting_for_request`; the consumer can call
|
|
95
|
+
`execution.resolveRequest({ resolution })` as soon as it receives `request.opened`, before its next
|
|
96
|
+
pull. A later queued waiting observation cannot revive an already answered request. No response
|
|
97
|
+
creates or acquires another output stream.
|
|
98
|
+
|
|
99
|
+
`resolveRequest` returns one of these delivery receipts:
|
|
100
|
+
|
|
101
|
+
- `{ status: "delivered" }`: the provider-owned handler accepted the exact canonical answer or step.
|
|
102
|
+
This includes a mapped form-intent step whose later field answer is still pending. It does not
|
|
103
|
+
establish a universal transport write, model consumption, effect completion, or turn success.
|
|
104
|
+
- `{ status: "rejected", requestState: "pending" | "unavailable", error }`: the provider definitively
|
|
105
|
+
refused this answer. Only `pending` preserves that exact callback's answerability; `unavailable`
|
|
106
|
+
retires it. Neither value terminalizes the original output owner.
|
|
107
|
+
- `{ status: "delivery_uncertain", error }`: native delivery started without definitive acceptance
|
|
108
|
+
or refusal. Further mutation and replay are forbidden; the original output/cleanup owner remains.
|
|
109
|
+
|
|
110
|
+
Local parsing, request correlation, expiry, and pre-abort failures reject with their precise original
|
|
111
|
+
errors before candidate entry. Validation atomically claims an answer before invoking the optional
|
|
112
|
+
synchronous `onProviderExecutionStarted` observer and then the candidate. The runtime strips this
|
|
113
|
+
observer from candidate input. A throwing observer prevents native entry and preserves the request
|
|
114
|
+
only while it remains answerable. An unexpected candidate response exception carries
|
|
115
|
+
`AgentProviderDelegatedOperationError` with `operation: "resolve_request"` and started-execution evidence;
|
|
116
|
+
it also forbids further mutation. Concurrent duplicate answers cannot enter the provider twice.
|
|
117
|
+
|
|
118
|
+
The turn input has the same synchronous start observer at its lazy candidate-entry boundary, after
|
|
119
|
+
prechecks pass. Abort or iterator return before the first pull releases the reservation without
|
|
120
|
+
poisoning the session. An aborted old handle rejects on a later pull. Abandoning an entered output
|
|
121
|
+
consumer stops admission and closes its native owner; an incomplete stream makes the session unusable.
|
|
122
|
+
Interruption remains a session control: dispatch acceptance leaves the original stream open to
|
|
123
|
+
observe actual native cancellation completion. Its `AgentProviderTurnInterruptionResult` carries only
|
|
124
|
+
status and optional error; interruption cannot return turn output. A reserved turn rejects interruption
|
|
125
|
+
with `active_turn_mismatch`, including after iterator acquisition; the first pull must enter the provider.
|
|
126
|
+
Abort or return cancels a reservation locally. Admitted interruption suspends exact request admission;
|
|
127
|
+
definitive failed interruptions restore only the unchanged live callback after every overlapping
|
|
128
|
+
interruption fails. A concurrent response refusal with `requestState: "pending"` retains that callback
|
|
129
|
+
through suspension; an accepted interruption withdraws it regardless of response completion order.
|
|
130
|
+
|
|
131
|
+
Close first stops admission, signals cancellation, and initiates native quiescence; it then joins
|
|
132
|
+
entered output/response work and drains host callbacks before releasing resources. Native close is
|
|
133
|
+
initiated before joining acknowledgments that depend on it. A failed close keeps the stopped owner
|
|
134
|
+
and host binding retained while close remains retryable. Successful close and disposal are idempotent.
|
|
135
|
+
|
|
136
|
+
Ordinary answers past a request's `expiresAt` fail locally. A canonical `canceled` resolution still
|
|
137
|
+
checks the live handler's availability; expiry alone cannot establish delivery. Terminal observation
|
|
138
|
+
fences new answers even while an entered response is settling, and waiting observations cannot
|
|
139
|
+
reopen an older request. A host must cancel human decisions when their execution ends and join
|
|
140
|
+
those tasks during cleanup. Keep the session or exact opening-cleanup receipt if cleanup fails;
|
|
141
|
+
disposing its registry or host resources before quiescence would lose the retained owner.
|
|
142
|
+
|
|
143
|
+
`runtime.retrying` reports a retry already owned by the provider on the active turn. It preserves
|
|
144
|
+
turn ownership, pending requests, and per-output backpressure; it neither starts another execution
|
|
145
|
+
nor completes the turn. It is rejected before `turn.started`, after `turn.completed`, after a
|
|
146
|
+
`final_diff` terminal materialization, and from session-operation observations. The event's neutral
|
|
147
|
+
failure category and recovery hint describe the observed condition. A recovery hint never authorizes
|
|
148
|
+
replaying work or changes an uncertain execution outcome. Contract failures, delegated-operation
|
|
149
|
+
started evidence, and retained cleanup responsibilities remain separate from those failure values.
|
|
100
150
|
|
|
101
151
|
When context usage is advertised, the validated session enforces the declared measurement scopes
|
|
102
152
|
and cumulative fields across turns. Identical consecutive samples and decreasing cumulative
|
|
@@ -104,13 +154,19 @@ counters are rejected. Occupancy may decrease only after a completed advertised
|
|
|
104
154
|
the next accepted sample consumes that allowance. Context output remains subject to the same
|
|
105
155
|
per-output backpressure and terminal ordering as every other provider event.
|
|
106
156
|
|
|
107
|
-
When `capabilities.turns.steer` is
|
|
157
|
+
When `capabilities.turns.steer.kind` is `supported`, the session exposes `steering.steerTurn`. Steering accepts
|
|
108
158
|
the existing turn ID plus the same canonical `parts` and optional `summary` used to start a turn.
|
|
109
|
-
It
|
|
110
|
-
|
|
159
|
+
It requires provider entry on the first output pull; a reserved or merely acquired execution rejects
|
|
160
|
+
steering with `active_turn_mismatch` without calling the provider.
|
|
161
|
+
It does not create or own an output stream: model output continues on the original execution's
|
|
162
|
+
`outputs` iterator. The receipt is exactly `delivered`, `rejected` with a bounded neutral error, or
|
|
111
163
|
`delivery_uncertain` with a bounded neutral error. A provider must use uncertainty when delivery
|
|
112
164
|
may have started but its authoritative acknowledgement was lost; callers cannot safely replay that
|
|
113
|
-
result.
|
|
165
|
+
result. A returned `delivery_uncertain` makes the validated session unusable for further mutation.
|
|
166
|
+
Keep its original output reader owned while it drains toward terminality; a pending request does not
|
|
167
|
+
end that stream or restore mutation authority. Close the unusable session to quiesce work that cannot
|
|
168
|
+
finish without another response, then rematerialize before further work. The validated runtime preserves pre-delegation
|
|
169
|
+
validation and abort errors unchanged, while
|
|
114
170
|
provider-delegated steering failures throw `AgentProviderDelegatedOperationError` with the original
|
|
115
171
|
cause and explicit started-execution evidence so a host can retire the unusable session. Platform
|
|
116
172
|
scheduling and provider-native queue modes are intentionally outside this SPI.
|
|
@@ -128,6 +184,73 @@ identity, declared bounds, forward-only lifecycle transitions, and per-output ba
|
|
|
128
184
|
the operation result may settle. Collaboration and generated-resource observations retain the
|
|
129
185
|
same correlation and lifecycle validation at their respective ports.
|
|
130
186
|
|
|
187
|
+
## Session-local host tools and instructions
|
|
188
|
+
|
|
189
|
+
A host can pass an optional `AgentSessionHostBinding` when creating, resuming or branching a
|
|
190
|
+
session. The binding contains tool definitions, an invocation callback, optional instructions and
|
|
191
|
+
an idempotent `release` callback. Its callbacks close over host authorization; model arguments do
|
|
192
|
+
not supply that authority. This is an additive runtime SPI. It does not enable managed delegation
|
|
193
|
+
or change native collaboration by itself.
|
|
194
|
+
|
|
195
|
+
The optional `@agen-ai/agent-orchestration` package sits above this SPI. It owns managed target policy,
|
|
196
|
+
scoped tools and generated delegation guidance; the runtime has no dependency on that package or
|
|
197
|
+
its explicit MCP transport. A parent's end-turn boundary leaves its session binding alive and does
|
|
198
|
+
not cancel independently owned children. AgenAI Workspaces preferences and Hostd production
|
|
199
|
+
integration remain separate product work.
|
|
200
|
+
|
|
201
|
+
An adapter declares `hostBinding` support, its `callback` or `mcp` transport, and the instruction
|
|
202
|
+
placements it can materialize. Missing support means unsupported. `system` and `developer`
|
|
203
|
+
placements require the corresponding trusted native channels; `tool_context` means tool/server
|
|
204
|
+
guidance. The runtime rejects unsupported requests before native session creation. Adapter authors
|
|
205
|
+
must reject collisions with their native tools and actually install the requested binding.
|
|
206
|
+
|
|
207
|
+
Tools use a bounded, closed JSON Schema 2020-12 subset: objects with declared properties and no
|
|
208
|
+
additional properties, bounded arrays and strings, finite numbers, safe integers, booleans and
|
|
209
|
+
null. Root schemas declare the dialect. References, remote loading, regular expressions, coercion
|
|
210
|
+
and executable validation are unsupported. A binding allows up to 32 tools, 64 KiB of definition
|
|
211
|
+
data, 32 KiB argument/result envelopes and 16 concurrent callbacks. Runtime validation precedes
|
|
212
|
+
callback entry and validates returned results; callback exceptions become sanitized tool errors.
|
|
213
|
+
|
|
214
|
+
Every opening needs a fresh binding object, including resume and branch. The validated opening
|
|
215
|
+
claims a minimally valid binding before other opening checks. After that claim, opening failures
|
|
216
|
+
own cleanup; malformed minimal bindings remain caller-owned and a duplicate claim cannot release
|
|
217
|
+
the original session's binding. `validateAgentSessionHostBinding` validates metadata without claiming it. Explicit transport
|
|
218
|
+
adapters can reuse `validateAgentHostToolInvocation({ binding, invocation })` against that validated
|
|
219
|
+
metadata and `validateAgentHostToolResult(value)` without duplicating the bounded schema interpreter.
|
|
220
|
+
These pure validators do not invoke callbacks, claim leases or provide lifecycle ownership.
|
|
221
|
+
|
|
222
|
+
If a provider opening fails before returning a session and native resources are still pending,
|
|
223
|
+
throw `createAgentProviderSessionCleanupError({ providerKey, openingInput, errors, retryCleanup })`.
|
|
224
|
+
`openingInput` must be the exact context object received by the create/resume/branch port. The
|
|
225
|
+
validated adapter adopts only that exact identity and provider key, stops new host callbacks, and
|
|
226
|
+
retains the original binding. Explicit cleanup retry first quiesces provider resources and then
|
|
227
|
+
releases the binding; propagation does not add another native cleanup attempt. Successful native
|
|
228
|
+
cleanup is retained when only host release needs retry. A plain or foreign cleanup error cannot
|
|
229
|
+
claim the current opening. Callers can use `isAgentProviderSessionCleanupErrorFor(error, input)`
|
|
230
|
+
against their original opening object to identify the retained responsibility.
|
|
231
|
+
|
|
232
|
+
Closing stops new invocations and aborts active callback signals, then joins provider cleanup and
|
|
233
|
+
callback settlement before releasing the host binding. Ending a turn does not release it. Providers
|
|
234
|
+
must quiesce their output/control producers before their close succeeds and clean up partial native
|
|
235
|
+
resources before throwing an opening error without a session. Preserve callback method receivers.
|
|
236
|
+
Hosts must close their sessions before disposing the instance or registry. Registry disposal retains
|
|
237
|
+
its existing instance ownership and does not inventory or close caller-owned session bindings.
|
|
238
|
+
|
|
239
|
+
Cleanup failure leaves authority closed and cleanup retryable. An opened session retries through
|
|
240
|
+
`session.close`. If opening fails and cleanup also fails, catch
|
|
241
|
+
`AgentProviderSessionCleanupError` and check
|
|
242
|
+
`isAgentProviderSessionCleanupErrorFor(error, originalOpeningInput)` before assigning its retained
|
|
243
|
+
handle to an opening. Use a distinct input object for each opening and preserve the exact object
|
|
244
|
+
passed to create, resume or branch; copied inputs
|
|
245
|
+
and constructed errors do not match the runtime's private ownership evidence. This also covers
|
|
246
|
+
invalid candidates without host tools. `AgentProviderHostBindingCleanupError` remains the compatible
|
|
247
|
+
subclass for failures involving a claimed host binding. A provider can propagate another opening's
|
|
248
|
+
error, so `instanceof` alone does not establish ownership. Call the matching error's single-flight
|
|
249
|
+
`retryCleanup()` to finish retained cleanup. It closes the exact rejected session before releasing
|
|
250
|
+
the binding; a completed close is not repeated when only host release failed. Keep this error and
|
|
251
|
+
its diagnostic causes on the trusted host boundary. Never serialize its handle or raw diagnostics
|
|
252
|
+
into a model tool result.
|
|
253
|
+
|
|
131
254
|
## Minimal driver
|
|
132
255
|
|
|
133
256
|
```ts
|
|
@@ -178,6 +301,19 @@ The runtime distinguishes programmer/contract failures from provider failures:
|
|
|
178
301
|
Provider-native exceptions should be normalized or safely wrapped at the provider boundary. Do
|
|
179
302
|
not attach credentials, raw prompts, product identities, or unbounded output to public errors.
|
|
180
303
|
|
|
304
|
+
Use `AgentError.category`, `code` and `recovery` to describe an expected operational condition.
|
|
305
|
+
They do not replace `AgentProviderContractError`, delegated-operation start evidence, or cleanup
|
|
306
|
+
exceptions. A `runtime.error` observation is not a terminal event; `runtime.retrying` keeps the same
|
|
307
|
+
turn active. Render a definitive failed turn from its `turn.completed` result instead of counting
|
|
308
|
+
each error observation as another failure. A transient classification or a quota reset never proves
|
|
309
|
+
that prior tool effects can be repeated.
|
|
310
|
+
|
|
311
|
+
Cancellation must retain the current stream and resource owners until native settlement and callback
|
|
312
|
+
quiescence are established. An accepted interruption acknowledges dispatch; it does not establish
|
|
313
|
+
cancellation completion. If the outcome remains unknown, preserve that uncertainty, close the unusable
|
|
314
|
+
session and retain any failed cleanup handle for explicit retry. Successful cleanup does not settle
|
|
315
|
+
the uncertain operation or authorize resending it.
|
|
316
|
+
|
|
181
317
|
## Scoped environment observation
|
|
182
318
|
|
|
183
319
|
`MaterializedAgentProviderInstance.environment` discovers a caller-selected workspace and
|
|
@@ -207,11 +343,12 @@ turn/request ordering, request resolution, steering, interruption, configuration
|
|
|
207
343
|
close, and idempotent disposal. Unsupported operations must remain explicit discriminants and
|
|
208
344
|
must not expose handlers.
|
|
209
345
|
|
|
210
|
-
The coordinated SDK version
|
|
211
|
-
|
|
212
|
-
|
|
346
|
+
The coordinated SDK version is `0.4.0` and implements Agent Protocol V10.
|
|
347
|
+
The earlier public `0.3.0` and `0.3.1` releases implemented V9 and remain historical.
|
|
348
|
+
Keep installed packages in the seven-package SDK release family on the coordinated version; the
|
|
349
|
+
orchestration and ACP packages remain optional. Every materialized instance requires `environment` and
|
|
213
350
|
`accountQuota`; every session requires `environment`. The former standalone `integrations`
|
|
214
|
-
observation port is removed. The public runtime implements Agent Protocol
|
|
351
|
+
observation port is removed. The public runtime implements Agent Protocol V10 with one current
|
|
215
352
|
adapter/session surface. Package versions remain independent of private transport and product
|
|
216
353
|
persistence versions.
|
|
217
354
|
|
|
@@ -222,3 +359,17 @@ From the public repository root, run `pnpm check`.
|
|
|
222
359
|
It installs tarballs into a temporary project outside the workspace, typechecks every documented
|
|
223
360
|
entrypoint, runs the fake provider and parser flow, and removes the temporary directory. It never
|
|
224
361
|
publishes or accesses release credentials.
|
|
362
|
+
|
|
363
|
+
The public repository also contains `examples/delegation`: a shared host composition with direct
|
|
364
|
+
and credentialless ACP workflows plus a separately qualified SQLite/inbox CLI. From that root,
|
|
365
|
+
`pnpm examples:delegation` exercises real steering, queued resume, cancellation and live approval;
|
|
366
|
+
`pnpm examples:delegation:acp` invokes scoped MCP tools and demonstrates a child surviving its
|
|
367
|
+
parent's end-turn boundary. The installed-tarball consumer runs the same source. Synthetic evidence
|
|
368
|
+
does not certify native agents or authorize replay of an uncertain turn.
|
|
369
|
+
|
|
370
|
+
For the smallest runnable lifecycle example, use
|
|
371
|
+
[`examples/live-request-output.ts`](../../examples/live-request-output.ts) in the public repository
|
|
372
|
+
with `pnpm examples:live-request-output`. It observes unrelated output before answering an exact
|
|
373
|
+
request, with one reader and joined cleanup. The public
|
|
374
|
+
[migration guide](../../MIGRATING-TO-0.4.md#continuous-turn-output-and-independent-answers) shows the
|
|
375
|
+
published-`0.3.1` before-example and an independent human-handler pattern for `0.4.0`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type AgentCapabilities } from "@agen-ai/agent-protocol";
|
|
2
2
|
import type { AgentProviderAdapter } from "./sessions.js";
|
|
3
|
-
export declare function validateAgentProviderAdapter(capabilityInput: AgentCapabilities,
|
|
3
|
+
export declare function validateAgentProviderAdapter(capabilityInput: AgentCapabilities, adapterInput: AgentProviderAdapter): AgentProviderAdapter;
|
|
4
4
|
//# sourceMappingURL=adapterValidation.d.ts.map
|
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
throwIfAgentOperationAborted
|
|
14
14
|
} from "./foundation.js";
|
|
15
15
|
import { containsAgentControlCharacter } from "./internal/controlCharacters.js";
|
|
16
|
+
import { withAgentSessionHostBinding } from "./hostBindings/lifetime.js";
|
|
17
|
+
import { invokeAgentSessionOpening } from "./hostBindings/opening.js";
|
|
18
|
+
import { validateAgentSessionHostBindingSupport } from "./hostBindings/validation.js";
|
|
16
19
|
import {
|
|
17
20
|
validateAgentProviderOperationResult,
|
|
18
21
|
validateAgentProviderOutputForContext
|
|
@@ -22,6 +25,7 @@ import {
|
|
|
22
25
|
openIdentityCreatingAgentProviderSession,
|
|
23
26
|
validateAgentProviderSession
|
|
24
27
|
} from "./sessionValidation.js";
|
|
28
|
+
const validatedAdapters = /* @__PURE__ */ new WeakMap();
|
|
25
29
|
function parseWorkingDirectory(value) {
|
|
26
30
|
if (value.length < 1 || value.length > 4096 || !path.isAbsolute(value) || path.normalize(value) !== value || containsAgentControlCharacter(value)) {
|
|
27
31
|
throw new TypeError(
|
|
@@ -40,8 +44,9 @@ function assertAuthenticationOutputKind(providerKey, output) {
|
|
|
40
44
|
"Authentication may emit only authentication, lifecycle, or evidence output."
|
|
41
45
|
);
|
|
42
46
|
}
|
|
43
|
-
function validateAgentProviderAdapter(capabilityInput,
|
|
47
|
+
function validateAgentProviderAdapter(capabilityInput, adapterInput) {
|
|
44
48
|
const capabilities = parseAgentCapabilities(capabilityInput);
|
|
49
|
+
const adapter = validatedAdapters.get(adapterInput) ?? adapterInput;
|
|
45
50
|
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")) {
|
|
46
51
|
throwAgentProviderContractError(
|
|
47
52
|
capabilities.providerKey,
|
|
@@ -57,83 +62,127 @@ function validateAgentProviderAdapter(capabilityInput, adapter) {
|
|
|
57
62
|
);
|
|
58
63
|
}
|
|
59
64
|
const declaredResumption = adapter.resumption;
|
|
65
|
+
let hostBinding;
|
|
66
|
+
try {
|
|
67
|
+
hostBinding = validateAgentSessionHostBindingSupport(adapter.hostBinding);
|
|
68
|
+
} catch {
|
|
69
|
+
return throwAgentProviderContractError(
|
|
70
|
+
capabilities.providerKey,
|
|
71
|
+
"invalid_adapter",
|
|
72
|
+
"Provider declared invalid session host binding support."
|
|
73
|
+
);
|
|
74
|
+
}
|
|
60
75
|
const resumption = declaredResumption.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
|
|
61
76
|
kind: "supported",
|
|
62
|
-
resumeSession: async (resumeInput) => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
parsed.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
);
|
|
80
|
-
const binding = parsed.binding;
|
|
81
|
-
const candidate = await declaredResumption.resumeSession({
|
|
82
|
-
sessionId: parsed.sessionId,
|
|
83
|
-
workingDirectory,
|
|
84
|
-
binding,
|
|
85
|
-
configuration: parsed.configuration,
|
|
86
|
-
...resumeInput.signal === void 0 ? {} : { signal: resumeInput.signal }
|
|
87
|
-
});
|
|
88
|
-
try {
|
|
89
|
-
return validateAgentProviderSession({
|
|
77
|
+
resumeSession: async (resumeInput) => withAgentSessionHostBinding({
|
|
78
|
+
providerKey: capabilities.providerKey,
|
|
79
|
+
binding: resumeInput.hostBinding,
|
|
80
|
+
openingInput: resumeInput,
|
|
81
|
+
support: hostBinding,
|
|
82
|
+
open: async (lease) => {
|
|
83
|
+
throwIfAgentOperationAborted(resumeInput.signal);
|
|
84
|
+
const parsed = parseAgentSessionOpenInput({
|
|
85
|
+
operation: "resume",
|
|
86
|
+
sessionId: resumeInput.sessionId,
|
|
87
|
+
binding: resumeInput.binding,
|
|
88
|
+
configuration: resumeInput.configuration
|
|
89
|
+
});
|
|
90
|
+
if (parsed.operation !== "resume") {
|
|
91
|
+
throw new TypeError("Expected a resume session input.");
|
|
92
|
+
}
|
|
93
|
+
assertAgentSessionConfigurationSupported(
|
|
90
94
|
capabilities,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
parsed.configuration
|
|
96
|
+
);
|
|
97
|
+
const workingDirectory = parseWorkingDirectory(
|
|
98
|
+
resumeInput.workingDirectory
|
|
99
|
+
);
|
|
100
|
+
const binding = parsed.binding;
|
|
101
|
+
const candidate = await invokeAgentSessionOpening({
|
|
102
|
+
providerKey: capabilities.providerKey,
|
|
103
|
+
openingInput: resumeInput,
|
|
104
|
+
lease,
|
|
105
|
+
open: (context) => declaredResumption.resumeSession(context),
|
|
106
|
+
candidateInput: {
|
|
107
|
+
sessionId: parsed.sessionId,
|
|
108
|
+
workingDirectory,
|
|
109
|
+
binding,
|
|
110
|
+
configuration: parsed.configuration,
|
|
111
|
+
...lease === void 0 ? {} : { hostBinding: lease.binding },
|
|
112
|
+
...resumeInput.signal === void 0 ? {} : { signal: resumeInput.signal }
|
|
113
|
+
}
|
|
94
114
|
});
|
|
95
|
-
|
|
96
|
-
|
|
115
|
+
try {
|
|
116
|
+
return validateAgentProviderSession({
|
|
117
|
+
capabilities,
|
|
118
|
+
sessionId: parsed.sessionId,
|
|
119
|
+
candidate,
|
|
120
|
+
expectedBinding: binding,
|
|
121
|
+
...lease === void 0 ? {} : { hostBindingLease: lease }
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return closeRejectedAgentProviderSession(
|
|
125
|
+
candidate,
|
|
126
|
+
error,
|
|
127
|
+
lease === void 0 ? void 0 : { providerKey: capabilities.providerKey, lease },
|
|
128
|
+
{ providerKey: capabilities.providerKey, input: resumeInput }
|
|
129
|
+
);
|
|
130
|
+
}
|
|
97
131
|
}
|
|
98
|
-
}
|
|
132
|
+
})
|
|
99
133
|
});
|
|
100
134
|
const declaredBranching = adapter.branching;
|
|
101
135
|
const branching = declaredBranching.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
|
|
102
136
|
kind: "through_turn",
|
|
103
|
-
branchSession: async (branchInput) => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
parsed.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
sourceBinding
|
|
127
|
-
|
|
137
|
+
branchSession: async (branchInput) => withAgentSessionHostBinding({
|
|
138
|
+
providerKey: capabilities.providerKey,
|
|
139
|
+
binding: branchInput.hostBinding,
|
|
140
|
+
openingInput: branchInput,
|
|
141
|
+
support: hostBinding,
|
|
142
|
+
open: async (lease) => {
|
|
143
|
+
throwIfAgentOperationAborted(branchInput.signal);
|
|
144
|
+
const parsed = parseAgentSessionOpenInput({
|
|
145
|
+
operation: "branch",
|
|
146
|
+
sessionId: branchInput.sessionId,
|
|
147
|
+
source: branchInput.source,
|
|
148
|
+
configuration: branchInput.configuration
|
|
149
|
+
});
|
|
150
|
+
if (parsed.operation !== "branch") {
|
|
151
|
+
throw new TypeError("Expected a branch session input.");
|
|
152
|
+
}
|
|
153
|
+
assertAgentSessionConfigurationSupported(
|
|
154
|
+
capabilities,
|
|
155
|
+
parsed.configuration
|
|
156
|
+
);
|
|
157
|
+
const workingDirectory = parseWorkingDirectory(
|
|
158
|
+
branchInput.workingDirectory
|
|
159
|
+
);
|
|
160
|
+
const sourceBinding = parsed.source.binding;
|
|
161
|
+
return openIdentityCreatingAgentProviderSession({
|
|
162
|
+
capabilities,
|
|
128
163
|
sessionId: parsed.sessionId,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
164
|
+
observer: branchInput.onBindingCreated,
|
|
165
|
+
sourceBinding,
|
|
166
|
+
openingInput: branchInput,
|
|
167
|
+
...lease === void 0 ? {} : { hostBindingLease: lease },
|
|
168
|
+
open: (observer) => invokeAgentSessionOpening({
|
|
169
|
+
providerKey: capabilities.providerKey,
|
|
170
|
+
openingInput: branchInput,
|
|
171
|
+
lease,
|
|
172
|
+
open: (context) => declaredBranching.branchSession(context),
|
|
173
|
+
candidateInput: {
|
|
174
|
+
sessionId: parsed.sessionId,
|
|
175
|
+
workingDirectory,
|
|
176
|
+
source: parsed.source,
|
|
177
|
+
configuration: parsed.configuration,
|
|
178
|
+
onBindingCreated: observer,
|
|
179
|
+
...lease === void 0 ? {} : { hostBinding: lease.binding },
|
|
180
|
+
...branchInput.signal === void 0 ? {} : { signal: branchInput.signal }
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
})
|
|
137
186
|
});
|
|
138
187
|
const declaredAuthentication = adapter.authentication;
|
|
139
188
|
const authenticationFlows = capabilities.authentication.kind === "supported" ? capabilities.authentication.flows : [];
|
|
@@ -204,38 +253,56 @@ function validateAgentProviderAdapter(capabilityInput, adapter) {
|
|
|
204
253
|
return result;
|
|
205
254
|
}
|
|
206
255
|
});
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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,
|
|
227
279
|
sessionId: parsed.sessionId,
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
280
|
+
observer: createInput.onBindingCreated,
|
|
281
|
+
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
|
+
}),
|
|
235
300
|
resumption,
|
|
236
301
|
branching,
|
|
237
302
|
authentication
|
|
238
303
|
});
|
|
304
|
+
validatedAdapters.set(validated, adapter);
|
|
305
|
+
return validated;
|
|
239
306
|
}
|
|
240
307
|
export {
|
|
241
308
|
validateAgentProviderAdapter
|
package/dist/contractErrors.d.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
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
4
|
export type AgentProviderContractErrorCode = (typeof AGENT_PROVIDER_CONTRACT_ERROR_CODES)[number];
|
|
4
|
-
export type AgentProviderDelegatedOperation = "steer_turn" | "invoke_operation" | "apply_configuration" | "spawn_collaboration" | "control_collaboration";
|
|
5
|
+
export type AgentProviderDelegatedOperation = "resolve_request" | "steer_turn" | "invoke_operation" | "apply_configuration" | "spawn_collaboration" | "control_collaboration";
|
|
5
6
|
export declare class AgentProviderContractError extends Error {
|
|
6
7
|
readonly providerKey: AgentProviderKey;
|
|
7
8
|
readonly code: AgentProviderContractErrorCode;
|
|
8
9
|
constructor(providerKey: AgentProviderKey, code: AgentProviderContractErrorCode, message: string);
|
|
9
10
|
}
|
|
11
|
+
export declare class AgentProviderSessionCleanupError extends AggregateError {
|
|
12
|
+
readonly providerKey: AgentProviderKey;
|
|
13
|
+
readonly retryCleanup: () => Promise<void>;
|
|
14
|
+
constructor(input: Readonly<{
|
|
15
|
+
providerKey: AgentProviderKey;
|
|
16
|
+
errors: readonly unknown[];
|
|
17
|
+
retryCleanup: () => MaybePromise<void>;
|
|
18
|
+
}>);
|
|
19
|
+
}
|
|
20
|
+
export declare class AgentProviderHostBindingCleanupError extends AgentProviderSessionCleanupError {
|
|
21
|
+
constructor(input: ConstructorParameters<typeof AgentProviderSessionCleanupError>[0]);
|
|
22
|
+
}
|
|
10
23
|
export declare class AgentProviderDelegatedOperationError extends Error {
|
|
11
24
|
readonly providerKey: AgentProviderKey;
|
|
12
25
|
readonly operation: AgentProviderDelegatedOperation;
|