Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ac9d4d5
feat(harness-deepagents): add DeepAgents HarnessV1 adapter
mlekhi Jun 18, 2026
55ff849
test(harness-deepagents): cover factory, auth, and bootstrap
mlekhi Jun 18, 2026
fb72884
feat(harness-deepagents): drive JS deepagents via a Node bridge
mlekhi Jun 18, 2026
4a51784
adding changelog
mlekhi Jun 18, 2026
c7ba882
feat(harness-deepagents): align with maintainer conventions + add exa…
mlekhi Jun 18, 2026
585d0bb
fix(harness-deepagents): validate against live sandbox
mlekhi Jun 19, 2026
f2ab7a6
cleanup
mlekhi Jun 19, 2026
efde658
feat(harness-deepagents): add session lifecycle + e2e-next example
mlekhi Jun 19, 2026
f88416c
feat(harness-deepagents): provider-aware auth (anthropic + openai)
mlekhi Jun 19, 2026
46d50d2
supporting recursive JSON-schema→zod for custom tools
mlekhi Jun 19, 2026
f93b4f9
Merge branch 'main' into harness-deepagents
mlekhi Jun 19, 2026
cad12fa
bumping ws version
mlekhi Jun 22, 2026
b3121f4
adding full tool list
mlekhi Jun 22, 2026
aaab5c5
adding full test coverage for e2e-tui
mlekhi Jun 22, 2026
7d63a1f
adding examples for manual test coverage
mlekhi Jun 22, 2026
664ce01
adding test coverage for e2e-next
mlekhi Jun 22, 2026
82d6d4b
adding attach flag
mlekhi Jun 22, 2026
6f8d41a
adding endReasoningBlock()
mlekhi Jun 22, 2026
eab34b3
adding built in tool approvals
mlekhi Jun 23, 2026
4740f6c
fixing CI checks
mlekhi Jun 23, 2026
7262e2d
attempt to fix konsistent
mlekhi Jun 23, 2026
335517a
count model-call usage once in totalUsage
mlekhi Jun 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/deepagents-harness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ai-sdk/harness-deepagents': major
---

feat(harness-deepagents): implement harness adapter
2 changes: 2 additions & 0 deletions .github/konsistent.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@
"kebabToPascalMap": {
"assemblyai": "AssemblyAI",
"bytedance": "ByteDance",
"deepagents": "DeepAgents",
"deepinfra": "DeepInfra",
"deepseek": "DeepSeek",
"elevenlabs": "ElevenLabs",
Expand All @@ -239,6 +240,7 @@
"togetherai": "TogetherAI"
},
"kebabToCamelMap": {
"deepagents": "deepAgents",
"lmnt": "lmnt"
}
}
195 changes: 195 additions & 0 deletions content/providers/02-ai-sdk-harnesses/05-deepagents.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
title: DeepAgents
description: Learn how to use the DeepAgents harness adapter.
---

# DeepAgents Harness

The DeepAgents harness adapter connects `HarnessAgent` to
[DeepAgents](https://github.com/deep-agents/deepagents), a LangGraph-based agent
runtime. The adapter runs a Node bridge inside the sandbox that drives the
`deepagents` package (`createDeepAgent`) and streams its `streamEvents` output
back to the host over a sandbox-exposed WebSocket.

<Note>
Harness packages are **experimental**. Expect breaking changes between
releases as this early API gets further refined.
</Note>

## Setup

<Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
<Tab>
<Snippet
text="pnpm add @ai-sdk/harness@beta @ai-sdk/harness-deepagents@beta @ai-sdk/sandbox-vercel@beta"
dark
/>
</Tab>
<Tab>
<Snippet
text="npm install @ai-sdk/harness@beta @ai-sdk/harness-deepagents@beta @ai-sdk/sandbox-vercel@beta"
dark
/>
</Tab>
<Tab>
<Snippet
text="yarn add @ai-sdk/harness@beta @ai-sdk/harness-deepagents@beta @ai-sdk/sandbox-vercel@beta"
dark
/>
</Tab>
<Tab>
<Snippet
text="bun add @ai-sdk/harness@beta @ai-sdk/harness-deepagents@beta @ai-sdk/sandbox-vercel@beta"
dark
/>
</Tab>
</Tabs>

The adapter bootstraps the bridge's Node dependencies (the `deepagents` package
and LangChain) inside the sandbox via `pnpm` when the first session starts.

