experimental-agent 0.0.0 → 0.0.2
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/README.md +118 -55
- package/dist/agent-workflow.d.mts +1 -1
- package/dist/agent-workflow.d.ts +1 -1
- package/dist/agent-workflow.js +474 -66
- package/dist/agent-workflow.mjs +1 -1
- package/dist/{chunk-DPPQO7DA.mjs → chunk-24DJSI7C.mjs} +34 -3
- package/dist/chunk-4RGMKC2M.mjs +755 -0
- package/dist/{chunk-2YI7MQGZ.mjs → chunk-6ICYKNCC.mjs} +24 -1
- package/dist/chunk-PGYYQ3WZ.mjs +1088 -0
- package/dist/{client-FCFZYOOB.mjs → client-4Y3UPWFR.mjs} +3 -3
- package/dist/client-BBpD9kKL.d.ts +193 -0
- package/dist/client-BGJViybU.d.mts +193 -0
- package/dist/{client-RRX3GDQD.mjs → client-HUG4HT5L.mjs} +1 -1
- package/dist/index.d.mts +5 -106
- package/dist/index.d.ts +5 -106
- package/dist/index.js +526 -77
- package/dist/index.mjs +57 -16
- package/dist/{lifecycle-workflow-steps-6BLGTYVB.mjs → lifecycle-workflow-steps-HHN46ZAD.mjs} +2 -2
- package/dist/lifecycle-workflow.d.mts +2 -2
- package/dist/lifecycle-workflow.d.ts +2 -2
- package/dist/lifecycle-workflow.js +217 -19
- package/dist/lifecycle-workflow.mjs +1 -1
- package/dist/{local-J6QFWSWB.mjs → local-BYPFRMLZ.mjs} +42 -4
- package/dist/{sandbox-Y3ENCNUA.mjs → sandbox-BFA4ECEQ.mjs} +3 -3
- package/dist/{storage-QSTSE2ZB.mjs → storage-2U2QFNWI.mjs} +2 -2
- package/dist/{types-vRxN1Qz1.d.mts → types-DPXFq_r6.d.mts} +110 -1
- package/dist/{types-vRxN1Qz1.d.ts → types-DPXFq_r6.d.ts} +110 -1
- package/package.json +13 -12
- package/dist/chunk-JQPR6M7D.mjs +0 -649
- package/dist/chunk-MR4UWCJT.mjs +0 -878
- package/dist/types-Lwut_0_u.d.mts +0 -80
- package/dist/types-ctZeJ3iQ.d.ts +0 -80
package/README.md
CHANGED
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
An LLM running in a loop, with access to a sandbox, tools, and session management. Nothing more.
|
|
4
4
|
|
|
5
|
+
```bash
|
|
6
|
+
pnpm i experimental-agent
|
|
7
|
+
```
|
|
8
|
+
|
|
5
9
|
## Why
|
|
6
10
|
|
|
7
11
|
- **AI SDK compatible** — Built on `ai`. Uses `UIMessage`, `GatewayModelId`, streams the same way. If you know AI SDK, you know this.
|
|
8
12
|
- **Managed workflow** — Runs inside Vercel Workflow. Retries, timeouts, resumption handled for you.
|
|
9
|
-
- **Managed sandbox** — Auto-
|
|
13
|
+
- **Managed sandbox** — Auto-starts on first use, auto-snapshots when idle, auto-resumes. You don't manage the VM.
|
|
14
|
+
- **Built-in storage** — Messages, tool calls, and sandbox state persisted automatically. Survives refreshes, crashes, reconnects.
|
|
10
15
|
- **Built-in tools** — Read, Grep, List, Bash. No setup.
|
|
11
16
|
|
|
12
17
|
## Quick Start
|
|
@@ -16,7 +21,7 @@ An LLM running in a loop, with access to a sandbox, tools, and session managemen
|
|
|
16
21
|
import { agent } from "experimental-agent";
|
|
17
22
|
|
|
18
23
|
export const myAgent = agent({
|
|
19
|
-
model: "anthropic/claude-
|
|
24
|
+
model: "anthropic/claude-opus-4.5",
|
|
20
25
|
instructions: "You are a helpful coding assistant.",
|
|
21
26
|
});
|
|
22
27
|
```
|
|
@@ -24,60 +29,36 @@ export const myAgent = agent({
|
|
|
24
29
|
```ts
|
|
25
30
|
// app/api/chat/[chatId]/route.ts
|
|
26
31
|
import { myAgent } from "@/lib/agent";
|
|
32
|
+
import { createUIMessageStreamResponse } from "ai";
|
|
27
33
|
|
|
28
34
|
export async function POST(req: Request, { params }: { params: { chatId: string } }) {
|
|
29
35
|
const { chatId } = await params;
|
|
30
|
-
const { message } = await req.json();
|
|
36
|
+
const { message } = await req.json(); // string or UIMessage
|
|
31
37
|
|
|
32
38
|
const session = await myAgent.session(chatId);
|
|
33
|
-
await session.send({ input: message });
|
|
39
|
+
await session.send({ input: message }); // accepts string, UIMessage, or parts array
|
|
34
40
|
const stream = await session.stream();
|
|
35
41
|
|
|
36
|
-
return
|
|
42
|
+
return createUIMessageStreamResponse({ stream });
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
export async function GET(req: Request, { params }: { params: { chatId: string } }) {
|
|
40
46
|
const { chatId } = await params;
|
|
41
47
|
const session = await myAgent.session(chatId);
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
const stream = await session.stream();
|
|
49
|
+
if (stream instanceof Error) {
|
|
50
|
+
console.error(stream);
|
|
51
|
+
if (stream instanceof SessionNotFoundError) {
|
|
52
|
+
return new Response("Session not found", { status: 404 });
|
|
53
|
+
}
|
|
54
|
+
return new Response("Internal server error", { status: 500 });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return createUIMessageStreamResponse({ stream });
|
|
45
58
|
}
|
|
46
59
|
```
|
|
47
60
|
|
|
48
|
-
## Tools
|
|
49
|
-
|
|
50
|
-
| Tool | Description |
|
|
51
|
-
|------|-------------|
|
|
52
|
-
| **Read** | Read files. Large files (>200 lines) paginate automatically. |
|
|
53
|
-
| **Grep** | ripgrep-powered search. Regex, file types, context lines. |
|
|
54
|
-
| **List** | Directory listing with depth control and glob filtering. |
|
|
55
|
-
| **Bash** | Shell commands. Background processes via `waitUntil: 0`. Persistent CWD. |
|
|
56
|
-
|
|
57
|
-
## Configuration
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
agent({
|
|
61
|
-
model: "anthropic/claude-sonnet-4", // Required
|
|
62
|
-
instructions: "...", // System prompt
|
|
63
|
-
sandbox: { type: "vercel" }, // or "local"
|
|
64
|
-
storage: { type: "vercel" }, // or "local" or "custom"
|
|
65
|
-
skillsDir: ".agent/skills", // Skills location
|
|
66
|
-
mcp: { ... }, // Custom tools
|
|
67
|
-
})
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Sessions
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
const session = await myAgent.session(sessionId);
|
|
74
|
-
|
|
75
|
-
await session.send({ input: "Hello" });
|
|
76
|
-
const stream = await session.stream();
|
|
77
61
|
|
|
78
|
-
// Tags for your app's metadata
|
|
79
|
-
await session.tag.set("category", "support");
|
|
80
|
-
```
|
|
81
62
|
|
|
82
63
|
## Rendering Messages
|
|
83
64
|
|
|
@@ -95,14 +76,18 @@ import { Chat } from "./chat-client";
|
|
|
95
76
|
export default async function ChatPage({ params }) {
|
|
96
77
|
const { chatId } = await params;
|
|
97
78
|
const session = await myAgent.session(chatId);
|
|
98
|
-
const
|
|
79
|
+
const ui = await session.ui();
|
|
80
|
+
if (ui instanceof Error) {
|
|
81
|
+
console.error(ui);
|
|
82
|
+
throw new Error("Failed to get messages");
|
|
83
|
+
}
|
|
99
84
|
|
|
100
85
|
return (
|
|
101
86
|
<>
|
|
102
|
-
{messages.map((m) => (
|
|
87
|
+
{ui.messages.map((m) => (
|
|
103
88
|
<MessageView key={m.id} message={m} />
|
|
104
89
|
))}
|
|
105
|
-
<Chat chatId={chatId} streamingMessageId={streamingMessageId} />
|
|
90
|
+
<Chat chatId={chatId} streamingMessageId={ui.streamingMessageId} />
|
|
106
91
|
</>
|
|
107
92
|
);
|
|
108
93
|
}
|
|
@@ -112,29 +97,100 @@ export default async function ChatPage({ params }) {
|
|
|
112
97
|
// chat-client.tsx
|
|
113
98
|
"use client";
|
|
114
99
|
|
|
115
|
-
import {
|
|
100
|
+
import { useEffect } from "react";
|
|
101
|
+
import { useChat, DefaultChatTransport } from "@ai-sdk/react";
|
|
116
102
|
|
|
117
103
|
export function Chat({ chatId, streamingMessageId }) {
|
|
118
|
-
const { messages,
|
|
104
|
+
const { messages, resumeStream, sendMessage, status } = useChat({
|
|
119
105
|
id: chatId,
|
|
120
|
-
|
|
121
|
-
|
|
106
|
+
transport: new DefaultChatTransport({
|
|
107
|
+
api: `/api/chat/${chatId}`,
|
|
108
|
+
prepareSendMessagesRequest: ({ messages }) => ({
|
|
109
|
+
body: { message: messages.at(-1) },
|
|
110
|
+
}),
|
|
111
|
+
prepareReconnectToStreamRequest: (request) => ({
|
|
112
|
+
...request,
|
|
113
|
+
api: `/api/chat/${chatId}`,
|
|
114
|
+
}),
|
|
115
|
+
}),
|
|
122
116
|
});
|
|
123
117
|
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (streamingMessageId) {
|
|
120
|
+
resumeStream();
|
|
121
|
+
}
|
|
122
|
+
}, [streamingMessageId, resumeStream]);
|
|
123
|
+
|
|
124
124
|
return (
|
|
125
125
|
<>
|
|
126
126
|
{messages.map((m) => (
|
|
127
127
|
<MessageView key={m.id} message={m} />
|
|
128
128
|
))}
|
|
129
|
-
<form
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
<form
|
|
130
|
+
onSubmit={async (e) => {
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
const form = e.currentTarget;
|
|
133
|
+
const textarea = form.elements.namedItem("composer") as HTMLTextAreaElement | null;
|
|
134
|
+
const text = textarea ? textarea.value : "";
|
|
135
|
+
if (text.trim()) {
|
|
136
|
+
await sendMessage({ parts: [{ type: "text", text }] });
|
|
137
|
+
textarea.value = "";
|
|
138
|
+
}
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
<textarea name="composer" />
|
|
142
|
+
<button type="submit" disabled={status !== "ready"}>
|
|
143
|
+
Send
|
|
144
|
+
</button>
|
|
132
145
|
</form>
|
|
133
146
|
</>
|
|
134
147
|
);
|
|
135
148
|
}
|
|
136
149
|
```
|
|
137
150
|
|
|
151
|
+
|
|
152
|
+
## Tools
|
|
153
|
+
|
|
154
|
+
| Tool | Description |
|
|
155
|
+
|------|-------------|
|
|
156
|
+
| **Read** | Read files. Large files (>200 lines) paginate automatically. |
|
|
157
|
+
| **Grep** | ripgrep-powered search. Regex, file types, context lines. |
|
|
158
|
+
| **List** | Directory listing with depth control and glob filtering. |
|
|
159
|
+
| **Bash** | Shell commands. Background processes via `waitUntil: 0`. Persistent CWD. |
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const myAgent = agent({
|
|
165
|
+
model: "anthropic/claude-haiku-4.5", // Required
|
|
166
|
+
instructions: "...", // System prompt
|
|
167
|
+
sandbox: { type: "vercel" }, // or "local"
|
|
168
|
+
storage: { type: "vercel" }, // or "local" or "custom"
|
|
169
|
+
skillsDir: ".agent/skills", // Skills location
|
|
170
|
+
mcp: { ... }, // Custom tools
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
// overrides per session
|
|
174
|
+
myAgent.session("<session-id>", {
|
|
175
|
+
model: "anthropic/claude-opus-4.5"
|
|
176
|
+
})
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Sessions
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const session = await myAgent.session(sessionId);
|
|
183
|
+
|
|
184
|
+
// input can be a string, UIMessage, or parts array
|
|
185
|
+
await session.send({ input: "Hello" });
|
|
186
|
+
await session.send({ input: { role: "user", parts: [{ type: "text", text: "Hello" }] } });
|
|
187
|
+
|
|
188
|
+
const stream = await session.stream();
|
|
189
|
+
|
|
190
|
+
// Tags for your app's metadata
|
|
191
|
+
await session.tag.set("category", "support");
|
|
192
|
+
```
|
|
193
|
+
|
|
138
194
|
### Durable UI
|
|
139
195
|
|
|
140
196
|
Unlike `useChat` alone, messages survive refresh. They're persisted to storage.
|
|
@@ -149,17 +205,24 @@ The server component renders completed messages from `session.ui()`. The client
|
|
|
149
205
|
|
|
150
206
|
```ts
|
|
151
207
|
// Local (dev)
|
|
152
|
-
agent({ sandbox: { type: "local" } })
|
|
208
|
+
const myAgent = agent({ sandbox: { type: "local" } })
|
|
153
209
|
|
|
154
210
|
// Vercel (prod)
|
|
155
|
-
agent({ sandbox: { type: "vercel", resources: { vcpus: 2 }, ports: [3000] } })
|
|
211
|
+
const myAgent = agent({ sandbox: { type: "vercel", resources: { vcpus: 2 }, ports: [3000] } })
|
|
212
|
+
|
|
213
|
+
const session = await myAgent.session("some-session")
|
|
156
214
|
|
|
157
215
|
// Direct access
|
|
158
216
|
await session.sandbox.exec({ command: "npm", args: ["install"] });
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Share a sandbox
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const sharedBox = await myAgent.sandbox("some-sandbox")
|
|
159
223
|
|
|
160
|
-
|
|
161
|
-
await myAgent.session("
|
|
162
|
-
await myAgent.session("s2", { sandbox: "shared_id" });
|
|
224
|
+
await myAgent.session("s1", { sandbox: sharedBox.id });
|
|
225
|
+
await myAgent.session("s2", { sandbox: sharedBox.id });
|
|
163
226
|
```
|
|
164
227
|
|
|
165
228
|
## Skills
|
package/dist/agent-workflow.d.ts
CHANGED