bashkit 0.2.4 → 0.3.1

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/AGENTS.md CHANGED
@@ -34,9 +34,12 @@ Runs in isolated Firecracker microVMs on Vercel's infrastructure.
34
34
  ```typescript
35
35
  import { createAgentTools, createVercelSandbox } from "bashkit";
36
36
 
37
- const sandbox = createVercelSandbox({
37
+ // Async - automatically installs ripgrep for Grep tool
38
+ const sandbox = await createVercelSandbox({
38
39
  runtime: "node22",
39
40
  resources: { vcpus: 2 },
41
+ // ensureTools: true (default) - auto-setup ripgrep
42
+ // ensureTools: false - skip for faster startup if you don't need Grep
40
43
  });
41
44
  const { tools } = createAgentTools(sandbox);
42
45
 
@@ -51,8 +54,11 @@ Runs in E2B's cloud sandboxes. Requires `@e2b/code-interpreter` peer dependency.
51
54
  ```typescript
52
55
  import { createAgentTools, createE2BSandbox } from "bashkit";
53
56
 
54
- const sandbox = createE2BSandbox({
57
+ // Async - automatically installs ripgrep for Grep tool
58
+ const sandbox = await createE2BSandbox({
55
59
  apiKey: process.env.E2B_API_KEY,
60
+ // ensureTools: true (default) - auto-setup ripgrep
61
+ // ensureTools: false - skip for faster startup if you don't need Grep
56
62
  });
57
63
  const { tools } = createAgentTools(sandbox);
58
64
 
@@ -65,18 +71,17 @@ Cloud sandboxes (E2B, Vercel) support reconnection via the `id` property and `sa
65
71
 
66
72
  ```typescript
67
73
  // Create a new sandbox
68
- const sandbox = createE2BSandbox({ apiKey: process.env.E2B_API_KEY });
74
+ const sandbox = await createE2BSandbox({ apiKey: process.env.E2B_API_KEY });
69
75
 
70
- // After first operation, the sandbox ID is available
71
- await sandbox.exec("echo hello");
76
+ // Sandbox ID is available immediately after creation
72
77
  const sandboxId = sandbox.id; // "sbx_abc123..."
73
78
 
74
79
  // Store sandboxId in your database (e.g., chat metadata)
75
80
  await db.chat.update({ where: { id: chatId }, data: { sandboxId } });
76
81
 
77
- // Later: reconnect to the same sandbox
82
+ // Later: reconnect to the same sandbox (fast - ripgrep already installed)
78
83
  const savedId = chat.sandboxId;
79
- const reconnected = createE2BSandbox({
84
+ const reconnected = await createE2BSandbox({
80
85
  apiKey: process.env.E2B_API_KEY,
81
86
  sandboxId: savedId, // Reconnects instead of creating new
82
87
  });
@@ -442,7 +447,7 @@ const config = {
442
447
  },
443
448
  };
444
449
 
445
- const sandbox = createVercelSandbox({});
450
+ const sandbox = await createVercelSandbox({});
446
451
  const { skills } = await setupAgentEnvironment(sandbox, config);
447
452
 
448
453
  // Use same config in prompt - stays in sync!
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # bashkit
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/bashkit)](https://www.npmjs.com/package/bashkit)
4
+
3
5
  Agentic coding tools for Vercel AI SDK. Give AI agents the ability to execute code, read/write files, and perform coding tasks in a sandboxed environment.
4
6
 
5
7
  ## Overview
@@ -69,7 +71,8 @@ import { anthropic } from '@ai-sdk/anthropic';
69
71
  import { streamText, stepCountIs } from 'ai';
70
72
 
71
73
  // Create a Vercel sandbox (isolated Firecracker microVM)
72
- const sandbox = createVercelSandbox({
74
+ // Note: async - automatically sets up ripgrep for Grep tool
75
+ const sandbox = await createVercelSandbox({
73
76
  runtime: 'node22',
74
77
  resources: { vcpus: 2 },
75
78
  });
@@ -144,17 +147,19 @@ Runs in isolated Firecracker microVMs on Vercel's infrastructure. **Use when you
144
147
  ```typescript
145
148
  import { createVercelSandbox } from 'bashkit';
146
149
 
147
- const sandbox = createVercelSandbox({
150
+ // Async - automatically installs ripgrep for Grep tool
151
+ const sandbox = await createVercelSandbox({
148
152
  runtime: 'node22',
149
153
  resources: { vcpus: 2 },
154
+ // ensureTools: true (default) - auto-setup ripgrep
155
+ // ensureTools: false - skip for faster startup if you don't need Grep
150
156
  });
151
157
 
152
- // After first operation, get the sandbox ID for persistence
153
- await sandbox.exec('echo hello');
158
+ // Sandbox ID available immediately after creation
154
159
  console.log(sandbox.id); // Sandbox ID for reconnection
155
160
 
156
- // Later: reconnect to the same sandbox
157
- const reconnected = createVercelSandbox({
161
+ // Later: reconnect to the same sandbox (fast - ripgrep already installed)
162
+ const reconnected = await createVercelSandbox({
158
163
  sandboxId: 'existing-sandbox-id',
159
164
  });
160
165
  ```
@@ -166,16 +171,18 @@ Runs in E2B's cloud sandboxes. Requires `@e2b/code-interpreter` peer dependency.
166
171
  ```typescript
167
172
  import { createE2BSandbox } from 'bashkit';
168
173
 
169
- const sandbox = createE2BSandbox({
174
+ // Async - automatically installs ripgrep for Grep tool
175
+ const sandbox = await createE2BSandbox({
170
176
  apiKey: process.env.E2B_API_KEY,
177
+ // ensureTools: true (default) - auto-setup ripgrep
178
+ // ensureTools: false - skip for faster startup if you don't need Grep
171
179
  });
172
180
 
173
- // After first operation, get the sandbox ID for persistence
174
- await sandbox.exec('echo hello');
181
+ // Sandbox ID available immediately after creation
175
182
  console.log(sandbox.id); // "sbx_abc123..."
176
183
 
177
- // Later: reconnect to the same sandbox
178
- const reconnected = createE2BSandbox({
184
+ // Later: reconnect to the same sandbox (fast - ripgrep already installed)
185
+ const reconnected = await createE2BSandbox({
179
186
  apiKey: process.env.E2B_API_KEY,
180
187
  sandboxId: 'sbx_abc123...', // Reconnect to existing sandbox
181
188
  });
@@ -782,14 +789,20 @@ interface Sandbox {
782
789
  writeFile(path: string, content: string): Promise<void>;
783
790
  readDir(path: string): Promise<string[]>;
784
791
  fileExists(path: string): Promise<boolean>;
792
+ isDirectory(path: string): Promise<boolean>;
785
793
  destroy(): Promise<void>;
786
794
 
787
795
  // Optional: Sandbox ID for reconnection (cloud providers only)
788
796
  readonly id?: string;
797
+
798
+ // Path to ripgrep binary (set by ensureSandboxTools)
799
+ rgPath?: string;
789
800
  }
790
801
  ```
791
802
 
792
- The `id` property is available on cloud sandboxes (E2B, Vercel) after the first operation. Use it to persist the sandbox ID and reconnect later.
803
+ The `id` property is available on cloud sandboxes (E2B, Vercel) after creation. Use it to persist the sandbox ID and reconnect later.
804
+
805
+ The `rgPath` property is set by `ensureSandboxTools()` (called automatically during sandbox creation). It points to the ripgrep binary for the Grep tool. Supports x86_64 and ARM64 architectures.
793
806
 
794
807
  ### Custom Sandbox Example
795
808
 
@@ -866,9 +879,10 @@ Creates a set of agent tools bound to a sandbox instance.
866
879
 
867
880
  ### Sandbox Factories
868
881
 
869
- - `createLocalSandbox(config?)` - Local execution sandbox
870
- - `createVercelSandbox(config?)` - Vercel Firecracker sandbox
871
- - `createE2BSandbox(config?)` - E2B cloud sandbox
882
+ - `createLocalSandbox(config?)` - Local execution sandbox (sync)
883
+ - `createVercelSandbox(config?)` - Vercel Firecracker sandbox (async, auto-installs ripgrep)
884
+ - `createE2BSandbox(config?)` - E2B cloud sandbox (async, auto-installs ripgrep)
885
+ - `ensureSandboxTools(sandbox)` - Manually setup tools (called automatically by default)
872
886
 
873
887
  ### Workflow Tools
874
888
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export type { UIMessageStreamWriter, StreamTextResult, Tool, ToolSet, LanguageModel, LanguageModelMiddleware, Output, } from "ai";
2
2
  export { anthropicPromptCacheMiddleware, anthropicPromptCacheMiddlewareV2, } from "./middleware";
3
3
  export type { E2BSandboxConfig, LocalSandboxConfig, VercelSandboxConfig, } from "./sandbox";
4
- export { createE2BSandbox, createLocalSandbox, createVercelSandbox, } from "./sandbox";
4
+ export { createE2BSandbox, createLocalSandbox, createVercelSandbox, ensureSandboxTools, } from "./sandbox";
5
5
  export type { ExecOptions, ExecResult, Sandbox } from "./sandbox/interface";
6
6
  export type { AgentToolsResult, AskUserError, AskUserOutput, AskUserResponseHandler, BashError, BashOutput, EditError, EditOutput, EnterPlanModeError, EnterPlanModeOutput, ExitPlanModeError, ExitPlanModeOutput, PlanModeState, GlobError, GlobOutput, GrepContentOutput, GrepCountOutput, GrepError, GrepFilesOutput, GrepMatch, GrepOutput, ReadDirectoryOutput, ReadError, ReadOutput, ReadTextOutput, SkillError, SkillOutput, SkillToolConfig, SubagentEventData, SubagentStepEvent, SubagentTypeConfig, TaskError, TaskOutput, TaskToolConfig, TodoItem, TodoState, TodoWriteError, TodoWriteOutput, WebFetchError, WebFetchOutput, WebSearchError, WebSearchOutput, WebSearchResult, WriteError, WriteOutput, } from "./tools";
7
7
  export { createAgentTools, createAskUserTool, createBashTool, createEditTool, createEnterPlanModeTool, createExitPlanModeTool, createGlobTool, createGrepTool, createReadTool, createSkillTool, createTaskTool, createTodoWriteTool, createWebFetchTool, createWebSearchTool, createWriteTool, } from "./tools";
package/dist/index.js CHANGED
@@ -49,15 +49,91 @@ var anthropicPromptCacheMiddleware = {
49
49
  specificationVersion: "v3",
50
50
  transformParams: async ({ params }) => applyCacheMarkers(params)
51
51
  };
52
+ // src/sandbox/lazy-singleton.ts
53
+ function createLazySingleton(factory) {
54
+ let promise = null;
55
+ return {
56
+ get: () => {
57
+ if (!promise) {
58
+ promise = factory();
59
+ }
60
+ return promise;
61
+ },
62
+ reset: () => {
63
+ promise = null;
64
+ }
65
+ };
66
+ }
67
+
68
+ // src/sandbox/ripgrep.ts
69
+ async function getBundledRgPath() {
70
+ try {
71
+ const { rgPath } = await import("@vscode/ripgrep");
72
+ return rgPath;
73
+ } catch {
74
+ return;
75
+ }
76
+ }
77
+ function getBundledRgPathSync() {
78
+ try {
79
+ const { rgPath } = __require("@vscode/ripgrep");
80
+ return rgPath;
81
+ } catch {
82
+ return;
83
+ }
84
+ }
85
+
86
+ // src/sandbox/ensure-tools.ts
87
+ var RIPGREP_VERSION = "14.1.0";
88
+ var ARCH_MAP = {
89
+ x86_64: "x86_64-unknown-linux-musl",
90
+ aarch64: "aarch64-unknown-linux-gnu",
91
+ arm64: "aarch64-unknown-linux-gnu"
92
+ };
93
+ async function ensureSandboxTools(sandbox) {
94
+ const bundledRgPath = await getBundledRgPath();
95
+ if (bundledRgPath) {
96
+ const bundledCheck = await sandbox.exec(`test -x "${bundledRgPath}" && echo found`);
97
+ if (bundledCheck.stdout.includes("found")) {
98
+ sandbox.rgPath = bundledRgPath;
99
+ return;
100
+ }
101
+ }
102
+ const tmpCheck = await sandbox.exec("test -x /tmp/rg && echo found");
103
+ if (tmpCheck.stdout.includes("found")) {
104
+ sandbox.rgPath = "/tmp/rg";
105
+ return;
106
+ }
107
+ const systemCheck = await sandbox.exec("which rg 2>/dev/null");
108
+ if (systemCheck.exitCode === 0 && systemCheck.stdout.trim()) {
109
+ sandbox.rgPath = systemCheck.stdout.trim();
110
+ return;
111
+ }
112
+ const archResult = await sandbox.exec("uname -m");
113
+ const arch = archResult.stdout.trim();
114
+ const ripgrepArch = ARCH_MAP[arch];
115
+ if (!ripgrepArch) {
116
+ throw new Error(`Unsupported architecture: ${arch}. Supported: ${Object.keys(ARCH_MAP).join(", ")}`);
117
+ }
118
+ const ripgrepUrl = `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-${ripgrepArch}.tar.gz`;
119
+ const tarPath = `ripgrep-${RIPGREP_VERSION}-${ripgrepArch}/rg`;
120
+ const installResult = await sandbox.exec(`
121
+ curl -sL "${ripgrepUrl}" |
122
+ tar xzf - -C /tmp --strip-components=1 ${tarPath} &&
123
+ chmod +x /tmp/rg
124
+ `);
125
+ if (installResult.exitCode !== 0) {
126
+ throw new Error(`Failed to install ripgrep: ${installResult.stderr}`);
127
+ }
128
+ sandbox.rgPath = "/tmp/rg";
129
+ }
130
+
52
131
  // src/sandbox/e2b.ts
53
- function createE2BSandbox(config = {}) {
54
- let sandbox = null;
132
+ async function createE2BSandbox(config = {}) {
55
133
  let sandboxId = config.sandboxId;
56
134
  const workingDirectory = config.cwd || "/home/user";
57
135
  const timeout = config.timeout ?? 300000;
58
- const ensureSandbox = async () => {
59
- if (sandbox)
60
- return sandbox;
136
+ const sandbox = createLazySingleton(async () => {
61
137
  let E2BSandboxSDK;
62
138
  try {
63
139
  const module = await import("@e2b/code-interpreter");
@@ -65,20 +141,21 @@ function createE2BSandbox(config = {}) {
65
141
  } catch {
66
142
  throw new Error("E2BSandbox requires @e2b/code-interpreter. Install with: npm install @e2b/code-interpreter");
67
143
  }
144
+ let sbx;
68
145
  if (config.sandboxId) {
69
- sandbox = await E2BSandboxSDK.connect(config.sandboxId);
146
+ sbx = await E2BSandboxSDK.connect(config.sandboxId);
70
147
  } else {
71
- sandbox = await E2BSandboxSDK.create({
148
+ sbx = await E2BSandboxSDK.create({
72
149
  apiKey: config.apiKey,
73
150
  timeoutMs: timeout,
74
151
  metadata: config.metadata
75
152
  });
76
- sandboxId = sandbox.sandboxId;
153
+ sandboxId = sbx.sandboxId;
77
154
  }
78
- return sandbox;
79
- };
155
+ return sbx;
156
+ });
80
157
  const exec = async (command, options) => {
81
- const sbx = await ensureSandbox();
158
+ const sbx = await sandbox.get();
82
159
  const startTime = performance.now();
83
160
  try {
84
161
  const result = await sbx.commands.run(command, {
@@ -118,11 +195,18 @@ function createE2BSandbox(config = {}) {
118
195
  throw error;
119
196
  }
120
197
  };
121
- return {
198
+ let rgPath;
199
+ const sandboxObj = {
122
200
  exec,
123
201
  get id() {
124
202
  return sandboxId;
125
203
  },
204
+ get rgPath() {
205
+ return rgPath;
206
+ },
207
+ set rgPath(path) {
208
+ rgPath = path;
209
+ },
126
210
  async readFile(path) {
127
211
  const result = await exec(`cat "${path}"`);
128
212
  if (result.exitCode !== 0) {
@@ -131,7 +215,7 @@ function createE2BSandbox(config = {}) {
131
215
  return result.stdout;
132
216
  },
133
217
  async writeFile(path, content) {
134
- const sbx = await ensureSandbox();
218
+ const sbx = await sandbox.get();
135
219
  await sbx.files.write(path, content);
136
220
  },
137
221
  async readDir(path) {
@@ -151,17 +235,23 @@ function createE2BSandbox(config = {}) {
151
235
  return result.exitCode === 0;
152
236
  },
153
237
  async destroy() {
154
- if (sandbox) {
155
- await sandbox.kill();
156
- sandbox = null;
157
- }
238
+ try {
239
+ const sbx = await sandbox.get();
240
+ await sbx.kill();
241
+ } catch {}
242
+ sandbox.reset();
158
243
  }
159
244
  };
245
+ if (config.ensureTools !== false) {
246
+ await ensureSandboxTools(sandboxObj);
247
+ }
248
+ return sandboxObj;
160
249
  }
161
250
  // src/sandbox/local.ts
162
251
  import { existsSync, mkdirSync } from "node:fs";
163
252
  function createLocalSandbox(config = {}) {
164
253
  const workingDirectory = config.cwd || "/tmp";
254
+ const rgPath = getBundledRgPathSync();
165
255
  if (!existsSync(workingDirectory)) {
166
256
  mkdirSync(workingDirectory, { recursive: true });
167
257
  }
@@ -201,6 +291,7 @@ function createLocalSandbox(config = {}) {
201
291
  };
202
292
  return {
203
293
  exec,
294
+ rgPath,
204
295
  async readFile(path) {
205
296
  const fullPath = path.startsWith("/") ? path : `${workingDirectory}/${path}`;
206
297
  const file = Bun.file(fullPath);
@@ -239,8 +330,7 @@ function createLocalSandbox(config = {}) {
239
330
  };
240
331
  }
241
332
  // src/sandbox/vercel.ts
242
- function createVercelSandbox(config = {}) {
243
- let sandbox = null;
333
+ async function createVercelSandbox(config = {}) {
244
334
  let sandboxId = config.sandboxId;
245
335
  const workingDirectory = config.cwd || "/vercel/sandbox";
246
336
  const resolvedConfig = {
@@ -248,9 +338,7 @@ function createVercelSandbox(config = {}) {
248
338
  resources: config.resources ?? { vcpus: 2 },
249
339
  timeout: config.timeout ?? 300000
250
340
  };
251
- const ensureSandbox = async () => {
252
- if (sandbox)
253
- return sandbox;
341
+ const sandbox = createLazySingleton(async () => {
254
342
  let VercelSandboxSDK;
255
343
  try {
256
344
  const module = await import("@vercel/sandbox");
@@ -269,16 +357,17 @@ function createVercelSandbox(config = {}) {
269
357
  token: config.token
270
358
  });
271
359
  }
360
+ let sbx;
272
361
  if (config.sandboxId) {
273
- sandbox = await VercelSandboxSDK.get({ sandboxId: config.sandboxId });
362
+ sbx = await VercelSandboxSDK.get({ sandboxId: config.sandboxId });
274
363
  } else {
275
- sandbox = await VercelSandboxSDK.create(createOptions);
364
+ sbx = await VercelSandboxSDK.create(createOptions);
276
365
  }
277
- sandboxId = sandbox.sandboxId;
278
- return sandbox;
279
- };
366
+ sandboxId = sbx.sandboxId;
367
+ return sbx;
368
+ });
280
369
  const exec = async (command, options) => {
281
- const sbx = await ensureSandbox();
370
+ const sbx = await sandbox.get();
282
371
  const startTime = performance.now();
283
372
  let interrupted = false;
284
373
  const abortController = new AbortController;
@@ -326,13 +415,20 @@ function createVercelSandbox(config = {}) {
326
415
  throw error;
327
416
  }
328
417
  };
329
- return {
418
+ let rgPath;
419
+ const sandboxObj = {
330
420
  exec,
331
421
  get id() {
332
422
  return sandboxId;
333
423
  },
424
+ get rgPath() {
425
+ return rgPath;
426
+ },
427
+ set rgPath(path) {
428
+ rgPath = path;
429
+ },
334
430
  async readFile(path) {
335
- const sbx = await ensureSandbox();
431
+ const sbx = await sandbox.get();
336
432
  const stream = await sbx.readFile({ path });
337
433
  if (!stream) {
338
434
  throw new Error(`File not found: ${path}`);
@@ -344,7 +440,7 @@ function createVercelSandbox(config = {}) {
344
440
  return Buffer.concat(chunks).toString("utf-8");
345
441
  },
346
442
  async writeFile(path, content) {
347
- const sbx = await ensureSandbox();
443
+ const sbx = await sandbox.get();
348
444
  await sbx.writeFiles([
349
445
  {
350
446
  path,
@@ -369,12 +465,17 @@ function createVercelSandbox(config = {}) {
369
465
  return result.exitCode === 0;
370
466
  },
371
467
  async destroy() {
372
- if (sandbox) {
373
- await sandbox.stop();
374
- sandbox = null;
375
- }
468
+ try {
469
+ const sbx = await sandbox.get();
470
+ await sbx.stop();
471
+ } catch {}
472
+ sandbox.reset();
376
473
  }
377
474
  };
475
+ if (config.ensureTools !== false) {
476
+ await ensureSandboxTools(sandboxObj);
477
+ }
478
+ return sandboxObj;
378
479
  }
379
480
  // src/cache/lru.ts
380
481
  class LRUCacheStore {
@@ -991,7 +1092,6 @@ function createGlobTool(sandbox, config) {
991
1092
  // src/tools/grep.ts
992
1093
  import { tool as tool7, zodSchema as zodSchema7 } from "ai";
993
1094
  import { z as z7 } from "zod";
994
- import { rgPath } from "@vscode/ripgrep";
995
1095
  var grepInputSchema = z7.object({
996
1096
  pattern: z7.string().describe("The regular expression pattern to search for in file contents"),
997
1097
  path: z7.string().optional().describe("File or directory to search in (defaults to cwd)"),
@@ -1057,7 +1157,13 @@ function createGrepTool(sandbox, config) {
1057
1157
  }
1058
1158
  }
1059
1159
  try {
1160
+ if (!sandbox.rgPath) {
1161
+ return {
1162
+ error: "Ripgrep not available. Call ensureSandboxTools(sandbox) before using Grep with remote sandboxes."
1163
+ };
1164
+ }
1060
1165
  const cmd = buildRipgrepCommand({
1166
+ rgPath: sandbox.rgPath,
1061
1167
  pattern,
1062
1168
  searchPath,
1063
1169
  output_mode,
@@ -1106,7 +1212,7 @@ function buildRipgrepCommand(opts) {
1106
1212
  if (opts.type)
1107
1213
  flags.push(`-t ${opts.type}`);
1108
1214
  const flagStr = flags.join(" ");
1109
- return `${rgPath} ${flagStr} "${opts.pattern}" ${opts.searchPath} 2>/dev/null`;
1215
+ return `${opts.rgPath} ${flagStr} "${opts.pattern}" ${opts.searchPath} 2>/dev/null`;
1110
1216
  }
1111
1217
  function parseFilesOutput(stdout) {
1112
1218
  const files = new Set;
@@ -2778,6 +2884,7 @@ export {
2778
2884
  estimateTokens,
2779
2885
  estimateMessagesTokens,
2780
2886
  estimateMessageTokens,
2887
+ ensureSandboxTools,
2781
2888
  discoverSkills,
2782
2889
  createWriteTool,
2783
2890
  createWebSearchTool,
@@ -7,5 +7,10 @@ export interface E2BSandboxConfig {
7
7
  timeout?: number;
8
8
  cwd?: string;
9
9
  metadata?: Record<string, string>;
10
+ /**
11
+ * Ensure tools like ripgrep are available in the sandbox.
12
+ * Defaults to true. Set to false for faster startup if you don't need Grep.
13
+ */
14
+ ensureTools?: boolean;
10
15
  }
11
- export declare function createE2BSandbox(config?: E2BSandboxConfig): Sandbox;
16
+ export declare function createE2BSandbox(config?: E2BSandboxConfig): Promise<Sandbox>;
@@ -0,0 +1,22 @@
1
+ import type { Sandbox } from "./interface";
2
+ /**
3
+ * Ensures required tools (ripgrep) are available in the sandbox.
4
+ * Call this once during sandbox setup, before using tools like Grep.
5
+ *
6
+ * - For local sandboxes: verifies bundled binary exists
7
+ * - For remote sandboxes: installs ripgrep to /tmp/rg if not present
8
+ *
9
+ * Supports x86_64 and ARM64 architectures.
10
+ *
11
+ * After calling, `sandbox.rgPath` will be set to the correct path.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const sandbox = await createVercelSandbox();
16
+ * await ensureSandboxTools(sandbox);
17
+ *
18
+ * const { tools } = createAgentTools(sandbox);
19
+ * // Grep now works
20
+ * ```
21
+ */
22
+ export declare function ensureSandboxTools(sandbox: Sandbox): Promise<void>;
@@ -1,4 +1,5 @@
1
1
  export { createE2BSandbox, type E2BSandboxConfig } from "./e2b";
2
+ export { ensureSandboxTools } from "./ensure-tools";
2
3
  export type { ExecOptions, ExecResult, Sandbox } from "./interface";
3
4
  export { createLocalSandbox, type LocalSandboxConfig } from "./local";
4
5
  export { createVercelSandbox, type VercelSandboxConfig } from "./vercel";
@@ -25,4 +25,9 @@ export interface Sandbox {
25
25
  * - For local sandboxes: always undefined
26
26
  */
27
27
  readonly id?: string;
28
+ /**
29
+ * Path to ripgrep binary for this sandbox.
30
+ * Set by ensureSandboxTools() or defaults to bundled binary for local sandboxes.
31
+ */
32
+ rgPath?: string;
28
33
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Creates a lazy singleton that initializes on first access.
3
+ * Safe for concurrent calls - all callers await the same promise.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const sandbox = createLazySingleton(async () => {
8
+ * const sdk = await import("@vercel/sandbox");
9
+ * return sdk.Sandbox.create({ ... });
10
+ * });
11
+ *
12
+ * // Safe for parallel calls:
13
+ * const [a, b] = await Promise.all([sandbox.get(), sandbox.get()]);
14
+ * // a === b (same instance)
15
+ *
16
+ * // Reset for cleanup:
17
+ * sandbox.reset();
18
+ * ```
19
+ */
20
+ export declare function createLazySingleton<T>(factory: () => Promise<T>): {
21
+ /** Get the singleton instance, creating it if needed */
22
+ get: () => Promise<T>;
23
+ /** Reset the singleton, allowing a new instance to be created */
24
+ reset: () => void;
25
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Dynamically imports @vscode/ripgrep to get the bundled binary path.
3
+ * Returns undefined if the package is not installed.
4
+ */
5
+ export declare function getBundledRgPath(): Promise<string | undefined>;
6
+ /**
7
+ * Synchronously gets the bundled ripgrep path using require.
8
+ * For use in synchronous contexts (e.g., LocalSandbox).
9
+ * Returns undefined if the package is not installed.
10
+ */
11
+ export declare function getBundledRgPathSync(): string | undefined;
@@ -11,5 +11,10 @@ export interface VercelSandboxConfig {
11
11
  teamId?: string;
12
12
  projectId?: string;
13
13
  token?: string;
14
+ /**
15
+ * Ensure tools like ripgrep are available in the sandbox.
16
+ * Defaults to true. Set to false for faster startup if you don't need Grep.
17
+ */
18
+ ensureTools?: boolean;
14
19
  }
15
- export declare function createVercelSandbox(config?: VercelSandboxConfig): Sandbox;
20
+ export declare function createVercelSandbox(config?: VercelSandboxConfig): Promise<Sandbox>;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Bashkit Workflow Integration
3
+ *
4
+ * Durable agent tools for Vercel's Workflow DevKit.
5
+ * Each tool execution is wrapped with "use step" for automatic
6
+ * durability, retries, and checkpointing.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createDurableAgentTools } from 'bashkit/workflow';
11
+ * import { DurableAgent } from '@workflow/ai/agent';
12
+ *
13
+ * export async function generateReport(sandboxId: string) {
14
+ * "use workflow";
15
+ *
16
+ * const { tools } = createDurableAgentTools(sandboxId);
17
+ *
18
+ * const agent = new DurableAgent({
19
+ * model: "anthropic/claude-sonnet-4-20250514",
20
+ * tools,
21
+ * });
22
+ *
23
+ * await agent.run({ prompt: "Generate the report" });
24
+ * }
25
+ * ```
26
+ */
27
+ import { type ToolSet } from "ai";
28
+ import type { AgentConfig } from "./types";
29
+ export interface DurableAgentConfig extends Omit<AgentConfig, "cache"> {
30
+ /**
31
+ * E2B API key (optional, uses ANTHROPIC_API_KEY env var by default)
32
+ */
33
+ apiKey?: string;
34
+ }
35
+ export interface DurableAgentToolsResult {
36
+ tools: ToolSet;
37
+ }
38
+ /**
39
+ * Creates durable agent tools for Workflow DevKit.
40
+ *
41
+ * Each tool execution:
42
+ * 1. Is wrapped with "use step" for durability
43
+ * 2. Reconnects to the E2B sandbox via sandboxId
44
+ * 3. Can retry independently on failure
45
+ * 4. Works with parallel tool calls (each gets own correlationId)
46
+ *
47
+ * @param sandboxId - E2B sandbox ID to reconnect to
48
+ * @param config - Optional tool configuration
49
+ */
50
+ export declare function createDurableAgentTools(sandboxId: string, config?: DurableAgentConfig): DurableAgentToolsResult;
51
+ export type { Sandbox } from "./sandbox/interface";
52
+ export type { ToolConfig, AgentConfig } from "./types";
@@ -0,0 +1,458 @@
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/workflow.ts
21
+ import { tool, zodSchema } from "ai";
22
+ import { z } from "zod";
23
+
24
+ // src/sandbox/lazy-singleton.ts
25
+ function createLazySingleton(factory) {
26
+ let promise = null;
27
+ return {
28
+ get: () => {
29
+ if (!promise) {
30
+ promise = factory();
31
+ }
32
+ return promise;
33
+ },
34
+ reset: () => {
35
+ promise = null;
36
+ }
37
+ };
38
+ }
39
+
40
+ // src/sandbox/ensure-tools.ts
41
+ import { rgPath as bundledRgPath } from "@vscode/ripgrep";
42
+ var RIPGREP_VERSION = "14.1.0";
43
+ var ARCH_MAP = {
44
+ x86_64: "x86_64-unknown-linux-musl",
45
+ aarch64: "aarch64-unknown-linux-gnu",
46
+ arm64: "aarch64-unknown-linux-gnu"
47
+ };
48
+ async function ensureSandboxTools(sandbox) {
49
+ const bundledCheck = await sandbox.exec(`test -x "${bundledRgPath}" && echo found`);
50
+ if (bundledCheck.stdout.includes("found")) {
51
+ sandbox.rgPath = bundledRgPath;
52
+ return;
53
+ }
54
+ const tmpCheck = await sandbox.exec("test -x /tmp/rg && echo found");
55
+ if (tmpCheck.stdout.includes("found")) {
56
+ sandbox.rgPath = "/tmp/rg";
57
+ return;
58
+ }
59
+ const systemCheck = await sandbox.exec("which rg 2>/dev/null");
60
+ if (systemCheck.exitCode === 0 && systemCheck.stdout.trim()) {
61
+ sandbox.rgPath = systemCheck.stdout.trim();
62
+ return;
63
+ }
64
+ const archResult = await sandbox.exec("uname -m");
65
+ const arch = archResult.stdout.trim();
66
+ const ripgrepArch = ARCH_MAP[arch];
67
+ if (!ripgrepArch) {
68
+ throw new Error(`Unsupported architecture: ${arch}. Supported: ${Object.keys(ARCH_MAP).join(", ")}`);
69
+ }
70
+ const ripgrepUrl = `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-${ripgrepArch}.tar.gz`;
71
+ const tarPath = `ripgrep-${RIPGREP_VERSION}-${ripgrepArch}/rg`;
72
+ const installResult = await sandbox.exec(`
73
+ curl -sL "${ripgrepUrl}" |
74
+ tar xzf - -C /tmp --strip-components=1 ${tarPath} &&
75
+ chmod +x /tmp/rg
76
+ `);
77
+ if (installResult.exitCode !== 0) {
78
+ throw new Error(`Failed to install ripgrep: ${installResult.stderr}`);
79
+ }
80
+ sandbox.rgPath = "/tmp/rg";
81
+ }
82
+
83
+ // src/sandbox/e2b.ts
84
+ async function createE2BSandbox(config = {}) {
85
+ let sandboxId = config.sandboxId;
86
+ const workingDirectory = config.cwd || "/home/user";
87
+ const timeout = config.timeout ?? 300000;
88
+ const sandbox = createLazySingleton(async () => {
89
+ let E2BSandboxSDK;
90
+ try {
91
+ const module = await import("@e2b/code-interpreter");
92
+ E2BSandboxSDK = module.Sandbox;
93
+ } catch {
94
+ throw new Error("E2BSandbox requires @e2b/code-interpreter. Install with: npm install @e2b/code-interpreter");
95
+ }
96
+ let sbx;
97
+ if (config.sandboxId) {
98
+ sbx = await E2BSandboxSDK.connect(config.sandboxId);
99
+ } else {
100
+ sbx = await E2BSandboxSDK.create({
101
+ apiKey: config.apiKey,
102
+ timeoutMs: timeout,
103
+ metadata: config.metadata
104
+ });
105
+ sandboxId = sbx.sandboxId;
106
+ }
107
+ return sbx;
108
+ });
109
+ const exec = async (command, options) => {
110
+ const sbx = await sandbox.get();
111
+ const startTime = performance.now();
112
+ try {
113
+ const result = await sbx.commands.run(command, {
114
+ cwd: options?.cwd || workingDirectory,
115
+ timeoutMs: options?.timeout
116
+ });
117
+ const durationMs = Math.round(performance.now() - startTime);
118
+ return {
119
+ stdout: result.stdout,
120
+ stderr: result.stderr,
121
+ exitCode: result.exitCode,
122
+ durationMs,
123
+ interrupted: false
124
+ };
125
+ } catch (error) {
126
+ const durationMs = Math.round(performance.now() - startTime);
127
+ if (error instanceof Error && error.message.toLowerCase().includes("timeout")) {
128
+ return {
129
+ stdout: "",
130
+ stderr: "Command timed out",
131
+ exitCode: 124,
132
+ durationMs,
133
+ interrupted: true
134
+ };
135
+ }
136
+ if (error instanceof Error) {
137
+ const exitMatch = error.message.match(/exit status (\d+)/i);
138
+ const exitCode = exitMatch ? parseInt(exitMatch[1], 10) : 1;
139
+ return {
140
+ stdout: "",
141
+ stderr: error.message,
142
+ exitCode,
143
+ durationMs,
144
+ interrupted: false
145
+ };
146
+ }
147
+ throw error;
148
+ }
149
+ };
150
+ let rgPath;
151
+ const sandboxObj = {
152
+ exec,
153
+ get id() {
154
+ return sandboxId;
155
+ },
156
+ get rgPath() {
157
+ return rgPath;
158
+ },
159
+ set rgPath(path) {
160
+ rgPath = path;
161
+ },
162
+ async readFile(path) {
163
+ const result = await exec(`cat "${path}"`);
164
+ if (result.exitCode !== 0) {
165
+ throw new Error(`Failed to read file: ${result.stderr}`);
166
+ }
167
+ return result.stdout;
168
+ },
169
+ async writeFile(path, content) {
170
+ const sbx = await sandbox.get();
171
+ await sbx.files.write(path, content);
172
+ },
173
+ async readDir(path) {
174
+ const result = await exec(`ls -1 "${path}"`);
175
+ if (result.exitCode !== 0) {
176
+ throw new Error(`Failed to read directory: ${result.stderr}`);
177
+ }
178
+ return result.stdout.split(`
179
+ `).filter(Boolean);
180
+ },
181
+ async fileExists(path) {
182
+ const result = await exec(`test -e "${path}"`);
183
+ return result.exitCode === 0;
184
+ },
185
+ async isDirectory(path) {
186
+ const result = await exec(`test -d "${path}"`);
187
+ return result.exitCode === 0;
188
+ },
189
+ async destroy() {
190
+ try {
191
+ const sbx = await sandbox.get();
192
+ await sbx.kill();
193
+ } catch {}
194
+ sandbox.reset();
195
+ }
196
+ };
197
+ if (config.ensureTools !== false) {
198
+ await ensureSandboxTools(sandboxObj);
199
+ }
200
+ return sandboxObj;
201
+ }
202
+
203
+ // src/workflow.ts
204
+ async function reconnectSandbox(sandboxId, apiKey) {
205
+ return createE2BSandbox({
206
+ sandboxId,
207
+ apiKey,
208
+ ensureTools: false
209
+ });
210
+ }
211
+ var readInputSchema = z.object({
212
+ file_path: z.string().describe("Absolute path to file or directory"),
213
+ offset: z.number().optional().describe("Line number to start reading from (1-indexed)"),
214
+ limit: z.number().optional().describe("Maximum number of lines to read")
215
+ });
216
+ var READ_DESCRIPTION = `Reads a file from the sandbox filesystem.
217
+
218
+ Usage:
219
+ - The file_path parameter must be an absolute path
220
+ - By default, reads up to 500 lines from the beginning
221
+ - Use offset and limit for large files
222
+ - Returns line numbers starting at 1`;
223
+ var writeInputSchema = z.object({
224
+ file_path: z.string().describe("Absolute path to the file to write"),
225
+ content: z.string().describe("Content to write to the file")
226
+ });
227
+ var WRITE_DESCRIPTION = `Writes content to a file in the sandbox filesystem.
228
+ Creates parent directories if they don't exist.`;
229
+ var editInputSchema = z.object({
230
+ file_path: z.string().describe("Absolute path to the file to edit"),
231
+ old_string: z.string().describe("The exact string to find and replace"),
232
+ new_string: z.string().describe("The string to replace it with")
233
+ });
234
+ var EDIT_DESCRIPTION = `Makes a targeted edit to a file by replacing old_string with new_string.
235
+ The old_string must match exactly (including whitespace and indentation).`;
236
+ var bashInputSchema = z.object({
237
+ command: z.string().describe("The bash command to execute"),
238
+ description: z.string().optional().describe("Brief description of what this command does"),
239
+ timeout: z.number().optional().describe("Timeout in milliseconds (default: 120000)")
240
+ });
241
+ var BASH_DESCRIPTION = `Executes a bash command in the sandbox.
242
+ Use for git, npm, system commands, etc.`;
243
+ var globInputSchema = z.object({
244
+ pattern: z.string().describe('Glob pattern (e.g., "**/*.ts", "src/**/*.json")'),
245
+ path: z.string().optional().describe("Directory to search in")
246
+ });
247
+ var GLOB_DESCRIPTION = `Finds files matching a glob pattern.
248
+ Returns list of matching file paths.`;
249
+ var grepInputSchema = z.object({
250
+ pattern: z.string().describe("Regex pattern to search for"),
251
+ path: z.string().optional().describe("File or directory to search in"),
252
+ glob: z.string().optional().describe('Glob pattern to filter files (e.g., "*.ts")'),
253
+ output_mode: z.enum(["content", "files_with_matches", "count"]).optional().describe("Output mode (default: files_with_matches)")
254
+ });
255
+ var GREP_DESCRIPTION = `Searches for a regex pattern in files.
256
+ Returns matching lines with file paths and line numbers.`;
257
+ async function executeRead(sandbox, params, config) {
258
+ const { file_path, offset, limit } = params;
259
+ if (config?.allowedPaths) {
260
+ const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
261
+ if (!isAllowed) {
262
+ return { error: `Path not allowed: ${file_path}` };
263
+ }
264
+ }
265
+ try {
266
+ const exists = await sandbox.fileExists(file_path);
267
+ if (!exists) {
268
+ return { error: `Path not found: ${file_path}` };
269
+ }
270
+ const isDir = await sandbox.isDirectory(file_path);
271
+ if (isDir) {
272
+ const entries = await sandbox.readDir(file_path);
273
+ return { type: "directory", entries, count: entries.length };
274
+ }
275
+ const content = await sandbox.readFile(file_path);
276
+ const allLines = content.split(`
277
+ `);
278
+ const totalLines = allLines.length;
279
+ const maxLinesWithoutLimit = config?.maxFileSize || 500;
280
+ if (!limit && totalLines > maxLinesWithoutLimit) {
281
+ return {
282
+ error: `File is large (${totalLines} lines). Use 'offset' and 'limit' to read in chunks.`
283
+ };
284
+ }
285
+ const startLine = offset ? offset - 1 : 0;
286
+ const endLine = limit ? startLine + limit : allLines.length;
287
+ const selectedLines = allLines.slice(startLine, endLine);
288
+ const lines = selectedLines.map((line, i) => ({
289
+ line_number: startLine + i + 1,
290
+ content: line
291
+ }));
292
+ return {
293
+ type: "text",
294
+ content: selectedLines.join(`
295
+ `),
296
+ lines,
297
+ total_lines: totalLines
298
+ };
299
+ } catch (error) {
300
+ return { error: error instanceof Error ? error.message : "Unknown error" };
301
+ }
302
+ }
303
+ async function executeWrite(sandbox, params) {
304
+ const { file_path, content } = params;
305
+ try {
306
+ const dir = file_path.split("/").slice(0, -1).join("/");
307
+ if (dir) {
308
+ await sandbox.exec(`mkdir -p "${dir}"`);
309
+ }
310
+ await sandbox.writeFile(file_path, content);
311
+ return { success: true, path: file_path, bytes_written: content.length };
312
+ } catch (error) {
313
+ return { error: error instanceof Error ? error.message : "Unknown error" };
314
+ }
315
+ }
316
+ async function executeEdit(sandbox, params) {
317
+ const { file_path, old_string, new_string } = params;
318
+ try {
319
+ const content = await sandbox.readFile(file_path);
320
+ if (!content.includes(old_string)) {
321
+ return { error: `old_string not found in file: ${file_path}` };
322
+ }
323
+ const newContent = content.replace(old_string, new_string);
324
+ await sandbox.writeFile(file_path, newContent);
325
+ return { success: true, path: file_path };
326
+ } catch (error) {
327
+ return { error: error instanceof Error ? error.message : "Unknown error" };
328
+ }
329
+ }
330
+ async function executeBash(sandbox, params, config) {
331
+ const { command, timeout } = params;
332
+ if (config?.blockedCommands) {
333
+ const isBlocked = config.blockedCommands.some((blocked) => command.includes(blocked));
334
+ if (isBlocked) {
335
+ return { error: `Command blocked by security policy` };
336
+ }
337
+ }
338
+ try {
339
+ const result = await sandbox.exec(command, {
340
+ timeout: timeout || config?.timeout || 120000
341
+ });
342
+ return {
343
+ stdout: result.stdout,
344
+ stderr: result.stderr,
345
+ exit_code: result.exitCode,
346
+ duration_ms: result.durationMs
347
+ };
348
+ } catch (error) {
349
+ return { error: error instanceof Error ? error.message : "Unknown error" };
350
+ }
351
+ }
352
+ async function executeGlob(sandbox, params) {
353
+ const { pattern, path } = params;
354
+ const searchPath = path || ".";
355
+ try {
356
+ const result = await sandbox.exec(`find ${searchPath} -type f -name "${pattern.replace(/\*\*/g, "*")}" 2>/dev/null | head -200`);
357
+ const files = result.stdout.split(`
358
+ `).filter(Boolean);
359
+ return { files, count: files.length };
360
+ } catch (error) {
361
+ return { error: error instanceof Error ? error.message : "Unknown error" };
362
+ }
363
+ }
364
+ async function executeGrep(sandbox, params) {
365
+ const { pattern, path, glob, output_mode } = params;
366
+ const searchPath = path || ".";
367
+ const mode = output_mode || "files_with_matches";
368
+ try {
369
+ let cmd;
370
+ const rgPath = sandbox.rgPath || "rg";
371
+ if (mode === "files_with_matches") {
372
+ cmd = `${rgPath} -l "${pattern}" ${searchPath}`;
373
+ } else if (mode === "count") {
374
+ cmd = `${rgPath} -c "${pattern}" ${searchPath}`;
375
+ } else {
376
+ cmd = `${rgPath} -n "${pattern}" ${searchPath}`;
377
+ }
378
+ if (glob) {
379
+ cmd += ` --glob "${glob}"`;
380
+ }
381
+ cmd += " 2>/dev/null | head -100";
382
+ const result = await sandbox.exec(cmd);
383
+ const lines = result.stdout.split(`
384
+ `).filter(Boolean);
385
+ if (mode === "files_with_matches") {
386
+ return { files: lines, count: lines.length };
387
+ } else if (mode === "count") {
388
+ return { matches: lines };
389
+ } else {
390
+ return { content: result.stdout, match_count: lines.length };
391
+ }
392
+ } catch (error) {
393
+ return { error: error instanceof Error ? error.message : "Unknown error" };
394
+ }
395
+ }
396
+ function createDurableAgentTools(sandboxId, config) {
397
+ const toolsConfig = config?.tools || {};
398
+ const tools = {
399
+ Read: tool({
400
+ description: READ_DESCRIPTION,
401
+ inputSchema: zodSchema(readInputSchema),
402
+ execute: async (params) => {
403
+ "use step";
404
+ const sandbox = await reconnectSandbox(sandboxId, config?.apiKey);
405
+ return executeRead(sandbox, params, toolsConfig.Read);
406
+ }
407
+ }),
408
+ Write: tool({
409
+ description: WRITE_DESCRIPTION,
410
+ inputSchema: zodSchema(writeInputSchema),
411
+ execute: async (params) => {
412
+ "use step";
413
+ const sandbox = await reconnectSandbox(sandboxId, config?.apiKey);
414
+ return executeWrite(sandbox, params);
415
+ }
416
+ }),
417
+ Edit: tool({
418
+ description: EDIT_DESCRIPTION,
419
+ inputSchema: zodSchema(editInputSchema),
420
+ execute: async (params) => {
421
+ "use step";
422
+ const sandbox = await reconnectSandbox(sandboxId, config?.apiKey);
423
+ return executeEdit(sandbox, params);
424
+ }
425
+ }),
426
+ Bash: tool({
427
+ description: BASH_DESCRIPTION,
428
+ inputSchema: zodSchema(bashInputSchema),
429
+ execute: async (params) => {
430
+ "use step";
431
+ const sandbox = await reconnectSandbox(sandboxId, config?.apiKey);
432
+ return executeBash(sandbox, params, toolsConfig.Bash);
433
+ }
434
+ }),
435
+ Glob: tool({
436
+ description: GLOB_DESCRIPTION,
437
+ inputSchema: zodSchema(globInputSchema),
438
+ execute: async (params) => {
439
+ "use step";
440
+ const sandbox = await reconnectSandbox(sandboxId, config?.apiKey);
441
+ return executeGlob(sandbox, params);
442
+ }
443
+ }),
444
+ Grep: tool({
445
+ description: GREP_DESCRIPTION,
446
+ inputSchema: zodSchema(grepInputSchema),
447
+ execute: async (params) => {
448
+ "use step";
449
+ const sandbox = await reconnectSandbox(sandboxId, config?.apiKey);
450
+ return executeGrep(sandbox, params);
451
+ }
452
+ })
453
+ };
454
+ return { tools };
455
+ }
456
+ export {
457
+ createDurableAgentTools
458
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bashkit",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "Agentic coding tools for the Vercel AI SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,5 +0,0 @@
1
- /**
2
- * HTTP status codes that indicate a retryable error.
3
- * Shared between web-search and web-fetch tools.
4
- */
5
- export declare const RETRYABLE_STATUS_CODES: number[];