experimental-agent 0.2.3 → 0.4.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.
Files changed (69) hide show
  1. package/README.md +103 -241
  2. package/dist/adapter-zgOel4wW.d.mts +256 -0
  3. package/dist/adapter-zgOel4wW.d.ts +256 -0
  4. package/dist/chunk-BFFNCESS.mjs +302 -0
  5. package/dist/chunk-C4VSUEY2.mjs +72 -0
  6. package/dist/chunk-GKASMIBR.mjs +50 -0
  7. package/dist/chunk-IV75IMEW.mjs +328 -0
  8. package/dist/chunk-JO3JDCH5.mjs +107 -0
  9. package/dist/chunk-MSTM6W3Y.mjs +99 -0
  10. package/dist/chunk-MSWINCCM.mjs +128 -0
  11. package/dist/chunk-RT72C52I.mjs +324 -0
  12. package/dist/chunk-ZUFJJYC4.mjs +150 -0
  13. package/dist/{handler-FRUPZ4LX.mjs → docker-QPCLWLYR.mjs} +3 -4
  14. package/dist/entry-6HYg5qqg.d.mts +36 -0
  15. package/dist/entry-BrWOmEK2.d.ts +36 -0
  16. package/dist/index.d.mts +401 -18
  17. package/dist/index.d.ts +401 -18
  18. package/dist/index.js +3396 -5500
  19. package/dist/index.mjs +3511 -1166
  20. package/dist/lifecycle-workflow-steps.d.mts +5 -0
  21. package/dist/lifecycle-workflow-steps.d.ts +5 -0
  22. package/dist/lifecycle-workflow-steps.js +263 -0
  23. package/dist/lifecycle-workflow-steps.mjs +9 -0
  24. package/dist/lifecycle-workflow.d.mts +6 -6
  25. package/dist/lifecycle-workflow.d.ts +6 -6
  26. package/dist/lifecycle-workflow.js +192 -905
  27. package/dist/lifecycle-workflow.mjs +3 -1
  28. package/dist/local-KJ3BSIFJ.mjs +8 -0
  29. package/dist/next/loader.js +31 -7
  30. package/dist/next/loader.mjs +1 -1
  31. package/dist/next.js +35 -8
  32. package/dist/next.mjs +6 -3
  33. package/dist/{process-manager-JDUJDYGU.mjs → process-manager-WQHAIVRB.mjs} +1 -1
  34. package/dist/sandbox.d.mts +6 -0
  35. package/dist/sandbox.d.ts +6 -0
  36. package/dist/sandbox.js +1070 -0
  37. package/dist/sandbox.mjs +19 -0
  38. package/dist/steps-BIsP57pm.d.mts +173 -0
  39. package/dist/steps-DShnXBLf.d.ts +173 -0
  40. package/dist/storage.d.mts +17 -0
  41. package/dist/storage.d.ts +17 -0
  42. package/dist/storage.js +368 -0
  43. package/dist/storage.mjs +16 -0
  44. package/dist/vercel-QZ6INPMV.mjs +11 -0
  45. package/package.json +29 -5
  46. package/dist/agent-workflow.d.mts +0 -30
  47. package/dist/agent-workflow.d.ts +0 -30
  48. package/dist/agent-workflow.js +0 -5433
  49. package/dist/agent-workflow.mjs +0 -14
  50. package/dist/chunk-7M6UPURS.mjs +0 -75
  51. package/dist/chunk-AML2VCQS.mjs +0 -1287
  52. package/dist/chunk-FQ67QZOI.mjs +0 -75
  53. package/dist/chunk-NO7RHGTH.mjs +0 -2367
  54. package/dist/chunk-NXDVNJRS.mjs +0 -106
  55. package/dist/chunk-OZZVS6L5.mjs +0 -139
  56. package/dist/chunk-SJVFFE5D.mjs +0 -402
  57. package/dist/chunk-TAXLUVIC.mjs +0 -1
  58. package/dist/chunk-TGNVXSMX.mjs +0 -399
  59. package/dist/chunk-YRYXN7W4.mjs +0 -48
  60. package/dist/chunk-ZIAHPXOJ.mjs +0 -595
  61. package/dist/client-BKA7XBGW.mjs +0 -15
  62. package/dist/client-CEeSFGva.d.mts +0 -2376
  63. package/dist/client-CEeSFGva.d.ts +0 -2376
  64. package/dist/docker-FB2MJTHJ.mjs +0 -12
  65. package/dist/local-fs-handlers-SYOCKTPN.mjs +0 -447
  66. package/dist/sandbox-UENKQV3T.mjs +0 -21
  67. package/dist/storage-LSDMRW73.mjs +0 -20
  68. package/dist/vercel-SD3JTECG.mjs +0 -20
  69. 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
