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.
- package/CHANGELOG.md +32 -0
- package/README.md +6 -6
- package/dist/index.js +1010 -1289
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +231 -257
- package/dist/internal/index.js.map +1 -1
- package/docs/02-foundations/02-providers-and-models.mdx +3 -0
- package/docs/02-foundations/03-prompts.mdx +1 -1
- package/docs/02-foundations/04-tools.mdx +2 -2
- package/docs/02-foundations/06-provider-options.mdx +11 -11
- package/docs/02-getting-started/00-choosing-a-provider.mdx +4 -4
- package/docs/02-getting-started/02-nextjs-app-router.mdx +3 -3
- package/docs/02-getting-started/03-nextjs-pages-router.mdx +3 -3
- package/docs/02-getting-started/04-svelte.mdx +7 -7
- package/docs/02-getting-started/05-nuxt.mdx +7 -7
- package/docs/02-getting-started/06-nodejs.mdx +3 -3
- package/docs/02-getting-started/07-expo.mdx +3 -3
- package/docs/02-getting-started/08-tanstack-start.mdx +3 -3
- package/docs/02-getting-started/09-coding-agents.mdx +1 -1
- package/docs/03-agents/03-workflows.mdx +2 -2
- package/docs/03-agents/04-loop-control.mdx +1 -1
- package/docs/03-agents/05-configuring-call-options.mdx +4 -2
- package/docs/03-agents/06-memory.mdx +2 -2
- package/docs/03-agents/06-policy-tool-approvals.mdx +2 -2
- package/docs/03-agents/07-workflow-agent.mdx +11 -11
- package/docs/03-agents/08-terminal-ui.mdx +14 -10
- package/docs/03-ai-sdk-core/05-generating-text.mdx +2 -2
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +6 -1
- package/docs/03-ai-sdk-core/17-mcp-apps.mdx +1 -1
- package/docs/03-ai-sdk-core/20-prompt-engineering.mdx +1 -1
- package/docs/03-ai-sdk-core/26-reasoning.mdx +13 -12
- package/docs/03-ai-sdk-core/31-reranking.mdx +12 -10
- package/docs/03-ai-sdk-core/32-evaluation.mdx +1 -1
- package/docs/03-ai-sdk-core/35-image-generation.mdx +2 -2
- package/docs/03-ai-sdk-core/37-speech.mdx +6 -6
- package/docs/03-ai-sdk-core/39-file-uploads.mdx +1 -1
- package/docs/03-ai-sdk-core/41-skill-uploads.mdx +1 -1
- package/docs/03-ai-sdk-core/42-batch.mdx +1 -1
- package/docs/03-ai-sdk-core/45-provider-management.mdx +21 -21
- package/docs/03-ai-sdk-core/60-telemetry.mdx +1 -1
- package/docs/03-ai-sdk-core/65-devtools.mdx +3 -3
- package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +1 -1
- package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +97 -107
- package/docs/03-ai-sdk-harnesses/03-tools.mdx +1 -22
- package/docs/03-ai-sdk-harnesses/04-skills.mdx +0 -4
- package/docs/03-ai-sdk-harnesses/06-workflow-utilities.mdx +43 -9
- package/docs/03-ai-sdk-harnesses/07-ui.mdx +33 -30
- package/docs/03-ai-sdk-harnesses/08-terminal-ui.mdx +8 -6
- package/docs/04-ai-sdk-ui/02-chatbot.mdx +3 -3
- package/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx +3 -3
- package/docs/04-ai-sdk-ui/03-chatbot-resume-streams.mdx +1 -1
- package/docs/05-ai-sdk-rsc/02-streaming-react-components.mdx +5 -5
- package/docs/05-ai-sdk-rsc/04-multistep-interfaces.mdx +1 -1
- package/docs/05-ai-sdk-rsc/06-loading-state.mdx +1 -1
- package/docs/05-ai-sdk-rsc/10-migrating-to-ui.mdx +3 -3
- package/docs/06-advanced/11-secure-url-fetching.mdx +39 -0
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +5 -3
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +5 -3
- package/docs/07-reference/01-ai-sdk-core/06-rerank.mdx +5 -5
- package/docs/07-reference/01-ai-sdk-core/11-transcribe.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/12-generate-speech.mdx +2 -2
- package/docs/07-reference/01-ai-sdk-core/16-tool-loop-agent.mdx +4 -2
- package/docs/07-reference/01-ai-sdk-core/20-start-batch.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/40-provider-registry.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/42-custom-provider.mdx +4 -4
- package/docs/07-reference/01-ai-sdk-core/60-wrap-language-model.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/61-wrap-image-model.mdx +1 -1
- package/docs/07-reference/02-ai-sdk-ui/50-direct-chat-transport.mdx +3 -3
- package/docs/07-reference/03-ai-sdk-rsc/01-stream-ui.mdx +1 -1
- package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +8 -8
- package/docs/07-reference/06-ai-sdk-tui/01-run-agent-tui.mdx +13 -9
- package/docs/09-troubleshooting/11-use-chat-custom-request-options.mdx +3 -3
- package/docs/09-troubleshooting/13-repeated-assistant-messages.mdx +2 -2
- package/docs/09-troubleshooting/17-use-chat-stale-body-data.mdx +1 -1
- package/docs/09-troubleshooting/70-high-memory-usage-with-images.mdx +2 -2
- package/package.json +12 -12
- package/src/evaluate/evaluate.ts +1 -10
- package/src/generate-text/restricted-telemetry-dispatcher.ts +6 -58
- package/src/prompt/file-part-data.ts +9 -3
- package/src/prompt/standardize-prompt.ts +2 -0
- package/src/telemetry/create-telemetry-dispatcher.ts +58 -6
- package/src/telemetry/filter-included-context.ts +57 -2
- 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
|
|
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
|
|
209
|
-
|
|
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
|
|
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({
|
|
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
|
|
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 `
|
|
473
|
-
|
|
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
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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 {
|
|
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
|
|
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
|
|
521
|
-
|
|
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
|
|
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
|
|
583
|
+
import { HarnessAgent } from '@ai-sdk/harness/agent';
|
|
588
584
|
import { claudeCode } from '@ai-sdk/harness-claude-code';
|
|
589
|
-
import {
|
|
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
|
|
597
|
-
|
|
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
|
|
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
|
|
628
|
+
import { HarnessAgent } from '@ai-sdk/harness/agent';
|
|
638
629
|
import { createClaudeCode } from '@ai-sdk/harness-claude-code';
|
|
639
|
-
import {
|
|
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
|
|
647
|
-
|
|
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 =
|
|
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
|
|
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',
|
|
@@ -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
|
|
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
|
|
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`
|
|
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`
|
|
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
|