ai 7.0.113 → 7.0.116

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +6 -6
  3. package/dist/index.js +1010 -1289
  4. package/dist/index.js.map +1 -1
  5. package/dist/internal/index.js +231 -257
  6. package/dist/internal/index.js.map +1 -1
  7. package/docs/02-foundations/02-providers-and-models.mdx +3 -0
  8. package/docs/02-foundations/03-prompts.mdx +1 -1
  9. package/docs/02-foundations/04-tools.mdx +2 -2
  10. package/docs/02-foundations/06-provider-options.mdx +11 -11
  11. package/docs/02-getting-started/00-choosing-a-provider.mdx +4 -4
  12. package/docs/02-getting-started/02-nextjs-app-router.mdx +3 -3
  13. package/docs/02-getting-started/03-nextjs-pages-router.mdx +3 -3
  14. package/docs/02-getting-started/04-svelte.mdx +7 -7
  15. package/docs/02-getting-started/05-nuxt.mdx +7 -7
  16. package/docs/02-getting-started/06-nodejs.mdx +3 -3
  17. package/docs/02-getting-started/07-expo.mdx +3 -3
  18. package/docs/02-getting-started/08-tanstack-start.mdx +3 -3
  19. package/docs/02-getting-started/09-coding-agents.mdx +1 -1
  20. package/docs/03-agents/03-workflows.mdx +2 -2
  21. package/docs/03-agents/04-loop-control.mdx +1 -1
  22. package/docs/03-agents/05-configuring-call-options.mdx +4 -2
  23. package/docs/03-agents/06-memory.mdx +2 -2
  24. package/docs/03-agents/06-policy-tool-approvals.mdx +2 -2
  25. package/docs/03-agents/07-workflow-agent.mdx +11 -11
  26. package/docs/03-agents/08-terminal-ui.mdx +14 -10
  27. package/docs/03-ai-sdk-core/05-generating-text.mdx +2 -2
  28. package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +6 -1
  29. package/docs/03-ai-sdk-core/17-mcp-apps.mdx +1 -1
  30. package/docs/03-ai-sdk-core/20-prompt-engineering.mdx +1 -1
  31. package/docs/03-ai-sdk-core/26-reasoning.mdx +13 -12
  32. package/docs/03-ai-sdk-core/31-reranking.mdx +12 -10
  33. package/docs/03-ai-sdk-core/32-evaluation.mdx +1 -1
  34. package/docs/03-ai-sdk-core/35-image-generation.mdx +2 -2
  35. package/docs/03-ai-sdk-core/37-speech.mdx +6 -6
  36. package/docs/03-ai-sdk-core/39-file-uploads.mdx +1 -1
  37. package/docs/03-ai-sdk-core/41-skill-uploads.mdx +1 -1
  38. package/docs/03-ai-sdk-core/42-batch.mdx +1 -1
  39. package/docs/03-ai-sdk-core/45-provider-management.mdx +21 -21
  40. package/docs/03-ai-sdk-core/60-telemetry.mdx +1 -1
  41. package/docs/03-ai-sdk-core/65-devtools.mdx +3 -3
  42. package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +1 -1
  43. package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +97 -107
  44. package/docs/03-ai-sdk-harnesses/03-tools.mdx +1 -22
  45. package/docs/03-ai-sdk-harnesses/04-skills.mdx +0 -4
  46. package/docs/03-ai-sdk-harnesses/06-workflow-utilities.mdx +43 -9
  47. package/docs/03-ai-sdk-harnesses/07-ui.mdx +33 -30
  48. package/docs/03-ai-sdk-harnesses/08-terminal-ui.mdx +8 -6
  49. package/docs/04-ai-sdk-ui/02-chatbot.mdx +3 -3
  50. package/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx +3 -3
  51. package/docs/04-ai-sdk-ui/03-chatbot-resume-streams.mdx +1 -1
  52. package/docs/05-ai-sdk-rsc/02-streaming-react-components.mdx +5 -5
  53. package/docs/05-ai-sdk-rsc/04-multistep-interfaces.mdx +1 -1
  54. package/docs/05-ai-sdk-rsc/06-loading-state.mdx +1 -1
  55. package/docs/05-ai-sdk-rsc/10-migrating-to-ui.mdx +3 -3
  56. package/docs/06-advanced/11-secure-url-fetching.mdx +39 -0
  57. package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +5 -3
  58. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +5 -3
  59. package/docs/07-reference/01-ai-sdk-core/06-rerank.mdx +5 -5
  60. package/docs/07-reference/01-ai-sdk-core/11-transcribe.mdx +1 -1
  61. package/docs/07-reference/01-ai-sdk-core/12-generate-speech.mdx +2 -2
  62. package/docs/07-reference/01-ai-sdk-core/16-tool-loop-agent.mdx +4 -2
  63. package/docs/07-reference/01-ai-sdk-core/20-start-batch.mdx +1 -1
  64. package/docs/07-reference/01-ai-sdk-core/40-provider-registry.mdx +1 -1
  65. package/docs/07-reference/01-ai-sdk-core/42-custom-provider.mdx +4 -4
  66. package/docs/07-reference/01-ai-sdk-core/60-wrap-language-model.mdx +1 -1
  67. package/docs/07-reference/01-ai-sdk-core/61-wrap-image-model.mdx +1 -1
  68. package/docs/07-reference/02-ai-sdk-ui/50-direct-chat-transport.mdx +3 -3
  69. package/docs/07-reference/03-ai-sdk-rsc/01-stream-ui.mdx +1 -1
  70. package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +8 -8
  71. package/docs/07-reference/06-ai-sdk-tui/01-run-agent-tui.mdx +13 -9
  72. package/docs/09-troubleshooting/11-use-chat-custom-request-options.mdx +3 -3
  73. package/docs/09-troubleshooting/13-repeated-assistant-messages.mdx +2 -2
  74. package/docs/09-troubleshooting/17-use-chat-stale-body-data.mdx +1 -1
  75. package/docs/09-troubleshooting/70-high-memory-usage-with-images.mdx +2 -2
  76. package/package.json +12 -12
  77. package/src/evaluate/evaluate.ts +1 -10
  78. package/src/generate-text/restricted-telemetry-dispatcher.ts +6 -58
  79. package/src/prompt/file-part-data.ts +9 -3
  80. package/src/prompt/standardize-prompt.ts +2 -0
  81. package/src/telemetry/create-telemetry-dispatcher.ts +58 -6
  82. package/src/telemetry/filter-included-context.ts +57 -2
  83. package/src/util/download/download.ts +2 -2
