@agen-ai/agent-runtime 0.4.3 → 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 (56) hide show
  1. package/README.md +297 -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/sessionInteractions/lifetime.d.ts +1 -0
  37. package/dist/sessionInteractions/lifetime.js +4 -0
  38. package/dist/sessionInteractions/request.d.ts +1 -0
  39. package/dist/sessionInteractions/request.js +3 -0
  40. package/dist/sessionValidation.d.ts +13 -3
  41. package/dist/sessionValidation.js +80 -6
  42. package/dist/sessions.d.ts +7 -1
  43. package/dist/structuredOutput/admission.d.ts +16 -0
  44. package/dist/structuredOutput/admission.js +89 -0
  45. package/dist/structuredOutput/candidate.d.ts +22 -0
  46. package/dist/structuredOutput/candidate.js +64 -0
  47. package/dist/structuredOutput/validation.d.ts +8 -0
  48. package/dist/structuredOutput/validation.js +26 -0
  49. package/dist/testing/conformance.d.ts +20 -1
  50. package/dist/testing/conformance.js +103 -3
  51. package/dist/testing/fakeProvider.js +5 -4
  52. package/dist/turnExecution/execution.d.ts +9 -1
  53. package/dist/turnExecution/execution.js +88 -7
  54. package/dist/turnExecution/terminalBuffer.d.ts +9 -0
  55. package/dist/turnExecution/terminalBuffer.js +38 -0
  56. package/package.json +6 -2