- - **Managed workflow** — Runs inside Vercel Workflow. Retries, timeouts, resumption handled for you.
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 storage** — Messages, tool calls, and sandbox state persisted automatically. Survives refreshes, crashes, reconnects.
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,9 +20,25 @@ 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.5",
23
+ export const myAgent = agent("my-agent", {
24
+ model: "anthropic/claude-opus-4.6",
25
25
  system: "You are a helpful coding assistant.",
26
+ skills: [
27
+ { type: "sandbox", path: ".agent/skills/project" },
28
+ { type: "host", path: "./skills/company" },
29
+ {
30
+ type: "git",
31
+ repo: "https://github.com/acme/agent-skills.git",
32
+ ref: "v1.2.0",
33
+ path: "skills",
34
+ },
35
+ {
36
+ type: "inline",
37
+ name: "incident-triage",
38
+ description: "Triage incidents safely",
39
+ instructions: "1. Gather context\n2. Confirm blast radius\n3. Propose mitigations",
40
+ },
41
+ ],
26
42
  });
27
43
  ```
28
44
 
@@ -33,10 +49,10 @@ import { createUIMessageStreamResponse } from "ai";
33
49
 
34
50
  export async function POST(req: Request, { params }: { params: { chatId: string } }) {
35
51
  const { chatId } = await params;
36
- const { message } = await req.json(); // string or UIMessage
52
+ const { message } = await req.json();
37
53
 
38
- const session = await myAgent.session(chatId);
39
- await session.send({ input: message }); // accepts string, UIMessage, or parts array
54
+ const session = myAgent.session(chatId);
55
+ await session.send(message);
40
56
  const stream = await session.stream();
41
57
 
42
58
  return createUIMessageStreamResponse({ stream });
@@ -44,275 +60,120 @@ export async function POST(req: Request, { params }: { params: { chatId: string
44
60
 
45
61
  export async function GET(req: Request, { params }: { params: { chatId: string } }) {
46
62
  const { chatId } = await params;
47
- const session = await myAgent.session(chatId);
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 });
63
+ const session = myAgent.session(chatId);
64
+ try {
65
+ const stream = await session.stream();
66
+ return createUIMessageStreamResponse({ stream });
67
+ } catch {
68
+ return Response.json(await session.history());
55
69
  }
56
-
57
- return createUIMessageStreamResponse({ stream });
58
70
  }
59
71
  ```
60
72
 
73
+ ## Adding Workflow
61
74
 
75
+ Everything works without workflow. To add durability:
62
76
 
63
- ## Rendering Messages
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)
77
+ ```ts
78
+ // workflow.ts
73
79
  import { myAgent } from "@/lib/agent";
74
- import { Chat } from "./chat-client";
75
-
76
- export default async function ChatPage({ params }) {
77
- const { chatId } = await params;
78
- const session = await myAgent.session(chatId);
79
- const ui = await session.ui();
80
- if (ui instanceof Error) {
81
- console.error(ui);
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
- );
80
+ import type { SessionSendArgs } from "experimental-agent";
81
+
82
+ export async function agentWorkflow(
83
+ sessionId: string,
84
+ ...args: SessionSendArgs<typeof myAgent>
85
+ ) {
86
+ "use workflow";
87
+ return await myAgent.session(sessionId).send(...args);
148
88
  }
149
89
  ```
