experimental-agent 0.0.0

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 ADDED
@@ -0,0 +1,243 @@
1
+ # Agent
2
+
3
+ An LLM running in a loop, with access to a sandbox, tools, and session management. Nothing more.
4
+
5
+ ## Why
6
+
7
+ - **AI SDK compatible** — Built on `ai`. Uses `UIMessage`, `GatewayModelId`, streams the same way. If you know AI SDK, you know this.
8
+ - **Managed workflow** — Runs inside Vercel Workflow. Retries, timeouts, resumption handled for you.
9
+ - **Managed sandbox** — Auto-provisions on first use, auto-snapshots when idle, auto-resumes. You don't manage the VM.
10
+ - **Built-in tools** — Read, Grep, List, Bash. No setup.
11
+
12
+ ## Quick Start
13
+
14
+ ```ts
15
+ // lib/agent.ts
16
+ import { agent } from "experimental-agent";
17
+
18
+ export const myAgent = agent({
19
+ model: "anthropic/claude-sonnet-4",
20
+ instructions: "You are a helpful coding assistant.",
21
+ });
22
+ ```
23
+
24
+ ```ts
25
+ // app/api/chat/[chatId]/route.ts
26
+ import { myAgent } from "@/lib/agent";
27
+
28
+ export async function POST(req: Request, { params }: { params: { chatId: string } }) {
29
+ const { chatId } = await params;
30
+ const { message } = await req.json();
31
+
32
+ const session = await myAgent.session(chatId);
33
+ await session.send({ input: message });
34
+ const stream = await session.stream();
35
+
36
+ return new Response(stream);
37
+ }
38
+
39
+ export async function GET(req: Request, { params }: { params: { chatId: string } }) {
40
+ const { chatId } = await params;
41
+ const session = await myAgent.session(chatId);
42
+ const { messages, streamingMessageId } = await session.ui();
43
+
44
+ return Response.json({ messages, streamingMessageId });
45
+ }
46
+ ```
47
+
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
+
78
+ // Tags for your app's metadata
79
+ await session.tag.set("category", "support");
80
+ ```
81
+
82
+ ## Rendering Messages
83
+
84
+ `session.ui()` returns `UIMessage[]` — the same format `useChat` uses.
85
+
86
+ ### RSC Pattern
87
+
88
+ Server component fetches initial state, client component handles streaming:
89
+
90
+ ```tsx
91
+ // app/chat/[chatId]/page.tsx (server component)
92
+ import { myAgent } from "@/lib/agent";
93
+ import { Chat } from "./chat-client";
94
+
95
+ export default async function ChatPage({ params }) {
96
+ const { chatId } = await params;
97
+ const session = await myAgent.session(chatId);
98
+ const { messages, streamingMessageId } = await session.ui();
99
+
100
+ return (
101
+ <>
102
+ {messages.map((m) => (
103
+ <MessageView key={m.id} message={m} />
104
+ ))}
105
+ <Chat chatId={chatId} streamingMessageId={streamingMessageId} />
106
+ </>
107
+ );
108
+ }
109
+ ```
110
+
111
+ ```tsx
112
+ // chat-client.tsx
113
+ "use client";
114
+
115
+ import { useChat } from "@ai-sdk/react";
116
+
117
+ export function Chat({ chatId, streamingMessageId }) {
118
+ const { messages, input, handleInputChange, handleSubmit, status } = useChat({
119
+ id: chatId,
120
+ api: `/api/chat/${chatId}`,
121
+ initialMessages: [], // hydrate from server if needed
122
+ });
123
+
124
+ return (
125
+ <>
126
+ {messages.map((m) => (
127
+ <MessageView key={m.id} message={m} />
128
+ ))}
129
+ <form onSubmit={handleSubmit}>
130
+ <input value={input} onChange={handleInputChange} />
131
+ <button type="submit" disabled={status !== "ready"}>Send</button>
132
+ </form>
133
+ </>
134
+ );
135
+ }
136
+ ```
137
+
138
+ ### Durable UI
139
+
140
+ Unlike `useChat` alone, messages survive refresh. They're persisted to storage.
141
+
142
+ - Close laptop, open tomorrow — conversation is there
143
+ - Browser crashes mid-stream — reload, partial response preserved
144
+ - Share a session URL — another user sees the same conversation
145
+
146
+ The server component renders completed messages from `session.ui()`. The client component handles new input and streaming. State lives in storage, not browser memory.
147
+
148
+ ## Sandbox
149
+
150
+ ```ts
151
+ // Local (dev)
152
+ agent({ sandbox: { type: "local" } })
153
+
154
+ // Vercel (prod)
155
+ agent({ sandbox: { type: "vercel", resources: { vcpus: 2 }, ports: [3000] } })
156
+
157
+ // Direct access
158
+ await session.sandbox.exec({ command: "npm", args: ["install"] });
159
+
160
+ // Share across sessions
161
+ await myAgent.session("s1", { sandbox: "shared_id" });
162
+ await myAgent.session("s2", { sandbox: "shared_id" });
163
+ ```
164
+
165
+ ## Skills
166
+
167
+ Skills are `SKILL.md` files discovered from the sandbox:
168
+
169
+ ```yaml
170
+ ---
171
+ name: deploy
172
+ description: Deploy to Vercel
173
+ ---
174
+
175
+ Run `vercel deploy` in the project root.
176
+ ```
177
+
178
+ ```ts
179
+ agent({ skillsDir: ".agent/skills" })
180
+ ```
181
+
182
+ ## MCP (this api will change soon)
183
+
184
+ Custom tools with type-safe schemas:
185
+
186
+ ```ts
187
+ import { agent, mcp } from "experimental-agent";
188
+ import { z } from "zod";
189
+
190
+ const myAgent = agent({
191
+ mcp: {
192
+ vercel: mcp({
193
+ url: "/api/mcp",
194
+ headersSchema: z.object({ authorization: z.string() }),
195
+ tools: {
196
+ listProjects: {
197
+ description: "List Vercel projects",
198
+ inputSchema: z.object({ teamId: z.string().optional() }),
199
+ },
200
+ },
201
+ }),
202
+ },
203
+ });
204
+
205
+ // Send with auth
206
+ await session.send({
207
+ input: "List my projects",
208
+ mcpContext: { vercel: { headers: { authorization: `Bearer ${token}` } } },
209
+ });
210
+
211
+ // Handle in your API
212
+ await myAgent.handleMcpToolCall(body, {
213
+ "vercel/listProjects": async ({ input, headers }) => {
214
+ return fetchProjects(input.teamId, headers.authorization);
215
+ },
216
+ });
217
+ ```
218
+
219
+ ## Storage
220
+
221
+ HTTP-based (workflow inputs must be serializable).
222
+
223
+ ```ts
224
+ agent({ storage: { type: "vercel" } }) // default — hosted service
225
+ agent({ storage: { type: "local" } }) // dev — filesystem
226
+ agent({ storage: { type: "custom", url: "https://...", headers: {} } })
227
+ ```
228
+
229
+ The SDK stores: `Session`, `Message`, `Part`, `SandboxRecord`.
230
+
231
+ Everything else—users, titles, access control—belongs in your database. Session ID is your join key.
232
+
233
+ ### Vercel Agent Storage
234
+
235
+ The default `{ type: "vercel" }` uses the hosted Vercel Agent Storage service:
236
+
237
+ - **Zero config** — authenticates via Vercel OIDC, no API keys needed
238
+ - **Multi-tenant** — data isolated by owner, project, and environment
239
+ - **Postgres-backed** — reliable, queryable, scales with you
240
+
241
+ Just deploy to Vercel and it works. No database setup, no connection strings.
242
+
243
+ For self-hosting or custom backends, see [apps/storage](../../apps/storage/) for a reference implementation using `handleStorageRpc`.
@@ -0,0 +1,21 @@
1
+ import * as workflow from 'workflow';
2
+ import { a as StorageConfig } from './types-vRxN1Qz1.mjs';
3
+ import 'ai';
4
+ import 'zod';
5
+ import 'errore';
6
+
7
+ type AgentInput = {
8
+ sessionId: string;
9
+ storageConfig: StorageConfig;
10
+ };
11
+ type AgentMessageInput = {
12
+ assistantMessageId: string;
13
+ hookToken: string;
14
+ };
15
+ declare const agentMessageHook: workflow.TypedHook<AgentMessageInput, AgentMessageInput>;
16
+ declare function agentWorkflow({ input, event, }: {
17
+ input: AgentInput;
18
+ event: AgentMessageInput;
19
+ }): Promise<void>;
20
+
21
+ export { type AgentInput, type AgentMessageInput, agentMessageHook, agentWorkflow };
@@ -0,0 +1,21 @@
1
+ import * as workflow from 'workflow';
2
+ import { a as StorageConfig } from './types-vRxN1Qz1.js';
3
+ import 'ai';
4
+ import 'zod';
5
+ import 'errore';
6
+
7
+ type AgentInput = {
8
+ sessionId: string;
9
+ storageConfig: StorageConfig;
10
+ };
11
+ type AgentMessageInput = {
12
+ assistantMessageId: string;
13
+ hookToken: string;
14
+ };
15
+ declare const agentMessageHook: workflow.TypedHook<AgentMessageInput, AgentMessageInput>;
16
+ declare function agentWorkflow({ input, event, }: {
17
+ input: AgentInput;
18
+ event: AgentMessageInput;
19
+ }): Promise<void>;
20
+
21
+ export { type AgentInput, type AgentMessageInput, agentMessageHook, agentWorkflow };