@agen-ai/agent-runtime 0.4.3 → 0.4.5
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 +297 -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/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 +13 -3
- package/dist/sessionValidation.js +80 -6
- 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
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { throwIfAgentOperationAborted } from "../foundation.js";
|
|
2
|
+
import { AgentGenerationError } from "./errors.js";
|
|
3
|
+
const AGENT_GENERATION_TIMEOUT_MS = 3e5;
|
|
4
|
+
const AGENT_GENERATION_MAX_TIMEOUT_MS = 36e5;
|
|
5
|
+
const AGENT_GENERATION_OBSERVER_TIMEOUT_MS = 3e4;
|
|
6
|
+
const AGENT_GENERATION_REQUEST_TIMEOUT_MS = 3e5;
|
|
7
|
+
const AGENT_GENERATION_RESPONSE_TIMEOUT_MS = 3e4;
|
|
8
|
+
const AGENT_GENERATION_CLEANUP_TIMEOUT_MS = 3e4;
|
|
9
|
+
const AGENT_GENERATION_REQUEST_LIMIT = 8;
|
|
10
|
+
function waitForGenerationWork(work, signal) {
|
|
11
|
+
if (signal.aborted) {
|
|
12
|
+
try {
|
|
13
|
+
throwIfAgentOperationAborted(signal);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
return Promise.reject(error);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const abort = () => {
|
|
20
|
+
try {
|
|
21
|
+
throwIfAgentOperationAborted(signal);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
reject(error);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
27
|
+
void work.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function createGenerationDeadline(deadlineAt, callerSignal) {
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const abort = () => {
|
|
33
|
+
try {
|
|
34
|
+
throwIfAgentOperationAborted(callerSignal);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
controller.abort(error);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
callerSignal?.addEventListener("abort", abort, { once: true });
|
|
40
|
+
if (callerSignal?.aborted) abort();
|
|
41
|
+
const expire = () => controller.abort(new AgentGenerationError("timeout", "Object generation exceeded its deadline."));
|
|
42
|
+
const timeout = setTimeout(expire, Math.max(0, deadlineAt - Date.now()));
|
|
43
|
+
return {
|
|
44
|
+
signal: controller.signal,
|
|
45
|
+
check() {
|
|
46
|
+
if (Date.now() >= deadlineAt && !controller.signal.aborted) expire();
|
|
47
|
+
throwIfAgentOperationAborted(controller.signal);
|
|
48
|
+
},
|
|
49
|
+
stop(error) {
|
|
50
|
+
controller.abort(error);
|
|
51
|
+
},
|
|
52
|
+
dispose() {
|
|
53
|
+
clearTimeout(timeout);
|
|
54
|
+
callerSignal?.removeEventListener("abort", abort);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function waitForBoundedGenerationWork(work, signal, timeoutMs, error) {
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const expiresAt = Date.now() + timeoutMs;
|
|
61
|
+
const timeout = setTimeout(() => controller.abort(error), timeoutMs);
|
|
62
|
+
try {
|
|
63
|
+
const result = await waitForGenerationWork(work, AbortSignal.any([signal, controller.signal]));
|
|
64
|
+
if (Date.now() >= expiresAt) throw error;
|
|
65
|
+
return result;
|
|
66
|
+
} finally {
|
|
67
|
+
clearTimeout(timeout);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
AGENT_GENERATION_CLEANUP_TIMEOUT_MS,
|
|
72
|
+
AGENT_GENERATION_MAX_TIMEOUT_MS,
|
|
73
|
+
AGENT_GENERATION_OBSERVER_TIMEOUT_MS,
|
|
74
|
+
AGENT_GENERATION_REQUEST_LIMIT,
|
|
75
|
+
AGENT_GENERATION_REQUEST_TIMEOUT_MS,
|
|
76
|
+
AGENT_GENERATION_RESPONSE_TIMEOUT_MS,
|
|
77
|
+
AGENT_GENERATION_TIMEOUT_MS,
|
|
78
|
+
createGenerationDeadline,
|
|
79
|
+
waitForBoundedGenerationWork,
|
|
80
|
+
waitForGenerationWork
|
|
81
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type AgentJsonObject, type AgentOutputDefinition, type AgentOutputSchema } from "@agen-ai/agent-protocol";
|
|
2
|
+
export declare function captureGenerationCandidate(schema: AgentOutputSchema, value: unknown, maxOutputBytes: number): AgentJsonObject;
|
|
3
|
+
export declare function decodeGenerationCandidate<T>(definition: AgentOutputDefinition<T>, candidate: AgentJsonObject, maxOutputBytes: number): T;
|
|
4
|
+
//# sourceMappingURL=decoding.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { normalizeAgentStructuredOutputIssues, validateAgentOutputValue } from "@agen-ai/agent-protocol";
|
|
2
|
+
import { AgentGenerationError } from "./errors.js";
|
|
3
|
+
function freezeJson(value) {
|
|
4
|
+
if (value === null || typeof value !== "object") return;
|
|
5
|
+
for (const child of Object.values(value)) freezeJson(child);
|
|
6
|
+
Object.freeze(value);
|
|
7
|
+
}
|
|
8
|
+
function captureGenerationCandidate(schema, value, maxOutputBytes) {
|
|
9
|
+
const parsed = validateAgentOutputValue(schema, value, { maxOutputBytes });
|
|
10
|
+
if (!parsed.success) throw new AgentGenerationError("generation_failed", "Structured generation did not produce a valid object.");
|
|
11
|
+
freezeJson(parsed.data);
|
|
12
|
+
return parsed.data;
|
|
13
|
+
}
|
|
14
|
+
function isJsonArray(value) {
|
|
15
|
+
return Array.isArray(value);
|
|
16
|
+
}
|
|
17
|
+
function equalJson(left, right) {
|
|
18
|
+
if (left === right) return true;
|
|
19
|
+
if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
|
|
20
|
+
if (isJsonArray(left) || isJsonArray(right)) {
|
|
21
|
+
return isJsonArray(left) && isJsonArray(right) && left.length === right.length && left.every((child, index) => equalJson(child, right[index]));
|
|
22
|
+
}
|
|
23
|
+
const keys = Object.keys(left);
|
|
24
|
+
return keys.length === Object.keys(right).length && keys.every((key) => Object.hasOwn(right, key) && equalJson(left[key], right[key]));
|
|
25
|
+
}
|
|
26
|
+
function decodeGenerationCandidate(definition, candidate, maxOutputBytes) {
|
|
27
|
+
let decoded;
|
|
28
|
+
try {
|
|
29
|
+
decoded = definition.decode(candidate);
|
|
30
|
+
} catch (cause) {
|
|
31
|
+
throw new AgentGenerationError("decoding_failed", "Object decoder failed.", { cause });
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
if (!decoded.success) throw new AgentGenerationError("decoding_failed", "Object decoder rejected the generated value.", {
|
|
35
|
+
issues: normalizeAgentStructuredOutputIssues(decoded.issues)
|
|
36
|
+
});
|
|
37
|
+
const value = decoded.data;
|
|
38
|
+
const admitted = validateAgentOutputValue(definition.schema, value, { maxOutputBytes });
|
|
39
|
+
if (!admitted.success) {
|
|
40
|
+
throw new AgentGenerationError("decoding_failed", "Object decoder returned invalid JSON data.", {
|
|
41
|
+
issues: normalizeAgentStructuredOutputIssues(admitted.issues)
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (!equalJson(candidate, admitted.data)) {
|
|
45
|
+
throw new AgentGenerationError("decoding_failed", "Object decoder must preserve the generated JSON value.", {
|
|
46
|
+
issues: normalizeAgentStructuredOutputIssues([{ code: "decoded_value_changed", path: [], message: "Object decoder changed the generated JSON value." }])
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
freezeJson(value);
|
|
50
|
+
return value;
|
|
51
|
+
} catch (cause) {
|
|
52
|
+
if (cause instanceof AgentGenerationError) throw cause;
|
|
53
|
+
throw new AgentGenerationError("decoding_failed", "Object decoder failed.", { cause });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
captureGenerationCandidate,
|
|
58
|
+
decodeGenerationCandidate
|
|
59
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AgentProviderKey, AgentStructuredOutputUnavailableReason, ValidationIssue } from "@agen-ai/agent-protocol";
|
|
2
|
+
import { AgentProviderSessionCleanupError } from "../contractErrors.js";
|
|
3
|
+
import type { AgentRequestResolutionResult } from "../sessions.js";
|
|
4
|
+
import type { AgentGeneratedObject } from "./types.js";
|
|
5
|
+
export declare const AGENT_GENERATION_ERROR_CODES: readonly ["unsupported", "interaction_required", "generation_failed", "decoding_failed", "observer_failed", "request_failed", "timeout"];
|
|
6
|
+
export type AgentGenerationErrorCode = (typeof AGENT_GENERATION_ERROR_CODES)[number];
|
|
7
|
+
export declare class AgentGenerationError extends Error {
|
|
8
|
+
readonly code: AgentGenerationErrorCode;
|
|
9
|
+
readonly reason?: AgentStructuredOutputUnavailableReason;
|
|
10
|
+
readonly issues?: readonly ValidationIssue[];
|
|
11
|
+
readonly receipt?: AgentRequestResolutionResult;
|
|
12
|
+
constructor(code: AgentGenerationErrorCode, message: string, options?: ErrorOptions & Readonly<{
|
|
13
|
+
reason?: AgentStructuredOutputUnavailableReason;
|
|
14
|
+
issues?: readonly ValidationIssue[];
|
|
15
|
+
receipt?: AgentRequestResolutionResult;
|
|
16
|
+
}>);
|
|
17
|
+
}
|
|
18
|
+
export declare class AgentGenerationCleanupError<T = unknown> extends AgentProviderSessionCleanupError {
|
|
19
|
+
readonly primaryError?: unknown;
|
|
20
|
+
readonly result?: AgentGeneratedObject<T>;
|
|
21
|
+
constructor(input: Readonly<{
|
|
22
|
+
providerKey: AgentProviderKey;
|
|
23
|
+
errors: readonly unknown[];
|
|
24
|
+
retryCleanup: () => Promise<void>;
|
|
25
|
+
primaryError?: unknown;
|
|
26
|
+
result?: AgentGeneratedObject<T>;
|
|
27
|
+
}>);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { AgentProviderSessionCleanupError } from "../contractErrors.js";
|
|
2
|
+
const AGENT_GENERATION_ERROR_CODES = [
|
|
3
|
+
"unsupported",
|
|
4
|
+
"interaction_required",
|
|
5
|
+
"generation_failed",
|
|
6
|
+
"decoding_failed",
|
|
7
|
+
"observer_failed",
|
|
8
|
+
"request_failed",
|
|
9
|
+
"timeout"
|
|
10
|
+
];
|
|
11
|
+
class AgentGenerationError extends Error {
|
|
12
|
+
constructor(code, message, options) {
|
|
13
|
+
super(message, options);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.name = "AgentGenerationError";
|
|
16
|
+
this.reason = options?.reason;
|
|
17
|
+
this.issues = options?.issues;
|
|
18
|
+
this.receipt = options?.receipt;
|
|
19
|
+
}
|
|
20
|
+
reason;
|
|
21
|
+
issues;
|
|
22
|
+
receipt;
|
|
23
|
+
}
|
|
24
|
+
class AgentGenerationCleanupError extends AgentProviderSessionCleanupError {
|
|
25
|
+
primaryError;
|
|
26
|
+
result;
|
|
27
|
+
constructor(input) {
|
|
28
|
+
super(input);
|
|
29
|
+
this.name = "AgentGenerationCleanupError";
|
|
30
|
+
this.message = "Object generation cleanup remains incomplete.";
|
|
31
|
+
this.primaryError = input.primaryError;
|
|
32
|
+
this.result = input.result;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
AGENT_GENERATION_ERROR_CODES,
|
|
37
|
+
AgentGenerationCleanupError,
|
|
38
|
+
AgentGenerationError
|
|
39
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentProtocolValidationError
|
|
3
|
+
} from "@agen-ai/agent-protocol";
|
|
4
|
+
import { AgentProviderContractError, AgentProviderSessionCleanupError } from "../contractErrors.js";
|
|
5
|
+
import { isAgentOperationAbortError } from "../foundation.js";
|
|
6
|
+
import { createGenerationCleanupOwner } from "./cleanup.js";
|
|
7
|
+
import { captureGenerationCandidate, decodeGenerationCandidate } from "./decoding.js";
|
|
8
|
+
import { AGENT_GENERATION_OBSERVER_TIMEOUT_MS, createGenerationDeadline, waitForBoundedGenerationWork, waitForGenerationWork } from "./deadline.js";
|
|
9
|
+
import { AgentGenerationCleanupError, AgentGenerationError } from "./errors.js";
|
|
10
|
+
import { preflightGeneration } from "./preflight.js";
|
|
11
|
+
import { createGenerationRequests } from "./requests.js";
|
|
12
|
+
function unavailableFailure(output) {
|
|
13
|
+
return new AgentGenerationError("generation_failed", "Object generation did not complete with a structured object.", output.kind === "unavailable" ? {
|
|
14
|
+
reason: output.reason,
|
|
15
|
+
...output.reason !== "schema_mismatch" || output.issues === void 0 ? {} : {
|
|
16
|
+
issues: Object.freeze(output.issues.map((issue) => Object.freeze({ ...issue, path: Object.freeze([...issue.path]) })))
|
|
17
|
+
}
|
|
18
|
+
} : void 0);
|
|
19
|
+
}
|
|
20
|
+
function executionFailure(cause) {
|
|
21
|
+
if (cause instanceof AgentGenerationError || cause instanceof AgentProtocolValidationError || cause instanceof AgentProviderContractError || isAgentOperationAbortError(cause)) return cause;
|
|
22
|
+
return new AgentGenerationError("generation_failed", "Object generation execution failed.", { cause });
|
|
23
|
+
}
|
|
24
|
+
async function generateObject(input) {
|
|
25
|
+
const admitted = preflightGeneration(input);
|
|
26
|
+
const deadline = createGenerationDeadline(admitted.deadlineAt, input.signal);
|
|
27
|
+
let primary;
|
|
28
|
+
let failed = false;
|
|
29
|
+
let result;
|
|
30
|
+
let stopRequests = () => {
|
|
31
|
+
};
|
|
32
|
+
const owner = createGenerationCleanupOwner({ turnId: admitted.turn.turnId, stopCallbacks: () => stopRequests() });
|
|
33
|
+
let shutdown;
|
|
34
|
+
const fail = (error) => {
|
|
35
|
+
if (failed) return;
|
|
36
|
+
failed = true;
|
|
37
|
+
primary = error;
|
|
38
|
+
shutdown = owner.cleanup(true);
|
|
39
|
+
void shutdown.catch(() => {
|
|
40
|
+
});
|
|
41
|
+
deadline.stop(error);
|
|
42
|
+
};
|
|
43
|
+
const aborted = () => fail(deadline.signal.reason);
|
|
44
|
+
deadline.signal.addEventListener("abort", aborted, { once: true });
|
|
45
|
+
let opening;
|
|
46
|
+
let opened = false;
|
|
47
|
+
try {
|
|
48
|
+
const output = Object.freeze({ kind: "json_schema", schema: admitted.schema });
|
|
49
|
+
let bindingSeen = false;
|
|
50
|
+
opening = Promise.resolve().then(() => input.instance.adapter.createSession({
|
|
51
|
+
...admitted.session,
|
|
52
|
+
signal: deadline.signal,
|
|
53
|
+
...admitted.capability.scope === "session" ? { output } : {},
|
|
54
|
+
onBindingCreated() {
|
|
55
|
+
bindingSeen = true;
|
|
56
|
+
}
|
|
57
|
+
}));
|
|
58
|
+
owner.setOpening(opening);
|
|
59
|
+
const session = await waitForGenerationWork(opening, deadline.signal);
|
|
60
|
+
opened = true;
|
|
61
|
+
deadline.check();
|
|
62
|
+
if (!bindingSeen) throw new AgentProviderContractError(input.instance.capabilities.providerKey, "binding_callback_missing", "Object generation opening did not report its session binding.");
|
|
63
|
+
const execution = session.startTurn({
|
|
64
|
+
...admitted.turn,
|
|
65
|
+
signal: deadline.signal,
|
|
66
|
+
...admitted.capability.scope === "turn" ? { output } : {},
|
|
67
|
+
onProviderExecutionStarted: owner.markEntered
|
|
68
|
+
});
|
|
69
|
+
const requests = createGenerationRequests({
|
|
70
|
+
handler: input.onRequest,
|
|
71
|
+
execution,
|
|
72
|
+
signal: deadline.signal,
|
|
73
|
+
deadlineAt: admitted.deadlineAt,
|
|
74
|
+
owner,
|
|
75
|
+
fail,
|
|
76
|
+
checkDeadline: deadline.check
|
|
77
|
+
});
|
|
78
|
+
stopRequests = requests.stop;
|
|
79
|
+
let terminal;
|
|
80
|
+
const reader = (async () => {
|
|
81
|
+
for await (const outputEvent of execution.outputs) {
|
|
82
|
+
if (outputEvent.kind === "event") {
|
|
83
|
+
const event = outputEvent.event;
|
|
84
|
+
if (event.type === "turn.completed") {
|
|
85
|
+
owner.markTerminal();
|
|
86
|
+
terminal = event.payload.outcome === "completed" && event.payload.structuredOutput.kind === "json" ? { outcome: "completed", candidate: captureGenerationCandidate(admitted.schema, event.payload.structuredOutput.value, admitted.capability.maxOutputBytes) } : { outcome: "failed", error: unavailableFailure(event.payload.structuredOutput) };
|
|
87
|
+
}
|
|
88
|
+
requests.observe(event);
|
|
89
|
+
}
|
|
90
|
+
if (!failed && input.onOutput) {
|
|
91
|
+
const observer = input.onOutput;
|
|
92
|
+
const callback = owner.retain(Promise.resolve().then(() => {
|
|
93
|
+
if (!failed) return observer(outputEvent);
|
|
94
|
+
}));
|
|
95
|
+
try {
|
|
96
|
+
await waitForBoundedGenerationWork(
|
|
97
|
+
callback,
|
|
98
|
+
deadline.signal,
|
|
99
|
+
AGENT_GENERATION_OBSERVER_TIMEOUT_MS,
|
|
100
|
+
new AgentGenerationError("observer_failed", "Object output observer exceeded its deadline.")
|
|
101
|
+
);
|
|
102
|
+
deadline.check();
|
|
103
|
+
} catch (cause) {
|
|
104
|
+
try {
|
|
105
|
+
deadline.check();
|
|
106
|
+
} catch (error) {
|
|
107
|
+
fail(error);
|
|
108
|
+
}
|
|
109
|
+
if (!failed) fail(cause instanceof AgentGenerationError ? cause : new AgentGenerationError("observer_failed", "Object output observer failed.", { cause }));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
})();
|
|
114
|
+
owner.retain(reader);
|
|
115
|
+
await waitForGenerationWork(reader, deadline.signal);
|
|
116
|
+
if (failed) throw primary;
|
|
117
|
+
deadline.check();
|
|
118
|
+
if (!terminal || terminal.outcome !== "completed") {
|
|
119
|
+
throw terminal?.outcome === "failed" ? terminal.error : new AgentGenerationError("generation_failed", "Object generation stream ended without a terminal result.");
|
|
120
|
+
}
|
|
121
|
+
const value = decodeGenerationCandidate({ schema: admitted.schema, decode: admitted.decode }, terminal.candidate, admitted.capability.maxOutputBytes);
|
|
122
|
+
deadline.check();
|
|
123
|
+
result = Object.freeze({ value, sessionId: admitted.session.sessionId, turnId: admitted.turn.turnId });
|
|
124
|
+
} catch (cause) {
|
|
125
|
+
if (!opened && cause instanceof AgentProviderSessionCleanupError && !failed) throw cause;
|
|
126
|
+
fail(executionFailure(cause));
|
|
127
|
+
} finally {
|
|
128
|
+
deadline.signal.removeEventListener("abort", aborted);
|
|
129
|
+
deadline.dispose();
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
await (shutdown ?? owner.cleanup(failed));
|
|
133
|
+
} catch (cleanupError) {
|
|
134
|
+
throw new AgentGenerationCleanupError({
|
|
135
|
+
providerKey: input.instance.capabilities.providerKey,
|
|
136
|
+
errors: failed ? [primary, cleanupError] : [cleanupError],
|
|
137
|
+
retryCleanup: () => owner.cleanup(failed),
|
|
138
|
+
...failed ? { primaryError: primary } : {},
|
|
139
|
+
...result === void 0 ? {} : { result }
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (failed) throw primary;
|
|
143
|
+
if (!result) throw new AgentGenerationError("generation_failed", "Object generation returned no validated result.");
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
export {
|
|
147
|
+
generateObject
|
|
148
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { generateObject } from "./generateObject.js";
|
|
2
|
+
export { AGENT_GENERATION_ERROR_CODES, AgentGenerationError, AgentGenerationCleanupError } from "./errors.js";
|
|
3
|
+
export type { AgentGenerationErrorCode } from "./errors.js";
|
|
4
|
+
export type { AgentGenerateObjectInput, AgentGeneratedObject } from "./types.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { generateObject } from "./generateObject.js";
|
|
2
|
+
import { AGENT_GENERATION_ERROR_CODES, AgentGenerationError, AgentGenerationCleanupError } from "./errors.js";
|
|
3
|
+
export {
|
|
4
|
+
AGENT_GENERATION_ERROR_CODES,
|
|
5
|
+
AgentGenerationCleanupError,
|
|
6
|
+
AgentGenerationError,
|
|
7
|
+
generateObject
|
|
8
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AgentGenerateObjectInput } from "./types.js";
|
|
2
|
+
export declare function preflightGeneration<T>(input: AgentGenerateObjectInput<T>): {
|
|
3
|
+
schema: import("@agen-ai/agent-protocol").AgentOutputSchema;
|
|
4
|
+
decode: (value: import("@agen-ai/agent-protocol").AgentJsonObject) => import("@agen-ai/agent-protocol").AgentProtocolParseResult<T>;
|
|
5
|
+
capability: Readonly<{
|
|
6
|
+
kind: "supported";
|
|
7
|
+
mechanism: "native";
|
|
8
|
+
schemaProfile: typeof import("@agen-ai/agent-protocol").AGENT_OUTPUT_SCHEMA_PROFILE;
|
|
9
|
+
scope: "turn" | "session";
|
|
10
|
+
maxSchemaBytes: number;
|
|
11
|
+
maxOutputBytes: number;
|
|
12
|
+
resume: false;
|
|
13
|
+
branch: false;
|
|
14
|
+
}>;
|
|
15
|
+
session: {
|
|
16
|
+
configuration: import("@agen-ai/agent-protocol").AgentSessionConfiguration;
|
|
17
|
+
interactions?: import("../index.js").AgentSessionInteractionContext;
|
|
18
|
+
sessionId: import("@agen-ai/agent-protocol").AgentSessionId;
|
|
19
|
+
workingDirectory: string;
|
|
20
|
+
hostBinding?: import("../index.js").AgentSessionHostBinding;
|
|
21
|
+
};
|
|
22
|
+
turn: import("@agen-ai/agent-protocol").AgentTurnRunInput;
|
|
23
|
+
deadlineAt: number;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=preflight.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
agentProtocolSerializedJsonBytes,
|
|
3
|
+
parseAgentOutputSchema,
|
|
4
|
+
parseAgentSessionOpenInput,
|
|
5
|
+
parseAgentTurnRunInput
|
|
6
|
+
} from "@agen-ai/agent-protocol";
|
|
7
|
+
import { assertAgentSessionConfigurationSupported } from "../configurationValidation.js";
|
|
8
|
+
import { throwAgentProviderContractError } from "../contractErrors.js";
|
|
9
|
+
import { throwIfAgentOperationAborted } from "../foundation.js";
|
|
10
|
+
import { assertAgentTurnInputCapability } from "../sessionValidation.js";
|
|
11
|
+
import { AGENT_GENERATION_MAX_TIMEOUT_MS, AGENT_GENERATION_TIMEOUT_MS } from "./deadline.js";
|
|
12
|
+
import { AgentGenerationError } from "./errors.js";
|
|
13
|
+
function preflightGeneration(input) {
|
|
14
|
+
throwIfAgentOperationAborted(input.signal);
|
|
15
|
+
const timeoutMs = input.timeoutMs ?? AGENT_GENERATION_TIMEOUT_MS;
|
|
16
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > AGENT_GENERATION_MAX_TIMEOUT_MS) {
|
|
17
|
+
throw new RangeError("Object generation timeout must be a positive integer of at most one hour.");
|
|
18
|
+
}
|
|
19
|
+
for (const key of ["output", "signal", "onBindingCreated", "nativeContinuations"]) {
|
|
20
|
+
if (Reflect.has(input.session, key)) throw new TypeError(`Object generation owns session ${key}.`);
|
|
21
|
+
}
|
|
22
|
+
for (const key of ["output", "signal", "onProviderExecutionStarted"]) {
|
|
23
|
+
if (Reflect.has(input.turn, key)) throw new TypeError(`Object generation owns turn ${key}.`);
|
|
24
|
+
}
|
|
25
|
+
if (typeof input.output?.decode !== "function") throw new TypeError("Object generation requires an output decoder.");
|
|
26
|
+
if (input.onOutput !== void 0 && typeof input.onOutput !== "function") throw new TypeError("Object output observer must be a function.");
|
|
27
|
+
if (input.onRequest !== void 0 && typeof input.onRequest !== "function") throw new TypeError("Object request handler must be a function.");
|
|
28
|
+
const schema = parseAgentOutputSchema(input.output.schema);
|
|
29
|
+
const capability = input.instance.capabilities.turns.structuredOutput;
|
|
30
|
+
if (capability.kind !== "supported") throw new AgentGenerationError("unsupported", "Provider does not support structured object generation.");
|
|
31
|
+
const boundedSchema = parseAgentOutputSchema(schema, { maxOutputBytes: capability.maxOutputBytes });
|
|
32
|
+
if (agentProtocolSerializedJsonBytes(boundedSchema) > capability.maxSchemaBytes) {
|
|
33
|
+
throwAgentProviderContractError(input.instance.capabilities.providerKey, "invalid_structured_output", "Object schema exceeds the provider's advertised byte limit.");
|
|
34
|
+
}
|
|
35
|
+
const session = parseAgentSessionOpenInput({ operation: "create", sessionId: input.session.sessionId, configuration: input.session.configuration });
|
|
36
|
+
assertAgentSessionConfigurationSupported(input.instance.capabilities, session.configuration);
|
|
37
|
+
const turn = parseAgentTurnRunInput(input.turn);
|
|
38
|
+
assertAgentTurnInputCapability({ capability: input.instance.capabilities.input, parts: turn.parts, providerKey: input.instance.capabilities.providerKey });
|
|
39
|
+
if (!input.instance.capabilities.turns.interactionModes.includes(turn.interactionMode)) {
|
|
40
|
+
throwAgentProviderContractError(input.instance.capabilities.providerKey, "input_capability_mismatch", "Provider does not support the requested interaction mode.");
|
|
41
|
+
}
|
|
42
|
+
const deadlineAt = Math.min(Date.now() + timeoutMs, turn.deadlineAt === void 0 ? Infinity : Date.parse(turn.deadlineAt));
|
|
43
|
+
if (deadlineAt <= Date.now()) throw new AgentGenerationError("timeout", "Object generation deadline has expired.");
|
|
44
|
+
return { schema: boundedSchema, decode: input.output.decode, capability, session: { ...input.session, configuration: session.configuration }, turn, deadlineAt };
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
preflightGeneration
|
|
48
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AgentEvent } from "@agen-ai/agent-protocol";
|
|
2
|
+
import type { AgentProviderTurnExecution } from "../sessions.js";
|
|
3
|
+
import type { GenerationCleanupOwner } from "./cleanup.js";
|
|
4
|
+
import type { AgentGenerateObjectInput } from "./types.js";
|
|
5
|
+
export declare function createGenerationRequests(input: Readonly<{
|
|
6
|
+
handler: AgentGenerateObjectInput<unknown>["onRequest"];
|
|
7
|
+
execution: AgentProviderTurnExecution;
|
|
8
|
+
signal: AbortSignal;
|
|
9
|
+
deadlineAt: number;
|
|
10
|
+
owner: GenerationCleanupOwner;
|
|
11
|
+
fail: (error: unknown) => void;
|
|
12
|
+
checkDeadline: () => void;
|
|
13
|
+
}>): {
|
|
14
|
+
stop(): void;
|
|
15
|
+
observe(event: AgentEvent): void;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=requests.d.ts.map
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { parseAgentRequest } from "@agen-ai/agent-protocol";
|
|
2
|
+
import { throwIfAgentOperationAborted } from "../foundation.js";
|
|
3
|
+
import {
|
|
4
|
+
AGENT_GENERATION_REQUEST_LIMIT,
|
|
5
|
+
AGENT_GENERATION_REQUEST_TIMEOUT_MS,
|
|
6
|
+
AGENT_GENERATION_RESPONSE_TIMEOUT_MS,
|
|
7
|
+
waitForBoundedGenerationWork
|
|
8
|
+
} from "./deadline.js";
|
|
9
|
+
import { AgentGenerationError } from "./errors.js";
|
|
10
|
+
function createGenerationRequests(input) {
|
|
11
|
+
const records = /* @__PURE__ */ new Map();
|
|
12
|
+
let stopped = false;
|
|
13
|
+
const retire = (record) => {
|
|
14
|
+
record.live = false;
|
|
15
|
+
record.controller.abort(new DOMException("The generation request is no longer available.", "AbortError"));
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
stop() {
|
|
19
|
+
stopped = true;
|
|
20
|
+
for (const record of records.values()) retire(record);
|
|
21
|
+
},
|
|
22
|
+
observe(event) {
|
|
23
|
+
if (event.type === "request.unavailable") {
|
|
24
|
+
const record2 = records.get(event.payload.requestId);
|
|
25
|
+
if (record2) retire(record2);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (event.type === "turn.completed") {
|
|
29
|
+
for (const record2 of records.values()) retire(record2);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (event.type !== "request.opened" || stopped) return;
|
|
33
|
+
if (!input.handler) {
|
|
34
|
+
input.fail(new AgentGenerationError("interaction_required", "Object generation requires a request handler."));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (records.size >= AGENT_GENERATION_REQUEST_LIMIT) {
|
|
38
|
+
input.fail(new AgentGenerationError("request_failed", "Object generation exceeded its active request handler limit."));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const request = parseAgentRequest(event.payload.request);
|
|
42
|
+
const record = { controller: new AbortController(), live: true, pending: 0, finished: false };
|
|
43
|
+
records.set(request.requestId, record);
|
|
44
|
+
const handler = input.handler;
|
|
45
|
+
const signal = AbortSignal.any([input.signal, record.controller.signal]);
|
|
46
|
+
let expiry;
|
|
47
|
+
if (request.expiresAt !== void 0) {
|
|
48
|
+
const expiresAt = Date.parse(request.expiresAt);
|
|
49
|
+
const remaining = expiresAt - Date.now();
|
|
50
|
+
if (remaining <= 0) retire(record);
|
|
51
|
+
else if (expiresAt < input.deadlineAt) expiry = setTimeout(() => retire(record), remaining);
|
|
52
|
+
}
|
|
53
|
+
const clearRecord = () => {
|
|
54
|
+
if (record.finished && record.pending === 0 && records.get(request.requestId) === record) records.delete(request.requestId);
|
|
55
|
+
};
|
|
56
|
+
const retain = (work) => {
|
|
57
|
+
record.pending += 1;
|
|
58
|
+
void work.then(() => {
|
|
59
|
+
record.pending -= 1;
|
|
60
|
+
clearRecord();
|
|
61
|
+
}, () => {
|
|
62
|
+
record.pending -= 1;
|
|
63
|
+
clearRecord();
|
|
64
|
+
});
|
|
65
|
+
return input.owner.retain(work);
|
|
66
|
+
};
|
|
67
|
+
let deliveryEntered = false;
|
|
68
|
+
const task = (async () => {
|
|
69
|
+
try {
|
|
70
|
+
const callback = retain(Promise.resolve().then(() => {
|
|
71
|
+
throwIfAgentOperationAborted(signal);
|
|
72
|
+
return handler(request, { signal });
|
|
73
|
+
}));
|
|
74
|
+
const resolution = await waitForBoundedGenerationWork(
|
|
75
|
+
callback,
|
|
76
|
+
signal,
|
|
77
|
+
AGENT_GENERATION_REQUEST_TIMEOUT_MS,
|
|
78
|
+
new AgentGenerationError("request_failed", "Object generation request handler exceeded its deadline.")
|
|
79
|
+
);
|
|
80
|
+
input.checkDeadline();
|
|
81
|
+
if (!record.live || stopped || signal.aborted) return;
|
|
82
|
+
const delivery = retain(Promise.resolve().then(() => {
|
|
83
|
+
input.checkDeadline();
|
|
84
|
+
if (!record.live || stopped) throw new DOMException("The generation request is no longer available.", "AbortError");
|
|
85
|
+
deliveryEntered = true;
|
|
86
|
+
return input.execution.resolveRequest({ resolution, signal: input.signal });
|
|
87
|
+
}));
|
|
88
|
+
const receipt = await waitForBoundedGenerationWork(
|
|
89
|
+
delivery,
|
|
90
|
+
input.signal,
|
|
91
|
+
AGENT_GENERATION_RESPONSE_TIMEOUT_MS,
|
|
92
|
+
new AgentGenerationError("request_failed", "Object generation request delivery exceeded its deadline.")
|
|
93
|
+
);
|
|
94
|
+
if (receipt.status !== "delivered") {
|
|
95
|
+
input.fail(new AgentGenerationError("request_failed", "Object generation request was not delivered.", { receipt, cause: receipt }));
|
|
96
|
+
}
|
|
97
|
+
} catch (cause) {
|
|
98
|
+
try {
|
|
99
|
+
input.checkDeadline();
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
if ((record.live || deliveryEntered) && !stopped && !input.signal.aborted) {
|
|
103
|
+
input.fail(cause instanceof AgentGenerationError ? cause : new AgentGenerationError("request_failed", "Object generation request handler or delivery failed.", { cause }));
|
|
104
|
+
}
|
|
105
|
+
} finally {
|
|
106
|
+
if (expiry !== void 0) clearTimeout(expiry);
|
|
107
|
+
record.finished = true;
|
|
108
|
+
clearRecord();
|
|
109
|
+
}
|
|
110
|
+
})();
|
|
111
|
+
input.owner.retain(task);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
createGenerationRequests
|
|
117
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AgentOutputDefinition, AgentRequest, AgentRequestResolution, AgentSessionId, AgentTurnId } from "@agen-ai/agent-protocol";
|
|
2
|
+
import type { MaybePromise } from "../foundation.js";
|
|
3
|
+
import type { ValidatedMaterializedAgentProviderInstance } from "../providerDriver.js";
|
|
4
|
+
import type { AgentCreateSessionInput, AgentProviderOutputObserver, AgentProviderStartTurnInput } from "../sessions.js";
|
|
5
|
+
export interface AgentGenerateObjectInput<T> {
|
|
6
|
+
readonly instance: ValidatedMaterializedAgentProviderInstance;
|
|
7
|
+
readonly session: Omit<AgentCreateSessionInput, "output" | "signal" | "onBindingCreated" | "nativeContinuations">;
|
|
8
|
+
readonly turn: Omit<AgentProviderStartTurnInput, "output" | "signal" | "onProviderExecutionStarted">;
|
|
9
|
+
readonly output: AgentOutputDefinition<T>;
|
|
10
|
+
readonly signal?: AbortSignal;
|
|
11
|
+
readonly timeoutMs?: number;
|
|
12
|
+
readonly onOutput?: AgentProviderOutputObserver;
|
|
13
|
+
readonly onRequest?: (request: AgentRequest, context: Readonly<{
|
|
14
|
+
signal: AbortSignal;
|
|
15
|
+
}>) => MaybePromise<AgentRequestResolution>;
|
|
16
|
+
}
|
|
17
|
+
export interface AgentGeneratedObject<T> {
|
|
18
|
+
readonly value: T;
|
|
19
|
+
readonly sessionId: AgentSessionId;
|
|
20
|
+
readonly turnId: AgentTurnId;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
File without changes
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ export * from "./readiness.js";
|
|
|
15
15
|
export * from "./sessions.js";
|
|
16
16
|
export * from "./sessionInteractions/index.js";
|
|
17
17
|
export * from "./nativeContinuations/index.js";
|
|
18
|
+
export { parseAgentExecutionInputMessageId, type AgentExecutionInput, type AgentExecutionInputMessage, type AgentExecutionInputMessageId, type AgentExecutionInputOutcome, type AgentExecutionInputReceipt, type AgentExecutionInputSupport, } from "./executionInput/index.js";
|
|
19
|
+
export { createAgentStructuredOutputCandidate } from "./structuredOutput/candidate.js";
|
|
20
|
+
export type { AgentStructuredOutputCandidate, AgentStructuredOutputCandidateResult, CreateAgentStructuredOutputCandidateInput, } from "./structuredOutput/candidate.js";
|
|
18
21
|
export { createAgentProviderSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "./sessionCleanup.js";
|
|
19
22
|
export * from "./text.js";
|
|
20
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -15,9 +15,15 @@ export * from "./readiness.js";
|
|
|
15
15
|
export * from "./sessions.js";
|
|
16
16
|
export * from "./sessionInteractions/index.js";
|
|
17
17
|
export * from "./nativeContinuations/index.js";
|
|
18
|
+
import {
|
|
19
|
+
parseAgentExecutionInputMessageId
|
|
20
|
+
} from "./executionInput/index.js";
|
|
21
|
+
import { createAgentStructuredOutputCandidate } from "./structuredOutput/candidate.js";
|
|
18
22
|
import { createAgentProviderSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "./sessionCleanup.js";
|
|
19
23
|
export * from "./text.js";
|
|
20
24
|
export {
|
|
21
25
|
createAgentProviderSessionCleanupError,
|
|
22
|
-
|
|
26
|
+
createAgentStructuredOutputCandidate,
|
|
27
|
+
isAgentProviderSessionCleanupErrorFor,
|
|
28
|
+
parseAgentExecutionInputMessageId
|
|
23
29
|
};
|
|
@@ -147,6 +147,7 @@ async function dispatchPreparedSessionResponse(input) {
|
|
|
147
147
|
if (outcomeFailure) throw outcomeFailure;
|
|
148
148
|
if (earlyOutcome) throw continuationError(owner, "invalid_native_continuation", "Prepared outcome forbids native response dispatch.");
|
|
149
149
|
if (value.kind === "native_continuation") {
|
|
150
|
+
owner.validationOwner.requireNativeContinuationSupport();
|
|
150
151
|
owner.requireContinuation(input.candidate.request.kind);
|
|
151
152
|
reservation = owner.validationOwner.reserveExecution();
|
|
152
153
|
const turnId = parseAgentTurnId(`native-${randomUUID()}`);
|