@ai-sdk/harness-deepagents 1.0.10 → 1.0.12

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.
@@ -8,6 +8,7 @@ import {
8
8
  type HarnessV1,
9
9
  type HarnessV1Bootstrap,
10
10
  type HarnessV1BuiltinTool,
11
+ type HarnessV1BuiltinToolFiltering,
11
12
  type HarnessV1ContinueTurnState,
12
13
  type HarnessV1NetworkSandboxSession,
13
14
  type HarnessV1PermissionMode,
@@ -20,8 +21,11 @@ import {
20
21
  } from '@ai-sdk/harness';
21
22
  import {
22
23
  markBridgeStarting,
24
+ resolveSandboxHomeDir,
23
25
  SandboxChannel,
26
+ shellQuote,
24
27
  waitForBridgeReady,
28
+ writeSkills as writeHarnessSkills,
25
29
  } from '@ai-sdk/harness/utils';
26
30
  import { tool, type Experimental_SandboxProcess } from '@ai-sdk/provider-utils';
27
31
  import { WebSocket } from 'ws';
@@ -247,6 +251,7 @@ export function createDeepAgents(
247
251
  isResume: true,
248
252
  attached: true,
249
253
  permissionMode,
254
+ builtinToolFiltering: startOpts.builtinToolFiltering,
250
255
  recursionLimit: settings.recursionLimit,
251
256
  });
252
257
  } catch {
@@ -340,6 +345,7 @@ export function createDeepAgents(
340
345
  attached: false,
341
346
  skillsPaths,
342
347
  permissionMode,
348
+ builtinToolFiltering: startOpts.builtinToolFiltering,
343
349
  recursionLimit: settings.recursionLimit,
344
350
  });
345
351
  },
@@ -378,27 +384,6 @@ async function readBridgeAsset(name: string): Promise<string> {
378
384
  throw lastErr ?? new Error(`bridge asset not found: ${name}`);
379
385
  }
380
386
 
381
- // Resolve the sandbox $HOME so skills can be written outside the work dir.
382
- async function resolveSandboxHomeDir({
383
- sandbox,
384
- abortSignal,
385
- }: {
386
- sandbox: ReturnType<HarnessV1NetworkSandboxSession['restricted']>;
387
- abortSignal?: AbortSignal;
388
- }): Promise<string> {
389
- const result = await sandbox.run({
390
- command: 'printf "%s" "$HOME"',
391
- abortSignal,
392
- });
393
- const homeDir = result.stdout.trim();
394
- if (result.exitCode !== 0 || !homeDir.startsWith('/')) {
395
- throw new Error(
396
- `Unable to resolve sandbox HOME directory: ${result.stderr || result.stdout}`,
397
- );
398
- }
399
- return homeDir;
400
- }
401
-
402
387
  // 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.
403
388
  async function writeSkills({
404
389
  sandbox,
@@ -411,50 +396,22 @@ async function writeSkills({
411
396
  skills: ReadonlyArray<HarnessV1Skill>;
412
397
  abortSignal?: AbortSignal;
413
398
  }): Promise<void> {
414
- for (const skill of skills) {
415
- const name = safeSkillName(skill.name);
416
- const skillDir = `${root}/${name}`;
417
- // SKILL.md `name` must match the parent directory name (deepagents requirement).
418
- const content = `---\nname: ${name}\ndescription: ${skill.description}\n---\n\n${skill.content}`;
419
- await sandbox.writeTextFile({
420
- path: `${skillDir}/SKILL.md`,
421
- content,
422
- abortSignal,
423
- });
424
- for (const file of skill.files ?? []) {
425
- await sandbox.writeTextFile({
426
- path: `${skillDir}/${safeSkillFilePath(name, file.path)}`,
427
- content: file.content,
428
- abortSignal,
429
- });
430
- }
431
- }
432
- }
433
-
434
- function safeSkillName(name: string): string {
435
- if (!/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/.test(name)) {
436
- throw new Error(
399
+ /*
400
+ * DeepAgents requires each `SKILL.md` frontmatter name to match the parent
401
+ * directory name, so keep the stricter lowercase skill-name policy here.
402
+ */
403
+ await writeHarnessSkills({
404
+ sandbox,
405
+ rootDir: root,
406
+ skills,
407
+ abortSignal,
408
+ skillNamePattern: /^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/,
409
+ invalidSkillNameMessage: ({ name }) =>
437
410
  `Invalid deepagents skill name '${name}': must be lowercase alphanumeric with hyphens, 1-64 chars.`,
438
- );
439
- }
440
- return name;
441
- }
442
-
443
- function safeSkillFilePath(skillName: string, filePath: string): string {
444
- const normalized = filePath.replace(/^\/+/, '');
445
- if (
446
- normalized === '' ||
447
- normalized.startsWith('../') ||
448
- normalized.includes('/../') ||
449
- normalized.endsWith('/..')
450
- ) {
451
- throw new Error(`Invalid skill file path for '${skillName}': ${filePath}`);
452
- }
453
- return normalized;
454
- }
455
-
456
- function shellQuote(value: string): string {
457
- return `'${value.replace(/'/g, `'\\''`)}'`;
411
+ filePathMode: 'strip-leading-slashes',
412
+ invalidSkillFilePathMessage: ({ skillName, filePath }) =>
413
+ `Invalid skill file path for '${skillName}': ${filePath}`,
414
+ });
458
415
  }
459
416
 
460
417
  function openWebSocket(url: string): Promise<WebSocket> {
@@ -506,6 +463,7 @@ function createSession({
506
463
  attached,
507
464
  skillsPaths,
508
465
  permissionMode,
466
+ builtinToolFiltering,
509
467
  recursionLimit,
510
468
  }: {
511
469
  sessionId: string;
@@ -523,6 +481,7 @@ function createSession({
523
481
  attached: boolean;
524
482
  skillsPaths?: string[];
525
483
  permissionMode?: HarnessV1PermissionMode;
484
+ builtinToolFiltering?: HarnessV1BuiltinToolFiltering;
526
485
  recursionLimit?: number;
527
486
  }): HarnessV1Session {
528
487
  let stopped = false;
@@ -679,6 +638,7 @@ function createSession({
679
638
  ...(model ? { model } : {}),
680
639
  ...(skillsPaths?.length ? { skillsPaths } : {}),
681
640
  ...(permissionMode ? { permissionMode } : {}),
641
+ ...(builtinToolFiltering ? { builtinToolFiltering } : {}),
682
642
  ...(recursionLimit != null ? { recursionLimit } : {}),
683
643
  });
684
644