@agent-compose/sdk 0.2.3 → 0.2.4

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 (88) hide show
  1. package/README.md +145 -33
  2. package/dist/agent/agent-loop.d.ts +83 -5
  3. package/dist/agent/run-agent.d.ts +34 -9
  4. package/dist/client.d.ts +247 -99
  5. package/dist/index.d.ts +26 -11
  6. package/dist/index.js +1967 -745
  7. package/dist/processors/builtins.d.ts +35 -0
  8. package/dist/processors/index.d.ts +4 -0
  9. package/dist/processors/processor.d.ts +91 -0
  10. package/dist/processors/processor.test.d.ts +1 -0
  11. package/dist/processors/runner.d.ts +19 -0
  12. package/dist/request-context/index.d.ts +2 -0
  13. package/dist/request-context/request-context.d.ts +159 -0
  14. package/dist/request-context/request-context.test.d.ts +1 -0
  15. package/dist/runtimes/claude.d.ts +27 -50
  16. package/dist/runtimes/openai-desktop.js +1918 -741
  17. package/dist/runtimes/vercel.d.ts +34 -0
  18. package/dist/runtimes/vercel.js +474 -0
  19. package/dist/sandbox.d.ts +29 -25
  20. package/dist/step-invocation/__tests__/invoker.test.d.ts +1 -0
  21. package/dist/step-invocation/__tests__/protocol.test.d.ts +1 -0
  22. package/dist/step-invocation/__tests__/server.test.d.ts +1 -0
  23. package/dist/step-invocation/index.d.ts +25 -0
  24. package/dist/step-invocation/invoker.d.ts +65 -0
  25. package/dist/step-invocation/protocol.d.ts +44 -0
  26. package/dist/step-invocation/server.d.ts +63 -0
  27. package/dist/step-invocation/types.d.ts +72 -0
  28. package/dist/tools/coding.d.ts +49 -0
  29. package/dist/tools/coding.test.d.ts +1 -0
  30. package/dist/tools/index.d.ts +2 -0
  31. package/dist/types/events.d.ts +36 -0
  32. package/dist/types/execution-context.d.ts +22 -0
  33. package/dist/types/runtime.d.ts +32 -0
  34. package/dist/types/sandbox-environment.d.ts +5 -2
  35. package/dist/types/sandbox.d.ts +14 -12
  36. package/dist/types/workflow-metadata.d.ts +51 -0
  37. package/dist/types/workflow-plan.d.ts +19 -0
  38. package/dist/types/workflow.d.ts +57 -17
  39. package/dist/utils/bundler.d.ts +62 -3
  40. package/dist/workflow-steps/__tests__/observability.test.d.ts +1 -0
  41. package/dist/workflow-steps/index.d.ts +10 -0
  42. package/dist/workflow-steps/observability.d.ts +58 -0
  43. package/dist/workflow-steps/runner.d.ts +96 -0
  44. package/dist/workflow-steps/step.d.ts +25 -0
  45. package/dist/workflow-steps/types.d.ts +135 -0
  46. package/dist/workflow-steps/workflow-steps.test.d.ts +1 -0
  47. package/dist/workflow-steps/workflow.d.ts +50 -0
  48. package/dist/workflows/engine.d.ts +27 -13
  49. package/dist/workflows/invoke-child.d.ts +10 -0
  50. package/package.json +25 -15
  51. package/src/agent/agent-loop.ts +197 -26
  52. package/src/agent/run-agent.ts +40 -15
  53. package/src/client.ts +326 -76
  54. package/src/index.ts +124 -10
  55. package/src/processors/builtins.ts +72 -0
  56. package/src/processors/index.ts +15 -0
  57. package/src/processors/processor.ts +103 -0
  58. package/src/processors/runner.ts +42 -0
  59. package/src/request-context/index.ts +17 -0
  60. package/src/request-context/request-context.ts +302 -0
  61. package/src/runtimes/claude.ts +123 -254
  62. package/src/runtimes/vercel.ts +180 -0
  63. package/src/sandbox.ts +53 -21
  64. package/src/step-invocation/index.ts +33 -0
  65. package/src/step-invocation/invoker.ts +204 -0
  66. package/src/step-invocation/protocol.ts +57 -0
  67. package/src/step-invocation/server.ts +184 -0
  68. package/src/step-invocation/types.ts +70 -0
  69. package/src/tools/coding.ts +126 -0
  70. package/src/tools/index.ts +8 -0
  71. package/src/types/events.ts +40 -0
  72. package/src/types/execution-context.ts +30 -0
  73. package/src/types/runtime.ts +24 -0
  74. package/src/types/sandbox-environment.ts +7 -5
  75. package/src/types/sandbox.ts +16 -12
  76. package/src/types/workflow-metadata.ts +84 -0
  77. package/src/types/workflow-plan.ts +24 -0
  78. package/src/types/workflow.ts +139 -25
  79. package/src/utils/bundler.ts +198 -18
  80. package/src/utils/source-loader.ts +2 -2
  81. package/src/workflow-steps/index.ts +30 -0
  82. package/src/workflow-steps/observability.ts +103 -0
  83. package/src/workflow-steps/runner.ts +244 -0
  84. package/src/workflow-steps/step.ts +38 -0
  85. package/src/workflow-steps/types.ts +134 -0
  86. package/src/workflow-steps/workflow.ts +95 -0
  87. package/src/workflows/engine.ts +69 -40
  88. package/src/workflows/invoke-child.ts +29 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Vercel AI SDK runtime — model-agnostic agent execution with agent-compose