@@ -11,7 +11,7 @@ results while a preconfigured harness powers these results.
11
11
 
12
12
  ## Installation
13
13
 
14
- Install the core harness package, a harness adapter, and a sandbox provider:
14
+ Install the core harness package, a harness adapter, and a sandbox adapter:
15
15
 
16
16
  <InstallPackages packages="@ai-sdk/harness @ai-sdk/harness-claude-code @ai-sdk/sandbox-vercel" />
17
17
 
@@ -24,15 +24,10 @@ like `@ai-sdk/sandbox-vercel`. Host-runtime harnesses such as Pi can also run wi
24
24
  ```ts
25
25
  import { HarnessAgent } from '@ai-sdk/harness/agent';
26
26
  import { claudeCode } from '@ai-sdk/harness-claude-code';
27
- import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
28
27
 
29
28
  export const agent = new HarnessAgent({
30
29
  harness: claudeCode,
31
30
  model: 'claude-sonnet-4-6',
32
- sandbox: createVercelSandbox({
33
- runtime: 'node24',
34
- ports: [4000],
35
- }),
36
31
  instructions:
37
32
  'You are a careful coding assistant. Prefer small changes and explain tradeoffs.',
38
33
  });
@@ -40,6 +35,17 @@ export const agent = new HarnessAgent({
40
35
 
41
36
  Construct the agent at module scope. It holds configuration, not a live session.
42
37
  Live state belongs to `HarnessAgentSession`.
38
+ Create a sandbox session separately and pass it to `agent.createSession()`:
39
+
40
+ ```ts
41
+ import { createVercelNetworkSandboxSession } from '@ai-sdk/sandbox-vercel';
42
+
43
+ const sandboxSession = await createVercelNetworkSandboxSession({
44
+ runtime: 'node24',
45
+ ports: [4000],
46
+ template: await agent.getSandboxTemplate(),
47
+ });
48
+ ```
43
49
 
44
50
  Set `model` to select the model that the harness runtime uses. Model identifiers
45
51
  are specific to each harness. When omitted, the harness uses its default model.
@@ -51,7 +57,7 @@ are set.
51
57
  ## Run a Turn
52
58
 
53
59
  ```ts