## Import

```ts
import { deepAgents, createDeepAgents } from '@ai-sdk/harness-deepagents';
```

`deepAgents` is equivalent to `createDeepAgents()` with its default
configuration.

## Basic Usage

```ts
import { HarnessAgent } from '@ai-sdk/harness/agent';
import { deepAgents } from '@ai-sdk/harness-deepagents';
import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';

const agent = new HarnessAgent({
harness: deepAgents,
sandbox: createVercelSandbox({
runtime: 'node24',
ports: [4000],
}),
});

const session = await agent.createSession();

let exitCode = 0;
try {
const result = await agent.stream({
session,
prompt: 'Analyze this codebase and suggest improvements.',
});

for await (const part of result.stream) {
if (part.type === 'text-delta') {
process.stdout.write(part.text);
}
}
} catch (err) {
exitCode = 1;
console.error(err);
} finally {
await session.destroy();
process.exit(exitCode);
}
```

To use this agent, ensure environment variables include `VERCEL_OIDC_TOKEN` for
Vercel Sandbox, and one of the variables listed under
[authentication](#authentication) for the model provider.

## Adapter Settings

Use `createDeepAgents()` to configure the runtime:

```ts
const harness = createDeepAgents({
model: 'claude-sonnet-4',
});
```

Settings:

- `auth`: Anthropic, OpenAI, or AI Gateway authentication settings.
- `model`: model id passed to the DeepAgents (LangChain) runtime. The bridge
converts it to LangChain's `provider:model` form internally.
- `port`: bridge port override.
- `startupTimeoutMs`: maximum time to wait for the bridge to start.

## Authentication

The provider is resolved from the model id (`anthropic/…` or `openai/…`,
defaulting to Anthropic). Authentication is resolved from the host environment
and forwarded to the sandbox bridge: explicit provider auth first, then AI
Gateway credentials, then ambient provider credentials.

Supported environment variables:

- `AI_GATEWAY_API_KEY`
- `VERCEL_OIDC_TOKEN`
- `AI_GATEWAY_BASE_URL`
- `ANTHROPIC_API_KEY`
- `ANTHROPIC_AUTH_TOKEN`
- `ANTHROPIC_BASE_URL`
- `OPENAI_API_KEY`
- `OPENAI_BASE_URL`
- `OPENAI_ORGANIZATION`
- `OPENAI_PROJECT`

You can also pass explicit auth settings (`anthropic`, `openai`, or `gateway`):

```ts
const harness = createDeepAgents({
model: 'openai/gpt-5',
auth: {
openai: {
apiKey: process.env.OPENAI_API_KEY,
},
},
});
```

## Sandbox

DeepAgents requires a network sandbox with at least one exposed port,
e.g. `@ai-sdk/sandbox-vercel`:

```ts
const sandbox = createVercelSandbox({
runtime: 'node24',
ports: [4000],
});
```

## Skills

Skills passed to the session are materialized as native DeepAgents skill folders
(`<name>/SKILL.md` plus any attached files) under `.deepagents/skills/` in the
sandbox, and loaded via DeepAgents' `skills` option — so the agent loads them on
demand and skill file references resolve.

## Built-in Tools

The adapter exposes these common DeepAgents built-ins through `agent.tools`:

- `read` (native `read_file`)
- `write` (native `write_file`)
- `bash` (native `shell`)
- `grep` (native `search`)

## Known Limitations

- **Built-in tool approvals** are not supported yet. Use
`permissionMode: 'allow-all'`. Host-executed AI SDK tool approvals still work.
- **Cross-process resume, turn continuation, and suspend/detach** are not
supported yet — DeepAgents holds conversation state in memory (LangGraph
`MemorySaver`), which does not survive a bridge restart. These methods throw
`HarnessCapabilityUnsupportedError`.
- **Manual compaction** is not supported.

## Related

- [HarnessAgent](/docs/ai-sdk-harnesses/harness-agent)
- [Harness tools](/docs/ai-sdk-harnesses/tools)
- [Harness adapters](/docs/ai-sdk-harnesses/harness-adapters)
1 change: 1 addition & 0 deletions examples/ai-functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@ai-sdk/harness": "workspace:*",
"@ai-sdk/harness-claude-code": "workspace:*",
"@ai-sdk/harness-codex": "workspace:*",
"@ai-sdk/harness-deepagents": "workspace:*",
"@ai-sdk/harness-pi": "workspace:*",
"@ai-sdk/huggingface": "workspace:*",
"@ai-sdk/hume": "workspace:*",
Expand Down
55 changes: 55 additions & 0 deletions examples/ai-functions/src/harness-agent/deepagents/attach.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
HarnessAgent,
type HarnessAgentResumeSessionState,
} from '@ai-sdk/harness/agent';
import { deepAgents } from '@ai-sdk/harness-deepagents';
import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
import { printFullStream } from '../../lib/print-full-stream';
import { run } from '../../lib/run';