3
+ * owned coding tools over SandboxProvider.
4
+ */
5
+ import { type LanguageModel } from "ai";
6
+ import type { AgentMessage, ModelExecutionContract, RuntimeOptions, SandboxProvider, ToolCallGateResult } from "../index.js";
7
+ import { type CodingTool } from "../tools/index.js";
8
+ import type { ProcessorContext, ToolCall } from "../processors/processor.js";
9
+ export interface VercelRuntimeConfig {
10
+ /** Vercel AI SDK language model (e.g. openai("gpt-5"), anthropic("claude-sonnet-4-5")). */
11
+ model: LanguageModel;
12
+ /** Optional system prompt prepended to every model call. */
13
+ system?: string;
14
+ /** Override/extend the default coding tools. Defaults: Read, Write, Edit, Bash. */
15
+ tools?: readonly CodingTool[];
16
+ }
17
+ export declare class VercelRunner implements ModelExecutionContract {
18
+ private readonly sandbox;
19
+ private readonly options;
20
+ private readonly config;
21
+ supportsToolCallProcessor: boolean;
22
+ private readonly tools;
23
+ private readonly messages;
24
+ constructor(sandbox: SandboxProvider, options: RuntimeOptions, config: VercelRuntimeConfig);
25
+ gateToolCall(call: ToolCall, ctx: ProcessorContext): Promise<ToolCallGateResult>;
26
+ private buildTools;
27
+ sendMessage(opts: {
28
+ prompt: string;
29
+ sessionId?: string;
30
+ iteration?: number;
31
+ signal?: AbortSignal;
32
+ }): AsyncGenerator<AgentMessage>;
33
+ }
34
+ export declare function createVercelRuntime(config: VercelRuntimeConfig): import("../index.js").AgentRuntime<SandboxProvider>;
@@ -0,0 +1,474 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
+
20
+ // src/runtimes/vercel.ts
21
+ import { streamText, stepCountIs, tool } from "ai";
22
+
23
+ // src/types/runtime.ts
24
+ function defineRuntime(pkg) {
25
+ if (typeof pkg.create !== "function")
26
+ throw new Error("defineRuntime: 'create' is required");
27
+ return pkg;
28
+ }
29
+
30
+ // src/tools/coding.ts
31
+ import { z } from "zod";
32
+ import { isAbsolute, join } from "node:path/posix";
33
+ function q(value) {
34
+ return JSON.stringify(value);
35
+ }
36
+ function resolvePath(path, cwd) {
37
+ return cwd && !isAbsolute(path) ? join(cwd, path) : path;
38
+ }
39
+ async function readFile(sandbox, path, opts) {
40
+ const script = `
41
+ const fs = require("fs");
42
+ const path = process.argv[1];
43
+ const offset = process.argv[2] ? Number(process.argv[2]) : 1;
44
+ const limit = process.argv[3] ? Number(process.argv[3]) : 2000;
45
+ const data = fs.readFileSync(path, "utf8");
46
+ const lines = data.split(/\\r?\\n/);
47
+ const start = Math.max(1, offset);
48
+ const end = Math.min(lines.length, start + Math.max(0, limit) - 1);
49
+ for (let i = start; i <= end; i++) console.log(String(i) + ": " + lines[i - 1]);
50
+ `;
51
+ const args = [q(path), opts?.offset !== undefined ? String(opts.offset) : "", opts?.limit !== undefined ? String(opts.limit) : ""].filter(Boolean).map(q).join(" ");
52
+ const { stdout } = await sandbox.commands.run(`node -e ${q(script)} ${args}`, { cwd: opts?.cwd, timeoutMs: 30000 });
53
+ return stdout;
54
+ }
55
+ async function readRawFile(sandbox, path, cwd) {
56
+ const script = `const fs = require("fs"); process.stdout.write(fs.readFileSync(process.argv[1], "utf8"));`;
57
+ const { stdout } = await sandbox.commands.run(`node -e ${q(script)} ${q(path)}`, { cwd, timeoutMs: 30000 });
58
+ return stdout;
59
+ }
60
+ var readTool = {
61
+ name: "Read",
62
+ description: "Read a UTF-8 text file from the sandbox. Returns numbered lines. Use offset/limit for large files.",
63
+ inputSchema: z.object({
64
+ path: z.string().describe("File path to read. Relative paths resolve from the agent working directory."),
65
+ offset: z.number().int().positive().optional().describe("1-based line number to start at."),
66
+ limit: z.number().int().positive().max(5000).optional().describe("Maximum number of lines to return.")
67
+ }),
68
+ execute: ({ path, offset, limit }, ctx) => readFile(ctx.sandbox, path, { offset, limit, cwd: ctx.cwd })
69
+ };
70
+ var writeTool = {
71
+ name: "Write",
72
+ description: "Write UTF-8 content to a file in the sandbox, creating or replacing the file.",
73
+ inputSchema: z.object({
74
+ path: z.string().describe("File path to write. Relative paths resolve from the agent working directory."),
75
+ content: z.string().describe("Complete file contents to write.")
76
+ }),
77
+ execute: async ({ path, content }, ctx) => {
78
+ const target = resolvePath(path, ctx.cwd);
79
+ await ctx.sandbox.files.write(target, content);
80
+ return `Wrote ${content.length} bytes to ${target}`;
81
+ }
82
+ };
83
+ var editTool = {
84
+ name: "Edit",
85
+ description: "Edit a text file by replacing an exact string. Fails if the old string is not found or is ambiguous without replaceAll.",
86
+ inputSchema: z.object({
87
+ path: z.string().describe("File path to edit. Relative paths resolve from the agent working directory."),
88
+ oldString: z.string().min(1).describe("Exact text to replace."),
89
+ newString: z.string().describe("Replacement text."),
90
+ replaceAll: z.boolean().optional().describe("Replace every occurrence instead of requiring exactly one match.")
91
+ }),
92
+ execute: async ({ path, oldString, newString, replaceAll }, ctx) => {
93
+ const target = resolvePath(path, ctx.cwd);
94
+ const before = await readRawFile(ctx.sandbox, target, ctx.cwd);
95
+ const count = before.split(oldString).length - 1;
96
+ if (count === 0)
97
+ throw new Error(`Edit failed: oldString not found in ${target}`);
98
+ if (!replaceAll && count !== 1)
99
+ throw new Error(`Edit failed: oldString occurs ${count} times in ${target}; set replaceAll=true or make oldString more specific`);
100
+ const after = replaceAll ? before.split(oldString).join(newString) : before.replace(oldString, newString);
101
+ await ctx.sandbox.files.write(target, after);
102
+ return `Edited ${target}: replaced ${replaceAll ? count : 1} occurrence${(replaceAll ? count : 1) === 1 ? "" : "s"}`;
103
+ }
104
+ };
105
+ var bashTool = {
106
+ name: "Bash",
107
+ description: "Run a shell command in the sandbox. Use for file discovery/search (e.g. globbing/grep via shell commands), tests, builds, and package commands.",
108
+ inputSchema: z.object({
109
+ command: z.string().min(1).describe("Shell command to execute."),
110
+ timeoutMs: z.number().int().positive().max(3600000).optional().describe("Timeout in milliseconds."),
111
+ cwd: z.string().optional().describe("Optional command working directory. Defaults to the agent working directory.")
112
+ }),
113
+ execute: async ({ command, timeoutMs, cwd }, ctx) => {
114
+ const result = await ctx.sandbox.commands.run(command, {
115
+ cwd: cwd ?? ctx.cwd,
116
+ timeoutMs: timeoutMs ?? 120000
117
+ });
118
+ if (result.exitCode !== 0)
119
+ throw new Error(`Command failed with exit code ${result.exitCode}${result.stdout ? `
120
+ ${result.stdout}` : ""}`);
121
+ return result.stdout;
122
+ }
123
+ };
124
+ var codingTools = [readTool, writeTool, editTool, bashTool];
125
+ // src/processors/runner.ts
126
+ async function runProcessorChain(processors, selectHook, initial, ctx) {
127
+ let current = initial;
128
+ for (const p of processors) {
129
+ const fn = selectHook(p);
130
+ if (!fn)
131
+ continue;
132
+ const verdict = await fn.call(p, current, ctx);
133
+ if (verdict.kind !== "continue")
134
+ return verdict;
135
+ current = verdict.value;
136
+ }
137
+ return { kind: "continue", value: current };
138
+ }
139
+
140
+ // src/request-context/request-context.ts
141
+ var AC_RESERVED_PREFIX = "ac__";
142
+ var AC_TEAM_ID = "ac__teamId";
143
+ var AC_RUN_ID = "ac__runId";
144
+ var AC_WORKFLOW_ID = "ac__workflowId";
145
+ var AC_FACTORY_ID = "ac__factoryId";
146
+ var AC_API_KEY_SCOPES = "ac__apiKeyScopes";
147
+ var AC_PARENT_RUN_ID = "ac__parentRunId";
148
+ var AC_ABORT_SIGNAL = "ac__abortSignal";
149
+ var RESERVED_KEYS = new Set([
150
+ AC_TEAM_ID,
151
+ AC_RUN_ID,
152
+ AC_WORKFLOW_ID,
153
+ AC_FACTORY_ID,
154
+ AC_API_KEY_SCOPES,
155
+ AC_PARENT_RUN_ID,
156
+ AC_ABORT_SIGNAL
157
+ ]);
158
+ var SERIALISABLE_RESERVED_KEYS = new Set([
159
+ AC_TEAM_ID,
160
+ AC_RUN_ID,
161
+ AC_WORKFLOW_ID,
162
+ AC_FACTORY_ID,
163
+ AC_API_KEY_SCOPES,
164
+ AC_PARENT_RUN_ID
165
+ ]);
166
+
167
+ class ReservedKeyError extends Error {
168
+ key;
169
+ constructor(key) {
170
+ super(`request-context: "${key}" is reserved and cannot be mutated downstream`);
171
+ this.name = "ReservedKeyError";
172
+ this.key = key;
173
+ }
174
+ }
175
+
176
+ class NonSerialisableValueError extends Error {
177
+ constructor(key, cause) {
178
+ super(`request-context: value for "${key}" is not JSON-serialisable`, { cause });
179
+ this.name = "NonSerialisableValueError";
180
+ }
181
+ }
182
+ function assertJsonSerialisable(key, value, seen = new WeakSet) {
183
+ if (value === null)
184
+ return;
185
+ const type = typeof value;
186
+ if (type === "string" || type === "boolean")
187
+ return;
188
+ if (type === "number") {
189
+ if (Number.isFinite(value))
190
+ return;
191
+ throw new NonSerialisableValueError(key, new TypeError("non-finite numbers are not JSON-serialisable"));
192
+ }
193
+ if (type !== "object") {
194
+ throw new NonSerialisableValueError(key, new TypeError(`${type} is not JSON-serialisable`));
195
+ }
196
+ const objectValue = value;
197
+ if (seen.has(objectValue)) {
198
+ throw new NonSerialisableValueError(key, new TypeError("cyclic values are not JSON-serialisable"));
199
+ }
200
+ seen.add(objectValue);
201
+ if (Array.isArray(value)) {
202
+ for (const item of value)
203
+ assertJsonSerialisable(key, item, seen);
204
+ return;
205
+ }
206
+ if (Object.getPrototypeOf(objectValue) !== Object.prototype) {
207
+ throw new NonSerialisableValueError(key, new TypeError("only plain objects are JSON-serialisable"));
208
+ }
209
+ for (const item of Object.values(objectValue))
210
+ assertJsonSerialisable(key, item, seen);
211
+ }
212
+
213
+ class RequestContext {
214
+ reserved;
215
+ user;
216
+ constructor(reserved, user) {
217
+ this.reserved = Object.freeze({
218
+ ...reserved,
219
+ apiKeyScopes: Object.freeze([...reserved.apiKeyScopes])
220
+ });
221
+ this.user = user;
222
+ }
223
+ static fromReserved(reserved) {
224
+ return new RequestContext(reserved, new Map);
225
+ }
226
+ static deserialise(wire) {
227
+ const ctx = new RequestContext({ ...wire.reserved, abortSignal: undefined }, new Map(Object.entries(wire.user)));
228
+ return ctx;
229
+ }
230
+ withAbortSignal(signal) {
231
+ return new RequestContext({ ...this.reserved, abortSignal: signal }, new Map(this.user));
232
+ }
233
+ get teamId() {
234
+ return this.reserved.teamId;
235
+ }
236
+ get runId() {
237
+ return this.reserved.runId;
238
+ }
239
+ get workflowId() {
240
+ return this.reserved.workflowId;
241
+ }
242
+ get factoryId() {
243
+ return this.reserved.factoryId;
244
+ }
245
+ get apiKeyScopes() {
246
+ return this.reserved.apiKeyScopes;
247
+ }
248
+ get parentRunId() {
249
+ return this.reserved.parentRunId;
250
+ }
251
+ get abortSignal() {
252
+ return this.reserved.abortSignal;
253
+ }
254
+ hasScope(scope) {
255
+ return this.reserved.apiKeyScopes.includes(scope);
256
+ }
257
+ set(key, value) {
258
+ if (RESERVED_KEYS.has(key) || key.startsWith(AC_RESERVED_PREFIX)) {
259
+ throw new ReservedKeyError(key);
260
+ }
261
+ assertJsonSerialisable(key, value);
262
+ this.user.set(key, value);
263
+ }
264
+ get(key) {
265
+ return this.user.get(key);
266
+ }
267
+ has(key) {
268
+ return this.user.has(key);
269
+ }
270
+ delete(key) {
271
+ if (RESERVED_KEYS.has(key) || key.startsWith(AC_RESERVED_PREFIX)) {
272
+ throw new ReservedKeyError(key);
273
+ }
274
+ return this.user.delete(key);
275
+ }
276
+ entries() {
277
+ return this.user.entries();
278
+ }
279
+ serialise() {
280
+ return {
281
+ reserved: {
282
+ teamId: this.reserved.teamId,
283
+ runId: this.reserved.runId,
284
+ workflowId: this.reserved.workflowId,
285
+ factoryId: this.reserved.factoryId,
286
+ apiKeyScopes: [...this.reserved.apiKeyScopes],
287
+ parentRunId: this.reserved.parentRunId
288
+ },
289
+ user: Object.fromEntries(this.user)
290
+ };
291
+ }
292
+ forChild(opts) {
293
+ return RequestContext.fromReserved({
294
+ teamId: this.reserved.teamId,
295
+ runId: opts.runId,
296
+ workflowId: opts.workflowId,
297
+ factoryId: this.reserved.factoryId,
298
+ apiKeyScopes: this.reserved.apiKeyScopes,
299
+ parentRunId: this.reserved.runId
300
+ });
301
+ }
302
+ }
303
+
304
+ // src/utils/errors.ts
305
+ function formatError(err) {
306
+ return err instanceof Error ? err.message : String(err);
307
+ }
308
+
309
+ // src/runtimes/vercel.ts
310
+ function now() {
311
+ return new Date().toISOString();
312
+ }
313
+ function serialiseOutput(value) {
314
+ return typeof value === "string" ? value : JSON.stringify(value ?? "");
315
+ }
316
+ function toAgentMessages(part) {
317
+ const ts = now();
318
+ switch (part.type) {
319
+ case "text-delta":
320
+ case "text":
321
+ return [{ type: "text", text: String(part.text ?? ""), timestamp: ts }];
322
+ case "reasoning-delta":
323
+ case "reasoning":
324
+ return [{ type: "thinking", text: String(part.text ?? ""), timestamp: ts }];
325
+ case "tool-call":
326
+ return [{
327
+ type: "tool_use",
328
+ toolName: String(part.toolName ?? ""),
329
+ toolInput: part.input ?? {},
330
+ toolUseId: String(part.toolCallId ?? ""),
331
+ timestamp: ts
332
+ }];
333
+ case "tool-result":
334
+ return [{
335
+ type: "tool_result",
336
+ toolUseId: String(part.toolCallId ?? ""),
337
+ output: serialiseOutput(part.output),
338
+ isError: false,
339
+ timestamp: ts
340
+ }];
341
+ case "tool-error":
342
+ return [{
343
+ type: "tool_result",
344
+ toolUseId: String(part.toolCallId ?? ""),
345
+ output: formatError(part.error),
346
+ isError: true,
347
+ timestamp: ts
348
+ }];
349
+ case "error":
350
+ return [{ type: "error", text: formatError(part.error), timestamp: ts }];
351
+ default:
352
+ return [];
353
+ }
354
+ }
355
+
356
+ class VercelRunner {
357
+ sandbox;
358
+ options;
359
+ config;
360
+ supportsToolCallProcessor = true;
361
+ tools;
362
+ messages = [];
363
+ constructor(sandbox, options, config) {
364
+ this.sandbox = sandbox;
365
+ this.options = options;
366
+ this.config = config;
367
+ this.tools = config.tools ?? codingTools;
368
+ }
369
+ async gateToolCall(call, ctx) {
370
+ const verdict = await runProcessorChain(this.options.processors ?? [], (p) => p.processToolCall, call, ctx);
371
+ if (verdict.kind === "continue")
372
+ return { kind: "allow", call: verdict.value };
373
+ return verdict;
374
+ }
375
+ buildTools(iteration, signal) {
376
+ const set = {};
377
+ for (const t of this.tools) {
378
+ if (this.options.allowedTools && !this.options.allowedTools.includes(t.name))
379
+ continue;
380
+ set[t.name] = tool({
381
+ description: t.description,
382
+ inputSchema: t.inputSchema,
383
+ execute: async (input, execOpts) => {
384
+ const call = {
385
+ toolName: t.name,
386
+ toolInput: input,
387
+ toolUseId: execOpts.toolCallId ?? `${t.name}-${Date.now()}`
388
+ };
389
+ const requestContext = this.options.requestContext ?? RequestContext.fromReserved({
390
+ teamId: "",
391
+ runId: "",
392
+ workflowId: "",
393
+ factoryId: null,
394
+ apiKeyScopes: [],
395
+ parentRunId: null
396
+ });
397
+ const gate = await this.gateToolCall(call, {
398
+ requestContext,
399
+ abortSignal: execOpts.abortSignal ?? signal ?? new AbortController().signal,
400
+ retryCount: 0,
401
+ agentId: this.options.agentId ?? "agent",
402
+ iteration
403
+ });
404
+ if (gate.kind === "deny")
405
+ return gate.reason;
406
+ if (gate.kind === "abort")
407
+ throw new Error(gate.reason);
408
+ return t.execute(gate.call.toolInput, {
409
+ sandbox: this.sandbox,
410
+ cwd: this.options.cwd,
411
+ abortSignal: execOpts.abortSignal ?? signal
412
+ });
413
+ }
414
+ });
415
+ }
416
+ return set;
417
+ }
418
+ async* sendMessage(opts) {
419
+ const sessionId = opts.sessionId ?? `vercel-${Date.now()}`;
420
+ yield { type: "init", sessionId, timestamp: now() };
421
+ const inputMessages = [...this.messages, { role: "user", content: opts.prompt }];
422
+ const result = streamText({
423
+ model: this.config.model,
424
+ ...this.config.system ? { system: this.config.system } : {},
425
+ messages: inputMessages,
426
+ tools: this.buildTools(opts.iteration ?? 1, opts.signal),
427
+ stopWhen: stepCountIs(this.options.maxTurns ?? 40),
428
+ abortSignal: opts.signal
429
+ });
430
+ try {
431
+ for await (const part of result.fullStream) {
432
+ for (const msg of toAgentMessages(part))
433
+ yield msg;
434
+ }
435
+ const response = await result.response;
436
+ const responseMessages = response.messages;
437
+ if (responseMessages) {
438
+ this.messages.length = 0;
439
+ this.messages.push(...responseMessages);
440
+ }
441
+ let usage;
442
+ try {
443
+ usage = await result.usage;
444
+ } catch {
445
+ usage = undefined;
446
+ }
447
+ if (usage) {
448
+ yield {
449
+ type: "usage",
450
+ inputTokens: usage.inputTokens ?? 0,
451
+ outputTokens: usage.outputTokens ?? 0,
452
+ cacheReadTokens: usage.inputTokenDetails?.cacheReadTokens ?? 0,
453
+ cacheCreationTokens: usage.inputTokenDetails?.cacheWriteTokens ?? 0,
454
+ durationMs: 0,
455
+ numTurns: opts.iteration ?? 1,
456
+ timestamp: now()
457
+ };
458
+ }
459
+ } catch (err) {
460
+ yield { type: "error", text: formatError(err), timestamp: now() };
461
+ return;
462
+ }
463
+ yield { type: "done", sessionId, timestamp: now() };
464
+ }
465
+ }
466
+ function createVercelRuntime(config) {
467
+ return defineRuntime({
468
+ create: (sandbox, opts) => new VercelRunner(sandbox, opts, config)
469
+ });
470
+ }
471
+ export {
472
+ createVercelRuntime,
473
+ VercelRunner
474
+ };
package/dist/sandbox.d.ts CHANGED
@@ -6,8 +6,9 @@
6
6
  */
