@agen-ai/agent-runtime 0.4.0 → 0.4.2
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 +200 -9
- package/dist/adapterValidation.d.ts +4 -2
- package/dist/adapterValidation.js +140 -51
- package/dist/artifacts.js +0 -1
- package/dist/contractErrors.d.ts +2 -2
- package/dist/contractErrors.js +11 -7
- package/dist/hostBindings/opening.d.ts +2 -0
- package/dist/hostBindings/opening.js +58 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/nativeContinuations/admission.d.ts +17 -0
- package/dist/nativeContinuations/admission.js +250 -0
- package/dist/nativeContinuations/index.d.ts +2 -0
- package/dist/nativeContinuations/index.js +1 -0
- package/dist/nativeContinuations/types.d.ts +52 -0
- package/dist/nativeContinuations/types.js +0 -0
- package/dist/outputValidation.js +3 -2
- package/dist/providerCatalog.d.ts +1 -1
- package/dist/providerDriver.d.ts +4 -1
- package/dist/providerDriver.js +0 -1
- package/dist/providerInstanceRegistry.d.ts +4 -4
- package/dist/providerInstanceRegistry.js +2 -1
- package/dist/sessionAdmission.d.ts +12 -0
- package/dist/sessionAdmission.js +54 -0
- package/dist/sessionInteractions/index.d.ts +2 -0
- package/dist/sessionInteractions/index.js +1 -0
- package/dist/sessionInteractions/lifetime.d.ts +81 -0
- package/dist/sessionInteractions/lifetime.js +383 -0
- package/dist/sessionInteractions/request.d.ts +43 -0
- package/dist/sessionInteractions/request.js +381 -0
- package/dist/sessionInteractions/types.d.ts +110 -0
- package/dist/sessionInteractions/types.js +10 -0
- package/dist/sessionInteractions/validation.d.ts +25 -0
- package/dist/sessionInteractions/validation.js +180 -0
- package/dist/sessionValidation.d.ts +82 -3
- package/dist/sessionValidation.js +240 -181
- package/dist/sessions.d.ts +34 -0
- package/dist/testing/conformance.d.ts +3 -1
- package/dist/testing/conformance.js +180 -86
- package/dist/testing/fakeProvider.js +5 -6
- package/dist/turnExecution/execution.d.ts +23 -6
- package/dist/turnExecution/execution.js +136 -32
- package/dist/turnExecution/requests.d.ts +15 -3
- package/dist/turnExecution/requests.js +93 -28
- package/package.json +2 -2
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { AgentProviderKey } from "@agen-ai/agent-protocol";
|
|
2
2
|
import type { MaybePromise } from "../foundation.js";
|
|
3
3
|
import type { AgentProviderSession, AgentProviderSessionContext } from "../sessions.js";
|
|
4
|
+
import type { SessionInteractionOwner } from "../sessionInteractions/lifetime.js";
|
|
4
5
|
import { type AgentHostBindingLease } from "./lifetime.js";
|
|
5
6
|
export declare function invokeAgentSessionOpening<Input extends AgentProviderSessionContext>(input: Readonly<{
|
|
6
7
|
providerKey: AgentProviderKey;
|
|
7
8
|
openingInput: object;
|
|
8
9
|
candidateInput: Input;
|
|
9
10
|
lease: AgentHostBindingLease | undefined;
|
|
11
|
+
interactions?: SessionInteractionOwner;
|
|
10
12
|
open: (context: Input) => MaybePromise<AgentProviderSession>;
|
|
11
13
|
}>): Promise<AgentProviderSession>;
|
|
12
14
|
//# sourceMappingURL=opening.d.ts.map
|
|
@@ -2,6 +2,7 @@ import { AgentProviderSessionCleanupError } from "../contractErrors.js";
|
|
|
2
2
|
import { associateAgentSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "../sessionCleanup.js";
|
|
3
3
|
import { createAgentHostBindingCleanupError } from "./lifetime.js";
|
|
4
4
|
async function invokeAgentSessionOpening(input) {
|
|
5
|
+
if (input.interactions) return invokeInteractiveSessionOpening({ ...input, interactions: input.interactions });
|
|
5
6
|
try {
|
|
6
7
|
return await input.open(input.candidateInput);
|
|
7
8
|
} catch (error) {
|
|
@@ -19,6 +20,63 @@ async function invokeAgentSessionOpening(input) {
|
|
|
19
20
|
throw associateAgentSessionCleanupError(retained, input.openingInput);
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
async function invokeInteractiveSessionOpening(input) {
|
|
24
|
+
let actual;
|
|
25
|
+
try {
|
|
26
|
+
input.interactions.requireUsable();
|
|
27
|
+
actual = Promise.resolve(input.open(input.candidateInput));
|
|
28
|
+
} catch (cause) {
|
|
29
|
+
actual = Promise.reject(cause);
|
|
30
|
+
}
|
|
31
|
+
void actual.catch(() => {
|
|
32
|
+
});
|
|
33
|
+
try {
|
|
34
|
+
const candidate = await Promise.race([actual, input.interactions.failure.then((cause) => {
|
|
35
|
+
throw cause;
|
|
36
|
+
})]);
|
|
37
|
+
input.interactions.requireUsable();
|
|
38
|
+
return candidate;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
input.interactions.stop();
|
|
41
|
+
input.lease?.stop();
|
|
42
|
+
let providerClosed = false;
|
|
43
|
+
let nativeCleanup;
|
|
44
|
+
let nativeCleanupRetained = false;
|
|
45
|
+
const cleanup = async () => {
|
|
46
|
+
let candidate;
|
|
47
|
+
try {
|
|
48
|
+
candidate = await actual;
|
|
49
|
+
} catch (cause) {
|
|
50
|
+
if (isAgentProviderSessionCleanupErrorFor(cause, input.candidateInput) && cause.providerKey === input.providerKey) {
|
|
51
|
+
nativeCleanup = cause;
|
|
52
|
+
if (!nativeCleanupRetained) {
|
|
53
|
+
nativeCleanupRetained = true;
|
|
54
|
+
throw cause;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!providerClosed) {
|
|
59
|
+
if (nativeCleanup) await nativeCleanup.retryCleanup();
|
|
60
|
+
else if (candidate) await candidate.close({ reason: "contract_rejected" });
|
|
61
|
+
providerClosed = true;
|
|
62
|
+
}
|
|
63
|
+
await input.interactions.join();
|
|
64
|
+
await input.lease?.release();
|
|
65
|
+
};
|
|
66
|
+
try {
|
|
67
|
+
await input.interactions.close(cleanup, false);
|
|
68
|
+
} catch (cleanupError) {
|
|
69
|
+
const ownership = {
|
|
70
|
+
providerKey: input.providerKey,
|
|
71
|
+
errors: [error, cleanupError],
|
|
72
|
+
retryCleanup: () => input.interactions.close(cleanup, false)
|
|
73
|
+
};
|
|
74
|
+
const retained = input.lease ? createAgentHostBindingCleanupError({ ...ownership, lease: input.lease }) : new AgentProviderSessionCleanupError(ownership);
|
|
75
|
+
throw associateAgentSessionCleanupError(retained, input.openingInput);
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
22
80
|
export {
|
|
23
81
|
invokeAgentSessionOpening
|
|
24
82
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export * from "./providerDriver.js";
|
|
|
13
13
|
export * from "./providerInstanceRegistry.js";
|
|
14
14
|
export * from "./readiness.js";
|
|
15
15
|
export * from "./sessions.js";
|
|
16
|
+
export * from "./sessionInteractions/index.js";
|
|
17
|
+
export * from "./nativeContinuations/index.js";
|
|
16
18
|
export { createAgentProviderSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "./sessionCleanup.js";
|
|
17
19
|
export * from "./text.js";
|
|
18
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,8 @@ export * from "./providerDriver.js";
|
|
|
13
13
|
export * from "./providerInstanceRegistry.js";
|
|
14
14
|
export * from "./readiness.js";
|
|
15
15
|
export * from "./sessions.js";
|
|
16
|
+
export * from "./sessionInteractions/index.js";
|
|
17
|
+
export * from "./nativeContinuations/index.js";
|
|
16
18
|
import { createAgentProviderSessionCleanupError, isAgentProviderSessionCleanupErrorFor } from "./sessionCleanup.js";
|
|
17
19
|
export * from "./text.js";
|
|
18
20
|
export {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type AgentSessionResponse } from "@agen-ai/agent-protocol";
|
|
2
|
+
import type { AgentRequestResolutionResult } from "../sessions.js";
|
|
3
|
+
import type { SessionInteractionOwner } from "../sessionInteractions/lifetime.js";
|
|
4
|
+
import type { AgentProviderPreparedSessionInteraction } from "../sessionInteractions/types.js";
|
|
5
|
+
import type { AgentSessionResponseAttemptId } from "./types.js";
|
|
6
|
+
export declare function mintSessionResponseAttemptId(): AgentSessionResponseAttemptId;
|
|
7
|
+
export declare function dispatchPreparedSessionResponse(input: Readonly<{
|
|
8
|
+
owner: SessionInteractionOwner;
|
|
9
|
+
candidate: AgentProviderPreparedSessionInteraction;
|
|
10
|
+
response: AgentSessionResponse;
|
|
11
|
+
attemptId: AgentSessionResponseAttemptId;
|
|
12
|
+
signal: AbortSignal;
|
|
13
|
+
requireCurrent: () => void;
|
|
14
|
+
onDispatch: (deadline: number) => void;
|
|
15
|
+
observeActualReceipt: (work: Promise<AgentRequestResolutionResult>) => void;
|
|
16
|
+
}>): Promise<AgentRequestResolutionResult>;
|
|
17
|
+
//# sourceMappingURL=admission.d.ts.map
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { parseAgentTurnId } from "@agen-ai/agent-protocol";
|
|
3
|
+
import { AgentProviderContractError, AgentProviderSessionCleanupError } from "../contractErrors.js";
|
|
4
|
+
import { interactionRecord } from "../sessionInteractions/validation.js";
|
|
5
|
+
import { hasExactOwnKeys } from "../internal/objectShape.js";
|
|
6
|
+
import { validateAgentRequestResolutionResult } from "../turnExecution/receipts.js";
|
|
7
|
+
function mintSessionResponseAttemptId() {
|
|
8
|
+
return randomUUID();
|
|
9
|
+
}
|
|
10
|
+
function continuationError(owner, code, message, cause) {
|
|
11
|
+
return new AgentProviderContractError(owner.providerKey, code, message, ...cause === void 0 ? [] : [{ cause }]);
|
|
12
|
+
}
|
|
13
|
+
function until(work, deadline, failure, signal) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const timer = setTimeout(() => reject(failure()), Math.max(0, deadline - Date.now()));
|
|
16
|
+
const aborted = () => reject(signal?.reason ?? new DOMException("Prepared response was aborted.", "AbortError"));
|
|
17
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
18
|
+
if (signal?.aborted) aborted();
|
|
19
|
+
void work.then(resolve, reject).finally(() => {
|
|
20
|
+
clearTimeout(timer);
|
|
21
|
+
signal?.removeEventListener("abort", aborted);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function capturePrepared(input) {
|
|
26
|
+
const value = interactionRecord(input);
|
|
27
|
+
if (typeof value.dispatch !== "function" || typeof value.cancel !== "function") throw new TypeError("Prepared response requires dispatch and cancellation ports.");
|
|
28
|
+
if (value.kind === "no_continuation" && hasExactOwnKeys(value, ["kind", "dispatch", "cancel"])) {
|
|
29
|
+
return Object.freeze({ kind: "no_continuation", dispatch: value.dispatch.bind(input), cancel: value.cancel.bind(input) });
|
|
30
|
+
}
|
|
31
|
+
if (value.kind === "native_continuation" && hasExactOwnKeys(value, ["kind", "dispatch", "cancel", "createExecution", "outcome"]) && typeof value.createExecution === "function" && value.outcome && typeof interactionRecord(value.outcome).then === "function") {
|
|
32
|
+
return Object.freeze({
|
|
33
|
+
kind: "native_continuation",
|
|
34
|
+
dispatch: value.dispatch.bind(input),
|
|
35
|
+
cancel: value.cancel.bind(input),
|
|
36
|
+
createExecution: value.createExecution.bind(input),
|
|
37
|
+
outcome: Promise.resolve(value.outcome)
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
throw new TypeError("Invalid prepared response.");
|
|
41
|
+
}
|
|
42
|
+
function outcome(input) {
|
|
43
|
+
const value = interactionRecord(input);
|
|
44
|
+
if (!hasExactOwnKeys(value, ["kind"]) || value.kind !== "started" && value.kind !== "not_started") throw new TypeError("Invalid continuation outcome.");
|
|
45
|
+
return Object.freeze({ kind: value.kind });
|
|
46
|
+
}
|
|
47
|
+
async function dispatchPreparedSessionResponse(input) {
|
|
48
|
+
const { owner } = input;
|
|
49
|
+
const readinessDeadline = Date.now() + 3e4;
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const signal = AbortSignal.any([input.signal, controller.signal, owner.openingSignal]);
|
|
52
|
+
let prepared;
|
|
53
|
+
let execution;
|
|
54
|
+
let reservation;
|
|
55
|
+
let entered = false;
|
|
56
|
+
let callback;
|
|
57
|
+
let callbackFailure;
|
|
58
|
+
let cancelAttempt;
|
|
59
|
+
let canceled = false;
|
|
60
|
+
let actualOutcome;
|
|
61
|
+
let earlyOutcome;
|
|
62
|
+
let outcomeFailure;
|
|
63
|
+
let resolveOutcome;
|
|
64
|
+
let rejectOutcome;
|
|
65
|
+
const publicOutcome = new Promise((resolve, reject) => {
|
|
66
|
+
resolveOutcome = resolve;
|
|
67
|
+
rejectOutcome = reject;
|
|
68
|
+
});
|
|
69
|
+
void publicOutcome.catch(() => {
|
|
70
|
+
});
|
|
71
|
+
const actualPreparation = owner.track(Promise.resolve().then(() => {
|
|
72
|
+
input.requireCurrent();
|
|
73
|
+
owner.requireUsable();
|
|
74
|
+
return input.candidate.prepareResponse({
|
|
75
|
+
response: input.response,
|
|
76
|
+
responseAttemptId: input.attemptId,
|
|
77
|
+
signal
|
|
78
|
+
});
|
|
79
|
+
}).then((value) => {
|
|
80
|
+
try {
|
|
81
|
+
return capturePrepared(value);
|
|
82
|
+
} catch (cause) {
|
|
83
|
+
throw continuationError(owner, "invalid_native_continuation", "Provider returned an invalid prepared response.", cause);
|
|
84
|
+
}
|
|
85
|
+
}).then((value) => {
|
|
86
|
+
prepared = value;
|
|
87
|
+
if (value.kind === "native_continuation") {
|
|
88
|
+
actualOutcome = owner.track(value.outcome.then((value2) => {
|
|
89
|
+
const parsed = outcome(value2);
|
|
90
|
+
if (!entered) {
|
|
91
|
+
earlyOutcome = parsed;
|
|
92
|
+
if (parsed.kind === "started") throw continuationError(owner, "invalid_native_continuation", "Provider started native work before dispatch.");
|
|
93
|
+
}
|
|
94
|
+
return parsed;
|
|
95
|
+
}).catch((cause) => {
|
|
96
|
+
throw cause instanceof AgentProviderContractError && cause.code === "invalid_native_continuation" ? cause : continuationError(owner, "invalid_native_continuation", "Provider native continuation outcome is invalid.", cause);
|
|
97
|
+
}));
|
|
98
|
+
void actualOutcome.catch((cause) => {
|
|
99
|
+
outcomeFailure = cause;
|
|
100
|
+
if (entered) owner.fail(cause);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
}));
|
|
105
|
+
void actualPreparation.catch(() => {
|
|
106
|
+
});
|
|
107
|
+
const cancel = () => {
|
|
108
|
+
if (canceled) return Promise.resolve();
|
|
109
|
+
if (cancelAttempt) return cancelAttempt;
|
|
110
|
+
controller.abort(new DOMException("Prepared response admission was canceled.", "AbortError"));
|
|
111
|
+
cancelAttempt = (async () => {
|
|
112
|
+
let value;
|
|
113
|
+
try {
|
|
114
|
+
value = prepared ?? await actualPreparation;
|
|
115
|
+
} catch (cause) {
|
|
116
|
+
cleanup();
|
|
117
|
+
owner.fail(cause);
|
|
118
|
+
throw continuationError(owner, "native_continuation_preparation_failed", "Preparation failed without returning its allocated cancellation owner.", cause);
|
|
119
|
+
}
|
|
120
|
+
execution?.stop(new DOMException("Prepared execution admission was canceled.", "AbortError"));
|
|
121
|
+
await value.cancel();
|
|
122
|
+
execution?.settleNoWork();
|
|
123
|
+
await execution?.join();
|
|
124
|
+
if (value.kind === "native_continuation") {
|
|
125
|
+
const result = await actualOutcome;
|
|
126
|
+
if (result?.kind !== "not_started") throw continuationError(owner, "invalid_native_continuation", "Canceled preparation did not prove no native work.");
|
|
127
|
+
}
|
|
128
|
+
resolveOutcome(Object.freeze({ kind: "not_started" }));
|
|
129
|
+
await callback?.catch(() => {
|
|
130
|
+
});
|
|
131
|
+
if (reservation) owner.validationOwner.releaseExecution(reservation);
|
|
132
|
+
canceled = true;
|
|
133
|
+
})().finally(() => {
|
|
134
|
+
cancelAttempt = void 0;
|
|
135
|
+
});
|
|
136
|
+
void cancelAttempt.catch(() => {
|
|
137
|
+
});
|
|
138
|
+
return cancelAttempt;
|
|
139
|
+
};
|
|
140
|
+
const cleanup = owner.retainPreparation(async () => {
|
|
141
|
+
if (!entered) await cancel();
|
|
142
|
+
});
|
|
143
|
+
try {
|
|
144
|
+
const value = await until(actualPreparation, readinessDeadline, () => continuationError(owner, "native_continuation_preparation_failed", "Native continuation preparation did not finish before its deadline."), signal);
|
|
145
|
+
input.requireCurrent();
|
|
146
|
+
owner.requireUsable();
|
|
147
|
+
if (outcomeFailure) throw outcomeFailure;
|
|
148
|
+
if (earlyOutcome) throw continuationError(owner, "invalid_native_continuation", "Prepared outcome forbids native response dispatch.");
|
|
149
|
+
if (value.kind === "native_continuation") {
|
|
150
|
+
owner.requireContinuation(input.candidate.request.kind);
|
|
151
|
+
reservation = owner.validationOwner.reserveExecution();
|
|
152
|
+
const turnId = parseAgentTurnId(`native-${randomUUID()}`);
|
|
153
|
+
execution = owner.validationOwner.installNativeExecution(reservation, turnId, (executionSignal) => value.createExecution({ turnId, signal: executionSignal }));
|
|
154
|
+
const offered = execution;
|
|
155
|
+
try {
|
|
156
|
+
callback = Promise.resolve(owner.continuations.onExecution(Object.freeze({
|
|
157
|
+
sessionId: owner.sessionId,
|
|
158
|
+
requestId: input.candidate.request.requestId,
|
|
159
|
+
responseAttemptId: input.attemptId,
|
|
160
|
+
execution: offered.execution,
|
|
161
|
+
outcome: publicOutcome
|
|
162
|
+
})));
|
|
163
|
+
} catch (cause) {
|
|
164
|
+
callbackFailure = { cause };
|
|
165
|
+
callback = Promise.reject(cause);
|
|
166
|
+
}
|
|
167
|
+
owner.track(callback);
|
|
168
|
+
const rejection = new Promise((_, reject) => {
|
|
169
|
+
void callback.catch(reject);
|
|
170
|
+
});
|
|
171
|
+
void callback.catch((cause) => {
|
|
172
|
+
callbackFailure = { cause };
|
|
173
|
+
if (entered) owner.fail(cause);
|
|
174
|
+
});
|
|
175
|
+
await until(
|
|
176
|
+
Promise.race([offered.readerReady, rejection]),
|
|
177
|
+
readinessDeadline,
|
|
178
|
+
() => continuationError(owner, "native_continuation_reader_not_ready", "Native continuation requires an outstanding output pull."),
|
|
179
|
+
signal
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
input.requireCurrent();
|
|
183
|
+
owner.requireUsable();
|
|
184
|
+
if (signal.aborted) throw signal.reason;
|
|
185
|
+
if (Date.now() >= readinessDeadline) throw continuationError(owner, "native_continuation_reader_not_ready", "Native continuation reader admission expired.");
|
|
186
|
+
if (callbackFailure) throw continuationError(owner, "native_continuation_reader_not_ready", "Native continuation host rejected output ownership before dispatch.", callbackFailure.cause);
|
|
187
|
+
if (outcomeFailure || earlyOutcome) throw outcomeFailure ?? continuationError(owner, "invalid_native_continuation", "Prepared outcome arrived before dispatch.");
|
|
188
|
+
if (reservation && !owner.validationOwner.ownsExecution(reservation)) throw continuationError(owner, "invalid_native_continuation", "Native continuation lost its execution reservation.");
|
|
189
|
+
try {
|
|
190
|
+
execution?.admitDispatch();
|
|
191
|
+
} catch (cause) {
|
|
192
|
+
throw continuationError(owner, "native_continuation_reader_not_ready", "Native continuation output ownership ended before dispatch.", cause);
|
|
193
|
+
}
|
|
194
|
+
const responseDeadline = Date.now() + owner.limits.responseTimeoutMs;
|
|
195
|
+
entered = true;
|
|
196
|
+
input.onDispatch(responseDeadline);
|
|
197
|
+
let actualReceipt;
|
|
198
|
+
try {
|
|
199
|
+
actualReceipt = Promise.resolve(value.dispatch()).then((result) => validateAgentRequestResolutionResult(result, owner.providerKey));
|
|
200
|
+
} catch (cause) {
|
|
201
|
+
actualReceipt = Promise.reject(cause);
|
|
202
|
+
}
|
|
203
|
+
owner.track(actualReceipt);
|
|
204
|
+
input.observeActualReceipt(actualReceipt);
|
|
205
|
+
if (actualOutcome && execution) {
|
|
206
|
+
const currentExecution = execution;
|
|
207
|
+
const exactReservation = reservation;
|
|
208
|
+
const observedOutcome = until(actualOutcome, responseDeadline, () => continuationError(owner, "invalid_native_continuation", "Native continuation outcome did not settle before its response deadline."));
|
|
209
|
+
owner.track(observedOutcome.then(async (result) => {
|
|
210
|
+
resolveOutcome(result);
|
|
211
|
+
if (result.kind === "not_started") currentExecution.settleNoWork();
|
|
212
|
+
else currentExecution.markStarted();
|
|
213
|
+
await currentExecution.completed;
|
|
214
|
+
await actualReceipt.catch(() => {
|
|
215
|
+
});
|
|
216
|
+
await callback?.catch(() => {
|
|
217
|
+
});
|
|
218
|
+
await currentExecution.join();
|
|
219
|
+
owner.validationOwner.releaseExecution(exactReservation);
|
|
220
|
+
}).catch((cause) => {
|
|
221
|
+
rejectOutcome(cause);
|
|
222
|
+
owner.fail(cause);
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
return await until(actualReceipt, responseDeadline, () => new Error("Session response delivery observation expired."));
|
|
226
|
+
} catch (cause) {
|
|
227
|
+
if (entered) throw cause;
|
|
228
|
+
let failure = cause;
|
|
229
|
+
if (!(cause instanceof AgentProviderContractError) && !(cause instanceof DOMException && cause.name === "AbortError")) {
|
|
230
|
+
failure = continuationError(owner, prepared ? "native_continuation_reader_not_ready" : "native_continuation_preparation_failed", "Native continuation failed before response entry.", cause);
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
await until(cancel(), Date.now() + owner.limits.cleanupTimeoutMs, () => new Error("Prepared cancellation did not finish before its deadline."));
|
|
234
|
+
} catch (cleanupFailure) {
|
|
235
|
+
owner.fail(failure);
|
|
236
|
+
throw new AgentProviderSessionCleanupError({ providerKey: owner.providerKey, errors: [failure, cleanupFailure], retryCleanup: async () => {
|
|
237
|
+
if (prepared) await cancel();
|
|
238
|
+
else await owner.cleanupFailedPreparation();
|
|
239
|
+
} });
|
|
240
|
+
}
|
|
241
|
+
if (failure instanceof AgentProviderContractError && failure.code === "invalid_native_continuation") owner.fail(failure);
|
|
242
|
+
throw failure;
|
|
243
|
+
} finally {
|
|
244
|
+
if (entered || canceled) cleanup();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
export {
|
|
248
|
+
dispatchPreparedSessionResponse,
|
|
249
|
+
mintSessionResponseAttemptId
|
|
250
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AgentRequestId, AgentSessionId, AgentTurnId } from "@agen-ai/agent-protocol";
|
|
2
|
+
import type { MaybePromise } from "../foundation.js";
|
|
3
|
+
import type { AgentProviderTurnExecution, AgentRequestResolutionResult } from "../sessions.js";
|
|
4
|
+
import type { AgentSessionInteractionOpeningOperation } from "../sessionInteractions/types.js";
|
|
5
|
+
declare const agentSessionResponseAttemptIdBrand: unique symbol;
|
|
6
|
+
export type AgentSessionResponseAttemptId = string & {
|
|
7
|
+
readonly [agentSessionResponseAttemptIdBrand]: "AgentSessionResponseAttemptId";
|
|
8
|
+
};
|
|
9
|
+
export type AgentNativeContinuationTiming = "after_open_return" | "before_open_return" | "before_binding";
|
|
10
|
+
export interface AgentNativeContinuationMapping {
|
|
11
|
+
readonly openingOperation: AgentSessionInteractionOpeningOperation;
|
|
12
|
+
readonly requestKind: "form" | "plan_review";
|
|
13
|
+
readonly timing: AgentNativeContinuationTiming;
|
|
14
|
+
}
|
|
15
|
+
export type AgentNativeContinuationSupport = Readonly<{
|
|
16
|
+
kind: "unsupported";
|
|
17
|
+
}> | Readonly<{
|
|
18
|
+
kind: "supported";
|
|
19
|
+
mappings: readonly [AgentNativeContinuationMapping, ...AgentNativeContinuationMapping[]];
|
|
20
|
+
producerOwnership: "exclusive";
|
|
21
|
+
}>;
|
|
22
|
+
export type AgentNativeContinuationOutcome = Readonly<{
|
|
23
|
+
kind: "started";
|
|
24
|
+
}> | Readonly<{
|
|
25
|
+
kind: "not_started";
|
|
26
|
+
}>;
|
|
27
|
+
export interface AgentPreparedSessionResponseControl {
|
|
28
|
+
readonly dispatch: () => Promise<AgentRequestResolutionResult>;
|
|
29
|
+
readonly cancel: () => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export type AgentPreparedSessionResponse = (AgentPreparedSessionResponseControl & Readonly<{
|
|
32
|
+
kind: "no_continuation";
|
|
33
|
+
}>) | (AgentPreparedSessionResponseControl & Readonly<{
|
|
34
|
+
kind: "native_continuation";
|
|
35
|
+
createExecution: (input: Readonly<{
|
|
36
|
+
turnId: AgentTurnId;
|
|
37
|
+
signal: AbortSignal;
|
|
38
|
+
}>) => AgentProviderTurnExecution;
|
|
39
|
+
outcome: Promise<AgentNativeContinuationOutcome>;
|
|
40
|
+
}>);
|
|
41
|
+
export interface AgentNativeContinuation {
|
|
42
|
+
readonly sessionId: AgentSessionId;
|
|
43
|
+
readonly requestId: AgentRequestId;
|
|
44
|
+
readonly responseAttemptId: AgentSessionResponseAttemptId;
|
|
45
|
+
readonly execution: AgentProviderTurnExecution;
|
|
46
|
+
readonly outcome: Promise<AgentNativeContinuationOutcome>;
|
|
47
|
+
}
|
|
48
|
+
export interface AgentNativeContinuationContext {
|
|
49
|
+
readonly onExecution: (continuation: AgentNativeContinuation) => MaybePromise<void>;
|
|
50
|
+
}
|
|
51
|
+
export {};
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
File without changes
|
package/dist/outputValidation.js
CHANGED
|
@@ -56,8 +56,8 @@ function isApprovalRequestCapabilitySupported(capabilities, event) {
|
|
|
56
56
|
if (request.requestKind !== "approval") return false;
|
|
57
57
|
const capability = capabilities.requests.approval;
|
|
58
58
|
return capability.kind === "supported" && request.options.every(
|
|
59
|
-
(
|
|
60
|
-
(mode) => mode.persistence ===
|
|
59
|
+
({ consequence }) => consequence.kind === "provider_managed_remembrance" ? capability.providerManagedRemembrance : capability.modes.some(
|
|
60
|
+
(mode) => mode.persistence === consequence.persistence && mode.scopeKinds.includes(consequence.scope.kind)
|
|
61
61
|
)
|
|
62
62
|
);
|
|
63
63
|
}
|
|
@@ -120,6 +120,7 @@ function isEventCapabilitySupported(capabilities, event) {
|
|
|
120
120
|
case "context.usage.updated":
|
|
121
121
|
return isContextUsageCapabilitySupported(capabilities, event);
|
|
122
122
|
case "turn.started":
|
|
123
|
+
case "request.unavailable":
|
|
123
124
|
case "turn.state_changed":
|
|
124
125
|
case "turn.completed":
|
|
125
126
|
case "progress.updated":
|
|
@@ -9,5 +9,5 @@ export interface AgentProviderInstanceCatalogEntry {
|
|
|
9
9
|
readonly capabilities: AgentCapabilities;
|
|
10
10
|
}
|
|
11
11
|
export declare function createAgentProviderCatalogEntries(drivers: Iterable<AgentProviderDriver>): readonly AgentProviderCatalogEntry[];
|
|
12
|
-
export declare function createAgentProviderInstanceCatalogEntries(instances: readonly MaterializedAgentProviderInstance[]): readonly AgentProviderInstanceCatalogEntry[];
|
|
12
|
+
export declare function createAgentProviderInstanceCatalogEntries(instances: readonly Pick<MaterializedAgentProviderInstance, "instanceId" | "capabilities">[]): readonly AgentProviderInstanceCatalogEntry[];
|
|
13
13
|
//# sourceMappingURL=providerCatalog.d.ts.map
|
package/dist/providerDriver.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { MaybePromise } from "./foundation.js";
|
|
|
3
3
|
import type { AgentAccountQuotaPort } from "./accountQuota/types.js";
|
|
4
4
|
import type { AgentEnvironmentDiscoveryPort } from "./environment/types.js";
|
|
5
5
|
import type { AgentProviderReadiness } from "./readiness.js";
|
|
6
|
-
import type { AgentProviderAdapter } from "./sessions.js";
|
|
6
|
+
import type { AgentProviderAdapter, AgentValidatedProviderAdapter } from "./sessions.js";
|
|
7
7
|
export interface AgentProviderInstanceDefinition {
|
|
8
8
|
readonly providerKey: AgentProviderKey;
|
|
9
9
|
readonly instanceId: AgentInstanceId;
|
|
@@ -21,6 +21,9 @@ export interface MaterializedAgentProviderInstance {
|
|
|
21
21
|
readonly checkReadiness: (input?: AgentProviderReadinessCheckInput) => MaybePromise<AgentProviderReadiness>;
|
|
22
22
|
readonly dispose: () => MaybePromise<void>;
|
|
23
23
|
}
|
|
24
|
+
export type ValidatedMaterializedAgentProviderInstance = Omit<MaterializedAgentProviderInstance, "adapter"> & Readonly<{
|
|
25
|
+
adapter: AgentValidatedProviderAdapter;
|
|
26
|
+
}>;
|
|
24
27
|
export interface AgentProviderDriverCreateInput<Configuration> {
|
|
25
28
|
readonly instanceId: AgentInstanceId;
|
|
26
29
|
readonly configuration: Configuration;
|
package/dist/providerDriver.js
CHANGED
|
@@ -11,7 +11,6 @@ class AgentProviderConfigurationError extends Error {
|
|
|
11
11
|
this.providerKey = providerKey;
|
|
12
12
|
this.name = "AgentProviderConfigurationError";
|
|
13
13
|
}
|
|
14
|
-
providerKey;
|
|
15
14
|
}
|
|
16
15
|
function defineAgentProviderDriver(definition) {
|
|
17
16
|
const providerKey = parseAgentProviderKey(definition.providerKey);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AgentInstanceId, type AgentProviderKey } from "@agen-ai/agent-protocol";
|
|
2
2
|
import { type AgentProviderCatalogEntry, type AgentProviderInstanceCatalogEntry } from "./providerCatalog.js";
|
|
3
|
-
import { type AgentProviderDriver, type AgentProviderInstanceDefinition, type
|
|
3
|
+
import { type AgentProviderDriver, type AgentProviderInstanceDefinition, type ValidatedMaterializedAgentProviderInstance } from "./providerDriver.js";
|
|
4
4
|
import { type AgentProviderReadiness } from "./readiness.js";
|
|
5
5
|
export declare const AGENT_PROVIDER_REGISTRY_ERROR_CODES: readonly ["invalid_driver", "duplicate_provider", "duplicate_instance", "provider_not_registered", "multiple_instances_unsupported", "invalid_instance_configuration", "instance_materialization_failed", "instance_contract_mismatch", "instance_not_found", "instance_disposed", "instance_cleanup_failed", "registry_disposed"];
|
|
6
6
|
export type AgentProviderRegistryErrorCode = (typeof AGENT_PROVIDER_REGISTRY_ERROR_CODES)[number];
|
|
@@ -22,10 +22,10 @@ export declare class AgentProviderRegistryError extends Error {
|
|
|
22
22
|
export interface AgentProviderRegistry {
|
|
23
23
|
readonly listProviderCatalogEntries: () => readonly AgentProviderCatalogEntry[];
|
|
24
24
|
readonly listInstanceCatalogEntries: () => readonly AgentProviderInstanceCatalogEntry[];
|
|
25
|
-
readonly listInstances: () => readonly
|
|
26
|
-
readonly getInstance: (instanceId: AgentInstanceId) =>
|
|
25
|
+
readonly listInstances: () => readonly ValidatedMaterializedAgentProviderInstance[];
|
|
26
|
+
readonly getInstance: (instanceId: AgentInstanceId) => ValidatedMaterializedAgentProviderInstance | null;
|
|
27
27
|
readonly hasInstance: (instanceId: AgentInstanceId) => boolean;
|
|
28
|
-
readonly requireInstance: (instanceId: AgentInstanceId) =>
|
|
28
|
+
readonly requireInstance: (instanceId: AgentInstanceId) => ValidatedMaterializedAgentProviderInstance;
|
|
29
29
|
readonly checkReadiness: (instanceId: AgentInstanceId, input?: Readonly<{
|
|
30
30
|
signal?: AbortSignal;
|
|
31
31
|
}>) => Promise<AgentProviderReadiness>;
|
|
@@ -213,7 +213,8 @@ async function materializeInstances(input) {
|
|
|
213
213
|
providerKey: definition.providerKey,
|
|
214
214
|
instanceId: definition.instanceId,
|
|
215
215
|
cleanupFailureInstanceIds,
|
|
216
|
-
message: error instanceof AgentProviderConfigurationError ? `Agent provider configuration is invalid for ${definition.instanceId}.` : `Agent provider instance ${definition.instanceId} could not be materialized
|
|
216
|
+
message: error instanceof AgentProviderConfigurationError ? `Agent provider configuration is invalid for ${definition.instanceId}.` : `Agent provider instance ${definition.instanceId} could not be materialized.`,
|
|
217
|
+
cause: error
|
|
217
218
|
});
|
|
218
219
|
}
|
|
219
220
|
let managed;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AgentProviderKey, AgentRequestId } from "@agen-ai/agent-protocol";
|
|
2
|
+
export declare function createAgentSessionAdmission<Execution = unknown>(providerKey: AgentProviderKey): {
|
|
3
|
+
reserveExecution(): object;
|
|
4
|
+
installExecution(token: object, execution: Execution): void;
|
|
5
|
+
releaseExecution(token: object): void;
|
|
6
|
+
readonly execution: Execution | null;
|
|
7
|
+
ownsExecution(token: object): boolean;
|
|
8
|
+
stop(): void;
|
|
9
|
+
claimRequest(requestId: AgentRequestId, duplicateCode: "invalid_turn_sequence" | "invalid_session_interaction"): void;
|
|
10
|
+
};
|
|
11
|
+
export type AgentSessionAdmission = Pick<ReturnType<typeof createAgentSessionAdmission>, "claimRequest">;
|
|
12
|
+
//# sourceMappingURL=sessionAdmission.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { throwAgentProviderContractError } from "./contractErrors.js";
|
|
2
|
+
const MAX_REQUEST_IDENTITIES = 4096;
|
|
3
|
+
const MAX_REQUEST_ID_BYTES = 1048576;
|
|
4
|
+
function createAgentSessionAdmission(providerKey) {
|
|
5
|
+
const requestIds = /* @__PURE__ */ new Set();
|
|
6
|
+
let requestIdBytes = 0;
|
|
7
|
+
let slot;
|
|
8
|
+
let stopped = false;
|
|
9
|
+
return {
|
|
10
|
+
reserveExecution() {
|
|
11
|
+
if (stopped) throwAgentProviderContractError(providerKey, "session_unusable", "Session admission is stopped.");
|
|
12
|
+
if (slot) throwAgentProviderContractError(providerKey, "concurrent_turn", "Provider session already owns an execution.");
|
|
13
|
+
const token = Object.freeze({});
|
|
14
|
+
slot = { kind: "reserved", token };
|
|
15
|
+
return token;
|
|
16
|
+
},
|
|
17
|
+
installExecution(token, execution) {
|
|
18
|
+
if (stopped || !slot || slot.token !== token || slot.kind !== "reserved") {
|
|
19
|
+
throwAgentProviderContractError(providerKey, "invalid_native_continuation", "Execution reservation is no longer installable.");
|
|
20
|
+
}
|
|
21
|
+
slot = { kind: "installed", token, execution };
|
|
22
|
+
},
|
|
23
|
+
releaseExecution(token) {
|
|
24
|
+
if (slot?.token === token) slot = void 0;
|
|
25
|
+
},
|
|
26
|
+
get execution() {
|
|
27
|
+
return slot?.kind === "installed" ? slot.execution : null;
|
|
28
|
+
},
|
|
29
|
+
ownsExecution(token) {
|
|
30
|
+
return !stopped && slot?.token === token;
|
|
31
|
+
},
|
|
32
|
+
stop() {
|
|
33
|
+
stopped = true;
|
|
34
|
+
},
|
|
35
|
+
claimRequest(requestId, duplicateCode) {
|
|
36
|
+
if (requestIds.has(requestId)) {
|
|
37
|
+
throwAgentProviderContractError(providerKey, duplicateCode, "Provider reused a request ID within this session generation.");
|
|
38
|
+
}
|
|
39
|
+
const bytes = Buffer.byteLength(requestId, "utf8");
|
|
40
|
+
if (requestIds.size >= MAX_REQUEST_IDENTITIES || requestIdBytes + bytes > MAX_REQUEST_ID_BYTES) {
|
|
41
|
+
throwAgentProviderContractError(
|
|
42
|
+
providerKey,
|
|
43
|
+
"request_identity_limit_exceeded",
|
|
44
|
+
"Provider exceeded this session generation's bounded request identity capacity."
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
requestIds.add(requestId);
|
|
48
|
+
requestIdBytes += bytes;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
createAgentSessionAdmission
|
|
54
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types.js";
|