@ai-sdk/harness-deepagents 1.0.77 → 1.0.79
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 +16 -0
- package/dist/bridge/index.mjs +147 -12
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +86 -27
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/deepagents-harness.ts +115 -30
|
@@ -24,7 +24,9 @@ import {
|
|
|
24
24
|
createBridgeStartupError,
|
|
25
25
|
drainBridgeProcessStream,
|
|
26
26
|
forwardBridgeProcessStream,
|
|
27
|
+
getRestrictedSandboxSession,
|
|
27
28
|
maskSandboxCredentials,
|
|
29
|
+
resolveSandboxDefaultWorkingDirectory,
|
|
28
30
|
resolveSandboxHomeDir,
|
|
29
31
|
SandboxChannel,
|
|
30
32
|
shellQuote,
|
|
@@ -32,7 +34,11 @@ import {
|
|
|
32
34
|
waitForBridgeReady,
|
|
33
35
|
writeSkills as writeHarnessSkills,
|
|
34
36
|
} from '@ai-sdk/harness/utils';
|
|
35
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
tool,
|
|
39
|
+
type Experimental_SandboxProcess,
|
|
40
|
+
type Experimental_SandboxSession as SandboxSession,
|
|
41
|
+
} from '@ai-sdk/provider-utils';
|
|
36
42
|
import { WebSocket } from 'ws';
|
|
37
43
|
import { z } from 'zod/v4';
|
|
38
44
|
import {
|
|
@@ -93,6 +99,11 @@ export type DeepAgentsHarnessSettings = {
|
|
|
93
99
|
readonly effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
94
100
|
/** Bridge port override; defaults to the sandbox's first declared port. */
|
|
95
101
|
readonly port?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Override the host endpoint used to connect to the sandbox bridge. Required
|
|
104
|
+
* together with `port` when using a basic sandbox session.
|
|
105
|
+
*/
|
|
106
|
+
readonly portEndpoint?: HarnessV1PortEndpoint;
|
|
96
107
|
/** Maximum milliseconds to wait for the bridge to advertise its port. Defaults to 120000. */
|
|
97
108
|
readonly startupTimeoutMs?: number;
|
|
98
109
|
/**
|
|
@@ -198,6 +209,26 @@ export function createDeepAgents(
|
|
|
198
209
|
doStart: async startOpts => {
|
|
199
210
|
const permissionMode = startOpts.permissionMode;
|
|
200
211
|
const sandboxSession = startOpts.sandboxSession;
|
|
212
|
+
const toolSafeSandboxSession =
|
|
213
|
+
getRestrictedSandboxSession(sandboxSession);
|
|
214
|
+
const sandboxId = 'id' in sandboxSession ? sandboxSession.id : undefined;
|
|
215
|
+
validateBasicSandboxSettings({
|
|
216
|
+
sandboxSession,
|
|
217
|
+
port: settings.port,
|
|
218
|
+
portEndpoint: settings.portEndpoint,
|
|
219
|
+
});
|
|
220
|
+
if (settings.mintBridgeToken != null && sandboxId == null) {
|
|
221
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
222
|
+
harnessId: 'deepagents',
|
|
223
|
+
message:
|
|
224
|
+
'The deepagents harness cannot use `mintBridgeToken` with a sandbox session that does not expose an id.',
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
const defaultWorkingDirectory =
|
|
228
|
+
await resolveSandboxDefaultWorkingDirectory({
|
|
229
|
+
sandboxSession,
|
|
230
|
+
abortSignal: startOpts.abortSignal,
|
|
231
|
+
});
|
|
201
232
|
const authenticationMode = resolveDeepAgentsAuthenticationMode({
|
|
202
233
|
auth: settings.auth,
|
|
203
234
|
});
|
|
@@ -205,7 +236,10 @@ export function createDeepAgents(
|
|
|
205
236
|
auth: settings.auth,
|
|
206
237
|
});
|
|
207
238
|
let sandboxAuthEnvironment = resolvedAuthEnvironment;
|
|
208
|
-
if (
|
|
239
|
+
if (
|
|
240
|
+
'addRequestTransformations' in sandboxSession &&
|
|
241
|
+
sandboxSession.addRequestTransformations != null
|
|
242
|
+
) {
|
|
209
243
|
const requestTransformations = createDeepAgentsRequestTransformations(
|
|
210
244
|
resolvedAuthEnvironment,
|
|
211
245
|
authenticationMode,
|
|
@@ -223,10 +257,8 @@ export function createDeepAgents(
|
|
|
223
257
|
} else {
|
|
224
258
|
warnCredentialBrokeringUnavailable();
|
|
225
259
|
}
|
|
226
|
-
const session = sandboxSession.restricted();
|
|
227
|
-
const sandboxId = sandboxSession.id;
|
|
228
260
|
const bootstrapDir = posix.resolve(
|
|
229
|
-
|
|
261
|
+
defaultWorkingDirectory,
|
|
230
262
|
BOOTSTRAP_DIR,
|
|
231
263
|
);
|
|
232
264
|
|
|
@@ -239,7 +271,7 @@ export function createDeepAgents(
|
|
|
239
271
|
: undefined;
|
|
240
272
|
|
|
241
273
|
const workDir = startOpts.sessionWorkDir;
|
|
242
|
-
const sessionDataDir = `${
|
|
274
|
+
const sessionDataDir = `${defaultWorkingDirectory}/.agent-runs/${startOpts.sessionId}`;
|
|
243
275
|
const bridgeStateDir = `${sessionDataDir}/bridge`;
|
|
244
276
|
const timeoutMs = settings.startupTimeoutMs ?? 120_000;
|
|
245
277
|
|
|
@@ -261,9 +293,10 @@ export function createDeepAgents(
|
|
|
261
293
|
// Attach to the still-running bridge (continueFrom replays past the cursor); on failure fall through to a fresh spawn.
|
|
262
294
|
if (coords) {
|
|
263
295
|
try {
|
|
264
|
-
const endpoint = await
|
|
296
|
+
const endpoint = await resolveBridgeEndpoint({
|
|
297
|
+
sandboxSession,
|
|
298
|
+
override: settings.portEndpoint,
|
|
265
299
|
port: coords.port,
|
|
266
|
-
protocol: 'ws',
|
|
267
300
|
});
|
|
268
301
|
const attachEndpoint = withBridgeToken({
|
|
269
302
|
endpoint,
|
|
@@ -299,11 +332,14 @@ export function createDeepAgents(
|
|
|
299
332
|
}
|
|
300
333
|
}
|
|
301
334
|
|
|
302
|
-
const port = resolveBridgePort(
|
|
335
|
+
const port = resolveBridgePort({
|
|
336
|
+
sandboxSession,
|
|
337
|
+
override: settings.port,
|
|
338
|
+
});
|
|
303
339
|
const token =
|
|
304
340
|
settings.mintBridgeToken == null
|
|
305
341
|
? randomBytes(32).toString('hex')
|
|
306
|
-
: settings.mintBridgeToken(sandboxId);
|
|
342
|
+
: settings.mintBridgeToken(sandboxId!);
|
|
307
343
|
|
|
308
344
|
// Always discover repo-provided skills under <workDir>/.agents/skills (e.g. a cloned repo); a missing dir is tolerated by deepagents.
|
|
309
345
|
// Absolute paths: LocalShellBackend (non-virtual) treats a leading-slash path as a real fs path.
|
|
@@ -311,12 +347,12 @@ export function createDeepAgents(
|
|
|
311
347
|
// Host-provided skills go to $HOME (out of the work dir) and take priority (listed last → wins on name collision).
|
|
312
348
|
if ((startOpts.skills?.length ?? 0) > 0) {
|
|
313
349
|
const homeDir = await resolveSandboxHomeDir({
|
|
314
|
-
sandbox:
|
|
350
|
+
sandbox: toolSafeSandboxSession,
|
|
315
351
|
abortSignal: startOpts.abortSignal,
|
|
316
352
|
});
|
|
317
353
|
const homeSkillsRoot = `${homeDir}${SKILLS_SOURCE_PATH}`;
|
|
318
354
|
await writeSkills({
|
|
319
|
-
sandbox:
|
|
355
|
+
sandbox: toolSafeSandboxSession,
|
|
320
356
|
root: homeSkillsRoot,
|
|
321
357
|
skills: startOpts.skills ?? [],
|
|
322
358
|
abortSignal: startOpts.abortSignal,
|
|
@@ -331,19 +367,19 @@ export function createDeepAgents(
|
|
|
331
367
|
BRIDGE_WS_PORT: String(port),
|
|
332
368
|
};
|
|
333
369
|
|
|
334
|
-
await
|
|
370
|
+
await toolSafeSandboxSession.run({
|
|
335
371
|
command: `mkdir -p ${shellQuote(workDir)} ${shellQuote(bridgeStateDir)}`,
|
|
336
372
|
abortSignal: startOpts.abortSignal,
|
|
337
373
|
});
|
|
338
374
|
|
|
339
375
|
await markBridgeStarting({
|
|
340
|
-
sandbox:
|
|
376
|
+
sandbox: toolSafeSandboxSession,
|
|
341
377
|
bridgeStateDir,
|
|
342
378
|
bridgeType: 'deepagents',
|
|
343
379
|
abortSignal: startOpts.abortSignal,
|
|
344
380
|
});
|
|
345
381
|
|
|
346
|
-
const proc = await
|
|
382
|
+
const proc = await toolSafeSandboxSession.spawn({
|
|
347
383
|
command: `node ${shellQuote(`${bootstrapDir}/bridge.mjs`)} --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --bootstrap-dir ${shellQuote(bootstrapDir)}`,
|
|
348
384
|
env,
|
|
349
385
|
abortSignal: startOpts.abortSignal,
|
|
@@ -358,7 +394,7 @@ export function createDeepAgents(
|
|
|
358
394
|
|
|
359
395
|
const { port: boundPort } = await waitForBridgeReady({
|
|
360
396
|
proc,
|
|
361
|
-
sandbox:
|
|
397
|
+
sandbox: toolSafeSandboxSession,
|
|
362
398
|
bridgeStateDir,
|
|
363
399
|
bridgeType: 'deepagents',
|
|
364
400
|
timeoutMs,
|
|
@@ -382,9 +418,10 @@ export function createDeepAgents(
|
|
|
382
418
|
});
|
|
383
419
|
void drainBridgeProcessStream(proc.stdout);
|
|
384
420
|
|
|
385
|
-
const endpoint = await
|
|
421
|
+
const endpoint = await resolveBridgeEndpoint({
|
|
422
|
+
sandboxSession,
|
|
423
|
+
override: settings.portEndpoint,
|
|
386
424
|
port: boundPort,
|
|
387
|
-
protocol: 'ws',
|
|
388
425
|
});
|
|
389
426
|
const bridgeEndpoint = withBridgeToken({ endpoint, token });
|
|
390
427
|
|
|
@@ -419,12 +456,17 @@ export function createDeepAgents(
|
|
|
419
456
|
};
|
|
420
457
|
}
|
|
421
458
|
|
|
422
|
-
function resolveBridgePort(
|
|
423
|
-
sandboxSession
|
|
424
|
-
override
|
|
425
|
-
|
|
459
|
+
function resolveBridgePort({
|
|
460
|
+
sandboxSession,
|
|
461
|
+
override,
|
|
462
|
+
}: {
|
|
463
|
+
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
464
|
+
override: number | undefined;
|
|
465
|
+
}): number {
|
|
426
466
|
if (override !== undefined) return override;
|
|
427
|
-
if (sandboxSession.ports.length > 0)
|
|
467
|
+
if ('ports' in sandboxSession && sandboxSession.ports.length > 0) {
|
|
468
|
+
return sandboxSession.ports[0];
|
|
469
|
+
}
|
|
428
470
|
throw new HarnessCapabilityUnsupportedError({
|
|
429
471
|
harnessId: 'deepagents',
|
|
430
472
|
message:
|
|
@@ -433,6 +475,52 @@ function resolveBridgePort(
|
|
|
433
475
|
});
|
|
434
476
|
}
|
|
435
477
|
|
|
478
|
+
function validateBasicSandboxSettings({
|
|
479
|
+
sandboxSession,
|
|
480
|
+
port,
|
|
481
|
+
portEndpoint,
|
|
482
|
+
}: {
|
|
483
|
+
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
484
|
+
port: number | undefined;
|
|
485
|
+
portEndpoint: HarnessV1PortEndpoint | undefined;
|
|
486
|
+
}): void {
|
|
487
|
+
if ('getPortEndpoint' in sandboxSession) return;
|
|
488
|
+
if (port == null) {
|
|
489
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
490
|
+
harnessId: 'deepagents',
|
|
491
|
+
message:
|
|
492
|
+
'The deepagents harness requires an explicit `port` when using a basic sandbox session.',
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
if (portEndpoint == null) {
|
|
496
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
497
|
+
harnessId: 'deepagents',
|
|
498
|
+
message:
|
|
499
|
+
'The deepagents harness requires an explicit `portEndpoint` when using a basic sandbox session.',
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
async function resolveBridgeEndpoint({
|
|
505
|
+
sandboxSession,
|
|
506
|
+
override,
|
|
507
|
+
port,
|
|
508
|
+
}: {
|
|
509
|
+
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
510
|
+
override: HarnessV1PortEndpoint | undefined;
|
|
511
|
+
port: number;
|
|
512
|
+
}): Promise<HarnessV1PortEndpoint> {
|
|
513
|
+
if (override != null) return override;
|
|
514
|
+
if ('getPortEndpoint' in sandboxSession) {
|
|
515
|
+
return sandboxSession.getPortEndpoint({ port, protocol: 'ws' });
|
|
516
|
+
}
|
|
517
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
518
|
+
harnessId: 'deepagents',
|
|
519
|
+
message:
|
|
520
|
+
'The deepagents harness requires an explicit `portEndpoint` when using a basic sandbox session.',
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
436
524
|
// Materialize each skill as a native deepagents `<name>/SKILL.md` folder (+ attached files) under the given root, so skills load on demand and file references resolve.
|
|
437
525
|
async function writeSkills({
|
|
438
526
|
sandbox,
|
|
@@ -440,7 +528,7 @@ async function writeSkills({
|
|
|
440
528
|
skills,
|
|
441
529
|
abortSignal,
|
|
442
530
|
}: {
|
|
443
|
-
sandbox:
|
|
531
|
+
sandbox: SandboxSession;
|
|
444
532
|
root: string;
|
|
445
533
|
skills: ReadonlyArray<HarnessV1Skill>;
|
|
446
534
|
abortSignal?: AbortSignal;
|
|
@@ -523,7 +611,7 @@ function createSession({
|
|
|
523
611
|
effort: 'low' | 'medium' | 'high' | 'xhigh' | 'max' | undefined;
|
|
524
612
|
bridgePort: number;
|
|
525
613
|
bridgeToken: string;
|
|
526
|
-
sandboxId: string;
|
|
614
|
+
sandboxId: string | undefined;
|
|
527
615
|
isResume: boolean;
|
|
528
616
|
// True only when attaching to a live bridge that already built the agent with
|
|
529
617
|
// its instructions. A fresh spawn (incl. a respawn on attach failure or a
|
|
@@ -641,9 +729,6 @@ function createSession({
|
|
|
641
729
|
isError: input.isError,
|
|
642
730
|
});
|
|
643
731
|
},
|
|
644
|
-
submitUserMessage: async text => {
|
|
645
|
-
channel.send({ type: 'user-message', text });
|
|
646
|
-
},
|
|
647
732
|
submitToolApproval: async input => {
|
|
648
733
|
channel.send({
|
|
649
734
|
type: 'tool-approval-response',
|
|
@@ -746,7 +831,7 @@ function createSession({
|
|
|
746
831
|
port: bridgePort,
|
|
747
832
|
token: bridgeToken,
|
|
748
833
|
lastSeenEventId,
|
|
749
|
-
sandboxId,
|
|
834
|
+
...(sandboxId == null ? {} : { sandboxId }),
|
|
750
835
|
},
|
|
751
836
|
},
|
|
752
837
|
};
|
|
@@ -770,7 +855,7 @@ function createSession({
|
|
|
770
855
|
port: bridgePort,
|
|
771
856
|
token: bridgeToken,
|
|
772
857
|
lastSeenEventId,
|
|
773
|
-
sandboxId,
|
|
858
|
+
...(sandboxId == null ? {} : { sandboxId }),
|
|
774
859
|
},
|
|
775
860
|
},
|
|
776
861
|
};
|