@@ -0,0 +1,241 @@
1
+ import { AgentProviderContractError, AgentProviderDelegatedOperationError } from "../contractErrors.js";
2
+ import { throwIfAgentOperationAborted } from "../foundation.js";
3
+ import { serializedAgentJsonValueBytes } from "../internal/serializedJsonBytes.js";
4
+ import { parseAgentExecutionInputMessage, validateAgentExecutionInputReceipt } from "./validation.js";
5
+ const RECEIPT_TIMEOUT_MS = 3e4;
6
+ const DRAIN_TIMEOUT_MS = 3e4;
7
+ const MAX_SESSION_IDENTITIES = 4096;
8
+ const MAX_SESSION_ID_BYTES = 1048576;
9
+ function createExecutionInputCensus(providerKey, exhausted) {
10
+ const identities = /* @__PURE__ */ new Set();
11
+ let retainedBytes = 0;
12
+ return {
13
+ claim(messageId) {
14
+ if (identities.has(messageId)) throw new AgentProviderContractError(
15
+ providerKey,
16
+ "execution_input_identity_reused",
17
+ "Execution input reused a message ID in this session generation."
18
+ );
19
+ const bytes = Buffer.byteLength(messageId, "utf8");
20
+ if (identities.size >= MAX_SESSION_IDENTITIES || retainedBytes + bytes > MAX_SESSION_ID_BYTES) {
21
+ exhausted();
22
+ throw new AgentProviderContractError(
23
+ providerKey,
24
+ "execution_input_identity_limit_exceeded",
25
+ "Execution input exhausted this session generation's bounded message identity census."
26
+ );
27
+ }
28
+ identities.add(messageId);
29
+ retainedBytes += bytes;
30
+ }
31
+ };
32
+ }
33
+ function unconfirmed(messageId, reason) {
34
+ return Object.freeze({ status: "unconfirmed", messageId, reason });
35
+ }
36
+ function uncertainError() {
37
+ return Object.freeze({
38
+ category: "transport",
39
+ code: "execution_input_delivery_uncertain",
40
+ message: "The native execution-input receipt was not authoritatively observed.",
41
+ recovery: Object.freeze({ kind: "none" })
42
+ });
43
+ }
44
+ function createExecutionInputOwner(input) {
45
+ let phase = { kind: "reserved" };
46
+ let enteredMessages = 0;
47
+ let retainedBytes = 0;
48
+ let failure;
49
+ let uncertain = false;
50
+ const messages = /* @__PURE__ */ new Map();
51
+ const work = /* @__PURE__ */ new Set();
52
+ const observe = (entry, outcome) => entry.observation.resolve(outcome);
53
+ const closeAdmission = (reason) => {
54
+ if (phase.kind === "settled") return;
55
+ if (phase.kind === "reserved" || phase.kind === "accepting") phase = { kind: "closing", ...reason === void 0 ? {} : { reason } };
56
+ else if (reason !== void 0 && phase.reason === void 0) phase = { kind: phase.kind, reason };
57
+ if (reason !== void 0) for (const entry of messages.values()) observe(entry, unconfirmed(entry.messageId, reason));
58
+ };
59
+ const fail = (cause) => {
60
+ failure ??= cause instanceof Error ? cause : new Error("Execution-input observation failed.", { cause });
61
+ input.fence();
62
+ closeAdmission("correlation_lost");
63
+ return failure;
64
+ };
65
+ const fenceUncertainty = () => {
66
+ uncertain = true;
67
+ input.fence();
68
+ closeAdmission("correlation_lost");
69
+ };
70
+ const finish = (entry) => {
71
+ if (entry.phase.kind === "settled") return;
72
+ entry.phase = { kind: "settled" };
73
+ messages.delete(entry.messageId);
74
+ retainedBytes -= entry.bytes;
75
+ };
76
+ const track = (promise) => {
77
+ work.add(promise);
78
+ void promise.then(() => work.delete(promise), (cause) => {
79
+ work.delete(promise);
80
+ fail(cause);
81
+ });
82
+ };
83
+ const unavailable = () => {
84
+ throw new AgentProviderContractError(
85
+ input.providerKey,
86
+ "active_turn_mismatch",
87
+ "Execution input requires the exact entered execution with open admission."
88
+ );
89
+ };
90
+ const sendMessage = async (candidate) => {
91
+ const message = parseAgentExecutionInputMessage(candidate);
92
+ throwIfAgentOperationAborted(message.signal);
93
+ input.requireAdmission();
94
+ if (phase.kind !== "accepting") return unavailable();
95
+ const port = phase.port;
96
+ const bytes = serializedAgentJsonValueBytes({
97
+ messageId: message.messageId,
98
+ parts: message.parts.map((part) => ({ type: part.type, text: part.text })),
99
+ ...message.summary === void 0 ? {} : { summary: message.summary }
100
+ });
101
+ if (enteredMessages >= input.support.limits.maxMessagesPerExecution || messages.size >= input.support.limits.maxOutstandingMessages || retainedBytes + bytes > input.support.limits.maxBufferedBytes) {
102
+ throw new AgentProviderContractError(
103
+ input.providerKey,
104
+ "execution_input_capacity_exhausted",
105
+ "Execution input exceeds this execution's bounded message capacity."
106
+ );
107
+ }
108
+ input.census.claim(message.messageId);
109
+ const observation = Promise.withResolvers();
110
+ const entry = { messageId: message.messageId, bytes, observation, phase: { kind: "entered", payload: message } };
111
+ messages.set(entry.messageId, entry);
112
+ enteredMessages += 1;
113
+ retainedBytes += bytes;
114
+ const receipt = Promise.withResolvers();
115
+ const context = { providerKey: input.providerKey, turnId: input.turnId, messageId: message.messageId, support: input.support };
116
+ const signal = message.signal === void 0 ? input.signal : AbortSignal.any([input.signal, message.signal]);
117
+ let waitState = "waiting";
118
+ const deadlineAt = Date.now() + RECEIPT_TIMEOUT_MS;
119
+ const cleanup = () => {
120
+ clearTimeout(timer);
121
+ signal.removeEventListener("abort", lost);
122
+ };
123
+ const lost = () => {
124
+ if (waitState === "settled") return;
125
+ waitState = "settled";
126
+ cleanup();
127
+ fenceUncertainty();
128
+ receipt.resolve(Object.freeze({ status: "delivery_uncertain", messageId: entry.messageId, error: uncertainError() }));
129
+ };
130
+ const deliver = (value) => {
131
+ if (waitState === "settled") return;
132
+ if (Date.now() >= deadlineAt) {
133
+ lost();
134
+ return;
135
+ }
136
+ waitState = "settled";
137
+ cleanup();
138
+ receipt.resolve(value);
139
+ };
140
+ const rejectReceipt = (cause) => {
141
+ if (waitState === "settled") return;
142
+ waitState = "settled";
143
+ cleanup();
144
+ receipt.reject(cause);
145
+ };
146
+ const timer = setTimeout(lost, RECEIPT_TIMEOUT_MS);
147
+ signal.addEventListener("abort", lost, { once: true });
148
+ let actual;
149
+ try {
150
+ actual = Promise.resolve(port.sendMessage({ ...message, signal }));
151
+ } catch (cause) {
152
+ fail(cause);
153
+ rejectReceipt(new AgentProviderDelegatedOperationError(input.providerKey, "send_execution_input", cause));
154
+ finish(entry);
155
+ return receipt.promise;
156
+ }
157
+ const actualReceipt = actual.then((value) => validateAgentExecutionInputReceipt(value, context));
158
+ track(actualReceipt.then(async (result) => {
159
+ if (result.status === "accepted") {
160
+ if (entry.phase.kind !== "settled") entry.phase = { kind: "accepted", payload: message };
161
+ deliver(Object.freeze({ status: "accepted", messageId: entry.messageId, settled: observation.promise }));
162
+ try {
163
+ const outcome = await result.settled;
164
+ observe(entry, outcome);
165
+ if (outcome.status === "unconfirmed" && outcome.reason === "correlation_lost") {
166
+ fail(new AgentProviderContractError(
167
+ input.providerKey,
168
+ "execution_input_correlation_lost",
169
+ "Provider lost ownership correlation for an accepted execution input."
170
+ ));
171
+ }
172
+ } catch (cause) {
173
+ fail(cause);
174
+ } finally {
175
+ finish(entry);
176
+ }
177
+ } else {
178
+ if (result.status === "delivery_uncertain") fenceUncertainty();
179
+ deliver(result);
180
+ finish(entry);
181
+ }
182
+ }, (cause) => {
183
+ const delegated = new AgentProviderDelegatedOperationError(input.providerKey, "send_execution_input", cause);
184
+ fail(cause);
185
+ rejectReceipt(delegated);
186
+ finish(entry);
187
+ }));
188
+ if (signal.aborted) lost();
189
+ return receipt.promise;
190
+ };
191
+ const join = async (producer) => {
192
+ let timer;
193
+ const deadline = new Promise((_, reject) => {
194
+ timer = setTimeout(() => reject(new AgentProviderContractError(
195
+ input.providerKey,
196
+ "invalid_operation_result",
197
+ "Execution-input ownership did not drain before its deadline."
198
+ )), DRAIN_TIMEOUT_MS);
199
+ });
200
+ try {
201
+ if (producer !== void 0) await Promise.race([Promise.all([producer, ...work]), deadline]);
202
+ while (work.size > 0) await Promise.race([Promise.all([...work]), deadline]);
203
+ } finally {
204
+ if (timer !== void 0) clearTimeout(timer);
205
+ }
206
+ };
207
+ return {
208
+ port: Object.freeze({ kind: "supported", support: input.support, sendMessage }),
209
+ attach(port) {
210
+ if (phase.kind === "reserved") phase = { kind: "accepting", port };
211
+ else if (phase.kind !== "closing") throw new AgentProviderContractError(
212
+ input.providerKey,
213
+ "invalid_operation_result",
214
+ "Provider execution-input port was attached more than once."
215
+ );
216
+ },
217
+ closeAdmission,
218
+ async drain(producer) {
219
+ closeAdmission();
220
+ phase = { kind: "draining", ...phase.kind === "closing" && phase.reason !== void 0 ? { reason: phase.reason } : {} };
221
+ try {
222
+ await join(producer);
223
+ } catch (cause) {
224
+ fail(cause);
225
+ }
226
+ if (failure) throw failure;
227
+ phase = { kind: "settled" };
228
+ },
229
+ join,
230
+ get failed() {
231
+ return failure !== void 0;
232
+ },
233
+ get retainsOwnership() {
234
+ return uncertain || failure !== void 0 || work.size > 0;
235
+ }
236
+ };
237
+ }
238
+ export {
239
+ createExecutionInputCensus,
240
+ createExecutionInputOwner
241
+ };
@@ -0,0 +1,61 @@
1
+ import type { AgentError, AgentOperationInputCapability, AgentTurnId, AgentTurnInputContent } from "@agen-ai/agent-protocol";
2
+ declare const executionInputMessageIdBrand: unique symbol;
3
+ /** Process-local submission identity; distinct from a turn or human request ID. */
4
+ export type AgentExecutionInputMessageId = string & {
5
+ readonly [executionInputMessageIdBrand]: true;
6
+ };
7
+ export type AgentExecutionInputSupport = Readonly<{
8
+ kind: "unsupported";
9
+ }> | Readonly<{
10
+ kind: "supported";
11
+ delivery: "current_or_owned_continuation";
12
+ consumptionReporting: "message_ids" | "unavailable";
13
+ input: AgentOperationInputCapability;
14
+ limits: Readonly<{
15
+ maxOutstandingMessages: number;
16
+ maxMessagesPerExecution: number;
17
+ maxBufferedBytes: number;
18
+ }>;
19
+ }>;
20
+ /** Initial admission accepts text only, using the canonical turn-content bounds. */
21
+ export interface AgentExecutionInputMessage extends AgentTurnInputContent {
22
+ readonly messageId: AgentExecutionInputMessageId;
23
+ readonly signal?: AbortSignal;
24
+ }
25
+ /** Consumption is attribution to native work, not proof that the instruction was obeyed. */
26
+ export type AgentExecutionInputOutcome = Readonly<{
27
+ status: "consumed";
28
+ messageId: AgentExecutionInputMessageId;
29
+ turnId: AgentTurnId;
30
+ placement: "original_native_command" | "owned_native_continuation";
31
+ }> | Readonly<{
32
+ status: "unconfirmed";
33
+ messageId: AgentExecutionInputMessageId;
34
+ reason: "provider_does_not_report_consumption" | "closed" | "interrupted" | "native_failed" | "correlation_lost";
35
+ }>;
36
+ /** In-process receipt and settlement handle; never a wire or persistence format. */
37
+ export type AgentExecutionInputReceipt = Readonly<{
38
+ status: "accepted";
39
+ messageId: AgentExecutionInputMessageId;
40
+ settled: Promise<AgentExecutionInputOutcome>;
41
+ }> | Readonly<{
42
+ status: "rejected";
43
+ messageId: AgentExecutionInputMessageId;
44
+ error: AgentError;
45
+ }> | Readonly<{
46
+ status: "delivery_uncertain";
47
+ messageId: AgentExecutionInputMessageId;
48
+ error: AgentError;
49
+ }>;
50
+ /** Keep the sole outputs reader running while awaiting a receipt and its settlement. */
51
+ export type AgentExecutionInput = Readonly<{
52
+ kind: "unsupported";
53
+ }> | Readonly<{
54
+ kind: "supported";
55
+ support: Extract<AgentExecutionInputSupport, {
56
+ kind: "supported";
57
+ }>;
58
+ sendMessage: (input: AgentExecutionInputMessage) => Promise<AgentExecutionInputReceipt>;
59
+ }>;
60
+ export {};
61
+ //# sourceMappingURL=types.d.ts.map
File without changes
@@ -0,0 +1,25 @@
1
+ import { type AgentProviderKey, type AgentTextInputPart, type AgentTurnId } from "@agen-ai/agent-protocol";
2
+ import type { AgentExecutionInput, AgentExecutionInputMessage, AgentExecutionInputMessageId, AgentExecutionInputOutcome, AgentExecutionInputReceipt, AgentExecutionInputSupport } from "./types.js";
3
+ export declare const AGENT_EXECUTION_INPUT_LIMITS: Readonly<{
4
+ maxOutstandingMessages: 2;
5
+ maxMessagesPerExecution: 16;
6
+ maxBufferedBytes: 1048576;
7
+ }>;
8
+ export declare function parseAgentExecutionInputMessageId(value: unknown): AgentExecutionInputMessageId;
9
+ export declare function validateAgentExecutionInputSupport(value: unknown): AgentExecutionInputSupport | undefined;
10
+ export declare function validateAgentExecutionInputPort(candidate: AgentExecutionInput | undefined, declared: AgentExecutionInputSupport | undefined, providerKey: AgentProviderKey): AgentExecutionInput | undefined;
11
+ export declare function parseAgentExecutionInputMessage(value: unknown): Omit<AgentExecutionInputMessage, "parts"> & Readonly<{
12
+ parts: readonly AgentTextInputPart[];
13
+ }>;
14
+ type ReceiptContext = Readonly<{
15
+ providerKey: AgentProviderKey;
16
+ messageId: AgentExecutionInputMessageId;
17
+ turnId: AgentTurnId;
18
+ support: Extract<AgentExecutionInputSupport, {
19
+ kind: "supported";
20
+ }>;
21
+ }>;
22
+ export declare function validateAgentExecutionInputOutcome(value: unknown, context: ReceiptContext): AgentExecutionInputOutcome;
23
+ export declare function validateAgentExecutionInputReceipt(value: unknown, context: ReceiptContext): AgentExecutionInputReceipt;
24
+ export {};
25
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1,178 @@
1
+ import {
2
+ parseAgentError,
3
+ parseAgentTurnId,
4
+ parseAgentTurnInputContent
5
+ } from "@agen-ai/agent-protocol";
6
+ import { throwAgentProviderContractError } from "../contractErrors.js";
7
+ import { parseAgentProviderTechnicalId } from "../foundation.js";
8
+ import { hasExactOwnKeys } from "../internal/objectShape.js";
9
+ const AGENT_EXECUTION_INPUT_LIMITS = Object.freeze({
10
+ maxOutstandingMessages: 2,
11
+ maxMessagesPerExecution: 16,
12
+ maxBufferedBytes: 1048576
13
+ });
14
+ function isRecord(value) {
15
+ return value !== null && typeof value === "object" && !Array.isArray(value);
16
+ }
17
+ function record(value) {
18
+ if (!isRecord(value)) throw new TypeError("Expected an execution-input object.");
19
+ return value;
20
+ }
21
+ function parseAgentExecutionInputMessageId(value) {
22
+ return parseAgentProviderTechnicalId(value, "execution input message ID");
23
+ }
24
+ function positiveLimit(value, ceiling) {
25
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > ceiling) {
26
+ throw new TypeError("Execution-input limits must be positive integers within SDK ceilings.");
27
+ }
28
+ return value;
29
+ }
30
+ function isUnsupported(value) {
31
+ return isRecord(value) && hasExactOwnKeys(value, ["kind"]) && value.kind === "unsupported";
32
+ }
33
+ function validateAgentExecutionInputSupport(value) {
34
+ if (value === void 0) return void 0;
35
+ if (isUnsupported(value)) return Object.freeze({ kind: "unsupported" });
36
+ const support = record(value);
37
+ if (!hasExactOwnKeys(support, ["kind", "delivery", "consumptionReporting", "input", "limits"]) || support.kind !== "supported" || support.delivery !== "current_or_owned_continuation" || support.consumptionReporting !== "message_ids" && support.consumptionReporting !== "unavailable") {
38
+ throw new TypeError("Invalid execution-input support descriptor.");
39
+ }
40
+ const input = record(support.input);
41
+ if (!hasExactOwnKeys(input, ["text", "images", "contentReferences"]) || input.text !== true || !isUnsupported(input.images) || !isUnsupported(input.contentReferences)) {
42
+ throw new TypeError("Execution input currently supports text only.");
43
+ }
44
+ const limits = record(support.limits);
45
+ if (!hasExactOwnKeys(limits, ["maxOutstandingMessages", "maxMessagesPerExecution", "maxBufferedBytes"])) {
46
+ throw new TypeError("Invalid execution-input limits.");
47
+ }
48
+ const maxMessagesPerExecution = positiveLimit(limits.maxMessagesPerExecution, AGENT_EXECUTION_INPUT_LIMITS.maxMessagesPerExecution);
49
+ const maxOutstandingMessages = positiveLimit(limits.maxOutstandingMessages, AGENT_EXECUTION_INPUT_LIMITS.maxOutstandingMessages);
50
+ if (maxOutstandingMessages > maxMessagesPerExecution) {
51
+ throw new TypeError("Outstanding execution inputs cannot exceed the execution message limit.");
52
+ }
53
+ return Object.freeze({
54
+ kind: "supported",
55
+ delivery: support.delivery,
56
+ consumptionReporting: support.consumptionReporting,
57
+ input: Object.freeze({
58
+ text: true,
59
+ images: Object.freeze({ kind: "unsupported" }),
60
+ contentReferences: Object.freeze({ kind: "unsupported" })
61
+ }),
62
+ limits: Object.freeze({
63
+ maxOutstandingMessages,
64
+ maxMessagesPerExecution,
65
+ maxBufferedBytes: positiveLimit(limits.maxBufferedBytes, AGENT_EXECUTION_INPUT_LIMITS.maxBufferedBytes)
66
+ })
67
+ });
68
+ }
69
+ function validateAgentExecutionInputPort(candidate, declared, providerKey) {
70
+ try {
71
+ if (candidate === void 0 && declared?.kind !== "supported") return void 0;
72
+ if (candidate?.kind === "unsupported" && hasExactOwnKeys(candidate, ["kind"]) && declared?.kind !== "supported") {
73
+ return Object.freeze({ kind: "unsupported" });
74
+ }
75
+ if (candidate?.kind !== "supported" || declared?.kind !== "supported" || !hasExactOwnKeys(candidate, ["kind", "support", "sendMessage"]) || typeof candidate.sendMessage !== "function") {
76
+ throw new TypeError("Execution input is undeclared or missing its supported port.");
77
+ }
78
+ const support = validateAgentExecutionInputSupport(candidate.support);
79
+ if (support?.kind !== "supported" || support.delivery !== declared.delivery || support.consumptionReporting !== declared.consumptionReporting || support.limits.maxOutstandingMessages !== declared.limits.maxOutstandingMessages || support.limits.maxMessagesPerExecution !== declared.limits.maxMessagesPerExecution || support.limits.maxBufferedBytes !== declared.limits.maxBufferedBytes) {
80
+ throw new TypeError("Execution-input support differs from the adapter declaration.");
81
+ }
82
+ return Object.freeze({ kind: "supported", support, sendMessage: candidate.sendMessage.bind(candidate) });
83
+ } catch {
84
+ return throwAgentProviderContractError(
85
+ providerKey,
86
+ "capability_port_mismatch",
87
+ "Provider execution input does not match its adapter support declaration."
88
+ );
89
+ }
90
+ }
91
+ function parseAgentExecutionInputMessage(value) {
92
+ const input = record(value);
93
+ const keys = [
94
+ "messageId",
95
+ "parts",
96
+ ...Object.hasOwn(input, "summary") ? ["summary"] : [],
97
+ ...Object.hasOwn(input, "signal") ? ["signal"] : []
98
+ ];
99
+ if (!hasExactOwnKeys(input, keys) || input.signal !== void 0 && !(input.signal instanceof AbortSignal)) {
100
+ throw new TypeError("Invalid execution-input message envelope.");
101
+ }
102
+ const messageId = parseAgentExecutionInputMessageId(input.messageId);
103
+ const content = parseAgentTurnInputContent({
104
+ parts: input.parts,
105
+ ...input.summary === void 0 ? {} : { summary: input.summary }
106
+ });
107
+ const parts = content.parts.map((part) => {
108
+ if (part.type !== "text") throw new TypeError("Execution input currently accepts text parts only.");
109
+ return Object.freeze({ type: part.type, text: part.text });
110
+ });
111
+ return Object.freeze({
112
+ messageId,
113
+ parts: Object.freeze(parts),
114
+ ...content.summary === void 0 ? {} : { summary: content.summary },
115
+ ...input.signal === void 0 ? {} : { signal: input.signal }
116
+ });
117
+ }
118
+ function validateAgentExecutionInputOutcome(value, context) {
119
+ try {
120
+ const outcome = record(value);
121
+ const messageId = parseAgentExecutionInputMessageId(outcome.messageId);
122
+ if (messageId !== context.messageId) throw new TypeError("Execution-input outcome changed message identity.");
123
+ if (outcome.status === "consumed" && hasExactOwnKeys(outcome, ["status", "messageId", "turnId", "placement"]) && context.support.consumptionReporting === "message_ids" && (outcome.placement === "original_native_command" || outcome.placement === "owned_native_continuation")) {
124
+ const turnId = parseAgentTurnId(outcome.turnId);
125
+ if (turnId !== context.turnId) throw new TypeError("Execution-input consumption changed execution identity.");
126
+ return Object.freeze({ status: "consumed", messageId, turnId, placement: outcome.placement });
127
+ }
128
+ if (outcome.status === "unconfirmed" && hasExactOwnKeys(outcome, ["status", "messageId", "reason"]) && (outcome.reason === "closed" || outcome.reason === "interrupted" || outcome.reason === "native_failed" || outcome.reason === "correlation_lost" || outcome.reason === "provider_does_not_report_consumption" && context.support.consumptionReporting === "unavailable")) {
129
+ return Object.freeze({ status: "unconfirmed", messageId, reason: outcome.reason });
130
+ }
131
+ throw new TypeError("Invalid execution-input outcome.");
132
+ } catch {
133
+ return throwAgentProviderContractError(
134
+ context.providerKey,
135
+ "invalid_operation_result",
136
+ "Provider returned an invalid execution-input outcome."
137
+ );
138
+ }
139
+ }
140
+ function validateAgentExecutionInputReceipt(value, context) {
141
+ try {
142
+ const receipt = record(value);
143
+ const settlement = Object.hasOwn(receipt, "settled") ? Promise.resolve(receipt.settled) : void 0;
144
+ void settlement?.catch(() => {
145
+ });
146
+ const messageId = parseAgentExecutionInputMessageId(receipt.messageId);
147
+ if (messageId !== context.messageId) throw new TypeError("Execution-input receipt changed message identity.");
148
+ if (receipt.status === "accepted" && hasExactOwnKeys(receipt, ["status", "messageId", "settled"]) && isRecord(receipt.settled) && typeof receipt.settled.then === "function" && settlement) {
149
+ const settled = settlement.then((outcome) => validateAgentExecutionInputOutcome(outcome, context), () => throwAgentProviderContractError(
150
+ context.providerKey,
151
+ "invalid_operation_result",
152
+ "Provider execution-input settlement rejected."
153
+ ));
154
+ void settled.catch(() => {
155
+ });
156
+ return Object.freeze({ status: "accepted", messageId, settled });
157
+ }
158
+ if ((receipt.status === "rejected" || receipt.status === "delivery_uncertain") && hasExactOwnKeys(receipt, ["status", "messageId", "error"])) {
159
+ return Object.freeze({ status: receipt.status, messageId, error: parseAgentError(receipt.error) });
160
+ }
161
+ throw new TypeError("Invalid execution-input receipt.");
162
+ } catch {
163
+ return throwAgentProviderContractError(
164
+ context.providerKey,
165
+ "invalid_operation_result",
166
+ "Provider returned an invalid execution-input receipt."
167
+ );
168
+ }
169
+ }
170
+ export {
171
+ AGENT_EXECUTION_INPUT_LIMITS,
172
+ parseAgentExecutionInputMessage,
173
+ parseAgentExecutionInputMessageId,
174
+ validateAgentExecutionInputOutcome,
175
+ validateAgentExecutionInputPort,
176
+ validateAgentExecutionInputReceipt,
177
+ validateAgentExecutionInputSupport
178
+ };
@@ -0,0 +1,15 @@
1
+ import type { AgentProviderSession } from "../sessions.js";
2
+ export declare function createGenerationCleanupOwner(input: Readonly<{
3
+ turnId: Parameters<Extract<AgentProviderSession["interruption"], {
4
+ kind: "supported";
5
+ }>["interruptTurn"]>[0]["turnId"];
6
+ stopCallbacks: () => void;
7
+ }>): {
8
+ retain: <T>(work: Promise<T>) => Promise<T>;
9
+ setOpening(work: Promise<AgentProviderSession>): void;
10
+ markEntered(): void;
11
+ markTerminal(): void;
12
+ cleanup(failed: boolean): Promise<void>;
13
+ };
14
+ export type GenerationCleanupOwner = ReturnType<typeof createGenerationCleanupOwner>;
15
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1,108 @@
1
+ import { AgentProviderSessionCleanupError } from "../contractErrors.js";
2
+ import { AGENT_GENERATION_CLEANUP_TIMEOUT_MS, waitForGenerationWork } from "./deadline.js";
3
+ function createGenerationCleanupOwner(input) {
4
+ const pending = /* @__PURE__ */ new Set();
5
+ let opening;
6
+ let session;
7
+ let openingCleanup;
8
+ let entered = false;
9
+ let terminal = false;
10
+ let interrupted = false;
11
+ let stopping = false;
12
+ let closed = false;
13
+ let close;
14
+ let closeFailed = false;
15
+ let attempt;
16
+ let cleanupSignal;
17
+ function retain(work) {
18
+ pending.add(work);
19
+ void work.then(() => pending.delete(work), () => pending.delete(work));
20
+ return work;
21
+ }
22
+ function startSessionCleanup(failed) {
23
+ if (!session || closed) return;
24
+ if (failed && entered && !terminal && !interrupted && session.interruption.kind === "supported") {
25
+ interrupted = true;
26
+ try {
27
+ retain(Promise.resolve(session.interruption.interruptTurn({
28
+ turnId: input.turnId,
29
+ reason: "other",
30
+ ...cleanupSignal === void 0 ? {} : { signal: cleanupSignal }
31
+ })));
32
+ } catch {
33
+ }
34
+ }
35
+ if (close) return;
36
+ try {
37
+ close = Promise.resolve(session.close({ reason: failed ? "error" : "idle", ...cleanupSignal === void 0 ? {} : { signal: cleanupSignal } }));
38
+ } catch (error) {
39
+ close = Promise.reject(error);
40
+ }
41
+ const current = close;
42
+ retain(current);
43
+ void current.then(() => {
44
+ closed = true;
45
+ }, () => {
46
+ if (close === current) closeFailed = true;
47
+ });
48
+ }
49
+ return {
50
+ retain,
51
+ setOpening(work) {
52
+ opening = work;
53
+ retain(work);
54
+ void work.then((opened) => {
55
+ session = opened;
56
+ if (stopping) startSessionCleanup(true);
57
+ }, (error) => {
58
+ if (error instanceof AgentProviderSessionCleanupError) openingCleanup = error;
59
+ });
60
+ },
61
+ markEntered() {
62
+ entered = true;
63
+ },
64
+ markTerminal() {
65
+ terminal = true;
66
+ },
67
+ cleanup(failed) {
68
+ if (attempt) return attempt;
69
+ stopping = true;
70
+ if (closeFailed) {
71
+ close = void 0;
72
+ closeFailed = false;
73
+ }
74
+ input.stopCallbacks();
75
+ const controller = new AbortController();
76
+ cleanupSignal = controller.signal;
77
+ const expiresAt = Date.now() + AGENT_GENERATION_CLEANUP_TIMEOUT_MS;
78
+ const timeoutError = new Error("Object generation cleanup exceeded its deadline.");
79
+ const timeout = setTimeout(() => controller.abort(timeoutError), AGENT_GENERATION_CLEANUP_TIMEOUT_MS);
80
+ startSessionCleanup(failed);
81
+ const drain = async () => {
82
+ if (opening) {
83
+ try {
84
+ session = await opening;
85
+ } catch (error) {
86
+ if (error instanceof AgentProviderSessionCleanupError) openingCleanup = error;
87
+ }
88
+ }
89
+ if (openingCleanup) await openingCleanup.retryCleanup();
90
+ startSessionCleanup(failed);
91
+ if (close) await close;
92
+ while (pending.size > 0) await Promise.allSettled([...pending]);
93
+ };
94
+ const work = waitForGenerationWork(drain(), controller.signal).then(() => {
95
+ if (Date.now() >= expiresAt) throw timeoutError;
96
+ }).finally(() => {
97
+ clearTimeout(timeout);
98
+ cleanupSignal = void 0;
99
+ attempt = void 0;
100
+ });
101
+ attempt = work;
102
+ return work;
103
+ }
104
+ };
105
+ }
106
+ export {
107
+ createGenerationCleanupOwner
108
+ };
@@ -0,0 +1,16 @@
1
+ export declare const AGENT_GENERATION_TIMEOUT_MS = 300000;
2
+ export declare const AGENT_GENERATION_MAX_TIMEOUT_MS = 3600000;
3
+ export declare const AGENT_GENERATION_OBSERVER_TIMEOUT_MS = 30000;
4
+ export declare const AGENT_GENERATION_REQUEST_TIMEOUT_MS = 300000;
5
+ export declare const AGENT_GENERATION_RESPONSE_TIMEOUT_MS = 30000;
6
+ export declare const AGENT_GENERATION_CLEANUP_TIMEOUT_MS = 30000;
7
+ export declare const AGENT_GENERATION_REQUEST_LIMIT = 8;
8
+ export declare function waitForGenerationWork<T>(work: Promise<T>, signal: AbortSignal): Promise<T>;
9
+ export declare function createGenerationDeadline(deadlineAt: number, callerSignal?: AbortSignal): {
10
+ signal: AbortSignal;
11
+ check(): void;
12
+ stop(error: unknown): void;
13
+ dispose(): void;
14
+ };
15
+ export declare function waitForBoundedGenerationWork<T>(work: Promise<T>, signal: AbortSignal, timeoutMs: number, error: Error): Promise<T>;
16
+ //# sourceMappingURL=deadline.d.ts.map