150
90
 
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
91
  ```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" }] } });
92
+ // route.ts
93
+ import { start } from "workflow/api";
94
+ import { agentWorkflow } from "./workflow";
187
95
 
188
- const stream = await session.stream();
189
-
190
- // Tags for your app's metadata
191
- await session.tag.set("category", "support");
96
+ const result = await start(agentWorkflow, [chatId, message, opts]);
97
+ const stream = await session.stream(result);
98
+ return createUIMessageStreamResponse({ stream });
192
99
  ```
193
100
 
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
101
+ ## Storage
212
102
 
213
103
  ```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")
104
+ // Dev — built-in filesystem storage
105
+ import { localStorage } from "experimental-agent/storage";
106
+ agent("my-agent", { storage: localStorage() })
107
+
108
+ // Prod your own database
109
+ agent("my-agent", {
110
+ async storage(store) {
111
+ return await store.on({
112
+ "session.get": async ({ id }) => await db.sessions.findById(id),
113
+ "session.set": async ({ id, value }) => await db.sessions.upsert(id, value),
114
+ // ... all handlers
115
+ });
116
+ },
117
+ })
221
118
 
222
- // Direct access
223
- await session.sandbox.exec({ command: "npm", args: ["install"] });
119
+ // To add workflow durability, just add "use step" at the top:
120
+ agent("my-agent", {
121
+ async storage(store) {
122
+ "use step";
123
+ return await store.on({ /* same handlers */ });
124
+ },
125
+ })
224
126
  ```
225
127
 
226
- ### Share a sandbox
227
-
228
- ```ts
229
- const sharedBox = await myAgent.sandbox("some-sandbox")
230
-
231
- await myAgent.session("s1", { sandbox: sharedBox.id });
232
- await myAgent.session("s2", { sandbox: sharedBox.id });
233
- ```
128
+ The SDK stores `Session`, `Message`, `Part`, `Sandbox`. Everything else — users, titles, access control — belongs in your database. Session ID is your join key.
234
129
 
235
130
  ## Skills
236
131
 
237
- Skills are `SKILL.md` files discovered from the sandbox:
132
+ `skills` is the canonical skills API for both `agent(...)` defaults and `session.update(...)` overrides.
238
133
 
239
- ```yaml
240
- ---
241
- name: deploy
242
- description: Deploy to Vercel
243
- ---
244
-
245
- Run `vercel deploy` in the project root.
246
- ```
134
+ - `{ type: "sandbox", path: "..." }` reads skills already present in sandbox
135
+ - `{ type: "host", path: "..." }` copies skills from host filesystem into sandbox materialized dirs
136
+ - `{ type: "git", ... }` clones a git repo into sandbox materialized dirs (optionally with `ref`, `path`, `name`)
137
+ - `{ type: "inline", ... }` writes an inline `SKILL.md` into sandbox materialized dirs
247
138
 
248
139
  ```ts
249
- agent({ skillsDir: ".agent/skills" })
140
+ const session = myAgent.session("chat-123");
141
+
142
+ await session.update({
143
+ skills: [
144
+ {
145
+ type: "git",
146
+ repo: "https://github.com/acme/agent-skills.git",
147
+ ref: "main",
148
+ path: "skills",
149
+ name: "release-playbook",
150
+ },
151
+ ],
152
+ });
250
153
  ```
251
154
 
252
- ## MCP (this api will change soon)
155
+ ### Migration
253
156
 
254
- Custom tools with type-safe schemas:
157
+ Legacy `skillsDir` configuration is deprecated. Migrate to `skills`:
255
158
 