7
7
  import { Sandbox } from "e2b";
8
8
  import { Sandbox as Desktop } from "@e2b/desktop";
9
- import type { SandboxProvider, DesktopSandboxProvider } from "./types/sandbox.js";
10
- export type { SandboxProvider, DesktopSandboxProvider } from "./types/sandbox.js";
9
+ import type { SandboxProvider, DesktopSandboxProvider, SandboxCommandResult } from "./types/sandbox.js";
10
+ export type { SandboxProvider, DesktopSandboxProvider, SandboxCommandRunOptions, SandboxCommandResult } from "./types/sandbox.js";
11
+ export type SandboxProviderName = "vercel" | "e2b" | "e2b-desktop";
11
12
  export declare const AGENT_COMPOSE_TAG: string;
12
13
  /**
13
14
  * Vercel-compatible network policy for outbound HTTPS requests.
@@ -15,16 +16,19 @@ export declare const AGENT_COMPOSE_TAG: string;
15
16
  * firewall injects the specified headers before forwarding — credentials never
16
17
  * exist inside the VM. E2B ignores this field (future self-hosted mapping TBD).
17
18
  */
19
+ export interface SandboxNetworkHeaderTransform {
20
+ headers?: Record<string, string>;
21
+ }
22
+ export interface SandboxNetworkAllowRule {
23
+ transform?: SandboxNetworkHeaderTransform[];
24
+ }
25
+ export interface SandboxNetworkSubnetPolicy {
26
+ allow?: string[];
27
+ deny?: string[];
28
+ }
18
29
  export type SandboxNetworkPolicy = "allow-all" | "deny-all" | {
19
- allow?: string[] | Record<string, Array<{
20
- transform?: Array<{
21
- headers?: Record<string, string>;
22
- }>;
23
- }>>;
24
- subnets?: {
25
- allow?: string[];
26
- deny?: string[];
27
- };
30
+ allow?: string[] | Record<string, SandboxNetworkAllowRule[]>;
31
+ subnets?: SandboxNetworkSubnetPolicy;
28
32
  };
