@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
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { AgentError, AgentProviderKey, AgentSessionId } from "@agen-ai/agent-protocol";
|
|
2
|
+
import type { MaybePromise } from "../foundation.js";
|
|
3
|
+
import type { AgentSessionAdmission } from "../sessionAdmission.js";
|
|
4
|
+
import type { AgentSessionValidationOwner } from "../sessionValidation.js";
|
|
5
|
+
import type { AgentNativeContinuationContext, AgentNativeContinuationSupport } from "../nativeContinuations/types.js";
|
|
6
|
+
import type { AgentProviderSessionInteractionContext, AgentSessionInteractionContext, AgentSessionInteractionOpeningOperation, AgentSessionInteractionSupport, AgentSessionInteractionTerminal } from "./types.js";
|
|
7
|
+
import { SessionInteractionRequest } from "./request.js";
|
|
8
|
+
import { type ValidatedSessionInteractionContext } from "./validation.js";
|
|
9
|
+
export declare function sessionInteractionError(code: string, message: string, uncertain?: boolean): AgentError;
|
|
10
|
+
export declare function waitForInteractionWork<Result>(work: Promise<Result>, timeoutMs: number): Promise<Result>;
|
|
11
|
+
export declare class SessionInteractionOwner {
|
|
12
|
+
readonly providerKey: AgentProviderKey;
|
|
13
|
+
readonly sessionId: AgentSessionId;
|
|
14
|
+
readonly validationOwner: AgentSessionValidationOwner;
|
|
15
|
+
readonly operation: AgentSessionInteractionOpeningOperation;
|
|
16
|
+
readonly support: AgentSessionInteractionSupport;
|
|
17
|
+
readonly nativeSupport: AgentNativeContinuationSupport;
|
|
18
|
+
readonly admission: AgentSessionAdmission;
|
|
19
|
+
readonly facade: AgentProviderSessionInteractionContext;
|
|
20
|
+
readonly openingSignal: AbortSignal;
|
|
21
|
+
readonly limits: ValidatedSessionInteractionContext["limits"];
|
|
22
|
+
readonly failure: Promise<unknown>;
|
|
23
|
+
private readonly controller;
|
|
24
|
+
private readonly requests;
|
|
25
|
+
private readonly work;
|
|
26
|
+
private state;
|
|
27
|
+
private retainedBytes;
|
|
28
|
+
private fatal;
|
|
29
|
+
private fenceCause;
|
|
30
|
+
private resolveFailure;
|
|
31
|
+
private removeOpeningAbort;
|
|
32
|
+
private automaticClose;
|
|
33
|
+
private cleanupAttempt;
|
|
34
|
+
private cleanupDeadline;
|
|
35
|
+
private cleanupComplete;
|
|
36
|
+
private retainedCleanup;
|
|
37
|
+
private cleanupFailure;
|
|
38
|
+
private adopted;
|
|
39
|
+
private bound;
|
|
40
|
+
private returned;
|
|
41
|
+
private readonly waiting;
|
|
42
|
+
private associationTimer;
|
|
43
|
+
private prebindingCount;
|
|
44
|
+
private prebindingBytes;
|
|
45
|
+
private readonly preparations;
|
|
46
|
+
private stoppedExecution;
|
|
47
|
+
readonly continuations: AgentNativeContinuationContext | undefined;
|
|
48
|
+
private readonly context;
|
|
49
|
+
constructor(providerKey: AgentProviderKey, sessionId: AgentSessionId, context: AgentSessionInteractionContext, validationOwner: AgentSessionValidationOwner, operation: AgentSessionInteractionOpeningOperation, support: AgentSessionInteractionSupport, nativeSupport: AgentNativeContinuationSupport, continuations?: AgentNativeContinuationContext, signal?: AbortSignal);
|
|
50
|
+
track<Result>(promise: Promise<Result>): Promise<Result>;
|
|
51
|
+
get accepting(): boolean;
|
|
52
|
+
requireUsable(): void;
|
|
53
|
+
private offer;
|
|
54
|
+
requireContinuation(kind: "form" | "plan_review"): void;
|
|
55
|
+
private canPresent;
|
|
56
|
+
bind(): void;
|
|
57
|
+
markReturned(): void;
|
|
58
|
+
private presentWaiting;
|
|
59
|
+
cleanupFailedPreparation(): Promise<void>;
|
|
60
|
+
quiesce(): void;
|
|
61
|
+
retainPreparation(cleanup: () => Promise<void>): () => void;
|
|
62
|
+
installClose(close: () => Promise<void>): void;
|
|
63
|
+
adopt(): void;
|
|
64
|
+
fence(cause: unknown, source: SessionInteractionRequest): void;
|
|
65
|
+
fail(cause: unknown): void;
|
|
66
|
+
stop(terminal?: AgentSessionInteractionTerminal): void;
|
|
67
|
+
join(): Promise<void>;
|
|
68
|
+
close(cleanup: () => MaybePromise<void>, explicit: boolean): Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
export declare function createSessionInteractionOwner(input: Readonly<{
|
|
71
|
+
providerKey: AgentProviderKey;
|
|
72
|
+
sessionId: AgentSessionId;
|
|
73
|
+
operation: AgentSessionInteractionOpeningOperation;
|
|
74
|
+
support: AgentSessionInteractionSupport;
|
|
75
|
+
nativeSupport: AgentNativeContinuationSupport;
|
|
76
|
+
validationOwner: AgentSessionValidationOwner;
|
|
77
|
+
context?: AgentSessionInteractionContext;
|
|
78
|
+
continuations?: AgentNativeContinuationContext;
|
|
79
|
+
signal?: AbortSignal;
|
|
80
|
+
}>): SessionInteractionOwner | undefined;
|
|
81
|
+
//# sourceMappingURL=lifetime.d.ts.map
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { AgentProviderContractError, AgentProviderSessionCleanupError } from "../contractErrors.js";
|
|
2
|
+
import { SessionInteractionRequest } from "./request.js";
|
|
3
|
+
import {
|
|
4
|
+
captureSessionInteractionCandidate,
|
|
5
|
+
interactionContractError,
|
|
6
|
+
validateSessionInteractionCandidate,
|
|
7
|
+
validateSessionInteractionContext,
|
|
8
|
+
validateNativeContinuationContext
|
|
9
|
+
} from "./validation.js";
|
|
10
|
+
function sessionInteractionError(code, message, uncertain = false) {
|
|
11
|
+
return Object.freeze({
|
|
12
|
+
category: uncertain ? "unknown" : "invalid_request",
|
|
13
|
+
code,
|
|
14
|
+
message,
|
|
15
|
+
recovery: Object.freeze({ kind: uncertain ? "unknown" : "none" })
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function waitForInteractionWork(work, timeoutMs) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const timer = setTimeout(() => reject(new Error("Session interaction cleanup did not finish before its deadline.")), timeoutMs);
|
|
21
|
+
work.then((result) => {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
resolve(result);
|
|
24
|
+
}, (error) => {
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
reject(error);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
class SessionInteractionOwner {
|
|
31
|
+
constructor(providerKey, sessionId, context, validationOwner, operation, support, nativeSupport, continuations, signal) {
|
|
32
|
+
this.providerKey = providerKey;
|
|
33
|
+
this.sessionId = sessionId;
|
|
34
|
+
this.validationOwner = validationOwner;
|
|
35
|
+
this.operation = operation;
|
|
36
|
+
this.support = support;
|
|
37
|
+
this.nativeSupport = nativeSupport;
|
|
38
|
+
this.admission = validationOwner.sessionAdmission;
|
|
39
|
+
this.continuations = validateNativeContinuationContext(continuations);
|
|
40
|
+
try {
|
|
41
|
+
this.context = validateSessionInteractionContext(context);
|
|
42
|
+
} catch (cause) {
|
|
43
|
+
throw new AgentProviderContractError(
|
|
44
|
+
providerKey,
|
|
45
|
+
"invalid_session_interaction",
|
|
46
|
+
"Session interaction context is invalid.",
|
|
47
|
+
{ cause }
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
this.limits = this.context.limits;
|
|
51
|
+
this.openingSignal = this.controller.signal;
|
|
52
|
+
this.failure = new Promise((resolve) => {
|
|
53
|
+
this.resolveFailure = resolve;
|
|
54
|
+
});
|
|
55
|
+
const { requestTimeoutMs: _timeout, ...nonHumanLimits } = this.limits;
|
|
56
|
+
const onRequest = (candidate) => this.offer(candidate);
|
|
57
|
+
this.facade = this.context.humanWait === "bounded" ? Object.freeze({ humanWait: Object.freeze({ kind: "bounded" }), limits: this.limits, onRequest }) : Object.freeze({ humanWait: Object.freeze({ kind: "until_withdrawn_or_closed" }), limits: Object.freeze(nonHumanLimits), onRequest });
|
|
58
|
+
if (signal) {
|
|
59
|
+
const aborted = () => this.fail(signal.reason ?? new DOMException("Session opening was aborted.", "AbortError"));
|
|
60
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
61
|
+
this.removeOpeningAbort = () => signal.removeEventListener("abort", aborted);
|
|
62
|
+
if (signal.aborted) aborted();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
admission;
|
|
66
|
+
facade;
|
|
67
|
+
openingSignal;
|
|
68
|
+
limits;
|
|
69
|
+
failure;
|
|
70
|
+
controller = new AbortController();
|
|
71
|
+
requests = /* @__PURE__ */ new Map();
|
|
72
|
+
work = /* @__PURE__ */ new Set();
|
|
73
|
+
state = "opening";
|
|
74
|
+
retainedBytes = 0;
|
|
75
|
+
fatal;
|
|
76
|
+
fenceCause;
|
|
77
|
+
resolveFailure;
|
|
78
|
+
removeOpeningAbort = () => {
|
|
79
|
+
};
|
|
80
|
+
automaticClose;
|
|
81
|
+
cleanupAttempt = null;
|
|
82
|
+
cleanupDeadline = 0;
|
|
83
|
+
cleanupComplete = false;
|
|
84
|
+
retainedCleanup;
|
|
85
|
+
cleanupFailure;
|
|
86
|
+
adopted = false;
|
|
87
|
+
bound = false;
|
|
88
|
+
returned = false;
|
|
89
|
+
waiting = /* @__PURE__ */ new Map();
|
|
90
|
+
associationTimer;
|
|
91
|
+
prebindingCount = 0;
|
|
92
|
+
prebindingBytes = 0;
|
|
93
|
+
preparations = /* @__PURE__ */ new Set();
|
|
94
|
+
stoppedExecution = null;
|
|
95
|
+
continuations;
|
|
96
|
+
context;
|
|
97
|
+
track(promise) {
|
|
98
|
+
this.work.add(promise);
|
|
99
|
+
void promise.then(() => this.work.delete(promise), () => this.work.delete(promise));
|
|
100
|
+
return promise;
|
|
101
|
+
}
|
|
102
|
+
get accepting() {
|
|
103
|
+
return this.state === "opening" || this.state === "open";
|
|
104
|
+
}
|
|
105
|
+
requireUsable() {
|
|
106
|
+
if (this.fatal) throw this.fatal.cause;
|
|
107
|
+
this.validationOwner.requireUsable();
|
|
108
|
+
if (!this.accepting) throw this.fenceCause ?? new AgentProviderContractError(
|
|
109
|
+
this.providerKey,
|
|
110
|
+
this.state === "closed" ? "session_closed" : "session_unusable",
|
|
111
|
+
"Session interactions no longer permit provider mutation."
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
offer(candidate) {
|
|
115
|
+
let captured;
|
|
116
|
+
try {
|
|
117
|
+
captured = captureSessionInteractionCandidate(candidate, (terminal) => {
|
|
118
|
+
this.track(terminal);
|
|
119
|
+
});
|
|
120
|
+
const validated = validateSessionInteractionCandidate(captured, this.sessionId, this.context);
|
|
121
|
+
this.requireUsable();
|
|
122
|
+
if (this.support.kind !== "supported" || !this.support.mappings.some((entry) => entry.openingOperation === this.operation && entry.requestKind === validated.request.kind)) {
|
|
123
|
+
throw interactionContractError(this.providerKey, "Provider offered an undeclared session request kind.");
|
|
124
|
+
}
|
|
125
|
+
if (validated.kind === "prepared") this.requireContinuation(validated.request.kind);
|
|
126
|
+
if (validated.signal.aborted) throw interactionContractError(this.providerKey, "Provider offered an unavailable session interaction.");
|
|
127
|
+
const bytes = Buffer.byteLength(JSON.stringify(validated.request), "utf8");
|
|
128
|
+
if (this.requests.has(validated.request.requestId) || this.requests.size >= this.limits.maxRetainedRequests || this.retainedBytes + bytes > this.limits.maxRetainedBytes) {
|
|
129
|
+
throw interactionContractError(this.providerKey, "Session interaction identity or retained capacity is unavailable.");
|
|
130
|
+
}
|
|
131
|
+
this.admission.claimRequest(validated.request.requestId, "invalid_session_interaction");
|
|
132
|
+
const request = new SessionInteractionRequest(this, validated, () => {
|
|
133
|
+
this.requests.delete(validated.request.requestId);
|
|
134
|
+
this.retainedBytes -= bytes;
|
|
135
|
+
});
|
|
136
|
+
this.requests.set(validated.request.requestId, request);
|
|
137
|
+
this.retainedBytes += bytes;
|
|
138
|
+
request.observe();
|
|
139
|
+
let resolveCallback;
|
|
140
|
+
let rejectCallback;
|
|
141
|
+
const callback = new Promise((resolve, reject) => {
|
|
142
|
+
resolveCallback = resolve;
|
|
143
|
+
rejectCallback = reject;
|
|
144
|
+
});
|
|
145
|
+
const present = () => {
|
|
146
|
+
this.waiting.delete(present);
|
|
147
|
+
if (!this.accepting || request.handle.signal.aborted) {
|
|
148
|
+
resolveCallback();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
Promise.resolve(this.context.onRequest(request.handle)).then(resolveCallback, rejectCallback);
|
|
153
|
+
} catch (cause) {
|
|
154
|
+
rejectCallback(cause);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
request.setCallback(callback);
|
|
158
|
+
this.track(callback);
|
|
159
|
+
void callback.catch((cause) => this.fail(cause));
|
|
160
|
+
if (validated.kind === "direct" || this.canPresent(validated.request.kind)) present();
|
|
161
|
+
else {
|
|
162
|
+
this.waiting.set(present, validated.request.kind);
|
|
163
|
+
if (!this.bound) {
|
|
164
|
+
this.prebindingCount += 1;
|
|
165
|
+
this.prebindingBytes += bytes;
|
|
166
|
+
if (this.prebindingCount > 16 || this.prebindingBytes > 1048576) throw new AgentProviderContractError(
|
|
167
|
+
this.providerKey,
|
|
168
|
+
"native_continuation_limit_exceeded",
|
|
169
|
+
"Prebinding association exceeded its retained material budget."
|
|
170
|
+
);
|
|
171
|
+
this.associationTimer ??= setTimeout(() => this.fail(new AgentProviderContractError(
|
|
172
|
+
this.providerKey,
|
|
173
|
+
"invalid_native_continuation",
|
|
174
|
+
"Prebinding association did not finish before its deadline."
|
|
175
|
+
)), 3e4);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return callback;
|
|
179
|
+
} catch (cause) {
|
|
180
|
+
const failure = cause instanceof AgentProviderContractError ? cause : new AgentProviderContractError(
|
|
181
|
+
this.providerKey,
|
|
182
|
+
"invalid_session_interaction",
|
|
183
|
+
"Provider offered an invalid session interaction.",
|
|
184
|
+
{ cause }
|
|
185
|
+
);
|
|
186
|
+
this.fail(failure);
|
|
187
|
+
const rejected = Promise.reject(failure);
|
|
188
|
+
void rejected.catch(() => {
|
|
189
|
+
});
|
|
190
|
+
return rejected;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
requireContinuation(kind) {
|
|
194
|
+
if (!this.continuations || this.nativeSupport.kind !== "supported" || !this.nativeSupport.mappings.some((entry) => entry.openingOperation === this.operation && entry.requestKind === kind)) {
|
|
195
|
+
throw new AgentProviderContractError(this.providerKey, "native_continuations_unsupported", "Provider native continuation requires its exact supported tuple and a host output handler.");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
canPresent(kind) {
|
|
199
|
+
return this.bound && this.nativeSupport.kind === "supported" && this.nativeSupport.mappings.some((entry) => entry.openingOperation === this.operation && entry.requestKind === kind && (entry.timing !== "after_open_return" || this.returned));
|
|
200
|
+
}
|
|
201
|
+
bind() {
|
|
202
|
+
this.requireUsable();
|
|
203
|
+
this.bound = true;
|
|
204
|
+
if (this.associationTimer) clearTimeout(this.associationTimer);
|
|
205
|
+
this.prebindingCount = 0;
|
|
206
|
+
this.prebindingBytes = 0;
|
|
207
|
+
this.presentWaiting();
|
|
208
|
+
}
|
|
209
|
+
markReturned() {
|
|
210
|
+
this.returned = true;
|
|
211
|
+
this.presentWaiting();
|
|
212
|
+
}
|
|
213
|
+
presentWaiting() {
|
|
214
|
+
for (const [present, kind] of this.waiting) if (!this.accepting || this.canPresent(kind)) present();
|
|
215
|
+
}
|
|
216
|
+
async cleanupFailedPreparation() {
|
|
217
|
+
if (this.cleanupComplete) return;
|
|
218
|
+
if (this.retainedCleanup) return this.close(this.retainedCleanup, false);
|
|
219
|
+
if (this.automaticClose) return this.automaticClose();
|
|
220
|
+
throw new Error("Opening cleanup has not acquired its native session owner.");
|
|
221
|
+
}
|
|
222
|
+
quiesce() {
|
|
223
|
+
if (this.adopted && this.automaticClose) void this.automaticClose().catch(() => {
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
retainPreparation(cleanup) {
|
|
227
|
+
this.preparations.add(cleanup);
|
|
228
|
+
return () => {
|
|
229
|
+
this.preparations.delete(cleanup);
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
installClose(close) {
|
|
233
|
+
this.automaticClose = close;
|
|
234
|
+
this.validationOwner.installClose(close);
|
|
235
|
+
}
|
|
236
|
+
adopt() {
|
|
237
|
+
this.requireUsable();
|
|
238
|
+
this.removeOpeningAbort();
|
|
239
|
+
this.state = "open";
|
|
240
|
+
this.adopted = true;
|
|
241
|
+
}
|
|
242
|
+
fence(cause, source) {
|
|
243
|
+
if (!this.accepting) return;
|
|
244
|
+
this.validationOwner.fence();
|
|
245
|
+
this.fenceCause = new AgentProviderContractError(
|
|
246
|
+
this.providerKey,
|
|
247
|
+
"session_unusable",
|
|
248
|
+
"Provider session cannot be reused after uncertain session interaction delivery.",
|
|
249
|
+
{ cause }
|
|
250
|
+
);
|
|
251
|
+
this.state = "mutation-fenced";
|
|
252
|
+
if (!this.adopted) {
|
|
253
|
+
this.resolveFailure(this.fenceCause);
|
|
254
|
+
this.controller.abort(new DOMException("Session opening lost confirmed interaction delivery.", "AbortError"));
|
|
255
|
+
}
|
|
256
|
+
const terminal = Object.freeze({ kind: "failed", error: sessionInteractionError(
|
|
257
|
+
"session_interaction_delivery_uncertain",
|
|
258
|
+
"A session interaction response could not be confirmed.",
|
|
259
|
+
true
|
|
260
|
+
) });
|
|
261
|
+
for (const request of this.requests.values()) if (request !== source) request.stop(terminal, false);
|
|
262
|
+
}
|
|
263
|
+
fail(cause) {
|
|
264
|
+
if (!this.fatal) {
|
|
265
|
+
this.fatal = { cause, observed: false };
|
|
266
|
+
this.resolveFailure(cause);
|
|
267
|
+
}
|
|
268
|
+
this.stop(Object.freeze({ kind: "failed", error: sessionInteractionError(
|
|
269
|
+
"session_interaction_failed",
|
|
270
|
+
"The session interaction owner failed."
|
|
271
|
+
) }));
|
|
272
|
+
if (this.adopted && this.automaticClose) {
|
|
273
|
+
void this.automaticClose().catch(() => {
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
stop(terminal = Object.freeze({ kind: "session_closed" })) {
|
|
278
|
+
if (this.state !== "closed" && this.state !== "cleanup-pending") this.state = "stopping";
|
|
279
|
+
this.stoppedExecution ??= this.validationOwner.activeTurn;
|
|
280
|
+
this.validationOwner.stop();
|
|
281
|
+
this.stoppedExecution?.owner.stop(new DOMException("The session interaction owner was stopped.", "AbortError"));
|
|
282
|
+
this.removeOpeningAbort();
|
|
283
|
+
if (this.associationTimer) clearTimeout(this.associationTimer);
|
|
284
|
+
this.presentWaiting();
|
|
285
|
+
this.controller.abort(new DOMException("The session interaction owner was stopped.", "AbortError"));
|
|
286
|
+
for (const request of this.requests.values()) request.stop(terminal);
|
|
287
|
+
}
|
|
288
|
+
async join() {
|
|
289
|
+
await Promise.all([...this.preparations].map((cleanup) => cleanup()));
|
|
290
|
+
while (this.work.size > 0) await Promise.allSettled([...this.work]);
|
|
291
|
+
await Promise.all([...this.requests.values()].map((request) => request.join()));
|
|
292
|
+
await this.stoppedExecution?.owner.join();
|
|
293
|
+
}
|
|
294
|
+
close(cleanup, explicit) {
|
|
295
|
+
this.retainedCleanup ??= cleanup;
|
|
296
|
+
this.stop();
|
|
297
|
+
if (!this.cleanupComplete && !this.cleanupAttempt) {
|
|
298
|
+
this.state = "cleanup-pending";
|
|
299
|
+
this.cleanupDeadline = Date.now() + this.limits.cleanupTimeoutMs;
|
|
300
|
+
const attempt = Promise.resolve().then(cleanup).then(() => {
|
|
301
|
+
this.cleanupComplete = true;
|
|
302
|
+
this.state = "closed";
|
|
303
|
+
});
|
|
304
|
+
this.cleanupAttempt = attempt;
|
|
305
|
+
void attempt.then(
|
|
306
|
+
() => {
|
|
307
|
+
if (this.cleanupAttempt === attempt) this.cleanupAttempt = null;
|
|
308
|
+
},
|
|
309
|
+
(cause) => {
|
|
310
|
+
this.cleanupFailure = { cause };
|
|
311
|
+
if (this.cleanupAttempt === attempt) this.cleanupAttempt = null;
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
const actual = this.cleanupAttempt ?? Promise.resolve();
|
|
316
|
+
return waitForInteractionWork(actual, this.cleanupComplete ? this.limits.cleanupTimeoutMs : Math.max(0, this.cleanupDeadline - Date.now())).then(() => {
|
|
317
|
+
if (explicit && this.fatal && !this.fatal.observed) {
|
|
318
|
+
this.fatal.observed = true;
|
|
319
|
+
if (this.cleanupFailure) throw new AggregateError(
|
|
320
|
+
[this.fatal.cause, this.cleanupFailure.cause],
|
|
321
|
+
"Session interactions failed; retained cleanup subsequently completed."
|
|
322
|
+
);
|
|
323
|
+
throw this.fatal.cause;
|
|
324
|
+
}
|
|
325
|
+
if (explicit) this.cleanupFailure = void 0;
|
|
326
|
+
}, (cleanupError) => {
|
|
327
|
+
const causes = [cleanupError];
|
|
328
|
+
if (explicit && this.fatal && !this.fatal.observed) {
|
|
329
|
+
this.fatal.observed = true;
|
|
330
|
+
causes.unshift(this.fatal.cause);
|
|
331
|
+
}
|
|
332
|
+
throw new AgentProviderSessionCleanupError({
|
|
333
|
+
providerKey: this.providerKey,
|
|
334
|
+
errors: causes,
|
|
335
|
+
retryCleanup: () => this.close(cleanup, true)
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function createSessionInteractionOwner(input) {
|
|
341
|
+
if (input.context === void 0) {
|
|
342
|
+
if (input.continuations !== void 0) throw new AgentProviderContractError(input.providerKey, "native_continuations_unsupported", "Native continuations require session interaction handlers.");
|
|
343
|
+
return void 0;
|
|
344
|
+
}
|
|
345
|
+
if (input.support.kind === "unsupported" || !input.support.mappings.some((entry) => entry.openingOperation === input.operation)) {
|
|
346
|
+
throw new AgentProviderContractError(
|
|
347
|
+
input.providerKey,
|
|
348
|
+
"session_interactions_unsupported",
|
|
349
|
+
"Provider does not support session interactions for this opening operation."
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
let parsed;
|
|
353
|
+
try {
|
|
354
|
+
parsed = validateSessionInteractionContext(input.context);
|
|
355
|
+
} catch (cause) {
|
|
356
|
+
throw new AgentProviderContractError(input.providerKey, "invalid_session_interaction", "Session interaction context is invalid.", { cause });
|
|
357
|
+
}
|
|
358
|
+
if (!input.support.humanWait.includes(parsed.humanWait)) throw new AgentProviderContractError(
|
|
359
|
+
input.providerKey,
|
|
360
|
+
"session_interactions_unsupported",
|
|
361
|
+
"Provider does not support the selected human wait policy."
|
|
362
|
+
);
|
|
363
|
+
if (input.continuations !== void 0 && (input.nativeSupport.kind !== "supported" || !input.nativeSupport.mappings.some((entry) => entry.openingOperation === input.operation))) {
|
|
364
|
+
throw new AgentProviderContractError(input.providerKey, "native_continuations_unsupported", "Provider does not support continuations for this opening operation.");
|
|
365
|
+
}
|
|
366
|
+
return new SessionInteractionOwner(
|
|
367
|
+
input.providerKey,
|
|
368
|
+
input.sessionId,
|
|
369
|
+
input.context,
|
|
370
|
+
input.validationOwner,
|
|
371
|
+
input.operation,
|
|
372
|
+
input.support,
|
|
373
|
+
input.nativeSupport,
|
|
374
|
+
input.continuations,
|
|
375
|
+
input.signal
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
export {
|
|
379
|
+
SessionInteractionOwner,
|
|
380
|
+
createSessionInteractionOwner,
|
|
381
|
+
sessionInteractionError,
|
|
382
|
+
waitForInteractionWork
|
|
383
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type SessionInteractionOwner } from "./lifetime.js";
|
|
2
|
+
import type { AgentSessionInteraction, AgentSessionInteractionTerminal } from "./types.js";
|
|
3
|
+
import { type ValidatedSessionInteractionCandidate } from "./validation.js";
|
|
4
|
+
type Candidate = ValidatedSessionInteractionCandidate;
|
|
5
|
+
export declare class SessionInteractionRequest {
|
|
6
|
+
private readonly owner;
|
|
7
|
+
private readonly candidate;
|
|
8
|
+
private readonly retire;
|
|
9
|
+
readonly handle: AgentSessionInteraction;
|
|
10
|
+
private state;
|
|
11
|
+
private readonly presentation;
|
|
12
|
+
private resolveTerminal;
|
|
13
|
+
private callback;
|
|
14
|
+
private callbackSettled;
|
|
15
|
+
private responseWork;
|
|
16
|
+
private rawTerminalSettled;
|
|
17
|
+
private nativeTerminal;
|
|
18
|
+
private lastAttempt;
|
|
19
|
+
private stopped;
|
|
20
|
+
private unavailable;
|
|
21
|
+
private retired;
|
|
22
|
+
private expiryTimer;
|
|
23
|
+
private terminalTimer;
|
|
24
|
+
private readonly nativeAborted;
|
|
25
|
+
constructor(owner: SessionInteractionOwner, candidate: Candidate, retire: () => void);
|
|
26
|
+
observe(): void;
|
|
27
|
+
setCallback(callback: Promise<void>): void;
|
|
28
|
+
private scheduleExpiry;
|
|
29
|
+
private expire;
|
|
30
|
+
private protocolFailure;
|
|
31
|
+
private observeTerminal;
|
|
32
|
+
private expectTerminal;
|
|
33
|
+
private finalize;
|
|
34
|
+
stop(terminal: AgentSessionInteractionTerminal, nativeRetirementExpected?: boolean): void;
|
|
35
|
+
private refusal;
|
|
36
|
+
private uncertain;
|
|
37
|
+
private receiptContradiction;
|
|
38
|
+
private respond;
|
|
39
|
+
private maybeRetire;
|
|
40
|
+
join(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=request.d.ts.map
|