256
159
  ```ts
257
- import { agent, mcp } from "experimental-agent";
258
- import { z } from "zod";
259
-
260
- const myAgent = agent({
261
- mcp: {
262
- vercel: mcp({
263
- url: "/api/mcp",
264
- headersSchema: z.object({ authorization: z.string() }),
265
- tools: {
266
- listProjects: {
267
- description: "List Vercel projects",
268
- inputSchema: z.object({ teamId: z.string().optional() }),
269
- },
270
- },
271
- }),
272
- },
160
+ // before
161
+ agent("my-agent", {
162
+ skillsDir: [".agent/skills", ".agent/skills/shared"],
273
163
  });
274
164
 
275
- // Send with auth
276
- await session.send({
277
- input: "List my projects",
278
- mcpContext: { vercel: { headers: { authorization: `Bearer ${token}` } } },
165
+ // after
166
+ agent("my-agent", {
167
+ skills: [
168
+ { type: "sandbox", path: ".agent/skills" },
169
+ { type: "sandbox", path: ".agent/skills/shared" },
170
+ ],
279
171
  });
280
-
281
- // Handle in your API
282
- await myAgent.handleMcpToolCall(body, {
283
- "vercel/listProjects": async ({ input, headers }) => {
284
- return fetchProjects(input.teamId, headers.authorization);
285
- },
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: {} } })
297
172
  ```
298
173
 
299
- The SDK stores: `Session`, `Message`, `Part`, `SandboxRecord`.
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
174
+ ## Documentation
306
175
 
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
312
-
313
- Just deploy to Vercel and it works. No database setup, no connection strings.
314
-
315
- For self-hosting or custom backends, see [apps/storage](../../apps/storage/) for a reference implementation using `handleStorageRpc`.
176
+ Full docs at [packages/agent/docs](./docs/).
316
177
 
317
178
  ## Development
318
179
 
@@ -324,3 +185,4 @@ pnpm test # vitest run
324
185
  pnpm test:watch # vitest watch
325
186
  pnpm typecheck # tsc --noEmit
