@agen-ai/agent-runtime 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +316 -10
  2. package/dist/adapterValidation.js +38 -4
  3. package/dist/contractErrors.d.ts +2 -2
  4. package/dist/contractErrors.js +8 -1
  5. package/dist/executionInput/index.d.ts +3 -0
  6. package/dist/executionInput/index.js +4 -0
  7. package/dist/executionInput/lifetime.d.ts +47 -0
  8. package/dist/executionInput/lifetime.js +241 -0
  9. package/dist/executionInput/types.d.ts +61 -0
  10. package/dist/executionInput/types.js +0 -0
  11. package/dist/executionInput/validation.d.ts +25 -0
  12. package/dist/executionInput/validation.js +178 -0
  13. package/dist/generation/cleanup.d.ts +15 -0
  14. package/dist/generation/cleanup.js +108 -0
  15. package/dist/generation/deadline.d.ts +16 -0
  16. package/dist/generation/deadline.js +81 -0
  17. package/dist/generation/decoding.d.ts +4 -0
  18. package/dist/generation/decoding.js +59 -0
  19. package/dist/generation/errors.d.ts +29 -0
  20. package/dist/generation/errors.js +39 -0
  21. package/dist/generation/generateObject.d.ts +3 -0
  22. package/dist/generation/generateObject.js +148 -0
  23. package/dist/generation/index.d.ts +5 -0
  24. package/dist/generation/index.js +8 -0
  25. package/dist/generation/preflight.d.ts +25 -0
  26. package/dist/generation/preflight.js +48 -0
  27. package/dist/generation/requests.d.ts +17 -0
  28. package/dist/generation/requests.js +117 -0
  29. package/dist/generation/types.d.ts +22 -0
  30. package/dist/generation/types.js +0 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.js +7 -1
  33. package/dist/nativeContinuations/admission.js +1 -0
  34. package/dist/outputValidation.d.ts +2 -0
  35. package/dist/outputValidation.js +29 -1
  36. package/dist/sessionAdmission.d.ts +2 -0
  37. package/dist/sessionAdmission.js +13 -0
  38. package/dist/sessionInteractions/lifetime.d.ts +1 -0
  39. package/dist/sessionInteractions/lifetime.js +4 -0
  40. package/dist/sessionInteractions/request.d.ts +1 -0
  41. package/dist/sessionInteractions/request.js +3 -0
  42. package/dist/sessionValidation.d.ts +15 -3
  43. package/dist/sessionValidation.js +215 -127
  44. package/dist/sessions.d.ts +7 -1
  45. package/dist/structuredOutput/admission.d.ts +16 -0
  46. package/dist/structuredOutput/admission.js +89 -0
  47. package/dist/structuredOutput/candidate.d.ts +22 -0
  48. package/dist/structuredOutput/candidate.js +64 -0
  49. package/dist/structuredOutput/validation.d.ts +8 -0
  50. package/dist/structuredOutput/validation.js +26 -0
  51. package/dist/testing/conformance.d.ts +20 -1
  52. package/dist/testing/conformance.js +103 -3
  53. package/dist/testing/fakeProvider.js +5 -4
  54. package/dist/turnExecution/execution.d.ts +9 -1
  55. package/dist/turnExecution/execution.js +88 -7
  56. package/dist/turnExecution/terminalBuffer.d.ts +9 -0
  57. package/dist/turnExecution/terminalBuffer.js +38 -0
  58. package/package.json +6 -2
