experimental-agent 0.2.1 → 0.3.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 +55 -254
- package/dist/adapter-BigchkkI.d.mts +201 -0
- package/dist/adapter-BigchkkI.d.ts +201 -0
- package/dist/chunk-BFFNCESS.mjs +302 -0
- package/dist/chunk-C4VSUEY2.mjs +72 -0
- package/dist/chunk-DOD4MC5D.mjs +196 -0
- package/dist/chunk-ELWIUJUK.mjs +96 -0
- package/dist/chunk-GKASMIBR.mjs +50 -0
- package/dist/chunk-JO3JDCH5.mjs +107 -0
- package/dist/chunk-MSWINCCM.mjs +128 -0
- package/dist/chunk-RT72C52I.mjs +324 -0
- package/dist/chunk-ZUFJJYC4.mjs +150 -0
- package/dist/{handler-FRUPZ4LX.mjs → docker-QPCLWLYR.mjs} +3 -4
- package/dist/entry-BmQ8FO-5.d.ts +36 -0
- package/dist/entry-CZd9aAwn.d.mts +36 -0
- package/dist/index.d.mts +415 -18
- package/dist/index.d.ts +415 -18
- package/dist/index.js +3036 -5494
- package/dist/index.mjs +3264 -1142
- package/dist/lifecycle-workflow-steps.d.mts +5 -0
- package/dist/lifecycle-workflow-steps.d.ts +5 -0
- package/dist/lifecycle-workflow-steps.js +263 -0
- package/dist/lifecycle-workflow-steps.mjs +9 -0
- package/dist/lifecycle-workflow.d.mts +6 -6
- package/dist/lifecycle-workflow.d.ts +6 -6
- package/dist/lifecycle-workflow.js +192 -905
- package/dist/lifecycle-workflow.mjs +3 -1
- package/dist/local-KJ3BSIFJ.mjs +8 -0
- package/dist/next/loader.d.mts +1 -0
- package/dist/next/loader.d.ts +1 -0
- package/dist/next/loader.js +44 -18
- package/dist/next/loader.mjs +18 -13
- package/dist/next.js +32 -9
- package/dist/next.mjs +6 -4
- package/dist/{process-manager-JDUJDYGU.mjs → process-manager-WQHAIVRB.mjs} +1 -1
- package/dist/sandbox.d.mts +6 -0
- package/dist/sandbox.d.ts +6 -0
- package/dist/sandbox.js +1070 -0
- package/dist/sandbox.mjs +19 -0
- package/dist/steps-BnkRQKlc.d.ts +173 -0
- package/dist/steps-u-mGDbP_.d.mts +173 -0
- package/dist/storage.d.mts +11 -0
- package/dist/storage.d.ts +11 -0
- package/dist/storage.js +234 -0
- package/dist/storage.mjs +12 -0
- package/dist/vercel-QZ6INPMV.mjs +11 -0
- package/package.json +26 -5
- package/dist/agent-workflow.d.mts +0 -30
- package/dist/agent-workflow.d.ts +0 -30
- package/dist/agent-workflow.js +0 -5433
- package/dist/agent-workflow.mjs +0 -14
- package/dist/chunk-AML2VCQS.mjs +0 -1287
- package/dist/chunk-FQ67QZOI.mjs +0 -75
- package/dist/chunk-NO7RHGTH.mjs +0 -2367
- package/dist/chunk-NXDVNJRS.mjs +0 -106
- package/dist/chunk-OZZVS6L5.mjs +0 -139
- package/dist/chunk-QRWGDFFY.mjs +0 -75
- package/dist/chunk-SJVFFE5D.mjs +0 -402
- package/dist/chunk-TAXLUVIC.mjs +0 -1
- package/dist/chunk-TGNVXSMX.mjs +0 -399
- package/dist/chunk-YRYXN7W4.mjs +0 -48
- package/dist/chunk-ZIAHPXOJ.mjs +0 -595
- package/dist/client-BKA7XBGW.mjs +0 -15
- package/dist/client-CEeSFGva.d.mts +0 -2376
- package/dist/client-CEeSFGva.d.ts +0 -2376
- package/dist/docker-FB2MJTHJ.mjs +0 -12
- package/dist/local-fs-handlers-SYOCKTPN.mjs +0 -447
- package/dist/sandbox-UENKQV3T.mjs +0 -21
- package/dist/storage-LSDMRW73.mjs +0 -20
- package/dist/vercel-SD3JTECG.mjs +0 -20
- package/dist/vercel-sdk-I6A4MVAN.mjs +0 -8
package/README.md
CHANGED
|
@@ -9,10 +9,10 @@ pnpm i experimental-agent
|
|
|
9
9
|
## Why
|
|
10
10
|
|
|
11
11
|
- **AI SDK compatible** — Built on `ai`. Uses `UIMessage`, `GatewayModelId`, streams the same way. If you know AI SDK, you know this.
|
|
12
|
-
- **
|
|
12
|
+
- **Opt-in durability** — Works in-process by default. Add a `"use workflow"` wrapper for full durability that survives crashes, timeouts, and deploys.
|
|
13
|
+
- **Bring your own storage** — Implement a flat handler map backed by any database. Built-in `localStorage()` for dev.
|
|
13
14
|
- **Managed sandbox** — Auto-starts on first use, auto-snapshots when idle, auto-resumes. You don't manage the VM.
|
|
14
|
-
- **Built-in
|
|
15
|
-
- **Built-in tools** — Read, Grep, List, Bash. No setup.
|
|
15
|
+
- **Built-in tools** — Read, Grep, List, Bash, Write, Edit, Skill, JavaScript. No setup.
|
|
16
16
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
@@ -20,8 +20,8 @@ pnpm i experimental-agent
|
|
|
20
20
|
// lib/agent.ts
|
|
21
21
|
import { agent } from "experimental-agent";
|
|
22
22
|
|
|
23
|
-
export const myAgent = agent({
|
|
24
|
-
model: "anthropic/claude-opus-4.
|
|
23
|
+
export const myAgent = agent("my-agent", {
|
|
24
|
+
model: "anthropic/claude-opus-4.6",
|
|
25
25
|
system: "You are a helpful coding assistant.",
|
|
26
26
|
});
|
|
27
27
|
```
|
|
@@ -33,10 +33,10 @@ import { createUIMessageStreamResponse } from "ai";
|
|
|
33
33
|
|
|
34
34
|
export async function POST(req: Request, { params }: { params: { chatId: string } }) {
|
|
35
35
|
const { chatId } = await params;
|
|
36
|
-
const { message } = await req.json();
|
|
36
|
+
const { message } = await req.json();
|
|
37
37
|
|
|
38
|
-
const session =
|
|
39
|
-
await session.send(
|
|
38
|
+
const session = myAgent.session(chatId);
|
|
39
|
+
await session.send(message);
|
|
40
40
|
const stream = await session.stream();
|
|
41
41
|
|
|
42
42
|
return createUIMessageStreamResponse({ stream });
|
|
@@ -44,275 +44,76 @@ export async function POST(req: Request, { params }: { params: { chatId: string
|
|
|
44
44
|
|
|
45
45
|
export async function GET(req: Request, { params }: { params: { chatId: string } }) {
|
|
46
46
|
const { chatId } = await params;
|
|
47
|
-
const session =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
return new Response("Internal server error", { status: 500 });
|
|
47
|
+
const session = myAgent.session(chatId);
|
|
48
|
+
try {
|
|
49
|
+
const stream = await session.stream();
|
|
50
|
+
return createUIMessageStreamResponse({ stream });
|
|
51
|
+
} catch {
|
|
52
|
+
return Response.json(await session.history());
|
|
55
53
|
}
|
|
56
|
-
|
|
57
|
-
return createUIMessageStreamResponse({ stream });
|
|
58
54
|
}
|
|
59
55
|
```
|
|
60
56
|
|
|
57
|
+
## Adding Workflow
|
|
61
58
|
|
|
59
|
+
Everything works without workflow. To add durability:
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
`session.ui()` returns `UIMessage[]` — the same format `useChat` uses.
|
|
66
|
-
|
|
67
|
-
### RSC Pattern
|
|
68
|
-
|
|
69
|
-
Server component fetches initial state, client component handles streaming:
|
|
70
|
-
|
|
71
|
-
```tsx
|
|
72
|
-
// app/chat/[chatId]/page.tsx (server component)
|
|
61
|
+
```ts
|
|
62
|
+
// workflow.ts
|
|
73
63
|
import { myAgent } from "@/lib/agent";
|
|
74
|
-
import {
|
|
75
|
-
|
|
76
|
-
export
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
throw new Error("Failed to get messages");
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<>
|
|
87
|
-
{ui.messages.map((m) => (
|
|
88
|
-
<MessageView key={m.id} message={m} />
|
|
89
|
-
))}
|
|
90
|
-
<Chat chatId={chatId} streamingMessageId={ui.streamingMessageId} />
|
|
91
|
-
</>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
```tsx
|
|
97
|
-
// chat-client.tsx
|
|
98
|
-
"use client";
|
|
99
|
-
|
|
100
|
-
import { useEffect } from "react";
|
|
101
|
-
import { useChat, DefaultChatTransport } from "@ai-sdk/react";
|
|
102
|
-
|
|
103
|
-
export function Chat({ chatId, streamingMessageId }) {
|
|
104
|
-
const { messages, resumeStream, sendMessage, status } = useChat({
|
|
105
|
-
id: chatId,
|
|
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
|
-
}),
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
if (streamingMessageId) {
|
|
120
|
-
resumeStream();
|
|
121
|
-
}
|
|
122
|
-
}, [streamingMessageId, resumeStream]);
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<>
|
|
126
|
-
{messages.map((m) => (
|
|
127
|
-
<MessageView key={m.id} message={m} />
|
|
128
|
-
))}
|
|
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>
|
|
145
|
-
</form>
|
|
146
|
-
</>
|
|
147
|
-
);
|
|
64
|
+
import type { SessionSendArgs } from "experimental-agent";
|
|
65
|
+
|
|
66
|
+
export async function agentWorkflow(
|
|
67
|
+
sessionId: string,
|
|
68
|
+
...args: SessionSendArgs<typeof myAgent>
|
|
69
|
+
) {
|
|
70
|
+
"use workflow";
|
|
71
|
+
return await myAgent.session(sessionId).send(...args);
|
|
148
72
|
}
|
|
149
73
|
```
|
|
150
74
|
|
|
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
|
-
system: "...", // 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
|
-
|
|
194
|
-
### Handles vs Records
|
|
195
|
-
|
|
196
|
-
- `myAgent.session(id)` and `myAgent.sandbox(id)` return orchestration handles.
|
|
197
|
-
- Handles are use-case APIs (send, stream, run tools, resume sandbox lifecycle).
|
|
198
|
-
- `myAgent.storage.*` is the raw record API (`Session`, `SandboxRecord`, `Message`, `Part`, `Command`, `SetupSnapshot`).
|
|
199
|
-
- Use `storage` for record lookups and filters (for example existence checks and list queries).
|
|
200
|
-
|
|
201
|
-
### Durable UI
|
|
202
|
-
|
|
203
|
-
Unlike `useChat` alone, messages survive refresh. They're persisted to storage.
|
|
204
|
-
|
|
205
|
-
- Close laptop, open tomorrow — conversation is there
|
|
206
|
-
- Browser crashes mid-stream — reload, partial response preserved
|
|
207
|
-
- Share a session URL — another user sees the same conversation
|
|
208
|
-
|
|
209
|
-
The server component renders completed messages from `session.ui()`. The client component handles new input and streaming. State lives in storage, not browser memory.
|
|
210
|
-
|
|
211
|
-
## Sandbox
|
|
212
|
-
|
|
213
|
-
```ts
|
|
214
|
-
// Local (dev)
|
|
215
|
-
const myAgent = agent({ sandbox: { type: "local" } })
|
|
216
|
-
|
|
217
|
-
// Vercel (prod)
|
|
218
|
-
const myAgent = agent({ sandbox: { type: "vercel", resources: { vcpus: 2 }, ports: [3000] } })
|
|
219
|
-
|
|
220
|
-
const session = await myAgent.session("some-session")
|
|
221
|
-
|
|
222
|
-
// Direct access
|
|
223
|
-
await session.sandbox.exec({ command: "npm", args: ["install"] });
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### Share a sandbox
|
|
227
|
-
|
|
228
75
|
```ts
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
await myAgent.session("s2", { sandbox: sharedBox.id });
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## Skills
|
|
236
|
-
|
|
237
|
-
Skills are `SKILL.md` files discovered from the sandbox:
|
|
76
|
+
// route.ts
|
|
77
|
+
import { start } from "workflow/api";
|
|
78
|
+
import { agentWorkflow } from "./workflow";
|
|
238
79
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
description: Deploy to Vercel
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
Run `vercel deploy` in the project root.
|
|
80
|
+
const result = await start(agentWorkflow, [chatId, message, opts]);
|
|
81
|
+
const stream = await session.stream(result);
|
|
82
|
+
return createUIMessageStreamResponse({ stream });
|
|
246
83
|
```
|
|
247
84
|
|
|
248
|
-
|
|
249
|
-
agent({ skillsDir: ".agent/skills" })
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
## MCP (this api will change soon)
|
|
253
|
-
|
|
254
|
-
Custom tools with type-safe schemas:
|
|
85
|
+
## Storage
|
|
255
86
|
|
|
256
87
|
```ts
|
|
257
|
-
|
|
258
|
-
import {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
},
|
|
270
|
-
},
|
|
271
|
-
}),
|
|
88
|
+
// Dev — built-in filesystem storage
|
|
89
|
+
import { localStorage } from "experimental-agent/storage";
|
|
90
|
+
agent("my-agent", { storage: localStorage() })
|
|
91
|
+
|
|
92
|
+
// Prod — your own database
|
|
93
|
+
agent("my-agent", {
|
|
94
|
+
async storage(store) {
|
|
95
|
+
return await store.on({
|
|
96
|
+
"session.get": async ({ id }) => await db.sessions.findById(id),
|
|
97
|
+
"session.set": async ({ id, value }) => await db.sessions.upsert(id, value),
|
|
98
|
+
// ... all handlers
|
|
99
|
+
});
|
|
272
100
|
},
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
// Send with auth
|
|
276
|
-
await session.send({
|
|
277
|
-
input: "List my projects",
|
|
278
|
-
mcpContext: { vercel: { headers: { authorization: `Bearer ${token}` } } },
|
|
279
|
-
});
|
|
101
|
+
})
|
|
280
102
|
|
|
281
|
-
//
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
103
|
+
// To add workflow durability, just add "use step" at the top:
|
|
104
|
+
agent("my-agent", {
|
|
105
|
+
async storage(store) {
|
|
106
|
+
"use step";
|
|
107
|
+
return await store.on({ /* same handlers */ });
|
|
285
108
|
},
|
|
286
|
-
})
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
## Storage
|
|
290
|
-
|
|
291
|
-
HTTP-based (workflow inputs must be serializable).
|
|
292
|
-
|
|
293
|
-
```ts
|
|
294
|
-
agent({ storage: { type: "vercel" } }) // default — hosted service
|
|
295
|
-
agent({ storage: { type: "local" } }) // dev — filesystem
|
|
296
|
-
agent({ storage: { type: "custom", url: "https://...", headers: {} } })
|
|
109
|
+
})
|
|
297
110
|
```
|
|
298
111
|
|
|
299
|
-
The SDK stores
|
|
300
|
-
|
|
301
|
-
Everything else—users, titles, access control—belongs in your database. Session ID is your join key.
|
|
302
|
-
|
|
303
|
-
For session queries scoped to a sandbox, use `storage.session.listBySandbox({ sandboxId, ... })`.
|
|
304
|
-
|
|
305
|
-
### Vercel Agent Storage
|
|
306
|
-
|
|
307
|
-
The default `{ type: "vercel" }` uses the hosted Vercel Agent Storage service:
|
|
308
|
-
|
|
309
|
-
- **Zero config** — authenticates via Vercel OIDC, no API keys needed
|
|
310
|
-
- **Multi-tenant** — data isolated by owner, project, and environment
|
|
311
|
-
- **Postgres-backed** — reliable, queryable, scales with you
|
|
112
|
+
The SDK stores `Session`, `Message`, `Part`, `Sandbox`. Everything else — users, titles, access control — belongs in your database. Session ID is your join key.
|
|
312
113
|
|
|
313
|
-
|
|
114
|
+
## Documentation
|
|
314
115
|
|
|
315
|
-
|
|
116
|
+
Full docs at [packages/agent/docs](./docs/).
|
|
316
117
|
|
|
317
118
|
## Development
|
|
318
119
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde';
|
|
2
|
+
import { NetworkPolicy } from '@vercel/sandbox';
|
|
3
|
+
import { UIMessage } from 'ai';
|
|
4
|
+
|
|
5
|
+
type Session = {
|
|
6
|
+
id: string;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
updatedAt: number;
|
|
9
|
+
model: string | null;
|
|
10
|
+
system: string | null;
|
|
11
|
+
sandboxId: string | null;
|
|
12
|
+
lastMessageId: string | null;
|
|
13
|
+
activeTools: string[] | null;
|
|
14
|
+
skillsDir: string[] | null;
|
|
15
|
+
generation: {
|
|
16
|
+
maxSteps?: number;
|
|
17
|
+
temperature?: number;
|
|
18
|
+
topK?: number;
|
|
19
|
+
topP?: number;
|
|
20
|
+
frequencyPenalty?: number;
|
|
21
|
+
presencePenalty?: number;
|
|
22
|
+
maxOutputTokens?: number;
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
} | null;
|
|
25
|
+
};
|
|
26
|
+
type Message = {
|
|
27
|
+
id: string;
|
|
28
|
+
sessionId: string;
|
|
29
|
+
role: "user" | "assistant" | "system";
|
|
30
|
+
createdAt: number;
|
|
31
|
+
startedAt: number | null;
|
|
32
|
+
completedAt: number | null;
|
|
33
|
+
interruptedAt: number | null;
|
|
34
|
+
interruptedLastPart: {
|
|
35
|
+
index: number;
|
|
36
|
+
part: unknown;
|
|
37
|
+
} | null;
|
|
38
|
+
usage: {
|
|
39
|
+
steps: {
|
|
40
|
+
stepIndex: number;
|
|
41
|
+
model: string;
|
|
42
|
+
inputTokens: number;
|
|
43
|
+
outputTokens: number;
|
|
44
|
+
totalTokens: number;
|
|
45
|
+
cacheReadTokens: number;
|
|
46
|
+
cacheWriteTokens: number;
|
|
47
|
+
reasoningTokens: number;
|
|
48
|
+
}[];
|
|
49
|
+
summary: {
|
|
50
|
+
model: string;
|
|
51
|
+
inputTokens: number;
|
|
52
|
+
outputTokens: number;
|
|
53
|
+
totalTokens: number;
|
|
54
|
+
cacheReadTokens: number;
|
|
55
|
+
cacheWriteTokens: number;
|
|
56
|
+
reasoningTokens: number;
|
|
57
|
+
stepCount: number;
|
|
58
|
+
};
|
|
59
|
+
} | null;
|
|
60
|
+
workflowRunId?: string | null;
|
|
61
|
+
};
|
|
62
|
+
type Part = {
|
|
63
|
+
id: string;
|
|
64
|
+
messageId: string;
|
|
65
|
+
sessionId: string;
|
|
66
|
+
index: number;
|
|
67
|
+
part: UIMessage["parts"][number];
|
|
68
|
+
};
|
|
69
|
+
type Sandbox = {
|
|
70
|
+
id: string;
|
|
71
|
+
setup: {
|
|
72
|
+
binding: string;
|
|
73
|
+
version: string | null;
|
|
74
|
+
completedAt: number | null;
|
|
75
|
+
metadata: Record<string, unknown> | null;
|
|
76
|
+
networkPolicy: NetworkPolicy | null;
|
|
77
|
+
};
|
|
78
|
+
createdAt: number | null;
|
|
79
|
+
lastActiveAt: number | null;
|
|
80
|
+
};
|
|
81
|
+
type Setup = {
|
|
82
|
+
version: string;
|
|
83
|
+
snapshotId: string | null;
|
|
84
|
+
createdAt: number;
|
|
85
|
+
lastUsedAt: number | null;
|
|
86
|
+
};
|
|
87
|
+
type SetupSnapshot = Setup;
|
|
88
|
+
|
|
89
|
+
type StorageHandlers = {
|
|
90
|
+
"session.get"(p: {
|
|
91
|
+
id: string;
|
|
92
|
+
}): Promise<Session | null>;
|
|
93
|
+
"session.set"(p: {
|
|
94
|
+
id: string;
|
|
95
|
+
value: Session;
|
|
96
|
+
}): Promise<void>;
|
|
97
|
+
"session.update"(p: {
|
|
98
|
+
id: string;
|
|
99
|
+
updates: Partial<Session>;
|
|
100
|
+
}): Promise<Session>;
|
|
101
|
+
"message.get"(p: {
|
|
102
|
+
id: string;
|
|
103
|
+
}): Promise<Message | null>;
|
|
104
|
+
"message.set"(p: {
|
|
105
|
+
id: string;
|
|
106
|
+
value: Message;
|
|
107
|
+
}): Promise<void>;
|
|
108
|
+
"message.update"(p: {
|
|
109
|
+
id: string;
|
|
110
|
+
updates: Partial<Message>;
|
|
111
|
+
}): Promise<Message>;
|
|
112
|
+
"message.listBySession"(p: {
|
|
113
|
+
sessionId: string;
|
|
114
|
+
}): Promise<Message[]>;
|
|
115
|
+
"part.get"(p: {
|
|
116
|
+
id: string;
|
|
117
|
+
}): Promise<Part | null>;
|
|
118
|
+
"part.set"(p: {
|
|
119
|
+
id: string;
|
|
120
|
+
value: Part;
|
|
121
|
+
}): Promise<void>;
|
|
122
|
+
"part.listBySession"(p: {
|
|
123
|
+
sessionId: string;
|
|
124
|
+
}): Promise<Part[]>;
|
|
125
|
+
"sandbox.get"(p: {
|
|
126
|
+
id: string;
|
|
127
|
+
}): Promise<Sandbox | null>;
|
|
128
|
+
"sandbox.set"(p: {
|
|
129
|
+
id: string;
|
|
130
|
+
value: Sandbox;
|
|
131
|
+
}): Promise<void>;
|
|
132
|
+
"sandbox.update"(p: {
|
|
133
|
+
id: string;
|
|
134
|
+
updates: Partial<Sandbox>;
|
|
135
|
+
}): Promise<Sandbox>;
|
|
136
|
+
"setup.get"(p: {
|
|
137
|
+
id: string;
|
|
138
|
+
}): Promise<Setup | null>;
|
|
139
|
+
"setup.set"(p: {
|
|
140
|
+
id: string;
|
|
141
|
+
value: Setup;
|
|
142
|
+
}): Promise<void>;
|
|
143
|
+
};
|
|
144
|
+
type StorageCall = {
|
|
145
|
+
[K in keyof StorageHandlers]: {
|
|
146
|
+
method: K;
|
|
147
|
+
} & Parameters<StorageHandlers[K]>[0];
|
|
148
|
+
}[keyof StorageHandlers];
|
|
149
|
+
declare class StorageStep {
|
|
150
|
+
event: StorageCall;
|
|
151
|
+
constructor(event: StorageCall);
|
|
152
|
+
static [WORKFLOW_SERIALIZE](instance: StorageStep): {
|
|
153
|
+
event: StorageCall;
|
|
154
|
+
};
|
|
155
|
+
static [WORKFLOW_DESERIALIZE](data: {
|
|
156
|
+
event: StorageCall;
|
|
157
|
+
}): StorageStep;
|
|
158
|
+
on(handlers: StorageHandlers): Promise<any>;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* A single function that handles all storage operations. Intended to be
|
|
162
|
+
* marked with `"use step"` so that its body (and Node.js-dependent
|
|
163
|
+
* imports like database clients) is extracted by the workflow bundler
|
|
164
|
+
* and runs server-side.
|
|
165
|
+
*
|
|
166
|
+
* The step receives a `StorageStep` instance (serializable via
|
|
167
|
+
* `@workflow/serde`) and should call `step.handle({ ... })` with a
|
|
168
|
+
* `StorageHandlers` map.
|
|
169
|
+
*/
|
|
170
|
+
type StorageStepFunction = (store: StorageStep) => Promise<any>;
|
|
171
|
+
interface Storage {
|
|
172
|
+
session: {
|
|
173
|
+
get(key: string): Promise<Session | null>;
|
|
174
|
+
set(key: string, value: Session): Promise<void>;
|
|
175
|
+
update(key: string, updates: Partial<Session>): Promise<Session>;
|
|
176
|
+
};
|
|
177
|
+
message: {
|
|
178
|
+
get(key: string): Promise<Message | null>;
|
|
179
|
+
set(key: string, value: Message): Promise<void>;
|
|
180
|
+
update(key: string, updates: Partial<Message>): Promise<Message>;
|
|
181
|
+
listBySession(sessionId: string): Promise<Message[]>;
|
|
182
|
+
};
|
|
183
|
+
part: {
|
|
184
|
+
get(key: string): Promise<Part | null>;
|
|
185
|
+
set(key: string, value: Part): Promise<void>;
|
|
186
|
+
listBySession(sessionId: string): Promise<Part[]>;
|
|
187
|
+
};
|
|
188
|
+
sandbox: {
|
|
189
|
+
get(key: string): Promise<Sandbox | null>;
|
|
190
|
+
set(key: string, value: Sandbox): Promise<void>;
|
|
191
|
+
update(key: string, updates: Partial<Sandbox>): Promise<Sandbox>;
|
|
192
|
+
};
|
|
193
|
+
setup: {
|
|
194
|
+
get(key: string): Promise<Setup | null>;
|
|
195
|
+
set(key: string, value: Setup): Promise<void>;
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
type StorageInput = StorageHandlers | StorageStepFunction;
|
|
199
|
+
declare function toStorage(h: StorageInput): Storage;
|
|
200
|
+
|
|
201
|
+
export { type Message as M, type Part as P, type StorageHandlers as S, type Sandbox as a, type Session as b, type Setup as c, type SetupSnapshot as d, type Storage as e, type StorageCall as f, StorageStep as g, type StorageStepFunction as h, type StorageInput as i, toStorage as t };
|