@ai-sdk/harness-deepagents 0.0.0-6b196531-20260710185421

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/dist/index.js ADDED
@@ -0,0 +1,722 @@
1
+ // src/deepagents-harness.ts
2
+ import { randomBytes } from "crypto";
3
+ import { readFile } from "fs/promises";
4
+ import { fileURLToPath } from "url";
5
+ import {
6
+ commonTool,
7
+ HarnessCapabilityUnsupportedError,
8
+ harnessV1DiagnosticFromBridgeFrame
9
+ } from "@ai-sdk/harness";
10
+ import {
11
+ markBridgeStarting,
12
+ createBridgeErrorHandler,
13
+ createBridgeStartupError,
14
+ drainBridgeProcessStream,
15
+ forwardBridgeProcessStream,
16
+ resolveSandboxHomeDir,
17
+ SandboxChannel,
18
+ shellQuote,
19
+ waitForBridgeReady,
20
+ writeSkills as writeHarnessSkills
21
+ } from "@ai-sdk/harness/utils";
22
+ import { tool } from "@ai-sdk/provider-utils";
23
+ import { WebSocket } from "ws";
24
+ import { z as z2 } from "zod/v4";
25
+
26
+ // src/deepagents-auth.ts
27
+ import { getAiGatewayAuthFromEnv } from "@ai-sdk/harness/utils";
28
+ function resolveDeepAgentsEnv({
29
+ auth,
30
+ processEnv = process.env
31
+ }) {
32
+ if (auth?.anthropic) {
33
+ return pickAnthropic({ explicit: auth.anthropic, processEnv });
34
+ }
35
+ const gatewayAuthFromEnv = getAiGatewayAuthFromEnv({ env: processEnv });
36
+ if (auth?.gateway) {
37
+ return pickGateway({ explicit: auth.gateway, gatewayAuthFromEnv });
38
+ }
39
+ if (gatewayAuthFromEnv.apiKey) {
40
+ return pickGateway({ explicit: {}, gatewayAuthFromEnv });
41
+ }
42
+ return pickAnthropic({ processEnv });
43
+ }
44
+ function pickAnthropic({
45
+ explicit,
46
+ processEnv
47
+ }) {
48
+ const env = {};
49
+ const apiKey = explicit?.apiKey ?? processEnv.ANTHROPIC_API_KEY;
50
+ if (apiKey) env.ANTHROPIC_API_KEY = apiKey;
51
+ const authToken = explicit?.authToken ?? processEnv.ANTHROPIC_AUTH_TOKEN;
52
+ if (authToken) env.ANTHROPIC_AUTH_TOKEN = authToken;
53
+ const baseUrl = explicit?.baseUrl ?? processEnv.ANTHROPIC_BASE_URL;
54
+ if (baseUrl) env.ANTHROPIC_BASE_URL = baseUrl;
55
+ return env;
56
+ }
57
+ function pickGateway({
58
+ explicit,
59
+ gatewayAuthFromEnv
60
+ }) {
61
+ const apiKey = explicit.apiKey ?? gatewayAuthFromEnv.apiKey;
62
+ const baseUrl = (explicit.baseUrl ?? gatewayAuthFromEnv.baseUrl).replace(
63
+ /\/+$/,
64
+ ""
65
+ );
66
+ const env = {};
67
+ if (apiKey) {
68
+ env.AI_GATEWAY_API_KEY = apiKey;
69
+ env.ANTHROPIC_API_KEY = apiKey;
70
+ }
71
+ env.ANTHROPIC_BASE_URL = baseUrl;
72
+ return env;
73
+ }
74
+
75
+ // src/deepagents-bridge-protocol.ts
76
+ import {
77
+ harnessV1BridgeInboundCommandSchemas,
78
+ harnessV1BridgeOutboundMessageSchema,
79
+ harnessV1BridgeReadySchema,
80
+ harnessV1BridgeStartBaseSchema
81
+ } from "@ai-sdk/harness";
82
+ import { z } from "zod/v4";
83
+ var outboundMessageSchema = harnessV1BridgeOutboundMessageSchema;
84
+ var startMessageSchema = harnessV1BridgeStartBaseSchema.extend({
85
+ // Prepended to the first user message (createDeepAgent takes no instructions param).
86
+ instructions: z.string().optional(),
87
+ // In-backend skills source dirs ($HOME and <workDir>), passed to createDeepAgent({ skills }).
88
+ skillsPaths: z.array(z.string()).optional(),
89
+ // Max LangGraph super-steps per turn (streamEvents recursionLimit).
90
+ recursionLimit: z.number().optional()
91
+ });
92
+ var inboundMessageSchema = z.discriminatedUnion("type", [
93
+ startMessageSchema,
94
+ ...harnessV1BridgeInboundCommandSchemas
95
+ ]);
96
+
97
+ // src/version.ts
98
+ var VERSION = true ? "0.0.0-6b196531-20260710185421" : "0.0.0-test";
99
+
100
+ // src/deepagents-harness.ts
101
+ var BOOTSTRAP_DIR = "/tmp/harness/deepagents";
102
+ var DEEPAGENTS_CLIENT_APP = `ai-sdk/harness-deepagents/${VERSION}`;
103
+ var RIPGREP_VERSION = "14.1.1";
104
+ var RIPGREP_SHA256_X64 = "4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e";
105
+ var RIPGREP_SHA256_ARM = "c827481c4ff4ea10c9dc7a4022c8de5db34a5737cb74484d62eb94a95841ab2f";
106
+ function installRipgrepCommand() {
107
+ const v = RIPGREP_VERSION;
108
+ return [
109
+ "command -v rg >/dev/null 2>&1 || {",
110
+ 'case "$(uname -m)" in',
111
+ `aarch64) a=aarch64-unknown-linux-gnu; sha=${RIPGREP_SHA256_ARM} ;;`,
112
+ `*) a=x86_64-unknown-linux-musl; sha=${RIPGREP_SHA256_X64} ;;`,
113
+ "esac;",
114
+ `f=/tmp/ripgrep-${v}.tar.gz;`,
115
+ `curl -fsSL "https://github.com/BurntSushi/ripgrep/releases/download/${v}/ripgrep-${v}-$a.tar.gz" -o "$f"`,
116
+ '&& echo "$sha $f" | sha256sum -c -',
117
+ '&& tar xzf "$f" -C /tmp',
118
+ `&& mv "/tmp/ripgrep-${v}-$a/rg" /usr/local/bin/rg && chmod +x /usr/local/bin/rg;`,
119
+ "}"
120
+ ].join(" ");
121
+ }
122
+ var SKILLS_SOURCE_PATH = "/.agents/skills";
123
+ var DEEPAGENTS_BUILTIN_TOOLS = {
124
+ read: commonTool("read", {
125
+ nativeName: "read_file",
126
+ toolUseKind: "readonly",
127
+ description: "Read file contents",
128
+ inputSchema: z2.object({ file_path: z2.string() })
129
+ }),
130
+ write: commonTool("write", {
131
+ nativeName: "write_file",
132
+ toolUseKind: "edit",
133
+ description: "Create a file",
134
+ inputSchema: z2.object({ file_path: z2.string(), content: z2.string() })
135
+ }),
136
+ edit: commonTool("edit", {
137
+ nativeName: "edit_file",
138
+ toolUseKind: "edit",
139
+ description: "Perform exact string replacements in a file",
140
+ inputSchema: z2.object({
141
+ file_path: z2.string(),
142
+ old_string: z2.string(),
143
+ new_string: z2.string()
144
+ })
145
+ }),
146
+ bash: commonTool("bash", {
147
+ nativeName: "execute",
148
+ toolUseKind: "bash",
149
+ description: "Run a shell command",
150
+ inputSchema: z2.object({ command: z2.string() })
151
+ }),
152
+ grep: commonTool("grep", {
153
+ nativeName: "grep",
154
+ toolUseKind: "readonly",
155
+ description: "Search file contents",
156
+ inputSchema: z2.object({ pattern: z2.string() })
157
+ }),
158
+ glob: commonTool("glob", {
159
+ nativeName: "glob",
160
+ toolUseKind: "readonly",
161
+ description: "Find files matching a glob pattern",
162
+ inputSchema: z2.object({ pattern: z2.string() })
163
+ }),
164
+ // No common-name equivalent — keyed by native name.
165
+ ls: tool({
166
+ description: "List files in a directory",
167
+ inputSchema: z2.object({ path: z2.string().optional() })
168
+ }),
169
+ task: tool({
170
+ description: "Spawn a subagent to handle a delegated task",
171
+ inputSchema: z2.object({
172
+ description: z2.string().optional(),
173
+ subagent_type: z2.string().optional()
174
+ })
175
+ }),
176
+ write_todos: tool({
177
+ description: "Manage a structured todo list",
178
+ inputSchema: z2.object({ todos: z2.array(z2.unknown()).optional() })
179
+ })
180
+ };
181
+ var deepAgentsBridgeCoordsSchema = z2.object({
182
+ port: z2.number(),
183
+ token: z2.string(),
184
+ lastSeenEventId: z2.number(),
185
+ sandboxId: z2.string().optional()
186
+ });
187
+ var deepAgentsResumeStateSchema = z2.object({
188
+ bridge: deepAgentsBridgeCoordsSchema.optional()
189
+ });
190
+ function createDeepAgents(settings = {}) {
191
+ let cachedBootstrap;
192
+ return {
193
+ specificationVersion: "harness-v1",
194
+ harnessId: "deepagents",
195
+ builtinTools: DEEPAGENTS_BUILTIN_TOOLS,
196
+ // Built-in tool approvals are gated in-bridge via DeepAgents' interruptOn (HITL) middleware.
197
+ supportsBuiltinToolApprovals: true,
198
+ lifecycleStateSchema: deepAgentsResumeStateSchema,
199
+ getBootstrap: async () => {
200
+ if (cachedBootstrap != null) return cachedBootstrap;
201
+ const [bridge, pkg, lock] = await Promise.all([
202
+ readBridgeAsset("index.mjs"),
203
+ readBridgeAsset("package.json"),
204
+ readBridgeAsset("pnpm-lock.yaml")
205
+ ]);
206
+ cachedBootstrap = {
207
+ harnessId: "deepagents",
208
+ bootstrapDir: BOOTSTRAP_DIR,
209
+ files: [
210
+ { path: `${BOOTSTRAP_DIR}/bridge.mjs`, content: bridge },
211
+ { path: `${BOOTSTRAP_DIR}/package.json`, content: pkg },
212
+ { path: `${BOOTSTRAP_DIR}/pnpm-lock.yaml`, content: lock }
213
+ ],
214
+ commands: [
215
+ { command: `mkdir -p ${BOOTSTRAP_DIR}` },
216
+ { command: installRipgrepCommand() },
217
+ {
218
+ command: `pnpm --dir ${BOOTSTRAP_DIR} install --frozen-lockfile --store-dir ${BOOTSTRAP_DIR}/.pnpm-store`
219
+ }
220
+ ]
221
+ };
222
+ return cachedBootstrap;
223
+ },
224
+ doStart: async (startOpts) => {
225
+ const permissionMode = startOpts.permissionMode;
226
+ const sandboxSession = startOpts.sandboxSession;
227
+ const session = sandboxSession.restricted();
228
+ const sandboxId = sandboxSession.id;
229
+ const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
230
+ const isResume = lifecycleState != null;
231
+ const isContinue = startOpts.continueFrom != null;
232
+ const coords = isResume && typeof lifecycleState?.data === "object" ? lifecycleState.data.bridge : void 0;
233
+ const workDir = startOpts.sessionWorkDir;
234
+ const sessionDataDir = `${sandboxSession.defaultWorkingDirectory}/.agent-runs/${startOpts.sessionId}`;
235
+ const bridgeStateDir = `${sessionDataDir}/bridge`;
236
+ const timeoutMs = settings.startupTimeoutMs ?? 12e4;
237
+ const report = startOpts.observability?.report;
238
+ const onDiagnostic = report ? (frame) => report(
239
+ harnessV1DiagnosticFromBridgeFrame(frame, {
240
+ sessionId: startOpts.sessionId,
241
+ timestamp: Date.now()
242
+ })
243
+ ) : void 0;
244
+ const onBridgeError = createBridgeErrorHandler({
245
+ harnessId: "deepagents",
246
+ sessionId: startOpts.sessionId
247
+ });
248
+ if (coords) {
249
+ try {
250
+ const attachUrl = await sandboxSession.getPortUrl({
251
+ port: coords.port,
252
+ protocol: "ws"
253
+ }) + `?agent_bridge_token=${encodeURIComponent(coords.token)}`;
254
+ const attachChannel = new SandboxChannel({
255
+ connect: () => openWebSocket(attachUrl),
256
+ outboundSchema: outboundMessageSchema,
257
+ initialLastSeenEventId: coords.lastSeenEventId,
258
+ onDiagnostic,
259
+ onBridgeError
260
+ });
261
+ await attachChannel.open(isContinue ? { resume: true } : void 0);
262
+ return createSession({
263
+ sessionId: startOpts.sessionId,
264
+ channel: attachChannel,
265
+ proc: void 0,
266
+ model: settings.model,
267
+ bridgePort: coords.port,
268
+ bridgeToken: coords.token,
269
+ sandboxId,
270
+ isResume: true,
271
+ attached: true,
272
+ permissionMode,
273
+ builtinToolFiltering: startOpts.builtinToolFiltering,
274
+ recursionLimit: settings.recursionLimit
275
+ });
276
+ } catch {
277
+ }
278
+ }
279
+ const port = resolveBridgePort(sandboxSession, settings.port);
280
+ const token = randomBytes(32).toString("hex");
281
+ const skillsPaths = [`${workDir}${SKILLS_SOURCE_PATH}`];
282
+ if ((startOpts.skills?.length ?? 0) > 0) {
283
+ const homeDir = await resolveSandboxHomeDir({
284
+ sandbox: session,
285
+ abortSignal: startOpts.abortSignal
286
+ });
287
+ const homeSkillsRoot = `${homeDir}${SKILLS_SOURCE_PATH}`;
288
+ await writeSkills({
289
+ sandbox: session,
290
+ root: homeSkillsRoot,
291
+ skills: startOpts.skills ?? [],
292
+ abortSignal: startOpts.abortSignal
293
+ });
294
+ skillsPaths.push(homeSkillsRoot);
295
+ }
296
+ const env = {
297
+ ...resolveDeepAgentsEnv({ auth: settings.auth }),
298
+ AI_SDK_HARNESS_CLIENT_APP: DEEPAGENTS_CLIENT_APP,
299
+ BRIDGE_CHANNEL_TOKEN: token,
300
+ BRIDGE_WS_PORT: String(port)
301
+ };
302
+ await session.run({
303
+ command: `mkdir -p ${shellQuote(workDir)} ${shellQuote(bridgeStateDir)}`,
304
+ abortSignal: startOpts.abortSignal
305
+ });
306
+ await markBridgeStarting({
307
+ sandbox: session,
308
+ bridgeStateDir,
309
+ bridgeType: "deepagents",
310
+ abortSignal: startOpts.abortSignal
311
+ });
312
+ const proc = await session.spawn({
313
+ command: `node ${BOOTSTRAP_DIR}/bridge.mjs --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --bootstrap-dir ${shellQuote(BOOTSTRAP_DIR)}`,
314
+ env,
315
+ abortSignal: startOpts.abortSignal
316
+ });
317
+ const stderrTail = [];
318
+ const bridgeStderrDone = forwardBridgeProcessStream({
319
+ stream: proc.stderr,
320
+ streamName: "stderr",
321
+ source: "deepagents",
322
+ collectTail: stderrTail
323
+ });
324
+ const { port: boundPort } = await waitForBridgeReady({
325
+ proc,
326
+ sandbox: session,
327
+ bridgeStateDir,
328
+ bridgeType: "deepagents",
329
+ timeoutMs,
330
+ abortSignal: startOpts.abortSignal,
331
+ createTimeoutError: ({ proc: proc2, stdoutTail }) => createBridgeStartupError({
332
+ message: "deepagents bridge did not become ready in time.",
333
+ proc: proc2,
334
+ stdoutTail,
335
+ stderrTail,
336
+ stderrDone: bridgeStderrDone
337
+ }),
338
+ createExitError: ({ proc: proc2, stdoutTail }) => createBridgeStartupError({
339
+ message: "deepagents bridge exited before becoming ready.",
340
+ proc: proc2,
341
+ stdoutTail,
342
+ stderrTail,
343
+ stderrDone: bridgeStderrDone
344
+ })
345
+ });
346
+ void drainBridgeProcessStream(proc.stdout);
347
+ const wsUrl = await sandboxSession.getPortUrl({
348
+ port: boundPort,
349
+ protocol: "ws"
350
+ }) + `?agent_bridge_token=${encodeURIComponent(token)}`;
351
+ const channel = new SandboxChannel({
352
+ connect: () => openWebSocket(wsUrl),
353
+ outboundSchema: outboundMessageSchema,
354
+ onDiagnostic,
355
+ onBridgeError
356
+ });
357
+ await channel.open();
358
+ return createSession({
359
+ sessionId: startOpts.sessionId,
360
+ channel,
361
+ proc,
362
+ model: settings.model,
363
+ bridgePort: boundPort,
364
+ bridgeToken: token,
365
+ sandboxId,
366
+ isResume,
367
+ // Freshly spawned bridge — it must receive the instructions on the first prompt.
368
+ attached: false,
369
+ skillsPaths,
370
+ permissionMode,
371
+ builtinToolFiltering: startOpts.builtinToolFiltering,
372
+ recursionLimit: settings.recursionLimit
373
+ });
374
+ }
375
+ };
376
+ }
377
+ function resolveBridgePort(sandboxSession, override) {
378
+ if (override !== void 0) return override;
379
+ if (sandboxSession.ports.length > 0) return sandboxSession.ports[0];
380
+ throw new HarnessCapabilityUnsupportedError({
381
+ harnessId: "deepagents",
382
+ message: "The deepagents harness needs a TCP port exposed by the sandbox. Create the sandbox with `ports: [<port>]` or pass `createDeepAgents({ port })`."
383
+ });
384
+ }
385
+ async function readBridgeAsset(name) {
386
+ const candidates = [
387
+ new URL(`./bridge/${name}`, import.meta.url),
388
+ new URL(`../bridge/${name}`, import.meta.url)
389
+ ];
390
+ let lastErr;
391
+ for (const url of candidates) {
392
+ try {
393
+ return await readFile(fileURLToPath(url), "utf8");
394
+ } catch (err) {
395
+ const code = err.code;
396
+ if (code !== "ENOENT") throw err;
397
+ lastErr = err;
398
+ }
399
+ }
400
+ throw lastErr ?? new Error(`bridge asset not found: ${name}`);
401
+ }
402
+ async function writeSkills({
403
+ sandbox,
404
+ root,
405
+ skills,
406
+ abortSignal
407
+ }) {
408
+ await writeHarnessSkills({
409
+ sandbox,
410
+ rootDir: root,
411
+ skills,
412
+ abortSignal,
413
+ skillNamePattern: /^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/,
414
+ invalidSkillNameMessage: ({ name }) => `Invalid deepagents skill name '${name}': must be lowercase alphanumeric with hyphens, 1-64 chars.`,
415
+ filePathMode: "strip-leading-slashes",
416
+ invalidSkillFilePathMessage: ({ skillName, filePath }) => `Invalid skill file path for '${skillName}': ${filePath}`
417
+ });
418
+ }
419
+ function openWebSocket(url) {
420
+ return new Promise((resolve, reject) => {
421
+ const ws = new WebSocket(url);
422
+ const onOpen = () => {
423
+ ws.off("error", onError);
424
+ resolve(ws);
425
+ };
426
+ const onError = (err) => {
427
+ ws.off("open", onOpen);
428
+ reject(err);
429
+ };
430
+ ws.once("open", onOpen);
431
+ ws.once("error", onError);
432
+ });
433
+ }
434
+ function createSession({
435
+ sessionId,
436
+ channel,
437
+ proc,
438
+ model,
439
+ bridgePort,
440
+ bridgeToken,
441
+ sandboxId,
442
+ isResume,
443
+ attached,
444
+ skillsPaths,
445
+ permissionMode,
446
+ builtinToolFiltering,
447
+ recursionLimit
448
+ }) {
449
+ let stopped = false;
450
+ let instructionsApplied = attached;
451
+ const wireTurn = (turnOpts) => {
452
+ let pendingResolve;
453
+ let pendingReject;
454
+ const done = new Promise((resolve, reject) => {
455
+ pendingResolve = resolve;
456
+ pendingReject = reject;
457
+ });
458
+ const unsubs = [];
459
+ const forward = (event) => {
460
+ try {
461
+ turnOpts.emit(event);
462
+ } catch {
463
+ }
464
+ };
465
+ const eventTypes = [
466
+ "stream-start",
467
+ "text-start",
468
+ "text-delta",
469
+ "text-end",
470
+ "reasoning-start",
471
+ "reasoning-delta",
472
+ "reasoning-end",
473
+ "tool-call",
474
+ "tool-approval-request",
475
+ "tool-result",
476
+ "file-change",
477
+ "finish-step",
478
+ "raw"
479
+ ];
480
+ let isSettled = false;
481
+ const settleSuccess = () => {
482
+ if (isSettled) return;
483
+ isSettled = true;
484
+ for (const u of unsubs) u();
485
+ pendingResolve();
486
+ };
487
+ const settleError = (err) => {
488
+ if (isSettled) return;
489
+ isSettled = true;
490
+ for (const u of unsubs) u();
491
+ pendingReject(err);
492
+ };
493
+ for (const type of eventTypes) {
494
+ unsubs.push(channel.on(type, (msg) => forward(msg)));
495
+ }
496
+ unsubs.push(
497
+ channel.on("finish", (msg) => {
498
+ forward(msg);
499
+ settleSuccess();
500
+ })
501
+ );
502
+ unsubs.push(
503
+ channel.on("error", (msg) => {
504
+ forward(msg);
505
+ settleError(msg.error);
506
+ })
507
+ );
508
+ const onClose = (_code, reason) => {
509
+ if (isSettled) return;
510
+ if (reason === "suspended") {
511
+ settleSuccess();
512
+ return;
513
+ }
514
+ settleError(
515
+ new Error("deepagents bridge closed before the turn finished.")
516
+ );
517
+ };
518
+ channel.onClose(onClose);
519
+ const onAbort = () => {
520
+ if (isSettled) return;
521
+ try {
522
+ channel.send({ type: "abort" });
523
+ } catch {
524
+ }
525
+ settleError(
526
+ turnOpts.abortSignal?.reason ?? new DOMException("Aborted", "AbortError")
527
+ );
528
+ };
529
+ if (turnOpts.abortSignal) {
530
+ if (turnOpts.abortSignal.aborted) {
531
+ onAbort();
532
+ } else {
533
+ turnOpts.abortSignal.addEventListener("abort", onAbort, { once: true });
534
+ }
535
+ }
536
+ return {
537
+ submitToolResult: async (input) => {
538
+ channel.send({
539
+ type: "tool-result",
540
+ toolCallId: input.toolCallId,
541
+ output: input.output,
542
+ isError: input.isError
543
+ });
544
+ },
545
+ submitUserMessage: async (text) => {
546
+ channel.send({ type: "user-message", text });
547
+ },
548
+ submitToolApproval: async (input) => {
549
+ channel.send({
550
+ type: "tool-approval-response",
551
+ approvalId: input.approvalId,
552
+ approved: input.approved,
553
+ ...input.reason != null ? { reason: input.reason } : {}
554
+ });
555
+ },
556
+ done
557
+ };
558
+ };
559
+ const unsupported = (capability) => {
560
+ throw new HarnessCapabilityUnsupportedError({
561
+ harnessId: "deepagents",
562
+ message: `Harness 'deepagents' does not support ${capability} yet.`
563
+ });
564
+ };
565
+ return {
566
+ sessionId,
567
+ isResume,
568
+ modelId: model,
569
+ doPromptTurn: async (promptOpts) => {
570
+ const control = wireTurn({
571
+ emit: promptOpts.emit,
572
+ abortSignal: promptOpts.abortSignal
573
+ });
574
+ const applyInstructions = !instructionsApplied && !!promptOpts.instructions;
575
+ instructionsApplied = true;
576
+ channel.send({
577
+ type: "start",
578
+ prompt: extractUserText(promptOpts.prompt),
579
+ ...applyInstructions ? { instructions: promptOpts.instructions } : {},
580
+ tools: (promptOpts.tools ?? []).map((t) => ({
581
+ name: t.name,
582
+ description: t.description,
583
+ inputSchema: t.inputSchema
584
+ })),
585
+ ...model ? { model } : {},
586
+ ...skillsPaths?.length ? { skillsPaths } : {},
587
+ ...permissionMode ? { permissionMode } : {},
588
+ ...builtinToolFiltering ? { builtinToolFiltering } : {},
589
+ ...recursionLimit != null ? { recursionLimit } : {}
590
+ });
591
+ return control;
592
+ },
593
+ doContinueTurn: async (continueOpts) => {
594
+ return wireTurn({
595
+ emit: continueOpts.emit,
596
+ abortSignal: continueOpts.abortSignal
597
+ });
598
+ },
599
+ doSuspendTurn: async () => {
600
+ if (stopped) {
601
+ throw new Error(
602
+ `deepagents session ${sessionId} is stopped; cannot suspend.`
603
+ );
604
+ }
605
+ stopped = true;
606
+ await channel.interrupt();
607
+ const lastSeenEventId = await channel.suspend();
608
+ const payload = {
609
+ type: "continue-turn",
610
+ harnessId: "deepagents",
611
+ specificationVersion: "harness-v1",
612
+ data: {
613
+ bridge: {
614
+ port: bridgePort,
615
+ token: bridgeToken,
616
+ lastSeenEventId,
617
+ sandboxId
618
+ }
619
+ }
620
+ };
621
+ return payload;
622
+ },
623
+ doDetach: async () => {
624
+ if (stopped) {
625
+ throw new Error(
626
+ `deepagents session ${sessionId} is already stopped; cannot detach.`
627
+ );
628
+ }
629
+ stopped = true;
630
+ const lastSeenEventId = await channel.suspend();
631
+ const payload = {
632
+ type: "resume-session",
633
+ harnessId: "deepagents",
634
+ specificationVersion: "harness-v1",
635
+ data: {
636
+ bridge: {
637
+ port: bridgePort,
638
+ token: bridgeToken,
639
+ lastSeenEventId,
640
+ sandboxId
641
+ }
642
+ }
643
+ };
644
+ return payload;
645
+ },
646
+ doCompact: async () => unsupported("manual compaction"),
647
+ doStop: async () => {
648
+ if (stopped) {
649
+ throw new Error(
650
+ `deepagents session ${sessionId} is already stopped; cannot stop.`
651
+ );
652
+ }
653
+ stopped = true;
654
+ await teardown(channel, proc);
655
+ const payload = {
656
+ type: "resume-session",
657
+ harnessId: "deepagents",
658
+ specificationVersion: "harness-v1",
659
+ data: {}
660
+ };
661
+ return payload;
662
+ },
663
+ doDestroy: async () => {
664
+ if (stopped) return;
665
+ stopped = true;
666
+ await teardown(channel, proc);
667
+ }
668
+ };
669
+ }
670
+ async function teardown(channel, proc) {
671
+ channel.beginClose();
672
+ try {
673
+ if (!channel.isClosed()) {
674
+ channel.send({ type: "shutdown" });
675
+ }
676
+ } catch {
677
+ }
678
+ let stopTimer;
679
+ try {
680
+ if (proc) {
681
+ await Promise.race([
682
+ proc.wait(),
683
+ new Promise((resolve) => {
684
+ stopTimer = setTimeout(resolve, 5e3);
685
+ stopTimer.unref?.();
686
+ })
687
+ ]);
688
+ }
689
+ } finally {
690
+ if (stopTimer) clearTimeout(stopTimer);
691
+ try {
692
+ await proc?.kill();
693
+ } catch {
694
+ }
695
+ channel.close();
696
+ }
697
+ }
698
+ function extractUserText(prompt) {
699
+ if (typeof prompt === "string") return prompt;
700
+ const { content } = prompt;
701
+ if (typeof content === "string") return content;
702
+ const parts = [];
703
+ for (const part of content) {
704
+ if (part.type !== "text") {
705
+ throw new HarnessCapabilityUnsupportedError({
706
+ harnessId: "deepagents",
707
+ message: `The deepagents harness does not yet support user message parts of type '${part.type}'. Pass a string or a user message whose content contains only text parts.`
708
+ });
709
+ }
710
+ parts.push(part.text);
711
+ }
712
+ return parts.join("\n\n");
713
+ }
714
+
715
+ // src/index.ts
716
+ var deepAgents = createDeepAgents();
717
+ export {
718
+ VERSION,
719
+ createDeepAgents,
720
+ deepAgents
721
+ };
722
+ //# sourceMappingURL=index.js.map