@ai-sdk/harness-deepagents 1.0.70 → 1.0.72
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 +20 -0
- package/dist/bridge/index.mjs +44 -44
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +185 -80
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/deepagents-auth.ts +101 -8
- package/src/deepagents-bootstrap.ts +87 -0
- package/src/deepagents-harness.ts +71 -94
- package/src/index.ts +4 -1
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
2
|
import { posix } from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
3
|
import {
|
|
6
4
|
commonTool,
|
|
7
5
|
HarnessCapabilityUnsupportedError,
|
|
8
6
|
harnessV1DiagnosticFromBridgeFrame,
|
|
9
7
|
type HarnessV1,
|
|
10
|
-
type HarnessV1Bootstrap,
|
|
11
8
|
type HarnessV1BuiltinTool,
|
|
12
9
|
type HarnessV1BuiltinToolFiltering,
|
|
13
10
|
type HarnessV1ContinueTurnState,
|
|
@@ -15,6 +12,7 @@ import {
|
|
|
15
12
|
type HarnessV1PermissionMode,
|
|
16
13
|
type HarnessV1Prompt,
|
|
17
14
|
type HarnessV1PromptControl,
|
|
15
|
+
type HarnessV1PortEndpoint,
|
|
18
16
|
type HarnessV1ResumeSessionState,
|
|
19
17
|
type HarnessV1Session,
|
|
20
18
|
type HarnessV1Skill,
|
|
@@ -26,9 +24,11 @@ import {
|
|
|
26
24
|
createBridgeStartupError,
|
|
27
25
|
drainBridgeProcessStream,
|
|
28
26
|
forwardBridgeProcessStream,
|
|
27
|
+
maskSandboxCredentials,
|
|
29
28
|
resolveSandboxHomeDir,
|
|
30
29
|
SandboxChannel,
|
|
31
30
|
shellQuote,
|
|
31
|
+
warnCredentialBrokeringUnavailable,
|
|
32
32
|
waitForBridgeReady,
|
|
33
33
|
writeSkills as writeHarnessSkills,
|
|
34
34
|
} from '@ai-sdk/harness/utils';
|
|
@@ -36,6 +36,13 @@ import { tool, type Experimental_SandboxProcess } from '@ai-sdk/provider-utils';
|
|
|
36
36
|
import { WebSocket } from 'ws';
|
|
37
37
|
import { z } from 'zod/v4';
|
|
38
38
|
import {
|
|
39
|
+
DEEPAGENTS_BOOTSTRAP_DIR as BOOTSTRAP_DIR,
|
|
40
|
+
getDeepAgentsBootstrap,
|
|
41
|
+
} from './deepagents-bootstrap';
|
|
42
|
+
import {
|
|
43
|
+
createDeepAgentsRequestTransformations,
|
|
44
|
+
DEEPAGENTS_CREDENTIAL_ENVIRONMENT_VARIABLES,
|
|
45
|
+
resolveDeepAgentsAuthenticationMode,
|
|
39
46
|
resolveDeepAgentsEnv,
|
|
40
47
|
type DeepAgentsAuthOptions,
|
|
41
48
|
} from './deepagents-auth';
|
|
@@ -48,12 +55,6 @@ import { VERSION } from './version';
|
|
|
48
55
|
|
|
49
56
|
type DeepAgentsChannel = SandboxChannel<OutboundMessage, InboundMessage>;
|
|
50
57
|
|
|
51
|
-
/*
|
|
52
|
-
* Bootstrap is derived state stored under the sandbox's default working
|
|
53
|
-
* directory so snapshot-capable providers preserve its installation and
|
|
54
|
-
* recipe marker without requiring root filesystem access.
|
|
55
|
-
*/
|
|
56
|
-
const BOOTSTRAP_DIR = '.harness-bootstrap/deepagents';
|
|
57
58
|
/**
|
|
58
59
|
* Value to use in User-Agent and `x-client-app` headers.
|
|
59
60
|
*/
|
|
@@ -73,32 +74,6 @@ export type DeepAgentsThinkingConfig =
|
|
|
73
74
|
readonly type: 'disabled';
|
|
74
75
|
};
|
|
75
76
|
|
|
76
|
-
// Pinned ripgrep release + per-arch tarball checksums (verified before install).
|
|
77
|
-
const RIPGREP_VERSION = '14.1.1';
|
|
78
|
-
const RIPGREP_SHA256_X64 =
|
|
79
|
-
'4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e';
|
|
80
|
-
const RIPGREP_SHA256_ARM =
|
|
81
|
-
'c827481c4ff4ea10c9dc7a4022c8de5db34a5737cb74484d62eb94a95841ab2f';
|
|
82
|
-
|
|
83
|
-
// Idempotent, checksum-verified install of a static ripgrep binary into a PATH dir.
|
|
84
|
-
// DeepAgents' grep shells out to `rg`; without it, its fallback reads the whole workdir (incl. node_modules) into memory and OOMs. Skipped if `rg` already exists.
|
|
85
|
-
function installRipgrepCommand(): string {
|
|
86
|
-
const v = RIPGREP_VERSION;
|
|
87
|
-
return [
|
|
88
|
-
'command -v rg >/dev/null 2>&1 || {',
|
|
89
|
-
'case "$(uname -m)" in',
|
|
90
|
-
`aarch64) a=aarch64-unknown-linux-gnu; sha=${RIPGREP_SHA256_ARM} ;;`,
|
|
91
|
-
`*) a=x86_64-unknown-linux-musl; sha=${RIPGREP_SHA256_X64} ;;`,
|
|
92
|
-
'esac;',
|
|
93
|
-
`f=/tmp/ripgrep-${v}.tar.gz;`,
|
|
94
|
-
`curl -fsSL "https://github.com/BurntSushi/ripgrep/releases/download/${v}/ripgrep-${v}-$a.tar.gz" -o "$f"`,
|
|
95
|
-
'&& echo "$sha $f" | sha256sum -c -',
|
|
96
|
-
'&& tar xzf "$f" -C /tmp',
|
|
97
|
-
`&& mv "/tmp/ripgrep-${v}-$a/rg" /usr/local/bin/rg && chmod +x /usr/local/bin/rg;`,
|
|
98
|
-
'}',
|
|
99
|
-
].join(' ');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
77
|
// Skills source subpath, written under $HOME (out of the work dir so it can't clash with code cloned into the work dir) and also discovered from <workDir> for repo-provided skills.
|
|
103
78
|
const SKILLS_SOURCE_PATH = '/.agents/skills';
|
|
104
79
|
|
|
@@ -212,8 +187,6 @@ type DeepAgentsBridgeCoords = z.infer<typeof deepAgentsBridgeCoordsSchema>;
|
|
|
212
187
|
export function createDeepAgents(
|
|
213
188
|
settings: DeepAgentsHarnessSettings = {},
|
|
214
189
|
): HarnessV1<typeof DEEPAGENTS_BUILTIN_TOOLS> {
|
|
215
|
-
let cachedBootstrap: HarnessV1Bootstrap | undefined;
|
|
216
|
-
|
|
217
190
|
return {
|
|
218
191
|
specificationVersion: 'harness-v1',
|
|
219
192
|
harnessId: 'deepagents',
|
|
@@ -221,33 +194,35 @@ export function createDeepAgents(
|
|
|
221
194
|
// Built-in tool approvals are gated in-bridge via DeepAgents' interruptOn (HITL) middleware.
|
|
222
195
|
supportsBuiltinToolApprovals: true,
|
|
223
196
|
lifecycleStateSchema: deepAgentsResumeStateSchema,
|
|
224
|
-
getBootstrap:
|
|
225
|
-
if (cachedBootstrap != null) return cachedBootstrap;
|
|
226
|
-
const [bridge, pkg, lock] = await Promise.all([
|
|
227
|
-
readBridgeAsset('index.mjs'),
|
|
228
|
-
readBridgeAsset('package.json'),
|
|
229
|
-
readBridgeAsset('pnpm-lock.yaml'),
|
|
230
|
-
]);
|
|
231
|
-
cachedBootstrap = {
|
|
232
|
-
harnessId: 'deepagents',
|
|
233
|
-
bootstrapDir: BOOTSTRAP_DIR,
|
|
234
|
-
files: [
|
|
235
|
-
{ path: `${BOOTSTRAP_DIR}/bridge.mjs`, content: bridge },
|
|
236
|
-
{ path: `${BOOTSTRAP_DIR}/package.json`, content: pkg },
|
|
237
|
-
{ path: `${BOOTSTRAP_DIR}/pnpm-lock.yaml`, content: lock },
|
|
238
|
-
],
|
|
239
|
-
commands: [
|
|
240
|
-
{ command: installRipgrepCommand() },
|
|
241
|
-
{
|
|
242
|
-
command: 'pnpm install --frozen-lockfile --store-dir .pnpm-store',
|
|
243
|
-
},
|
|
244
|
-
],
|
|
245
|
-
};
|
|
246
|
-
return cachedBootstrap;
|
|
247
|
-
},
|
|
197
|
+
getBootstrap: getDeepAgentsBootstrap,
|
|
248
198
|
doStart: async startOpts => {
|
|
249
199
|
const permissionMode = startOpts.permissionMode;
|
|
250
200
|
const sandboxSession = startOpts.sandboxSession;
|
|
201
|
+
const authenticationMode = resolveDeepAgentsAuthenticationMode({
|
|
202
|
+
auth: settings.auth,
|
|
203
|
+
});
|
|
204
|
+
const resolvedAuthEnvironment = resolveDeepAgentsEnv({
|
|
205
|
+
auth: settings.auth,
|
|
206
|
+
});
|
|
207
|
+
let sandboxAuthEnvironment = resolvedAuthEnvironment;
|
|
208
|
+
if (sandboxSession.addRequestTransformations != null) {
|
|
209
|
+
const requestTransformations = createDeepAgentsRequestTransformations(
|
|
210
|
+
resolvedAuthEnvironment,
|
|
211
|
+
authenticationMode,
|
|
212
|
+
);
|
|
213
|
+
if (requestTransformations.length > 0) {
|
|
214
|
+
await sandboxSession.addRequestTransformations(
|
|
215
|
+
requestTransformations,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
sandboxAuthEnvironment = maskSandboxCredentials({
|
|
219
|
+
environment: resolvedAuthEnvironment,
|
|
220
|
+
credentialEnvironmentVariables:
|
|
221
|
+
DEEPAGENTS_CREDENTIAL_ENVIRONMENT_VARIABLES,
|
|
222
|
+
});
|
|
223
|
+
} else {
|
|
224
|
+
warnCredentialBrokeringUnavailable();
|
|
225
|
+
}
|
|
251
226
|
const session = sandboxSession.restricted();
|
|
252
227
|
const sandboxId = sandboxSession.id;
|
|
253
228
|
const bootstrapDir = posix.resolve(
|
|
@@ -286,13 +261,16 @@ export function createDeepAgents(
|
|
|
286
261
|
// Attach to the still-running bridge (continueFrom replays past the cursor); on failure fall through to a fresh spawn.
|
|
287
262
|
if (coords) {
|
|
288
263
|
try {
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
264
|
+
const endpoint = await sandboxSession.getPortEndpoint({
|
|
265
|
+
port: coords.port,
|
|
266
|
+
protocol: 'ws',
|
|
267
|
+
});
|
|
268
|
+
const attachEndpoint = withBridgeToken({
|
|
269
|
+
endpoint,
|
|
270
|
+
token: coords.token,
|
|
271
|
+
});
|
|
294
272
|
const attachChannel: DeepAgentsChannel = new SandboxChannel({
|
|
295
|
-
connect: () => openWebSocket(
|
|
273
|
+
connect: () => openWebSocket(attachEndpoint),
|
|
296
274
|
outboundSchema: outboundMessageSchema,
|
|
297
275
|
initialLastSeenEventId: coords.lastSeenEventId,
|
|
298
276
|
onDiagnostic,
|
|
@@ -347,7 +325,7 @@ export function createDeepAgents(
|
|
|
347
325
|
}
|
|
348
326
|
|
|
349
327
|
const env = {
|
|
350
|
-
...
|
|
328
|
+
...sandboxAuthEnvironment,
|
|
351
329
|
AI_SDK_HARNESS_CLIENT_APP: DEEPAGENTS_CLIENT_APP,
|
|
352
330
|
BRIDGE_CHANNEL_TOKEN: token,
|
|
353
331
|
BRIDGE_WS_PORT: String(port),
|
|
@@ -404,14 +382,14 @@ export function createDeepAgents(
|
|
|
404
382
|
});
|
|
405
383
|
void drainBridgeProcessStream(proc.stdout);
|
|
406
384
|
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
385
|
+
const endpoint = await sandboxSession.getPortEndpoint({
|
|
386
|
+
port: boundPort,
|
|
387
|
+
protocol: 'ws',
|
|
388
|
+
});
|
|
389
|
+
const bridgeEndpoint = withBridgeToken({ endpoint, token });
|
|
412
390
|
|
|
413
391
|
const channel: DeepAgentsChannel = new SandboxChannel({
|
|
414
|
-
connect: () => openWebSocket(
|
|
392
|
+
connect: () => openWebSocket(bridgeEndpoint),
|
|
415
393
|
outboundSchema: outboundMessageSchema,
|
|
416
394
|
onDiagnostic,
|
|
417
395
|
onBridgeError,
|
|
@@ -455,24 +433,6 @@ function resolveBridgePort(
|
|
|
455
433
|
});
|
|
456
434
|
}
|
|
457
435
|
|
|
458
|
-
async function readBridgeAsset(name: string): Promise<string> {
|
|
459
|
-
const candidates = [
|
|
460
|
-
new URL(`./bridge/${name}`, import.meta.url),
|
|
461
|
-
new URL(`../bridge/${name}`, import.meta.url),
|
|
462
|
-
];
|
|
463
|
-
let lastErr: unknown;
|
|
464
|
-
for (const url of candidates) {
|
|
465
|
-
try {
|
|
466
|
-
return await readFile(fileURLToPath(url), 'utf8');
|
|
467
|
-
} catch (err) {
|
|
468
|
-
const code = (err as NodeJS.ErrnoException).code;
|
|
469
|
-
if (code !== 'ENOENT') throw err;
|
|
470
|
-
lastErr = err;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
throw lastErr ?? new Error(`bridge asset not found: ${name}`);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
436
|
// 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.
|
|
477
437
|
async function writeSkills({
|
|
478
438
|
sandbox,
|
|
@@ -503,9 +463,14 @@ async function writeSkills({
|
|
|
503
463
|
});
|
|
504
464
|
}
|
|
505
465
|
|
|
506
|
-
function openWebSocket(
|
|
466
|
+
function openWebSocket({
|
|
467
|
+
url,
|
|
468
|
+
headers,
|
|
469
|
+
}: HarnessV1PortEndpoint): Promise<WebSocket> {
|
|
507
470
|
return new Promise((resolve, reject) => {
|
|
508
|
-
const ws = new WebSocket(url
|
|
471
|
+
const ws = new WebSocket(url, {
|
|
472
|
+
headers: headers == null ? undefined : { ...headers },
|
|
473
|
+
});
|
|
509
474
|
const onOpen = () => {
|
|
510
475
|
ws.off('error', onError);
|
|
511
476
|
resolve(ws);
|
|
@@ -519,6 +484,18 @@ function openWebSocket(url: string): Promise<WebSocket> {
|
|
|
519
484
|
});
|
|
520
485
|
}
|
|
521
486
|
|
|
487
|
+
function withBridgeToken({
|
|
488
|
+
endpoint,
|
|
489
|
+
token,
|
|
490
|
+
}: {
|
|
491
|
+
endpoint: HarnessV1PortEndpoint;
|
|
492
|
+
token: string;
|
|
493
|
+
}): HarnessV1PortEndpoint {
|
|
494
|
+
const bridgeUrl = new URL(endpoint.url);
|
|
495
|
+
bridgeUrl.searchParams.set('agent_bridge_token', token);
|
|
496
|
+
return { ...endpoint, url: bridgeUrl.toString() };
|
|
497
|
+
}
|
|
498
|
+
|
|
522
499
|
function createSession({
|
|
523
500
|
sessionId,
|
|
524
501
|
channel,
|
package/src/index.ts
CHANGED
|
@@ -9,4 +9,7 @@ export type {
|
|
|
9
9
|
DeepAgentsHarnessSettings,
|
|
10
10
|
DeepAgentsThinkingConfig,
|
|
11
11
|
} from './deepagents-harness';
|
|
12
|
-
export type {
|
|
12
|
+
export type {
|
|
13
|
+
DeepAgentsAuthenticationMode,
|
|
14
|
+
DeepAgentsAuthOptions,
|
|
15
|
+
} from './deepagents-auth';
|