@@ -5,8 +5,11 @@ import {
5
5
  import { AgentProviderContractError, AgentProviderDelegatedOperationError, throwAgentProviderContractError } from "../contractErrors.js";
6
6
  import { throwIfAgentOperationAborted } from "../foundation.js";
7
7
  import { hasExactOwnKeys } from "../internal/objectShape.js";
8
+ import { validateAgentExecutionInputPort } from "../executionInput/validation.js";
9
+ import { createExecutionInputOwner } from "../executionInput/lifetime.js";
8
10
  import { validateAgentRequestResolutionResult } from "./receipts.js";
9
11
  import { createAgentTurnRequestAdmission } from "./requests.js";
12
+ import { createAgentTurnTerminalBuffer } from "./terminalBuffer.js";
10
13
  function createValidatedAgentTurnExecution(input) {
11
14
  return createExecution(input, { kind: "caller", input: input.turnInput, start: input.start });
12
15
  }
@@ -17,14 +20,17 @@ function createExecution(input, origin) {
17
20
  const turnId = origin.kind === "caller" ? origin.input.turnId : origin.turnId;
18
21
  const callerSignal = origin.kind === "caller" ? origin.input.signal : void 0;
19
22
  const lifetime = new AbortController();
23
+ let inputOwner;
20
24
  const validateCandidate = (candidate) => {
21
- if (candidate === null || typeof candidate !== "object" || candidate.turnId !== turnId || !hasExactOwnKeys(candidate, ["turnId", "outputs", "resolveRequest"]) || typeof candidate.resolveRequest !== "function" || !candidate.outputs || typeof candidate.outputs[Symbol.asyncIterator] !== "function") {
25
+ if (candidate === null || typeof candidate !== "object" || candidate.turnId !== turnId || !hasExactOwnKeys(candidate, ["turnId", "outputs", "resolveRequest", ...Object.hasOwn(candidate, "input") ? ["input"] : []]) || typeof candidate.resolveRequest !== "function" || !candidate.outputs || typeof candidate.outputs[Symbol.asyncIterator] !== "function") {
22
26
  throwAgentProviderContractError(
23
27
  input.providerKey,
24
28
  origin.kind === "native" ? "invalid_native_continuation" : "invalid_operation_result",
25
29
  "Provider returned an invalid turn execution."
26
30
  );
27
31
  }
32
+ const port = validateAgentExecutionInputPort(candidate.input, input.executionInputSupport, input.providerKey);
33
+ if (port?.kind === "supported") inputOwner?.attach(port);
28
34
  return candidate;
29
35
  };
30
36
  let state;
@@ -54,6 +60,8 @@ function createExecution(input, origin) {
54
60
  resolveDisposition = resolve;
55
61
  });
56
62
  let source;
63
+ let sourceExhausted = false;
64
+ let terminalTail;
57
65
  const requests = createAgentTurnRequestAdmission({
58
66
  providerKey: input.providerKey,
59
67
  sessionAdmission: input.sessionAdmission,
@@ -61,6 +69,40 @@ function createExecution(input, origin) {
61
69
  canRestore: () => state.kind === "entered" && !input.isTerminal(),
62
70
  canRestoreRequest: input.canRestoreRequest
63
71
  });
72
+ let resolvingRequests = 0;
73
+ if (origin.kind === "caller" && input.executionInputSupport?.kind === "supported") {
74
+ inputOwner = createExecutionInputOwner({
75
+ providerKey: input.providerKey,
76
+ turnId,
77
+ support: input.executionInputSupport,
78
+ census: input.executionInputCensus,
79
+ signal: lifetime.signal,
80
+ fence: input.retire,
81
+ requireAdmission() {
82
+ input.requireUsable();
83
+ if (state.kind !== "entered" || !input.ownsExecution() || input.isTerminal()) {
84
+ throwAgentProviderContractError(
85
+ input.providerKey,
86
+ "active_turn_mismatch",
87
+ "Execution input does not identify the exact active entered execution."
88
+ );
89
+ }
90
+ if (input.structuredOutputBound) throwAgentProviderContractError(
91
+ input.providerKey,
92
+ "structured_output_unsupported",
93
+ "Schema-bound execution cannot admit additional input."
94
+ );
95
+ input.requireInputContext();
96
+ if (requests.hasUnclaimedRequest() || resolvingRequests > 0) {
97
+ throwAgentProviderContractError(
98
+ input.providerKey,
99
+ "request_pending",
100
+ "Execution input cannot enter while a human request or its response remains unsettled."
101
+ );
102
+ }
103
+ }
104
+ });
105
+ }
64
106
  let acquired = false;
65
107
  let pulling = false;
66
108
  let returnPromise;
@@ -75,8 +117,11 @@ function createExecution(input, origin) {
75
117
  return new DOMException("The turn execution was stopped.", "AbortError");
76
118
  };
77
119
  const detachAbort = () => callerSignal?.removeEventListener("abort", abort);
78
- const stop = (reason) => {
120
+ const stop = (reason, inputReason = "native_failed") => {
79
121
  if (state.kind === "settled" || state.kind === "stopped") return;
122
+ inputOwner?.closeAdmission(inputReason);
123
+ terminalTail?.clear();
124
+ terminalTail = void 0;
80
125
  const entered = dispatched || state.kind === "entered" || state.kind === "entering";
81
126
  state = { kind: "stopped", reason };
82
127
  requests.withdraw();
@@ -97,7 +142,7 @@ function createExecution(input, origin) {
97
142
  } catch (error) {
98
143
  if (error instanceof Error) reason = error;
99
144
  }
100
- stop(reason);
145
+ stop(reason, "interrupted");
101
146
  };
102
147
  callerSignal?.addEventListener("abort", abort, { once: true });
103
148
  const track = (operation) => {
@@ -105,6 +150,7 @@ function createExecution(input, origin) {
105
150
  void operation.then(() => nativeOperations.delete(operation), () => nativeOperations.delete(operation));
106
151
  return operation;
107
152
  };
153
+ const pullNative = () => track(Promise.resolve().then(() => state.kind === "entered" ? sourceExhausted ? { done: true, value: void 0 } : state.iterator.next() : origin.kind === "native" && state.kind === "settled" ? { done: true, value: void 0 } : Promise.reject(abortError())));
108
154
  let joinPromise;
109
155
  const join = () => {
110
156
  if (joinPromise) return joinPromise;
@@ -113,6 +159,7 @@ function createExecution(input, origin) {
113
159
  await Promise.allSettled([...nativeOperations, ...input.inFlightOperations]);
114
160
  }
115
161
  if (source?.return) await source.return();
162
+ await inputOwner?.join();
116
163
  })();
117
164
  joinPromise = attempt;
118
165
  void attempt.catch(() => {
@@ -150,6 +197,7 @@ function createExecution(input, origin) {
150
197
  settle = resolve;
151
198
  });
152
199
  input.inFlightOperations.add(settlement);
200
+ resolvingRequests += 1;
153
201
  try {
154
202
  try {
155
203
  requestInput.onProviderExecutionStarted?.();
@@ -174,6 +222,7 @@ function createExecution(input, origin) {
174
222
  if (result.status === "delivery_uncertain") input.retire();
175
223
  return result;
176
224
  } finally {
225
+ resolvingRequests -= 1;
177
226
  input.inFlightOperations.delete(settlement);
178
227
  settle();
179
228
  }
@@ -186,6 +235,12 @@ function createExecution(input, origin) {
186
235
  let removeAbort = () => {
187
236
  };
188
237
  try {
238
+ if (terminalTail) {
239
+ throwIfAgentOperationAborted(lifetime.signal);
240
+ const trailing = terminalTail.take();
241
+ if (trailing !== void 0) return { done: false, value: trailing };
242
+ terminalTail = void 0;
243
+ }
189
244
  if (state.kind === "parked") {
190
245
  ready();
191
246
  await dispatch;
@@ -224,7 +279,7 @@ function createExecution(input, origin) {
224
279
  }
225
280
  }
226
281
  if (state.kind !== "entered") throw abortError();
227
- const nativePull = track(Promise.resolve().then(() => state.kind === "entered" ? state.iterator.next() : origin.kind === "native" && state.kind === "settled" ? { done: true, value: void 0 } : Promise.reject(abortError())));
282
+ const nativePull = pullNative();
228
283
  const stopped = new Promise((_, reject) => {
229
284
  const onAbort = () => reject(abortError());
230
285
  lifetime.signal.addEventListener("abort", onAbort, { once: true });
@@ -236,6 +291,24 @@ function createExecution(input, origin) {
236
291
  if (origin.kind === "native" && currentState().kind === "settled") throwAgentProviderContractError(input.providerKey, "invalid_native_continuation", "A no-work execution emitted native output.");
237
292
  observedOutput = true;
238
293
  const output = input.observe(result.value, requests);
294
+ if (inputOwner && output.kind === "event" && output.event.type === "turn.completed") {
295
+ inputOwner.closeAdmission();
296
+ const buffer = createAgentTurnTerminalBuffer(input.providerKey, output);
297
+ const exhaustSource = (async () => {
298
+ for (; ; ) {
299
+ const trailing = await Promise.race([pullNative(), stopped]);
300
+ throwIfAgentOperationAborted(lifetime.signal);
301
+ if (trailing.done) {
302
+ sourceExhausted = true;
303
+ return;
304
+ }
305
+ buffer.append(input.observe(trailing.value, requests));
306
+ }
307
+ })();
308
+ await Promise.race([inputOwner.drain(exhaustSource), stopped]);
309
+ throwIfAgentOperationAborted(lifetime.signal);
310
+ terminalTail = buffer;
311
+ }
239
312
  return { done: false, value: output };
240
313
  }
241
314
  while (input.inFlightOperations.size > 0) {
@@ -247,7 +320,7 @@ function createExecution(input, origin) {
247
320
  requests.withdraw();
248
321
  state = { kind: "settled" };
249
322
  detachAbort();
250
- input.release();
323
+ if (!inputOwner?.retainsOwnership) input.release();
251
324
  complete();
252
325
  return { done: true, value: void 0 };
253
326
  } catch (error) {
@@ -256,6 +329,8 @@ function createExecution(input, origin) {
256
329
  void input.abandon().catch(() => {
257
330
  });
258
331
  }
332
+ if (inputOwner?.failed) void input.abandon().catch(() => {
333
+ });
259
334
  throw error;
260
335
  } finally {
261
336
  removeAbort();
@@ -268,7 +343,7 @@ function createExecution(input, origin) {
268
343
  if (returnPromise) return returnPromise;
269
344
  if (state.kind === "settled") return Promise.resolve({ done: true, value: void 0 });
270
345
  const entered = dispatched || state.kind === "entered" || source !== void 0;
271
- stop(new DOMException("The turn output consumer returned.", "AbortError"));
346
+ stop(new DOMException("The turn output consumer returned.", "AbortError"), "closed");
272
347
  if (entered && origin.kind === "native") {
273
348
  void input.abandon().catch(() => {
274
349
  });
@@ -285,10 +360,16 @@ function createExecution(input, origin) {
285
360
  }
286
361
  });
287
362
  return {
288
- execution: Object.freeze({ turnId, outputs, resolveRequest }),
363
+ execution: Object.freeze({
364
+ turnId,
365
+ outputs,
366
+ resolveRequest,
367
+ ...inputOwner === void 0 ? origin.kind === "native" && input.executionInputSupport?.kind === "supported" ? { input: Object.freeze({ kind: "unsupported" }) } : {} : { input: inputOwner.port }
368
+ }),
289
369
  hasEntered: () => dispatched && state.kind === "parked" || state.kind === "entering" || state.kind === "entered",
290
370
  stop,
291
371
  join,
372
+ closeInputAdmission: () => inputOwner?.closeAdmission(),
292
373
  suspendRequests: requests.suspend,
293
374
  completed,
294
375
  readerReady,
@@ -0,0 +1,9 @@
1
+ import type { AgentProviderKey } from "@agen-ai/agent-protocol";
2
+ import type { AgentProviderOutput } from "../outputs.js";
3
+ /** Holds validated observations in native order until the terminal and its input ownership can publish. */
4
+ export declare function createAgentTurnTerminalBuffer(providerKey: AgentProviderKey, terminal: AgentProviderOutput): {
5
+ append(output: AgentProviderOutput): void;
6
+ take(): AgentProviderOutput | undefined;
7
+ clear(): void;
8
+ };
9
+ //# sourceMappingURL=terminalBuffer.d.ts.map
@@ -0,0 +1,38 @@
1
+ import { throwAgentProviderContractError } from "../contractErrors.js";
2
+ const MAX_TRAILING_OUTPUTS = 32;
3
+ const MAX_RETAINED_BYTES = 16 * 1024 * 1024;
4
+ function retainedOutputBytes(output) {
5
+ if (output.kind === "artifact" && output.candidate.source.kind === "bytes") {
6
+ const metadata = { ...output, candidate: { ...output.candidate, source: { kind: "bytes" } } };
7
+ return output.candidate.source.bytes.byteLength + Buffer.byteLength(JSON.stringify(metadata), "utf8");
8
+ }
9
+ return Buffer.byteLength(JSON.stringify(output), "utf8");
10
+ }
11
+ function createAgentTurnTerminalBuffer(providerKey, terminal) {
12
+ const trailing = [];
13
+ let retainedBytes = retainedOutputBytes(terminal);
14
+ return {
15
+ append(output) {
16
+ const bytes = retainedOutputBytes(output);
17
+ if (trailing.length >= MAX_TRAILING_OUTPUTS || retainedBytes + bytes > MAX_RETAINED_BYTES) {
18
+ throwAgentProviderContractError(
19
+ providerKey,
20
+ "invalid_operation_result",
21
+ "Provider exceeded bounded terminal output capacity."
22
+ );
23
+ }
24
+ trailing.push(output);
25
+ retainedBytes += bytes;
26
+ },
27
+ take() {
28
+ return trailing.shift();
29
+ },
30
+ clear() {
31
+ trailing.length = 0;
32
+ retainedBytes = 0;
33
+ }
34
+ };
35
+ }
36
+ export {
37
+ createAgentTurnTerminalBuffer
38
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agen-ai/agent-runtime",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "private": false,
5
5
  "description": "Provider-neutral driver, instance, adapter, session, output, and conformance contracts for coding-agent runtimes.",
6
6
  "type": "module",
@@ -28,10 +28,14 @@
28
28
  "./environment": {
29
29
  "types": "./dist/environment/index.d.ts",
30
30
  "import": "./dist/environment/index.js"
31
+ },
32
+ "./generation": {
33
+ "types": "./dist/generation/index.d.ts",
34
+ "import": "./dist/generation/index.js"
31
35
  }
32
36
  },
33
37
  "dependencies": {
34
- "@agen-ai/agent-protocol": "^0.4.2"
38
+ "@agen-ai/agent-protocol": "^0.4.4"
35
39
  },
36
40
  "devDependencies": {
37
41
  "tsup": "^8.5.0"