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.
Files changed (32) hide show
  1. package/README.md +118 -55
  2. package/dist/agent-workflow.d.mts +1 -1
  3. package/dist/agent-workflow.d.ts +1 -1
  4. package/dist/agent-workflow.js +474 -66
  5. package/dist/agent-workflow.mjs +1 -1
  6. package/dist/{chunk-DPPQO7DA.mjs → chunk-24DJSI7C.mjs} +34 -3
  7. package/dist/chunk-4RGMKC2M.mjs +755 -0
  8. package/dist/{chunk-2YI7MQGZ.mjs → chunk-6ICYKNCC.mjs} +24 -1
  9. package/dist/chunk-PGYYQ3WZ.mjs +1088 -0
  10. package/dist/{client-FCFZYOOB.mjs → client-4Y3UPWFR.mjs} +3 -3
  11. package/dist/client-BBpD9kKL.d.ts +193 -0
  12. package/dist/client-BGJViybU.d.mts +193 -0
  13. package/dist/{client-RRX3GDQD.mjs → client-HUG4HT5L.mjs} +1 -1
  14. package/dist/index.d.mts +5 -106
  15. package/dist/index.d.ts +5 -106
  16. package/dist/index.js +526 -77
  17. package/dist/index.mjs +57 -16
  18. package/dist/{lifecycle-workflow-steps-6BLGTYVB.mjs → lifecycle-workflow-steps-HHN46ZAD.mjs} +2 -2
  19. package/dist/lifecycle-workflow.d.mts +2 -2
  20. package/dist/lifecycle-workflow.d.ts +2 -2
  21. package/dist/lifecycle-workflow.js +217 -19
  22. package/dist/lifecycle-workflow.mjs +1 -1
  23. package/dist/{local-J6QFWSWB.mjs → local-BYPFRMLZ.mjs} +42 -4
  24. package/dist/{sandbox-Y3ENCNUA.mjs → sandbox-BFA4ECEQ.mjs} +3 -3
  25. package/dist/{storage-QSTSE2ZB.mjs → storage-2U2QFNWI.mjs} +2 -2
  26. package/dist/{types-vRxN1Qz1.d.mts → types-DPXFq_r6.d.mts} +110 -1
  27. package/dist/{types-vRxN1Qz1.d.ts → types-DPXFq_r6.d.ts} +110 -1
  28. package/package.json +13 -12
  29. package/dist/chunk-JQPR6M7D.mjs +0 -649
  30. package/dist/chunk-MR4UWCJT.mjs +0 -878
  31. package/dist/types-Lwut_0_u.d.mts +0 -80
  32. 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-provisions on first use, auto-snapshots when idle, auto-resumes. You don't manage the VM.
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-sonnet-4",
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 new Response(stream);
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 { messages, streamingMessageId } = await session.ui();
43
-
44
- return Response.json({ messages, streamingMessageId });
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 { messages, streamingMessageId } = await session.ui();
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 { useChat } from "@ai-sdk/react";
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, input, handleInputChange, handleSubmit, status } = useChat({
104
+ const { messages, resumeStream, sendMessage, status } = useChat({
119
105
  id: chatId,
120
- api: `/api/chat/${chatId}`,
121
- initialMessages: [], // hydrate from server if needed
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 onSubmit={handleSubmit}>
130
- <input value={input} onChange={handleInputChange} />
131
- <button type="submit" disabled={status !== "ready"}>Send</button>
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
- // Share across sessions
161
- await myAgent.session("s1", { sandbox: "shared_id" });
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
@@ -1,5 +1,5 @@
1
1
  import * as workflow from 'workflow';
2
- import { a as StorageConfig } from './types-vRxN1Qz1.mjs';
2
+ import { i as StorageConfig } from './types-DPXFq_r6.mjs';
3
3
  import 'ai';
4
4
  import 'zod';
5
5
  import 'errore';
@@ -1,5 +1,5 @@
1
1
  import * as workflow from 'workflow';
2
- import { a as StorageConfig } from './types-vRxN1Qz1.js';
2
+ import { i as StorageConfig } from './types-DPXFq_r6.js';
3
3
  import 'ai';
4
4
  import 'zod';
5
5
  import 'errore';