// Cross-process ATTACH: detach parks the live bridge + sandbox and returns
// coordinates; a fresh HarnessAgent reattaches and continues mid-conversation
// (the in-memory conversation survives because the bridge stays alive).
run(async () => {
const sandbox = createVercelSandbox({
runtime: 'node24',
ports: [4000],
timeout: 10 * 60 * 1000,
});

let sessionId: string;
let resumeState: HarnessAgentResumeSessionState;
{
const agent = new HarnessAgent({ harness: deepAgents, sandbox });
const session = await agent.createSession();
sessionId = session.sessionId;
console.log('--- turn 1 ---');
const result = await agent.stream({
session,
prompt: 'My name is Ada. Remember it.',
});
await printFullStream({ result });
resumeState = await session.detach();
console.log('[handle] live coords:', JSON.stringify(resumeState));
}

{
const agent = new HarnessAgent({ harness: deepAgents, sandbox });
const session = await agent.createSession({
sessionId,
resumeFrom: resumeState,
});
console.log('--- turn 2 ---');
if (!session.isResume) {
throw new Error('expected resumed session');
}
const result = await agent.stream({
session,
prompt: 'What is my name? Answer in one word.',
});
await printFullStream({ result });
await session.destroy();
}

process.exit(0);
});
30 changes: 30 additions & 0 deletions examples/ai-functions/src/harness-agent/deepagents/bash-shell.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { HarnessAgent } from '@ai-sdk/harness/agent';
import { deepAgents } from '@ai-sdk/harness-deepagents';
import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
import { printFullStream } from '../../lib/print-full-stream';
import { run } from '../../lib/run';

run(async () => {
const sandbox = createVercelSandbox({
runtime: 'node24',
ports: [4000],
timeout: 10 * 60 * 1000,
});
const agent = new HarnessAgent({ harness: deepAgents, sandbox });

let exitCode = 0;
const session = await agent.createSession();
try {
const result = await agent.stream({
session,
prompt: 'Run `uname -a` and tell me what kernel this sandbox is running.',
});
await printFullStream({ result });
} catch (err) {
exitCode = 1;
console.error('[example] failed:', err);
} finally {
await session.destroy();
process.exit(exitCode);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { HarnessAgent } from '@ai-sdk/harness/agent';
import { deepAgents } from '@ai-sdk/harness-deepagents';
import { printFullStream } from '../../lib/print-full-stream';
import { run } from '../../lib/run';
import {
createToolApprovalResponseMessages,
printFullStreamAndCaptureToolApproval,
} from '../../lib/harness-tool-approval';
import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';

run(async () => {
const sandbox = createVercelSandbox({
runtime: 'node24',
ports: [4000],
timeout: 10 * 60 * 1000,
});
const agent = new HarnessAgent({
harness: deepAgents,
sandbox,
permissionMode: 'allow-edits',
});

let exitCode = 0;
const session = await agent.createSession();
try {
const first = await agent.stream({
session,
prompt: 'Run `pwd` with the bash tool and tell me the working directory.',
});
const approval = await printFullStreamAndCaptureToolApproval({
result: first,
});
if (approval == null) {
throw new Error('Expected a built-in bash tool approval request.');
}

const second = await agent.stream({
session,
messages: createToolApprovalResponseMessages({
approval,
approved: true,
}),
});
await printFullStream({ result: second });
} catch (err) {
exitCode = 1;
console.error('[example] failed:', err);
} finally {
await session.destroy();
process.exit(exitCode);
}
});
Loading
Loading