@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.
- package/README.md +316 -10
- package/dist/adapterValidation.js +38 -4
- package/dist/contractErrors.d.ts +2 -2
- package/dist/contractErrors.js +8 -1
- package/dist/executionInput/index.d.ts +3 -0
- package/dist/executionInput/index.js +4 -0
- package/dist/executionInput/lifetime.d.ts +47 -0
- package/dist/executionInput/lifetime.js +241 -0
- package/dist/executionInput/types.d.ts +61 -0
- package/dist/executionInput/types.js +0 -0
- package/dist/executionInput/validation.d.ts +25 -0
- package/dist/executionInput/validation.js +178 -0
- package/dist/generation/cleanup.d.ts +15 -0
- package/dist/generation/cleanup.js +108 -0
- package/dist/generation/deadline.d.ts +16 -0
- package/dist/generation/deadline.js +81 -0
- package/dist/generation/decoding.d.ts +4 -0
- package/dist/generation/decoding.js +59 -0
- package/dist/generation/errors.d.ts +29 -0
- package/dist/generation/errors.js +39 -0
- package/dist/generation/generateObject.d.ts +3 -0
- package/dist/generation/generateObject.js +148 -0
- package/dist/generation/index.d.ts +5 -0
- package/dist/generation/index.js +8 -0
- package/dist/generation/preflight.d.ts +25 -0
- package/dist/generation/preflight.js +48 -0
- package/dist/generation/requests.d.ts +17 -0
- package/dist/generation/requests.js +117 -0
- package/dist/generation/types.d.ts +22 -0
- package/dist/generation/types.js +0 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -1
- package/dist/nativeContinuations/admission.js +1 -0
- package/dist/outputValidation.d.ts +2 -0
- package/dist/outputValidation.js +29 -1
- package/dist/sessionAdmission.d.ts +2 -0
- package/dist/sessionAdmission.js +13 -0
- package/dist/sessionInteractions/lifetime.d.ts +1 -0
- package/dist/sessionInteractions/lifetime.js +4 -0
- package/dist/sessionInteractions/request.d.ts +1 -0
- package/dist/sessionInteractions/request.js +3 -0
- package/dist/sessionValidation.d.ts +15 -3
- package/dist/sessionValidation.js +215 -127
- package/dist/sessions.d.ts +7 -1
- package/dist/structuredOutput/admission.d.ts +16 -0
- package/dist/structuredOutput/admission.js +89 -0
- package/dist/structuredOutput/candidate.d.ts +22 -0
- package/dist/structuredOutput/candidate.js +64 -0
- package/dist/structuredOutput/validation.d.ts +8 -0
- package/dist/structuredOutput/validation.js +26 -0
- package/dist/testing/conformance.d.ts +20 -1
- package/dist/testing/conformance.js +103 -3
- package/dist/testing/fakeProvider.js +5 -4
- package/dist/turnExecution/execution.d.ts +9 -1
- package/dist/turnExecution/execution.js +88 -7
- package/dist/turnExecution/terminalBuffer.d.ts +9 -0
- package/dist/turnExecution/terminalBuffer.js +38 -0
- 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 =
|
|
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({
|
|
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.
|
|
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.
|
|
38
|
+
"@agen-ai/agent-protocol": "^0.4.4"
|
|
35
39
|
},
|
|
36
40
|
"devDependencies": {
|
|
37
41
|
"tsup": "^8.5.0"
|