326
187
  ```
188
+
@@ -0,0 +1,256 @@
1
+ import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde';
2
+ import { NetworkPolicy } from '@vercel/sandbox';
3
+ import { UIMessage } from 'ai';
4
+
5
+ type SkillSourceType = "sandbox" | "host" | "git" | "inline";
6
+ type SandboxSkillInput = {
7
+ type: "sandbox";
8
+ path: string;
9
+ };
10
+ type HostSkillInput = {
11
+ type: "host";
12
+ path: string;
13
+ };
14
+ type GitSkillInput = {
15
+ type: "git";
16
+ repo: string;
17
+ ref?: string;
18
+ path?: string;
19
+ /**
20
+ * Skill directory name under `path`.
21
+ */
22
+ name?: string;
23
+ };
24
+ type InlineSkillInput = {
25
+ type: "inline";
26
+ name: string;
27
+ description: string;
28
+ instructions: string;
29
+ };
30
+ type SkillInput = SandboxSkillInput | HostSkillInput | GitSkillInput | InlineSkillInput;
31
+ type SandboxSkillEntry = {
32
+ type: "sandbox";
33
+ path: string;
34
+ };
35
+ type HostSkillEntry = {
36
+ type: "host";
37
+ path: string;
38
+ };
39
+ type GitSkillEntry = {
40
+ type: "git";
41
+ repo: string;
42
+ ref?: string;
43
+ path?: string;
44
+ /**
45
+ * Skill directory name under `path`.
46
+ */
47
+ name?: string;
48
+ };
49
+ type InlineSkillEntry = {
50
+ type: "inline";
51
+ name: string;
52
+ description: string;
53
+ instructions: string;
54
+ };
55
+ type SkillEntry = SandboxSkillEntry | HostSkillEntry | GitSkillEntry | InlineSkillEntry;
56
+
57
+ type GenerationOptions = {
58
+ maxSteps?: number;
59
+ temperature?: number;
60
+ topK?: number;
61
+ topP?: number;
62
+ frequencyPenalty?: number;
63
+ presencePenalty?: number;
64
+ maxOutputTokens?: number;
65
+ headers?: Record<string, string>;
66
+ };
67
+ type Session = {
68
+ id: string;
69
+ createdAt: number;
70
+ updatedAt: number;
71
+ model: string | null;
72
+ system: string | null;
73
+ sandboxId: string | null;
74
+ lastMessageId: string | null;
75
+ activeTools: string[] | null;
76
+ skills: SkillInput[] | null;
77
+ generation: GenerationOptions | null;
78
+ };
79
+ type MessageUsage = {
80
+ steps: {
81
+ stepIndex: number;
82
+ model: string;
83
+ inputTokens: number;
84
+ outputTokens: number;
85
+ totalTokens: number;
86
+ cacheReadTokens: number;
87
+ cacheWriteTokens: number;
88
+ reasoningTokens: number;
89
+ }[];
90
+ summary: {
91
+ model: string;
92
+ inputTokens: number;
93
+ outputTokens: number;
94
+ totalTokens: number;
95
+ cacheReadTokens: number;
96
+ cacheWriteTokens: number;
97
+ reasoningTokens: number;
98
+ stepCount: number;
99
+ };
100
+ };
101
+ type Message = {
102
+ id: string;
103
+ sessionId: string;
104
+ role: "user" | "assistant" | "system";
105
+ createdAt: number;
106
+ startedAt: number | null;
107
+ completedAt: number | null;
108
+ interruptedAt: number | null;
109
+ interruptedLastPart: {
110
+ index: number;
111
+ part: unknown;
112
+ } | null;
113
+ usage: MessageUsage | null;
114
+ workflowRunId?: string | null;
115
+ metadata?: Record<string, unknown> | null;
116
+ };
117
+ type Part = {
118
+ id: string;
119
+ messageId: string;
120
+ sessionId: string;
121
+ index: number;
122
+ part: UIMessage["parts"][number];
123
+ };
124
+ type Sandbox = {
125
+ id: string;
126
+ setup: {
127
+ binding: string;
128
+ version: string | null;
129
+ completedAt: number | null;
130
+ metadata: Record<string, unknown> | null;
131
+ networkPolicy: NetworkPolicy | null;
132
+ };
133
+ createdAt: number | null;
134
+ lastActiveAt: number | null;
135
+ };
136
+ type Setup = {
137
+ version: string;
138
+ snapshotId: string | null;
139
+ createdAt: number;
140
+ lastUsedAt: number | null;
141
+ };
142
+ type SetupSnapshot = Setup;
143
+
144
+ type StorageHandlers = {
145
+ "session.get"(p: {
146
+ id: string;
147
+ }): Promise<Session | null>;
148
+ "session.set"(p: {
149
+ id: string;
150
+ value: Session;
151
+ }): Promise<void>;
152
+ "session.update"(p: {
153
+ id: string;
154
+ updates: Partial<Session>;
155
+ }): Promise<Session>;
156
+ "message.get"(p: {
157
+ id: string;
158
+ }): Promise<Message | null>;
159
+ "message.set"(p: {
160
+ id: string;
161
+ value: Message;
162
+ }): Promise<void>;
163
+ "message.update"(p: {
164
+ id: string;
165
+ updates: Partial<Message>;
166
+ }): Promise<Message>;
167
+ "message.listBySession"(p: {
168
+ sessionId: string;
169
+ }): Promise<Message[]>;
170
+ "part.get"(p: {
171
+ id: string;
172
+ }): Promise<Part | null>;
173
+ "part.set"(p: {
174
+ id: string;
175
+ value: Part;
176
+ }): Promise<void>;
177
+ "part.listBySession"(p: {
178
+ sessionId: string;
179
+ }): Promise<Part[]>;
180
+ "sandbox.get"(p: {
181
+ id: string;
182
+ }): Promise<Sandbox | null>;
183
+ "sandbox.set"(p: {
184
+ id: string;
185
+ value: Sandbox;
186
+ }): Promise<void>;
187
+ "sandbox.update"(p: {
188
+ id: string;
189
+ updates: Partial<Sandbox>;
190
+ }): Promise<Sandbox>;
191
+ "setup.get"(p: {
192
+ id: string;
193
+ }): Promise<Setup | null>;
194
+ "setup.set"(p: {
195
+ id: string;
196
+ value: Setup;
197
+ }): Promise<void>;
198
+ };
199
+ type StorageCall = {
200
+ [K in keyof StorageHandlers]: {
201
+ method: K;
202
+ } & Parameters<StorageHandlers[K]>[0];
203
+ }[keyof StorageHandlers];
204
+ declare class StorageStep {
205
+ event: StorageCall;
206
+ constructor(event: StorageCall);
207
+ static [WORKFLOW_SERIALIZE](instance: StorageStep): {
208
+ event: StorageCall;
209
+ };
210
+ static [WORKFLOW_DESERIALIZE](data: {
211
+ event: StorageCall;
212
+ }): StorageStep;
213
+ on(handlers: StorageHandlers): Promise<any>;
214
+ }
215
+ /**
216
+ * A single function that handles all storage operations. Intended to be
217
+ * marked with `"use step"` so that its body (and Node.js-dependent
218
+ * imports like database clients) is extracted by the workflow bundler
219
+ * and runs server-side.
220
+ *
221
+ * The step receives a `StorageStep` instance (serializable via
222
+ * `@workflow/serde`) and should call `step.handle({ ... })` with a
223
+ * `StorageHandlers` map.
224
+ */
225
+ type StorageStepFunction = (store: StorageStep) => Promise<any>;
226
+ interface Storage {
227
+ session: {
228
+ get(key: string): Promise<Session | null>;
229
+ set(key: string, value: Session): Promise<void>;
230
+ update(key: string, updates: Partial<Session>): Promise<Session>;
231
+ };
232
+ message: {
233
+ get(key: string): Promise<Message | null>;
234
+ set(key: string, value: Message): Promise<void>;
235
+ update(key: string, updates: Partial<Message>): Promise<Message>;
236
+ listBySession(sessionId: string): Promise<Message[]>;
237
+ };
238
+ part: {
239
+ get(key: string): Promise<Part | null>;
240
+ set(key: string, value: Part): Promise<void>;
241
+ listBySession(sessionId: string): Promise<Part[]>;
242
+ };
243
+ sandbox: {
244
+ get(key: string): Promise<Sandbox | null>;
245
+ set(key: string, value: Sandbox): Promise<void>;
246
+ update(key: string, updates: Partial<Sandbox>): Promise<Sandbox>;
247
+ };
248
+ setup: {
249
+ get(key: string): Promise<Setup | null>;
250
+ set(key: string, value: Setup): Promise<void>;
251
+ };
252
+ }
253
+ type StorageInput = StorageHandlers | StorageStepFunction;
254
+ declare function toStorage(h: StorageInput): Storage;
255
+
256
+ export { type GenerationOptions as G, type HostSkillEntry as H, type InlineSkillEntry as I, type Message as M, type Part as P, type StorageHandlers as S, type MessageUsage as a, type Sandbox as b, type Session as c, type Setup as d, type SetupSnapshot as e, type Storage as f, type StorageCall as g, StorageStep as h, type StorageStepFunction as i, type SkillInput as j, type StorageInput as k, type GitSkillEntry as l, type GitSkillInput as m, type HostSkillInput as n, type InlineSkillInput as o, type SandboxSkillEntry as p, type SandboxSkillInput as q, type SkillEntry as r, type SkillSourceType as s, toStorage as t };