@ai-sdk/harness 1.0.116 → 1.0.118
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/CHANGELOG.md +32 -0
- package/dist/agent/index.js +64 -18
- package/dist/agent/index.js.map +1 -1
- package/dist/utils/index.d.ts +19 -3
- package/dist/utils/index.js +220 -53
- package/dist/utils/index.js.map +1 -1
- package/package.json +4 -4
- package/src/agent/internal/strip-work-dir.ts +91 -20
- package/src/utils/index.ts +2 -0
- package/src/utils/sandbox-channel.ts +220 -57
- package/src/utils/sleep.ts +30 -0
package/dist/utils/index.d.ts
CHANGED
|
@@ -22,13 +22,19 @@ type SandboxChannelDebugEvent = {
|
|
|
22
22
|
cause: unknown;
|
|
23
23
|
};
|
|
24
24
|
interface SandboxChannelReconnectOptions {
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Give up reconnecting after this many milliseconds, including connection
|
|
27
|
+
* establishment and backoff delays. Default 30_000.
|
|
28
|
+
*/
|
|
26
29
|
readonly maxElapsedMs?: number;
|
|
27
30
|
/** First backoff delay. Default 50. */
|
|
28
31
|
readonly initialDelayMs?: number;
|
|
29
32
|
/** Backoff ceiling. Default 2_000. */
|
|
30
33
|
readonly maxDelayMs?: number;
|
|
31
34
|
}
|
|
35
|
+
interface SandboxChannelConnectOptions {
|
|
36
|
+
readonly abortSignal: AbortSignal;
|
|
37
|
+
}
|
|
32
38
|
interface SandboxChannelOptions<TOut> {
|
|
33
39
|
/**
|
|
34
40
|
* Open a fresh WebSocket to the bridge and resolve once it is ready to carry
|
|
@@ -37,7 +43,7 @@ interface SandboxChannelOptions<TOut> {
|
|
|
37
43
|
* every transient reconnect. Must reject if the connection cannot be
|
|
38
44
|
* established.
|
|
39
45
|
*/
|
|
40
|
-
connect: () => Promise<WebSocket>;
|
|
46
|
+
connect: (options: SandboxChannelConnectOptions) => Promise<WebSocket>;
|
|
41
47
|
/** Schema validating inbound (bridge → host) frames. */
|
|
42
48
|
outboundSchema: FlexibleSchema<TOut>;
|
|
43
49
|
reconnect?: SandboxChannelReconnectOptions;
|
|
@@ -123,6 +129,8 @@ declare class SandboxChannel<TOut extends {
|
|
|
123
129
|
private _lastSeenEventId;
|
|
124
130
|
private readonly pendingSends;
|
|
125
131
|
private dispatchChain;
|
|
132
|
+
private activeConnectAbortController;
|
|
133
|
+
private reconnectAbortController;
|
|
126
134
|
constructor(options: SandboxChannelOptions<TOut>);
|
|
127
135
|
/**
|
|
128
136
|
* Highest bridge event `seq` this channel has observed. Persist it (e.g. via
|
|
@@ -174,6 +182,9 @@ declare class SandboxChannel<TOut extends {
|
|
|
174
182
|
isClosed(): boolean;
|
|
175
183
|
private wire;
|
|
176
184
|
private reconnectLoop;
|
|
185
|
+
private connect;
|
|
186
|
+
private abortActiveConnect;
|
|
187
|
+
private failReconnect;
|
|
177
188
|
private rawSend;
|
|
178
189
|
private flushPending;
|
|
179
190
|
private enqueue;
|
|
@@ -700,4 +711,9 @@ declare function resolveSandboxDefaultWorkingDirectory({ sandboxSession, abortSi
|
|
|
700
711
|
|
|
701
712
|
declare function getRestrictedSandboxSession(sandboxSession: HarnessV1NetworkSandboxSession | Experimental_SandboxSession): Experimental_SandboxSession;
|
|
702
713
|
|
|
703
|
-
|
|
714
|
+
declare function sleep({ ms, abortSignal, }: {
|
|
715
|
+
ms: number;
|
|
716
|
+
abortSignal?: AbortSignal;
|
|
717
|
+
}): Promise<void>;
|
|
718
|
+
|
|
719
|
+
export { type BridgeReadyErrorContext, type BridgeReadySource, type DiskLogRecoveryMode, type Experimental_BridgeUserMessageRequest, type Experimental_BridgeUserMessageResponse, type Experimental_BridgeUserMessageSubmitter, type OAuthCredential, type RefreshOAuthAccessTokenResult, SandboxChannel, type SandboxChannelConnectOptions, type SandboxChannelDebugEvent, type SandboxChannelOptions, type SandboxChannelReconnectOptions, type SkillFilePathMode, type WaitForBridgeReadyOptions, type WaitForBridgeReadyResult, type WriteInstructionsOptions, type WriteInstructionsResult, type WriteSkillsOptions, type WriteSkillsResult, applyCredentialForwarding, classifyDiskLog, createBridgeErrorHandler, createBridgeStartupError, createBridgeToken, createCredentialRequestTransformation, createReadBridgeAsset, createSandboxCredentialEnvironment, drainBridgeProcessStream, experimental_createBridgeUserMessageSubmitter, formatBridgeError, forwardBridgeProcessStream, generateSandboxCredentialPlaceholder, getAiGatewayAuthFromEnv, getJwtExpiresAt, getRestrictedSandboxSession, isAccessTokenExpiringSoon, isHarnessAuthenticationEnvironment, isLinux, isMacOS, isSandboxCredentialPlaceholder, isWindows, logBridgeError, markBridgeStarting, maskSandboxCredentials, parseJwtPayload, readLinuxSecretServicePassword, readMacOSKeychainPassword, readWindowsCredentialManagerPassword, refreshOAuthAccessToken, resolveSandboxDefaultWorkingDirectory, resolveSandboxHomeDir, shellQuote, shouldResolveNativeSubscription, sleep, waitForBridgeReady, warnCredentialBrokeringUnavailable, withBridgeToken, writeInstructions, writeSkills };
|
package/dist/utils/index.js
CHANGED
|
@@ -3,14 +3,95 @@ import {
|
|
|
3
3
|
safeParseJSON,
|
|
4
4
|
safeValidateTypes
|
|
5
5
|
} from "@ai-sdk/provider-utils";
|
|
6
|
+
|
|
7
|
+
// src/utils/sleep.ts
|
|
8
|
+
function sleep({
|
|
9
|
+
ms,
|
|
10
|
+
abortSignal
|
|
11
|
+
}) {
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
var _a;
|
|
14
|
+
if (abortSignal == null ? void 0 : abortSignal.aborted) {
|
|
15
|
+
resolve();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
let settled = false;
|
|
19
|
+
let onAbort = () => {
|
|
20
|
+
};
|
|
21
|
+
const finish = () => {
|
|
22
|
+
if (settled) return;
|
|
23
|
+
settled = true;
|
|
24
|
+
abortSignal == null ? void 0 : abortSignal.removeEventListener("abort", onAbort);
|
|
25
|
+
resolve();
|
|
26
|
+
};
|
|
27
|
+
const timer = setTimeout(finish, ms);
|
|
28
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
29
|
+
onAbort = () => {
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
finish();
|
|
32
|
+
};
|
|
33
|
+
abortSignal == null ? void 0 : abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/utils/sandbox-channel.ts
|
|
6
38
|
var sandboxChannelEventCheckpointSymbol = /* @__PURE__ */ Symbol.for(
|
|
7
39
|
"vercel.ai.harness.sandboxChannelEventCheckpoint"
|
|
8
40
|
);
|
|
9
|
-
|
|
41
|
+
function getAbortReason({
|
|
42
|
+
abortSignal
|
|
43
|
+
}) {
|
|
10
44
|
var _a;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
})
|
|
45
|
+
return (_a = abortSignal.reason) != null ? _a : new Error("SandboxChannel connection aborted");
|
|
46
|
+
}
|
|
47
|
+
function closeWebSocket({ ws }) {
|
|
48
|
+
try {
|
|
49
|
+
const terminable = ws;
|
|
50
|
+
if (terminable.terminate != null) {
|
|
51
|
+
terminable.terminate();
|
|
52
|
+
} else {
|
|
53
|
+
ws.close();
|
|
54
|
+
}
|
|
55
|
+
} catch (e) {
|
|
56
|
+
try {
|
|
57
|
+
ws.close();
|
|
58
|
+
} catch (e2) {
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function awaitWebSocketConnection({
|
|
63
|
+
connection,
|
|
64
|
+
abortSignal
|
|
65
|
+
}) {
|
|
66
|
+
if (abortSignal.aborted) {
|
|
67
|
+
void connection.then((ws) => closeWebSocket({ ws })).catch(() => {
|
|
68
|
+
});
|
|
69
|
+
throw getAbortReason({ abortSignal });
|
|
70
|
+
}
|
|
71
|
+
let onAbort;
|
|
72
|
+
const aborted = new Promise((_, reject) => {
|
|
73
|
+
onAbort = () => reject(getAbortReason({ abortSignal }));
|
|
74
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
const ws = await Promise.race([connection, aborted]);
|
|
78
|
+
if (abortSignal.aborted) {
|
|
79
|
+
closeWebSocket({ ws });
|
|
80
|
+
throw getAbortReason({ abortSignal });
|
|
81
|
+
}
|
|
82
|
+
return ws;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (abortSignal.aborted) {
|
|
85
|
+
void connection.then((ws) => closeWebSocket({ ws })).catch(() => {
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
throw error;
|
|
89
|
+
} finally {
|
|
90
|
+
if (onAbort != null) {
|
|
91
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
14
95
|
var SandboxChannel = class {
|
|
15
96
|
constructor(options) {
|
|
16
97
|
this.listeners = /* @__PURE__ */ new Map();
|
|
@@ -66,17 +147,27 @@ var SandboxChannel = class {
|
|
|
66
147
|
if (this.terminal) {
|
|
67
148
|
throw new Error("SandboxChannel: cannot open a closed channel.");
|
|
68
149
|
}
|
|
69
|
-
const
|
|
70
|
-
this.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
150
|
+
const abortController = new AbortController();
|
|
151
|
+
this.activeConnectAbortController = abortController;
|
|
152
|
+
try {
|
|
153
|
+
const ws = await this.connect({
|
|
154
|
+
abortSignal: abortController.signal
|
|
155
|
+
});
|
|
156
|
+
this.wire(ws);
|
|
157
|
+
this.ws = ws;
|
|
158
|
+
this.connected = true;
|
|
159
|
+
if (opts == null ? void 0 : opts.resume) {
|
|
160
|
+
this.rawSend(
|
|
161
|
+
JSON.stringify({
|
|
162
|
+
type: "resume",
|
|
163
|
+
lastSeenEventId: this._lastSeenEventId
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
} finally {
|
|
168
|
+
if (this.activeConnectAbortController === abortController) {
|
|
169
|
+
this.activeConnectAbortController = void 0;
|
|
170
|
+
}
|
|
80
171
|
}
|
|
81
172
|
}
|
|
82
173
|
on(type, listener) {
|
|
@@ -122,11 +213,13 @@ var SandboxChannel = class {
|
|
|
122
213
|
*/
|
|
123
214
|
beginClose() {
|
|
124
215
|
this.closing = true;
|
|
216
|
+
this.abortActiveConnect();
|
|
125
217
|
}
|
|
126
218
|
close() {
|
|
127
219
|
var _a;
|
|
128
220
|
if (this.terminal) return;
|
|
129
221
|
this.closing = true;
|
|
222
|
+
this.abortActiveConnect();
|
|
130
223
|
try {
|
|
131
224
|
(_a = this.ws) == null ? void 0 : _a.close();
|
|
132
225
|
} catch (e) {
|
|
@@ -157,6 +250,7 @@ var SandboxChannel = class {
|
|
|
157
250
|
}
|
|
158
251
|
this.suspended = true;
|
|
159
252
|
this.closing = true;
|
|
253
|
+
this.abortActiveConnect();
|
|
160
254
|
this.onClose(
|
|
161
255
|
() => resolve(pinnedSuspensionCursor != null ? pinnedSuspensionCursor : this._lastSeenEventId)
|
|
162
256
|
);
|
|
@@ -204,58 +298,129 @@ var SandboxChannel = class {
|
|
|
204
298
|
async reconnectLoop() {
|
|
205
299
|
var _a, _b, _c;
|
|
206
300
|
if (this.terminal || this.closing) return;
|
|
207
|
-
const
|
|
301
|
+
const deadline = Date.now() + this.maxElapsedMs;
|
|
208
302
|
let attempt = 0;
|
|
209
303
|
let delay = this.initialDelayMs;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
ws.close();
|
|
222
|
-
} catch (e) {
|
|
223
|
-
}
|
|
304
|
+
const reconnectAbortController = new AbortController();
|
|
305
|
+
this.reconnectAbortController = reconnectAbortController;
|
|
306
|
+
try {
|
|
307
|
+
while (!this.terminal && !this.closing) {
|
|
308
|
+
const remaining = deadline - Date.now();
|
|
309
|
+
if (remaining <= 0) {
|
|
310
|
+
this.failReconnect({
|
|
311
|
+
attempts: attempt,
|
|
312
|
+
cause: new Error("Reconnect deadline expired")
|
|
313
|
+
});
|
|
224
314
|
return;
|
|
225
315
|
}
|
|
226
|
-
|
|
227
|
-
this.
|
|
228
|
-
|
|
229
|
-
this.rawSend(
|
|
230
|
-
JSON.stringify({
|
|
231
|
-
type: "resume",
|
|
232
|
-
lastSeenEventId: this._lastSeenEventId
|
|
233
|
-
})
|
|
234
|
-
);
|
|
235
|
-
this.flushPending();
|
|
236
|
-
for (const handler of this.onReconnectHandlers) handler();
|
|
237
|
-
(_b = this.onDebug) == null ? void 0 : _b.call(this, {
|
|
238
|
-
event: "reconnected",
|
|
316
|
+
attempt++;
|
|
317
|
+
(_a = this.onDebug) == null ? void 0 : _a.call(this, {
|
|
318
|
+
event: "reconnect-attempt",
|
|
239
319
|
attempt,
|
|
240
320
|
lastSeenEventId: this._lastSeenEventId
|
|
241
321
|
});
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
322
|
+
const abortController = new AbortController();
|
|
323
|
+
this.activeConnectAbortController = abortController;
|
|
324
|
+
const abortConnect = () => abortController.abort(reconnectAbortController.signal.reason);
|
|
325
|
+
reconnectAbortController.signal.addEventListener(
|
|
326
|
+
"abort",
|
|
327
|
+
abortConnect,
|
|
328
|
+
{
|
|
329
|
+
once: true
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
const timeout = setTimeout(() => {
|
|
333
|
+
abortController.abort(new Error("Reconnect deadline expired"));
|
|
334
|
+
}, remaining);
|
|
335
|
+
(_b = timeout.unref) == null ? void 0 : _b.call(timeout);
|
|
336
|
+
try {
|
|
337
|
+
const ws = await this.connect({
|
|
338
|
+
abortSignal: abortController.signal
|
|
339
|
+
});
|
|
340
|
+
if (this.terminal || this.closing) {
|
|
341
|
+
closeWebSocket({ ws });
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
this.wire(ws);
|
|
345
|
+
this.ws = ws;
|
|
346
|
+
this.connected = true;
|
|
347
|
+
this.rawSend(
|
|
348
|
+
JSON.stringify({
|
|
349
|
+
type: "resume",
|
|
350
|
+
lastSeenEventId: this._lastSeenEventId
|
|
351
|
+
})
|
|
352
|
+
);
|
|
353
|
+
this.flushPending();
|
|
354
|
+
for (const handler of this.onReconnectHandlers) handler();
|
|
246
355
|
(_c = this.onDebug) == null ? void 0 : _c.call(this, {
|
|
247
|
-
event: "
|
|
248
|
-
|
|
249
|
-
lastSeenEventId: this._lastSeenEventId
|
|
250
|
-
cause
|
|
356
|
+
event: "reconnected",
|
|
357
|
+
attempt,
|
|
358
|
+
lastSeenEventId: this._lastSeenEventId
|
|
251
359
|
});
|
|
252
360
|
return;
|
|
361
|
+
} catch (cause) {
|
|
362
|
+
if (this.terminal || this.closing || reconnectAbortController.signal.aborted) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const remainingAfterFailure = deadline - Date.now();
|
|
366
|
+
if (remainingAfterFailure <= 0) {
|
|
367
|
+
this.failReconnect({ attempts: attempt, cause });
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
await sleep({
|
|
371
|
+
ms: Math.min(delay, remainingAfterFailure),
|
|
372
|
+
abortSignal: reconnectAbortController.signal
|
|
373
|
+
});
|
|
374
|
+
delay = Math.min(delay * 1.5, this.maxDelayMs);
|
|
375
|
+
} finally {
|
|
376
|
+
clearTimeout(timeout);
|
|
377
|
+
reconnectAbortController.signal.removeEventListener(
|
|
378
|
+
"abort",
|
|
379
|
+
abortConnect
|
|
380
|
+
);
|
|
381
|
+
if (this.activeConnectAbortController === abortController) {
|
|
382
|
+
this.activeConnectAbortController = void 0;
|
|
383
|
+
}
|
|
253
384
|
}
|
|
254
|
-
|
|
255
|
-
|
|
385
|
+
}
|
|
386
|
+
} finally {
|
|
387
|
+
if (this.reconnectAbortController === reconnectAbortController) {
|
|
388
|
+
this.reconnectAbortController = void 0;
|
|
256
389
|
}
|
|
257
390
|
}
|
|
258
391
|
}
|
|
392
|
+
connect({
|
|
393
|
+
abortSignal
|
|
394
|
+
}) {
|
|
395
|
+
return awaitWebSocketConnection({
|
|
396
|
+
connection: Promise.resolve().then(
|
|
397
|
+
() => this.connectThunk({ abortSignal })
|
|
398
|
+
),
|
|
399
|
+
abortSignal
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
abortActiveConnect() {
|
|
403
|
+
var _a;
|
|
404
|
+
const controller = this.activeConnectAbortController;
|
|
405
|
+
this.activeConnectAbortController = void 0;
|
|
406
|
+
controller == null ? void 0 : controller.abort(new Error("SandboxChannel connection aborted"));
|
|
407
|
+
(_a = this.reconnectAbortController) == null ? void 0 : _a.abort(
|
|
408
|
+
new Error("SandboxChannel connection aborted")
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
failReconnect({
|
|
412
|
+
attempts,
|
|
413
|
+
cause
|
|
414
|
+
}) {
|
|
415
|
+
var _a;
|
|
416
|
+
this.finalizeClose(1006, "reconnect failed");
|
|
417
|
+
(_a = this.onDebug) == null ? void 0 : _a.call(this, {
|
|
418
|
+
event: "reconnect-failed",
|
|
419
|
+
attempts,
|
|
420
|
+
lastSeenEventId: this._lastSeenEventId,
|
|
421
|
+
cause
|
|
422
|
+
});
|
|
423
|
+
}
|
|
259
424
|
rawSend(text) {
|
|
260
425
|
if (this.connected && this.ws) {
|
|
261
426
|
this.ws.send(text);
|
|
@@ -342,6 +507,7 @@ var SandboxChannel = class {
|
|
|
342
507
|
}
|
|
343
508
|
finalizeClose(code, reason) {
|
|
344
509
|
if (this.terminal) return;
|
|
510
|
+
this.abortActiveConnect();
|
|
345
511
|
this.terminal = true;
|
|
346
512
|
this.connected = false;
|
|
347
513
|
for (const h of this.onCloseHandlers) h(code, reason);
|
|
@@ -2315,6 +2481,7 @@ export {
|
|
|
2315
2481
|
resolveSandboxHomeDir,
|
|
2316
2482
|
shellQuote,
|
|
2317
2483
|
shouldResolveNativeSubscription,
|
|
2484
|
+
sleep,
|
|
2318
2485
|
waitForBridgeReady,
|
|
2319
2486
|
warnCredentialBrokeringUnavailable,
|
|
2320
2487
|
withBridgeToken,
|