29
33
  export interface SandboxCreateOpts {
30
34
  envs: Record<string, string>;
@@ -80,17 +84,15 @@ interface SandboxProviderDef {
80
84
  }
81
85
  export declare function makeSandboxProvider(sb: Sandbox | Desktop): SandboxProvider;
82
86
  export declare function makeDesktopSandboxProvider(sb: Desktop): DesktopSandboxProvider;
87
+ export interface ParseSseExecStreamOptions {
88
+ onStdout?: (data: string) => void;
89
+ onStderr?: (data: string) => void;
90
+ }
83
91
  /**
84
92
  * Parse an SSE exec stream from a ReadableStream.
85
93
  * Used by the agent sandbox broker in runner.ts.
86
94
  */
87
- export declare function parseSseExecStream(body: ReadableStream<Uint8Array>, opts?: {
88
- onStdout?: (d: string) => void;
89
- onStderr?: (d: string) => void;
90
- }): Promise<{
91
- exitCode: number;
92
- stdout: string;
93
- }>;
95
+ export declare function parseSseExecStream(body: ReadableStream<Uint8Array>, opts?: ParseSseExecStreamOptions): Promise<SandboxCommandResult>;
94
96
  /** Minimal SandboxProvider that targets the current process's host VM.
95
97
  * `commands.run` → `child_process.spawn`; `files.write` → `fs.writeFile`;
96
98
  * `kill` is a no-op because the caller IS the VM. Used by
@@ -100,27 +102,29 @@ export declare function parseSseExecStream(body: ReadableStream<Uint8Array>, opt
100
102
  export declare function makeLocalSandboxProvider(): SandboxProvider;
101
103
  declare const SANDBOX_PROVIDERS: Record<string, SandboxProviderDef>;
102
104
  /** Provision a sandbox for the named provider. */
103
- export declare function createSandbox(provider: string, opts: SandboxCreateOpts): Promise<SandboxProvider>;
105
+ export declare function createSandbox(provider: SandboxProviderName, opts: SandboxCreateOpts): Promise<SandboxProvider>;
104
106
  /** Reconnect to an existing sandbox by provider + provider-native sandbox ID. */
105
- export declare function reconnectSandbox(provider: string, sandboxId: string): Promise<SandboxProvider>;
107
+ export declare function reconnectSandbox(provider: SandboxProviderName, sandboxId: string): Promise<SandboxProvider>;
106
108
  /** Delete a snapshot by id on the named provider. No live sandbox needed. */
107
- export declare function deleteSandboxSnapshot(provider: string, snapshotId: string): Promise<void>;
109
+ export declare function deleteSandboxSnapshot(provider: SandboxProviderName, snapshotId: string): Promise<void>;
108
110
  /**
109
111
  * Current active-sandbox count per configured provider, for quota gauges.
110
112
  * Skips providers whose required env isn't set or which don't implement
111
113
  * `getActiveCount`. Errors are surfaced per-provider so one flaky provider
112
114
  * doesn't silence the rest.
113
115
  */
114
- export declare function getSandboxQuotas(): Promise<Record<string, number | Error>>;
116
+ export type SandboxQuotaResult = Partial<Record<SandboxProviderName, number | Error>>;
117
+ export declare function getSandboxQuotas(): Promise<SandboxQuotaResult>;
115
118
  /**
116
119
  * List every alive sandbox owned by this fleet, across all configured
117
120
  * providers — the orphan-reconciler SoT. Providers without `listOwned`
118
121
  * or missing env are skipped. Errors propagate per-provider so one flaky
119
122
  * provider doesn't silence the rest.
120
123
  */
121
- export declare function listOwnedSandboxes(): Promise<Record<string, OwnedSandbox[] | Error>>;
124
+ export type OwnedSandboxResult = Partial<Record<SandboxProviderName, OwnedSandbox[] | Error>>;
125
+ export declare function listOwnedSandboxes(): Promise<OwnedSandboxResult>;
122
126
  /** Kill a sandbox by provider + native ID. Used by the orphan reconciler. */
123
- export declare function killSandboxById(provider: string, sandboxId: string): Promise<void>;
127
+ export declare function killSandboxById(provider: SandboxProviderName, sandboxId: string): Promise<void>;
124
128
  /** Kill all sandboxes across all registered providers. Call at startup to clean up after crashes. */
125
- export declare function killAllSandboxes(onError?: (provider: string, err: unknown) => void): Promise<void>;
129
+ export declare function killAllSandboxes(onError?: (provider: SandboxProviderName, err: unknown) => void): Promise<void>;
126
130
  export { SANDBOX_PROVIDERS };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ /**
2
+ * StepInvocation — cross-process protocol that lets a server-side
3
+ * activity (Temporal `executeStep`, future Inngest equivalent) drive
4
+ * one workflow step inside an existing runner sandbox.
5
+ *
6
+ * Two halves, single source of truth for the wire:
7
+ *
8
+ * - `invokeStep(sandbox, request)` — server side. Writes input +
9
+ * request context, mints a token, builds envs, spawns the runner,
10
+ * parses the tokenised stdout sentinel, returns a kinded `StepResult`.
11
+ *
12
+ * - `serveStep(handler)` — runner side. Reads input + request context
13
+ * from env-pointed files, runs the handler, emits the tokenised
14
+ * sentinel result line, exits with the right code.
15
+ *
16
+ * The protocol is intentionally narrow: one input file + one context
17
+ * file + one tokenised stdout result line. Easy to reason about, easy
18
+ * to test in isolation.
19
+ */
20
+ export { STEP_RESULT_PREFIX, STEP_ENV, RUNNER_BUNDLE_PATH, RUNNER_COMMAND, stepInputPath, requestContextPath, } from "./protocol.js";
21
+ export { invokeStep, parseStepResult, buildStepEnvs } from "./invoker.js";
22
+ export { serveStep } from "./server.js";
23
+ export type { StepHandler, ServeStepRequest, StepHandlerResult } from "./server.js";
24
+ export { StepExecutionError } from "./types.js";
25
+ export type { StepRequest, StepResult, StepInvocationError } from "./types.js";