54
- const session = await agent.createSession();
60
+ const session = await agent.createSession({ sandboxSession });
55
61
 
56
62
  let exitCode = 0;
57
63
  try {
@@ -66,6 +72,7 @@ try {
66
72
  console.error(err);
67
73
  } finally {
68
74
  await session.destroy();
75
+ await sandboxSession.destroy();
69
76
  process.exit(exitCode);
70
77
  }
71
78
  ```
@@ -75,7 +82,7 @@ try {
75
82
  Use `stream()` for incremental output:
76
83
 
77
84
  ```ts
78
- const session = await agent.createSession();
85
+ const session = await agent.createSession({ sandboxSession });
79
86
 
80
87
  let exitCode = 0;
81
88
  try {
@@ -94,6 +101,7 @@ try {
94
101
  console.error(err);
95
102
  } finally {
96
103
  await session.destroy();
104
+ await sandboxSession.destroy();
97
105
  process.exit(exitCode);
98
106
  }
99
107
  ```
@@ -106,7 +114,6 @@ steps, and tool executions:
106
114
  ```ts
107
115
  const agent = new HarnessAgent({
108
116
  harness: claudeCode,
109
- sandbox,
110
117
  tools: { weather },
111
118
  onStart: event => console.log('call started', event.callId),
112
119
  onStepStart: event => console.log('step started', event.stepNumber),
@@ -145,7 +152,6 @@ import { z } from 'zod';
145
152
 
146
153
  const agent = new HarnessAgent({
147
154
  harness: claudeCode,
148
- sandbox,
149
155
  output: Output.object({
150
156
  schema: z.object({
151
157
  recipe: z.object({
@@ -163,7 +169,7 @@ const agent = new HarnessAgent({
163
169
  }),
164
170
  });
165
171
 
166
- const session = await agent.createSession();
172
+ const session = await agent.createSession({ sandboxSession });
167
173
  try {
168
174
  const result = await agent.generate({
169
175
  session,
@@ -205,10 +211,10 @@ of relying on message replay.
205
211
  End every session explicitly:
206
212
 
207
213
  - `session.destroy()` stops the runtime and discards resumability.
208
- - `session.detach()` parks the runtime and sandbox, returns resume state, and
209
- keeps the sandbox warm for a later attach. If the turn is unfinished, the
214
+ - `session.detach()` parks the runtime, returns resume state, and leaves a
215
+ supplied sandbox running for a later attach. If the turn is unfinished, the
210
216
  resume state includes the continuation state.
211
- - `session.stop()` saves resume state, then stops the runtime and sandbox. If
217
+ - `session.stop()` saves resume state, then stops the runtime. If
212
218
  the turn is unfinished, the resume state includes the continuation state.
213
219
  - `session.suspendTurn()` is for advanced active-turn continuation across a
214
220
  process boundary.
@@ -237,7 +243,6 @@ const getPolicy = tool({
237
243
 
238
244
  const agent = new HarnessAgent({
239
245
  harness: claudeCode,
240
- sandbox,
241
246
  tools: { getPolicy },
242
247
  callOptionsSchema: z.object({
243
248
  area: z.enum(['frontend', 'backend']),
@@ -253,7 +258,7 @@ const agent = new HarnessAgent({
253
258
  }),
254
259
  });
255
260
 
256
- const session = await agent.createSession();
261
+ const session = await agent.createSession({ sandboxSession });
257
262
  try {
258
263
  await agent.generate({
259
264
  session,
@@ -305,7 +310,10 @@ the harness runtime but do not stop or destroy the supplied sandbox session.
305
310
  In this case, the agent does not need a `sandbox` provider in its constructor.
306
311
 
307
312
  ```ts
308
- const session = await agent.createSession({ sessionId: chatId });
313
+ const session = await agent.createSession({
314
+ sessionId: chatId,
315
+ sandboxSession,
316
+ });
309
317
 
310
318
  try {
311
319
  const result = await agent.stream({ session, messages });
@@ -326,22 +334,39 @@ try {
326
334
 
327
335
  ## Resuming
328
336
 
329
- Persist the opaque resume state and pass it back with the original `sessionId`:
337
+ Persist the opaque resume state. In this example, the harness `sessionId` is
338
+ `chatId`, and the sandbox ID is derived from the same known ID. If your
339
+ application cannot derive the sandbox ID, persist `sandboxSession.id` alongside
340
+ the harness `sessionId` and resume state instead. Reattach the sandbox before
341
+ asking the agent to resume. When you choose `sandboxId` on creation, it names a **new**
342
+ live sandbox and never looks one up. Reusing that ID with the creator fails
343
+ if the named sandbox exists; only the resume function performs a lookup.
330
344
 
331
345
  ```ts
332
346
  const resumeState = await loadResumeState({ chatId });
347
+ const sandboxId = `chat-${chatId}`;
348
+
349
+ const sandboxSession = resumeState
350
+ ? await resumeVercelNetworkSandboxSession({ sandboxId })
351
+ : await createVercelNetworkSandboxSession({
352
+ sandboxId,
353
+ ports: [4000],
354
+ template: await agent.getSandboxTemplate(),
355
+ });
333
356
 
334
357
  const session = await agent.createSession(
335
358
  resumeState
336
- ? { sessionId: chatId, resumeFrom: resumeState }
337
- : { sessionId: chatId },
359
+ ? { sessionId: chatId, resumeFrom: resumeState, sandboxSession }
360
+ : { sessionId: chatId, sandboxSession },
338
361
  );
339
362
  ```
340
363
 
341
364
  `HarnessAgent` validates that the resume state was produced by the same harness
342
365
  adapter before handing it to the runtime. If the resume state includes an
343
366
  unfinished turn, call `continueStream()` or `continueGenerate()` before sending a
344
- new prompt.
367
+ new prompt. The Vercel resume function reattaches whether the sandbox is still
368
+ running after `detach()` or stopped with a restorable snapshot. After
369
+ `sandboxSession.destroy()` deletes it, that ID cannot be resumed.
345
370
 
346
371
  ## Continue a Suspended Turn
347
372
 
@@ -362,6 +387,7 @@ When you only have raw continuation state from `suspendTurn()`, resume with
362
387
  const session = await agent.createSession({
363
388
  sessionId: chatId,
364
389
  continueFrom: continuationState,
390
+ sandboxSession,
365
391
  // Rebind this when the suspended turn used host-only tool context:
366
392
  toolsContext,
367
393
  });
@@ -391,14 +417,10 @@ import { isStepCount } from 'ai';
391
417
 
392
418
  const steppedAgent = new HarnessAgent({
393
419
  harness: claudeCode,
394
- sandbox: createVercelSandbox({
395
- runtime: 'node24',
396
- ports: [4000],
397
- }),
398
420
  stopWhen: isStepCount(1),
399
421
  });
400
422
 
401
- const session = await steppedAgent.createSession();
423
+ const session = await steppedAgent.createSession({ sandboxSession });
402
424
  const result = await steppedAgent.generate({
403
425
  session,
404
426
  prompt: 'Create a short TODO.md for this repository.',
@@ -434,10 +456,6 @@ files or lightweight configuration.
434
456
  ```ts
435
457
  const agent = new HarnessAgent({
436
458
  harness: claudeCode,
437
- sandbox: createVercelSandbox({
438
- runtime: 'node24',
439
- ports: [4000],
440
- }),
441
459
  sandboxConfig: {
442
460
  workDir: 'repo',
443
461
  bootstrapHash: 'ripgrep-v1',
@@ -469,76 +487,55 @@ while `onBootstrap` receives the sandbox's default working directory.
469
487
 
470
488
  ## Prepare Reusable Sandboxes
471
489
 
472
- Use `prepareHarnessSandboxTemplate()` when you want the sandbox provider to
473
- create or refresh its reusable template for one harness ahead of time:
490
+ Use `agent.getSandboxTemplate()` to resolve one harness's bootstrap recipe and
491
+ `sandboxConfig.onBootstrap` before creating a sandbox. The template's identity
492
+ is known before sandbox creation. Vercel persists the prepared snapshot and
493
+ creates a live sandbox from it; identical calls reuse that snapshot:
474
494
 
475
495
  ```ts
476
- import { prepareHarnessSandboxTemplate } from '@ai-sdk/harness/agent';
477
-
478
- await prepareHarnessSandboxTemplate({
479
- harness: claudeCode,
480
- sandboxProvider: createVercelSandbox({
481
- runtime: 'node24',
482
- ports: [4000],
483
- }),
484
- sandboxConfig: {
485
- bootstrapHash: 'ripgrep-v1',
486
- onBootstrap: async ({ session, abortSignal }) => {
487
- await session.run({
488
- command:
489
- 'command -v rg >/dev/null || (apt-get update && apt-get install -y ripgrep)',
490
- abortSignal,
491
- });
492
- },
493
- },
496
+ const sandboxSession = await createVercelNetworkSandboxSession({
497
+ runtime: 'node24',
498
+ ports: [4000],
499
+ template: await agent.getSandboxTemplate(),
494
500
  });
501
+
502
+ const session = await agent.createSession({ sandboxSession });
503
+ try {
504
+ await agent.generate({ session, prompt: 'Inspect this repository.' });
505
+ } finally {
506
+ await session.destroy();
507
+ await sandboxSession.destroy();
508
+ }
495
509
  ```
496
510
 
497
- Use `prepareSandboxForHarness()` when you own the native sandbox lifecycle and
498
- want to snapshot the prepared sandbox yourself. It applies the selected harness
499
- bootstrap recipes and `sandboxConfig.onBootstrap`, then returns preparation
500
- metadata. It does not stop or snapshot the sandbox.
511
+ For multiple harnesses sharing a caller bootstrap hook, build one template from
512
+ all adapters. The last adapter wins for duplicate `harnessId` values, and the
513
+ recipe identities are sorted before hashing. `onSession` is not part of the
514
+ template and runs separately on every acquired session.
501
515
 
502
516
  ```ts
503
- import { HarnessAgent, prepareSandboxForHarness } from '@ai-sdk/harness/agent';
504
- import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
505
- import { Sandbox } from '@vercel/sandbox';
517
+ import { createHarnessSandboxTemplate } from '@ai-sdk/harness/agent';
506
518
 
507
- const nativeSandbox = await Sandbox.create({
508
- runtime: 'node24',
509
- ports: [4000],
510
- });
511
- const sandboxProvider = createVercelSandbox({ sandbox: nativeSandbox });
512
- const session = await sandboxProvider.createSession();
513
-
514
- const preparation = await prepareSandboxForHarness({
515
- session: session.restricted(),
519
+ const template = await createHarnessSandboxTemplate({
516
520
  harnesses: [claudeCode, codex],
517
521
  sandboxConfig,
518
522
  });
523
+ console.log(template?.identity);
519
524
 
520
- const { snapshot } = await nativeSandbox.stop();
521
- if (snapshot == null) {
522
- throw new Error('Prepared sandbox did not create a snapshot.');
523
- }
524
-
525
- const sandboxFromSnapshot = await Sandbox.create({
526
- source: {
527
- type: 'snapshot',
528
- snapshotId: snapshot.id,
529
- },
525
+ const sandboxSession = await createVercelNetworkSandboxSession({
526
+ source: { type: 'snapshot', snapshotId: baseSnapshotId },
530
527
  ports: [4000],
528
+ template,
531
529
  });
532
-
533
- const agent = new HarnessAgent({
534
- harness: claudeCode,
535
- sandbox: createVercelSandbox({ sandbox: sandboxFromSnapshot }),
536
- sandboxConfig,
537
- });
538
-
539
- console.log(preparation.identity);
540
530
  ```
541
531
 
532
+ The derived template includes the source snapshot ID in its cache key, so
533
+ repeated calls with the same source and template reuse the prepared snapshot.
534
+ Pin mutable Git revisions, image tags, and source URLs when relying on cached
535
+ templates. `sandboxId` names the live sandbox, not its reusable template.
536
+ The existing native `name` option still names the live sandbox; if both are
537
+ supplied, their values must match.
538
+
542
539
  ## Settings
543
540
 
544
541
  `HarnessAgent` accepts these main settings:
@@ -546,7 +543,6 @@ console.log(preparation.identity);
546
543
  - `harness`: the adapter instance.
547
544
  - `model`: optional harness-specific model identifier. When omitted, the
548
545
  harness uses its default model.
549
- - `sandbox`: a `HarnessV1SandboxProvider`.
550
546
  - `id`: optional stable agent identifier.
551
547
  - `instructions`: instructions appended to the runtime's system or developer
552
548
  prompt when supported, or prepended to the user prompt otherwise.
@@ -580,26 +576,21 @@ Adapter-specific settings belong on the adapter factory, for example
580
576
 
581
577
  When your application creates and manages sandboxes itself, prepare the network
582
578
  sandbox session first and then pass that same session to `agent.createSession()`.
583
- The agent does not need a sandbox provider and does not stop or destroy the
579
+ The agent does not create the sandbox and does not stop or destroy the
584
580
  caller-owned sandbox.
585
581
 
586
582
  ```ts
587
- import { HarnessAgent, prepareSandboxForHarness } from '@ai-sdk/harness/agent';
583
+ import { HarnessAgent } from '@ai-sdk/harness/agent';
588
584
  import { claudeCode } from '@ai-sdk/harness-claude-code';
589
- import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
585
+ import { createVercelNetworkSandboxSessionFromNativeSandbox } from '@ai-sdk/sandbox-vercel';
590
586
  import { Sandbox } from '@vercel/sandbox';
591
587
 
592
588
  const sandbox = await Sandbox.create({
593
589
  runtime: 'node24',
594
590
  ports: [4000],
595
591
  });
596
- const sandboxProvider = createVercelSandbox({ sandbox });
597
- const sandboxSession = await sandboxProvider.createSession();
598
-
599
- await prepareSandboxForHarness({
600
- session: sandboxSession.restricted(),
601
- harnesses: [claudeCode],
602
- });
592
+ const sandboxSession =
593
+ createVercelNetworkSandboxSessionFromNativeSandbox(sandbox);
603
594
 
604
595
  const agent = new HarnessAgent({ harness: claudeCode });
605
596
  const session = await agent.createSession({ sandboxSession });
@@ -617,7 +608,7 @@ try {
617
608
  }
618
609
  } finally {
619
610
  await session.destroy();
620
- await sandbox.stop();
611
+ await sandboxSession.destroy();
621
612
  }
622
613
  ```
623
614
 
@@ -634,29 +625,28 @@ configure the bridge port and its externally reachable endpoint on the adapter
634
625
  because the basic session cannot resolve them.
635
626
 
636
627
  ```ts
637
- import { HarnessAgent, prepareSandboxForHarness } from '@ai-sdk/harness/agent';
628
+ import { HarnessAgent } from '@ai-sdk/harness/agent';
638
629
  import { createClaudeCode } from '@ai-sdk/harness-claude-code';
639
- import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
630
+ import {
631
+ createVercelNetworkSandboxSessionFromNativeSandbox,
632
+ createVercelSandboxSessionFromNativeSandbox,
633
+ } from '@ai-sdk/sandbox-vercel';
640
634
  import { Sandbox } from '@vercel/sandbox';
641
635
 
642
636
  const sandbox = await Sandbox.create({
643
637
  runtime: 'node24',
644
638
  ports: [4000],
645
639
  });
646
- const sandboxProvider = createVercelSandbox({ sandbox });
647
- const sandboxSession = await sandboxProvider.createSession();
640
+ const sandboxSession =
641
+ createVercelNetworkSandboxSessionFromNativeSandbox(sandbox);
648
642
  const portEndpoint = await sandboxSession.getPortEndpoint({
649
643
  port: 4000,
650
644
  protocol: 'ws',
651
645
  });
652
- const restrictedSandboxSession = sandboxSession.restricted();
646
+ const restrictedSandboxSession =
647
+ createVercelSandboxSessionFromNativeSandbox(sandbox);
653
648
  const claudeCode = createClaudeCode({ port: 4000, portEndpoint });
654
649
 
655
- await prepareSandboxForHarness({
656
- session: restrictedSandboxSession,
657
- harnesses: [claudeCode],
658
- });
659
-
660
650
  const agent = new HarnessAgent({ harness: claudeCode });
661
651
  const session = await agent.createSession({
662
652
  sandboxSession: restrictedSandboxSession,
@@ -675,7 +665,7 @@ try {
675
665
  }
676
666
  } finally {
677
667
  await session.destroy();
678
- await sandbox.stop();
668
+ await sandboxSession.destroy();
679
669
  }
680
670
  ```
681
671
 
@@ -25,10 +25,6 @@ the combined tool set through `agent.tools`.
25
25
  ```ts
26
26
  const agent = new HarnessAgent({
27
27
  harness: claudeCode,
28
- sandbox: createVercelSandbox({
29
- runtime: 'node24',
30
- ports: [4000],
31
- }),
32
28
  });
33
29
 
34
30
  agent.tools.bash;
@@ -60,7 +56,6 @@ Pass AI SDK tools to `HarnessAgent` the same way you do for a `ToolLoopAgent`:
60
56
  ```ts
61
57
  import { HarnessAgent } from '@ai-sdk/harness/agent';
62
58
  import { claudeCode } from '@ai-sdk/harness-claude-code';
63
- import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
64
59
  import { tool } from 'ai';
65
60
  import { z } from 'zod';
66
61
 
@@ -82,10 +77,6 @@ const weather = tool({
82
77
 
83
78
  const agent = new HarnessAgent({
84
79
  harness: claudeCode,
85
- sandbox: createVercelSandbox({
86
- runtime: 'node24',
87
- ports: [4000],
88
- }),
89
80
  tools: { weather },
90
81
  });
91
82
  ```
@@ -129,6 +120,7 @@ When recreating a session for an unfinished turn, rebind it explicitly:
129
120
  const session = await agent.createSession({
130
121
  sessionId,
131
122
  continueFrom,
123
+ sandboxSession,
132
124
  toolsContext: {
133
125
  lookupAccount: { userId: 'user-123' },
134
126
  },
@@ -191,10 +183,6 @@ with `tools`.
191
183
  ```ts
192
184
  const agent = new HarnessAgent({
193
185
  harness: claudeCode,
194
- sandbox: createVercelSandbox({
195
- runtime: 'node24',
196
- ports: [4000],
197
- }),
198
186
  tools: { weather },
199
187
  activeTools: ['weather'],
200
188
  });
@@ -205,10 +193,6 @@ const agent = new HarnessAgent({
205
193
  ```ts
206
194
  const agent = new HarnessAgent({
207
195
  harness: claudeCode,
208
- sandbox: createVercelSandbox({
209
- runtime: 'node24',
210
- ports: [4000],
211
- }),
212
196
  tools: { weather },
213
197
  inactiveTools: ['bash', 'write'],
214
198
  });
@@ -260,7 +244,6 @@ Use `permissionMode` for adapter-native built-ins:
260
244
  ```ts
261
245
  const agent = new HarnessAgent({
262
246
  harness: pi,
263
- sandbox: createVercelSandbox({ runtime: 'node24' }),
264
247
  permissionMode: 'allow-edits',
265
248
  });
266
249
  ```
@@ -279,10 +262,6 @@ Use `toolApproval` for host-executed tools:
279
262
  ```ts
280
263
  const agent = new HarnessAgent({
281
264
  harness: claudeCode,
282
- sandbox: createVercelSandbox({
283
- runtime: 'node24',
284
- ports: [4000],
285
- }),
286
265
  tools: { weather },
287
266
  toolApproval: {
288
267
  weather: 'user-approval',
@@ -18,10 +18,6 @@ Pass skills to `HarnessAgent` with the `skills` setting:
18
18
  ```ts
19
19
  const agent = new HarnessAgent({
20
20
  harness: claudeCode,
21
- sandbox: createVercelSandbox({
22
- runtime: 'node24',
23
- ports: [4000],
24
- }),
25
21
  skills: [
26
22
  {
27
23
  name: 'careful-refactors',
@@ -23,7 +23,7 @@ before following the examples.
23
23
  <InstallPackages packages="@ai-sdk/workflow-harness workflow" />
24
24
 
25
25
  In addition to the workflow specific packages, install the core harness package,
26
- a harness adapter, and a sandbox provider as shown in
26
+ a harness adapter, and a sandbox adapter as shown in
27
27
  [HarnessAgent](/docs/ai-sdk-harnesses/harness-agent).
28
28
 
29
29
  ## Configuring the Harness Agent
@@ -35,15 +35,10 @@ semantic agent steps, set `stopWhen` to `isStepCount(1)` so one call to
35
35
  ```ts filename='harness-workflow/agent.ts' highlight="13-17"
36
36
  import { HarnessAgent } from '@ai-sdk/harness/agent';
37
37
  import { claudeCode } from '@ai-sdk/harness-claude-code';
38
- import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
39
38
  import { isStepCount } from 'ai';
40
39
 
41
40
  export const agent = new HarnessAgent({
42
41
  harness: claudeCode,
43
- sandbox: createVercelSandbox({
44
- runtime: 'node24',
45
- ports: [4000],
46
- }),
47
42
  instructions: 'You are a helpful coding assistant.',
48
43
  /*
49
44
  * Only needed for semantic agent-step workflows.
@@ -62,7 +57,7 @@ the shared agent with the highlighted `stopWhen` option shown above, then call
62
57
  ### Defining the Agent Step
63
58
 
64
59
  Keep the Workflow step in its own module and import the agent dynamically inside
65
- the step body. This keeps the agent, sandbox provider, and other Node.js
60
+ the step body. This keeps the agent, sandbox adapter, and other Node.js
66
61
  dependencies out of the workflow bundle.
67
62
 
68
63
  ```ts filename='harness-workflow/agent-step.ts'
@@ -77,10 +72,25 @@ export async function agentStep(
77
72
  'use step';
78
73
 
79
74
  const { agent } = await import('./agent');
75
+ const {
76
+ createVercelNetworkSandboxSession,
77
+ resumeVercelNetworkSandboxSession,
78
+ } = await import('@ai-sdk/sandbox-vercel');
79
+ const sandboxId = `harness-${state.sessionId}`;
80
+ const isFirstStep = state.resumeFrom == null && state.continueFrom == null;
81
+ const sandboxSession = isFirstStep
82
+ ? await createVercelNetworkSandboxSession({
83
+ sandboxId,
84
+ runtime: 'node24',
85
+ ports: [4000],
86
+ template: await agent.getSandboxTemplate(),
87
+ })
88
+ : await resumeVercelNetworkSandboxSession({ sandboxId });
80
89
 
81
90
  return runHarnessAgentStep({
82
91
  agent,
83
92
  state,
93
+ sandboxSession,
84
94
  });
85
95
  }
86
96
  ```
@@ -165,7 +175,9 @@ export async function POST(request: Request) {
165
175
  }
166
176
  ```
167
177
 
168
- The `sessionId` gives the sandbox a stable identity across workflow runs. Keep
178
+ The workflow state `sessionId` identifies the harness session. The step derives
179
+ `sandboxId` from it before first creating the sandbox and uses that same ID to
180
+ reattach on later steps. The state does not serialize a live sandbox. Keep
169
181
  `agent.ts`, `agent-step.ts`, `workflow.ts`, and the route in separate modules so
170
182
  the workflow bundle does not include Node-heavy agent, sandbox, or framework
171
183
  dependencies.
@@ -194,10 +206,25 @@ export async function timeSliceStep(
194
206
  'use step';
195
207
 
196
208
  const { agent } = await import('./agent');
209
+ const {
210
+ createVercelNetworkSandboxSession,
211
+ resumeVercelNetworkSandboxSession,
212
+ } = await import('@ai-sdk/sandbox-vercel');
213
+ const sandboxId = `harness-${state.sessionId}`;
214
+ const isFirstStep = state.resumeFrom == null && state.continueFrom == null;
215
+ const sandboxSession = isFirstStep
216
+ ? await createVercelNetworkSandboxSession({
217
+ sandboxId,
218
+ runtime: 'node24',
219
+ ports: [4000],
220
+ template: await agent.getSandboxTemplate(),
221
+ })
222
+ : await resumeVercelNetworkSandboxSession({ sandboxId });
197
223
 
198
224
  return runHarnessAgentTimeSlice({
199
225
  agent,
200
226
  state,
227
+ sandboxSession,
201
228
  });
202
229
  }
203
230
  ```
@@ -281,11 +308,18 @@ export async function POST(request: Request) {
281
308
  }
282
309
  ```
283
310
 
284
- The `sessionId` gives the sandbox a stable identity across workflow runs. Keep
311
+ The workflow state `sessionId` identifies the harness session. The step derives
312
+ `sandboxId` from it; otherwise, you must persist `sandboxSession.id` separately
313
+ to reattach across steps. Keep
285
314
  `agent.ts`, `time-slice-step.ts`, `workflow.ts`, and the route in separate
286
315
  modules so the workflow bundle does not include Node-heavy agent, sandbox, or
287
316
  framework dependencies.
288
317
 
318
+ `destroyOnFinish` destroys the harness session, but never the supplied sandbox.
319
+ The caller can explicitly call `sandboxSession.destroy()` on the network
320
+ session when no later step needs to reattach. Never destroy it at a
321
+ time-slice boundary where a subsequent step must resume it.
322
+
289
323
  ## Resume Persistence
290
324
 
291
325
  Workflow automatically persists the `HarnessWorkflowState` returned by each