@better-agent/plugins 0.1.0-canary.5 → 0.2.0-beta.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/dist/index.mjs CHANGED
@@ -1,114 +1,25 @@
1
1
  import { BetterAgentError } from "@better-agent/shared/errors";
2
2
  import { defineTool } from "@better-agent/core";
3
3
 
4
- //#region src/shared/json.ts
5
- /** Creates a JSON response with a default content type. */
6
- function jsonResponse(body, init) {
7
- const headers = new Headers(init?.headers);
8
- if (!headers.has("content-type")) headers.set("content-type", "application/json");
9
- return new Response(JSON.stringify(body), {
10
- ...init,
11
- headers
12
- });
13
- }
14
-
15
- //#endregion
16
- //#region src/auth/responses.ts
17
- /** Creates the default unauthorized response. */
18
- function createUnauthorizedResponse() {
19
- return jsonResponse({
20
- error: "unauthorized",
21
- message: "Invalid API key."
22
- }, { status: 401 });
23
- }
24
-
25
- //#endregion
26
- //#region src/shared/validation.ts
27
- /** Creates a plugin validation error. */
28
- function createValidationError$1(message, at) {
29
- return BetterAgentError.fromCode("VALIDATION_FAILED", message, { trace: [{ at }] });
30
- }
31
- /** Requires a positive finite number. */
32
- function requirePositiveNumber(value, name, at) {
33
- if (!Number.isFinite(value) || value <= 0) throw createValidationError$1(`\`${name}\` must be a positive number.`, at);
34
- }
35
- /** Requires a non-empty array. */
36
- function requireNonEmptyArray(value, name, at) {
37
- if (!value || value.length === 0) throw createValidationError$1(`\`${name}\` must contain at least one value.`, at);
38
- }
39
-
40
- //#endregion
41
- //#region src/auth/validate.ts
42
- /** Validates `authPlugin` configuration. */
43
- function validateAuthPluginConfig(config) {
44
- if (!config.validate && (!config.apiKeys || config.apiKeys.length === 0)) throw createValidationError$1("`authPlugin` requires either `apiKeys` or `validate`.", "plugins.authPlugin");
45
- if (config.header !== void 0 && config.header.trim().length === 0) throw createValidationError$1("`authPlugin` requires `header` to be a non-empty string when provided.", "plugins.authPlugin");
46
- if (config.apiKeys) {
47
- if (config.apiKeys.filter((key) => typeof key === "string").map((key) => key.trim()).filter((key) => key.length > 0).length === 0 && !config.validate) throw createValidationError$1("`authPlugin` requires `apiKeys` to contain at least one non-empty key.", "plugins.authPlugin");
48
- }
49
- }
50
-
51
- //#endregion
52
- //#region src/auth/plugin.ts
53
- /**
54
- * Creates an API-key auth plugin.
55
- *
56
- * Provide either `apiKeys` or `validate`.
57
- *
58
- * @example
59
- * ```ts
60
- * const plugin = authPlugin({
61
- * apiKeys: ["dev-key"],
62
- * });
63
- * ```
64
- */
65
- const authPlugin = (config) => {
66
- validateAuthPluginConfig(config);
67
- const header = config.header?.trim() || "x-api-key";
68
- const apiKeys = new Set((config.apiKeys ?? []).map((key) => key.trim()).filter(Boolean));
69
- return {
70
- id: config.id ?? "auth",
71
- guards: [async (ctx) => {
72
- const keyValue = config.getKey ? await config.getKey({
73
- agentName: ctx.agentName,
74
- mode: ctx.mode,
75
- request: ctx.request
76
- }) : ctx.request.headers.get(header);
77
- const key = typeof keyValue === "string" && keyValue.trim().length > 0 ? keyValue : null;
78
- if (config.validate ? await config.validate({
79
- key,
80
- agentName: ctx.agentName,
81
- mode: ctx.mode,
82
- request: ctx.request
83
- }) : key !== null && apiKeys.has(key)) return null;
84
- return config.onUnauthorized ? await config.onUnauthorized({
85
- key,
86
- agentName: ctx.agentName,
87
- mode: ctx.mode,
88
- request: ctx.request
89
- }) : createUnauthorizedResponse();
90
- }]
91
- };
92
- };
93
-
94
- //#endregion
95
4
  //#region src/ip-allowlist/ip.ts
96
5
  /** Parses an IPv4 string. */
97
6
  function parseIpv4(input) {
98
7
  const parts = input.split(".");
99
8
  if (parts.length !== 4) return null;
100
9
  let value = 0n;
10
+ const normalizedParts = [];
101
11
  for (const part of parts) {
102
12
  if (!/^\d+$/.test(part)) return null;
103
13
  const octet = Number(part);
104
14
  if (octet < 0 || octet > 255) return null;
105
15
  value = value << 8n | BigInt(octet);
16
+ normalizedParts.push(String(octet));
106
17
  }
107
18
  return {
108
19
  kind: "ipv4",
109
20
  value,
110
21
  bits: 32,
111
- normalized: parts.join(".")
22
+ normalized: normalizedParts.join(".")
112
23
  };
113
24
  }
114
25
  /** Expands an IPv6 string into eight normalized segments. */
@@ -210,6 +121,18 @@ function parseAllowEntry(input) {
210
121
  };
211
122
  }
212
123
 
124
+ //#endregion
125
+ //#region src/shared/json.ts
126
+ /** Creates a JSON response with a default content type. */
127
+ function jsonResponse(body, init) {
128
+ const headers = new Headers(init?.headers);
129
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
130
+ return new Response(JSON.stringify(body), {
131
+ ...init,
132
+ headers
133
+ });
134
+ }
135
+
213
136
  //#endregion
214
137
  //#region src/ip-allowlist/responses.ts
215
138
  /** Creates the default IP denied response. */
@@ -220,12 +143,27 @@ function createIpDeniedResponse() {
220
143
  }, { status: 403 });
221
144
  }
222
145
 
146
+ //#endregion
147
+ //#region src/shared/validation.ts
148
+ /** Creates a plugin validation error. */
149
+ function createValidationError$1(message, at) {
150
+ return BetterAgentError.fromCode("VALIDATION_FAILED", message, { trace: [{ at }] });
151
+ }
152
+ /** Requires a positive finite number. */
153
+ function requirePositiveNumber(value, name, at) {
154
+ if (!Number.isFinite(value) || value <= 0) throw createValidationError$1(`\`${name}\` must be a positive number.`, at);
155
+ }
156
+ /** Requires a non-empty array. */
157
+ function requireNonEmptyArray(value, name, at) {
158
+ if (!value || value.length === 0) throw createValidationError$1(`\`${name}\` must contain at least one value.`, at);
159
+ }
160
+
223
161
  //#endregion
224
162
  //#region src/ip-allowlist/validate.ts
225
- /** Validates `ipAllowlistPlugin` configuration. */
226
- function validateIpAllowlistPluginConfig(config) {
227
- requireNonEmptyArray(config.allow, "allow", "plugins.ipAllowlistPlugin");
228
- for (const entry of config.allow) if (typeof entry !== "string" || !parseAllowEntry(entry)) throw createValidationError$1(`\`ipAllowlistPlugin\` received an invalid allow entry: '${String(entry)}'.`, "plugins.ipAllowlistPlugin");
163
+ /** Validates `ipAllowlist` configuration. */
164
+ function validateIpAllowlistConfig(config) {
165
+ requireNonEmptyArray(config.allow, "allow", "plugins.ipAllowlist");
166
+ for (const entry of config.allow) if (typeof entry !== "string" || !parseAllowEntry(entry)) throw createValidationError$1(`\`ipAllowlist\` received an invalid allow entry: '${String(entry)}'.`, "plugins.ipAllowlist");
229
167
  }
230
168
 
231
169
  //#endregion
@@ -261,13 +199,13 @@ function getDirectIp(request) {
261
199
  *
262
200
  * @example
263
201
  * ```ts
264
- * const plugin = ipAllowlistPlugin({
202
+ * const plugin = ipAllowlist({
265
203
  * allow: ["127.0.0.1", "10.0.0.0/8"],
266
204
  * });
267
205
  * ```
268
206
  */
269
- const ipAllowlistPlugin = (config) => {
270
- validateIpAllowlistPluginConfig(config);
207
+ const ipAllowlist = (config) => {
208
+ validateIpAllowlistConfig(config);
271
209
  const matchers = config.allow.map((entry) => {
272
210
  const matcher = parseAllowEntry(entry);
273
211
  if (!matcher) throw new Error(`Invalid allowlist entry: ${entry}`);
@@ -278,7 +216,6 @@ const ipAllowlistPlugin = (config) => {
278
216
  guards: [async (ctx) => {
279
217
  const resolvedIp = config.getIp ? await config.getIp({
280
218
  agentName: ctx.agentName,
281
- mode: ctx.mode,
282
219
  request: ctx.request
283
220
  }) : config.trustProxy ? getProxyIp(ctx.request) : getDirectIp(ctx.request);
284
221
  const normalizedIp = typeof resolvedIp === "string" && resolvedIp.trim().length > 0 ? normalizeIp(resolvedIp) : null;
@@ -287,7 +224,6 @@ const ipAllowlistPlugin = (config) => {
287
224
  return config.onDenied ? await config.onDenied({
288
225
  ip: normalizedIp,
289
226
  agentName: ctx.agentName,
290
- mode: ctx.mode,
291
227
  request: ctx.request
292
228
  }) : createIpDeniedResponse();
293
229
  }]
@@ -325,11 +261,6 @@ function redactHeaders(headers, extraHeaders) {
325
261
  return result;
326
262
  }
327
263
 
328
- //#endregion
329
- //#region src/logging/validate.ts
330
- /** Validates `loggingPlugin` configuration. */
331
- function validateLoggingPluginConfig(_config) {}
332
-
333
264
  //#endregion
334
265
  //#region src/logging/plugin.ts
335
266
  function safeInvoke(fn, payload) {
@@ -338,6 +269,14 @@ function safeInvoke(fn, payload) {
338
269
  fn(payload);
339
270
  } catch {}
340
271
  }
272
+ function safeMap(fn, input) {
273
+ if (!fn) return input;
274
+ try {
275
+ return fn(input);
276
+ } catch {
277
+ return input;
278
+ }
279
+ }
341
280
  /** Resolves the logger methods for the configured sink. */
342
281
  function getLoggerMethods(config) {
343
282
  return {
@@ -350,7 +289,7 @@ function getLoggerMethods(config) {
350
289
  /** Emits one log entry. */
351
290
  function emitLog(config, entry) {
352
291
  if (!shouldLog(config.level ?? "info", entry)) return;
353
- const output = config.format ? config.format(entry) : entry;
292
+ const output = safeMap(config.format, entry);
354
293
  safeInvoke(getLoggerMethods(config)[entry.level], output);
355
294
  }
356
295
  /** Maps one runtime event to a log level. */
@@ -358,16 +297,25 @@ function getEventLevel(event) {
358
297
  if (event.type.endsWith("_ERROR")) return "error";
359
298
  return "info";
360
299
  }
361
- /** Creates request log data. */
362
- function createRequestData(ctx) {
300
+ function createRedactedRequestData(ctx, config) {
363
301
  return {
364
- mode: ctx.mode,
365
302
  url: ctx.request.url,
366
303
  method: ctx.request.method,
367
- headers: redactHeaders(ctx.request.headers),
304
+ headers: redactHeaders(ctx.request.headers, config.redactHeaders),
368
305
  input: ctx.input
369
306
  };
370
307
  }
308
+ function redactBody(config, phase, body) {
309
+ if (!config.redactBody) return body;
310
+ try {
311
+ return config.redactBody({
312
+ body,
313
+ phase
314
+ });
315
+ } catch {
316
+ return body;
317
+ }
318
+ }
371
319
  /** Creates step log data. */
372
320
  function createStepData(ctx) {
373
321
  return {
@@ -376,49 +324,36 @@ function createStepData(ctx) {
376
324
  messageCount: ctx.messages.length
377
325
  };
378
326
  }
379
- /** Creates save log data. */
380
- function createSaveData(ctx) {
381
- const messageCount = ctx.items.filter((item) => item.type === "message").length;
382
- return {
383
- itemCount: ctx.items.length,
384
- messageCount
385
- };
386
- }
387
327
  /**
388
328
  * Creates a logging plugin.
389
329
  *
390
330
  * @example
391
331
  * ```ts
392
- * const plugin = loggingPlugin({
332
+ * const plugin = logging({
393
333
  * level: "info",
394
334
  * include: { requests: true, toolCalls: true },
395
335
  * });
396
336
  * ```
397
337
  */
398
- const loggingPlugin = (config = {}) => {
399
- /* @__PURE__ */ validateLoggingPluginConfig(config);
338
+ const logging = (config = {}) => {
400
339
  const include = {
401
340
  requests: config.include?.requests ?? true,
402
341
  events: config.include?.events ?? true,
403
342
  steps: config.include?.steps ?? true,
404
343
  modelCalls: config.include?.modelCalls ?? true,
405
344
  toolCalls: config.include?.toolCalls ?? true,
406
- saves: config.include?.saves ?? false,
407
345
  errors: config.include?.errors ?? true
408
346
  };
409
347
  const plugin = { id: config.id ?? "logging" };
410
348
  if (include.requests) plugin.guards = [async (ctx) => {
411
- const body = config.redactBody ? config.redactBody({
412
- body: ctx.input,
413
- phase: "request"
414
- }) : ctx.input;
349
+ const body = config.redactBody ? redactBody(config, "request", ctx.input) : ctx.input;
415
350
  emitLog(config, {
416
351
  level: "info",
417
352
  event: "request.received",
418
353
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
419
354
  agentName: ctx.agentName,
420
355
  data: {
421
- ...createRequestData(ctx),
356
+ ...createRedactedRequestData(ctx, config),
422
357
  input: body
423
358
  }
424
359
  });
@@ -430,10 +365,10 @@ const loggingPlugin = (config = {}) => {
430
365
  emitLog(config, {
431
366
  level,
432
367
  event: "run.event",
433
- timestamp: new Date(event.timestamp).toISOString(),
368
+ timestamp: new Date(event.timestamp ?? Date.now()).toISOString(),
434
369
  agentName: ctx.agentName,
435
370
  runId: ctx.runId,
436
- conversationId: ctx.conversationId,
371
+ conversationId: ctx.threadId,
437
372
  data: { type: event.type }
438
373
  });
439
374
  };
@@ -444,7 +379,7 @@ const loggingPlugin = (config = {}) => {
444
379
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
445
380
  agentName: ctx.agentName,
446
381
  runId: ctx.runId,
447
- conversationId: ctx.conversationId,
382
+ conversationId: ctx.threadId,
448
383
  data: createStepData(ctx)
449
384
  });
450
385
  };
@@ -456,10 +391,10 @@ const loggingPlugin = (config = {}) => {
456
391
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
457
392
  agentName: ctx.agentName,
458
393
  runId: ctx.runId,
459
- conversationId: ctx.conversationId,
394
+ conversationId: ctx.threadId,
460
395
  data: {
461
396
  stepIndex: ctx.stepIndex,
462
- inputCount: ctx.input.length,
397
+ inputCount: ctx.messages.length,
463
398
  toolCount: ctx.tools.length,
464
399
  toolChoice: ctx.toolChoice
465
400
  }
@@ -472,13 +407,10 @@ const loggingPlugin = (config = {}) => {
472
407
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
473
408
  agentName: ctx.agentName,
474
409
  runId: ctx.runId,
475
- conversationId: ctx.conversationId,
410
+ conversationId: ctx.threadId,
476
411
  data: {
477
412
  stepIndex: ctx.stepIndex,
478
- response: config.redactBody ? config.redactBody({
479
- body: ctx.response,
480
- phase: "response"
481
- }) : ctx.response
413
+ response: config.redactBody ? redactBody(config, "response", ctx.response) : ctx.response
482
414
  }
483
415
  });
484
416
  };
@@ -491,14 +423,11 @@ const loggingPlugin = (config = {}) => {
491
423
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
492
424
  agentName: ctx.agentName,
493
425
  runId: ctx.runId,
494
- conversationId: ctx.conversationId,
426
+ conversationId: ctx.threadId,
495
427
  data: {
496
428
  toolName: ctx.toolName,
497
429
  toolCallId: ctx.toolCallId,
498
- args: config.redactBody ? config.redactBody({
499
- body: ctx.args,
500
- phase: "tool_args"
501
- }) : ctx.args
430
+ args: config.redactBody ? redactBody(config, "tool_args", ctx.input) : ctx.input
502
431
  }
503
432
  });
504
433
  };
@@ -509,36 +438,16 @@ const loggingPlugin = (config = {}) => {
509
438
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
510
439
  agentName: ctx.agentName,
511
440
  runId: ctx.runId,
512
- conversationId: ctx.conversationId,
441
+ conversationId: ctx.threadId,
513
442
  data: {
514
443
  toolName: ctx.toolName,
515
444
  toolCallId: ctx.toolCallId,
516
445
  error: ctx.error,
517
- result: config.redactBody ? config.redactBody({
518
- body: ctx.result,
519
- phase: "tool_result"
520
- }) : ctx.result
446
+ result: config.redactBody ? redactBody(config, "tool_result", ctx.result) : ctx.result
521
447
  }
522
448
  });
523
449
  };
524
450
  }
525
- if (include.saves) plugin.onBeforeSave = async (ctx) => {
526
- emitLog(config, {
527
- level: "debug",
528
- event: "save.before",
529
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
530
- agentName: ctx.agentName,
531
- runId: ctx.runId,
532
- conversationId: ctx.conversationId,
533
- data: {
534
- ...createSaveData(ctx),
535
- items: config.redactBody ? config.redactBody({
536
- body: ctx.items,
537
- phase: "save"
538
- }) : ctx.items
539
- }
540
- });
541
- };
542
451
  return plugin;
543
452
  };
544
453
 
@@ -562,17 +471,29 @@ function createBucket(params) {
562
471
  /** Creates an in-memory CAS store for rate limiting. */
563
472
  function createMemoryStore() {
564
473
  const rows = /* @__PURE__ */ new Map();
474
+ const pruneExpiredRows = (bucket) => {
475
+ const nowMs = bucket.now.getTime();
476
+ for (const [id, row] of rows) if (row.windowEndMs <= nowMs) rows.delete(id);
477
+ };
478
+ const toStoredRow = (bucket, state) => ({
479
+ ...state,
480
+ windowEndMs: bucket.windowEnd.getTime()
481
+ });
565
482
  return {
566
- read: async ({ bucket }) => rows.get(bucket.id) ?? null,
483
+ read: async ({ bucket }) => {
484
+ pruneExpiredRows(bucket);
485
+ return rows.get(bucket.id) ?? null;
486
+ },
567
487
  write: async ({ bucket, prevVersion, next }) => {
488
+ pruneExpiredRows(bucket);
568
489
  const current = rows.get(bucket.id) ?? null;
569
490
  if (prevVersion === null) {
570
491
  if (current) return false;
571
- rows.set(bucket.id, next);
492
+ rows.set(bucket.id, toStoredRow(bucket, next));
572
493
  return true;
573
494
  }
574
495
  if (!current || current.version !== prevVersion) return false;
575
- rows.set(bucket.id, next);
496
+ rows.set(bucket.id, toStoredRow(bucket, next));
576
497
  return true;
577
498
  }
578
499
  };
@@ -609,16 +530,16 @@ function createRateLimitStorageUnavailableResponse() {
609
530
  }
610
531
  /** Creates the CAS retries exceeded error. */
611
532
  function createCasRetriesExceededError() {
612
- return BetterAgentError.fromCode("INTERNAL", "Rate limit write failed after CAS retries.", { trace: [{ at: "plugins.rateLimitPlugin" }] });
533
+ return BetterAgentError.fromCode("INTERNAL", "Rate limit write failed after CAS retries.", { trace: [{ at: "plugins.rateLimit" }] });
613
534
  }
614
535
 
615
536
  //#endregion
616
537
  //#region src/rate-limit/validate.ts
617
- /** Validates `rateLimitPlugin` configuration. */
618
- function validateRateLimitPluginConfig(config) {
619
- requirePositiveNumber(config.windowMs, "windowMs", "plugins.rateLimitPlugin");
620
- requirePositiveNumber(config.max, "max", "plugins.rateLimitPlugin");
621
- requirePositiveNumber(config.casRetries ?? 8, "casRetries", "plugins.rateLimitPlugin");
538
+ /** Validates `rateLimit` configuration. */
539
+ function validateRateLimitConfig(config) {
540
+ requirePositiveNumber(config.windowMs, "windowMs", "plugins.rateLimit");
541
+ requirePositiveNumber(config.max, "max", "plugins.rateLimit");
542
+ requirePositiveNumber(config.casRetries ?? 8, "casRetries", "plugins.rateLimit");
622
543
  }
623
544
 
624
545
  //#endregion
@@ -630,23 +551,23 @@ function validateRateLimitPluginConfig(config) {
630
551
  *
631
552
  * @example
632
553
  * ```ts
633
- * const plugin = rateLimitPlugin({
554
+ * const plugin = rateLimit({
634
555
  * windowMs: 60_000,
635
556
  * max: 30,
636
557
  * });
637
558
  * ```
638
559
  */
639
- const rateLimitPlugin = (config) => {
640
- validateRateLimitPluginConfig(config);
560
+ const rateLimit = (config) => {
561
+ validateRateLimitConfig(config);
641
562
  const casRetries = config.casRetries ?? 8;
642
563
  const storage = config.storage ?? createMemoryStore();
643
564
  return {
644
565
  id: config.id ?? "rate-limit",
645
566
  guards: [async (ctx) => {
646
567
  const request = {
647
- mode: ctx.mode,
648
568
  agentName: ctx.agentName,
649
- request: ctx.request
569
+ request: ctx.request,
570
+ auth: ctx.auth
650
571
  };
651
572
  const now = /* @__PURE__ */ new Date();
652
573
  const nowMs = now.getTime();
@@ -722,27 +643,28 @@ const normalizeFileEntries = (files) => files.map((entry) => ({
722
643
  path: entry.path ?? entry.name ?? "",
723
644
  type: entry.type ?? (entry.isDir ? "directory" : "file")
724
645
  }));
725
- /** Creates a sandbox client backed by the Daytona SDK. */
646
+ /**
647
+ * Creates a sandbox client backed by the Daytona SDK.
648
+ */
726
649
  function createDaytonaSandboxClient(config = {}) {
727
650
  const clientConfig = removeUndefined$1({
728
651
  apiKey: config.apiKey,
729
652
  apiUrl: config.apiUrl,
730
653
  target: config.target
731
654
  });
655
+ const toProviderMinutes = (value) => value !== void 0 ? Math.max(1, Math.ceil(value / 6e4)) : void 0;
732
656
  const buildCreateParams = (overrides) => {
733
- const template = overrides?.template ?? config.template;
657
+ const template = overrides?.template;
734
658
  const templateKind = config.templateKind ?? "snapshot";
735
659
  return removeUndefined$1({
736
660
  language: config.language,
737
- envVars: overrides?.envs ?? config.envs,
738
- labels: overrides?.metadata ?? config.metadata,
661
+ envVars: overrides?.envs,
662
+ labels: overrides?.metadata,
739
663
  public: config.public,
740
- autoStopInterval: config.autoStopInterval,
741
- autoArchiveInterval: config.autoArchiveInterval,
742
- autoDeleteInterval: config.autoDeleteInterval,
743
- ...template !== void 0 ? templateKind === "image" ? { image: template } : { snapshot: template } : {},
744
- ...config.snapshot !== void 0 ? { snapshot: config.snapshot } : {},
745
- ...config.image !== void 0 ? { image: config.image } : {}
664
+ autoStopInterval: toProviderMinutes(overrides?.lifecycle?.idleStopMs),
665
+ autoArchiveInterval: toProviderMinutes(overrides?.lifecycle?.archiveAfterMs),
666
+ autoDeleteInterval: toProviderMinutes(overrides?.lifecycle?.deleteAfterMs),
667
+ ...template !== void 0 ? templateKind === "image" ? { image: template } : { snapshot: template } : {}
746
668
  });
747
669
  };
748
670
  const getClient = async () => {
@@ -780,7 +702,7 @@ function createDaytonaSandboxClient(config = {}) {
780
702
  },
781
703
  async createSandbox(params) {
782
704
  const client = await getClient();
783
- const timeout = toSeconds(params?.timeoutMs ?? config.timeoutMs);
705
+ const timeout = toSeconds(params?.startupTimeoutMs);
784
706
  return { sandboxId: (await client.create(buildCreateParams(params), { ...timeout !== void 0 ? { timeout } : {} })).id };
785
707
  },
786
708
  async runCommand(params) {
@@ -874,7 +796,9 @@ const loadE2B = async () => {
874
796
  });
875
797
  }
876
798
  };
877
- /** Creates a sandbox client backed by the E2B SDK. */
799
+ /**
800
+ * Creates a sandbox client backed by the E2B SDK.
801
+ */
878
802
  function createE2BSandboxClient(config = {}) {
879
803
  const connectionOptions = removeUndefined({
880
804
  apiKey: config.apiKey,
@@ -882,11 +806,11 @@ function createE2BSandboxClient(config = {}) {
882
806
  domain: config.domain,
883
807
  requestTimeoutMs: config.requestTimeoutMs
884
808
  });
885
- const toCreateOptions = (overrides) => removeUndefined({
809
+ const toCreateOptions = (params) => removeUndefined({
886
810
  ...connectionOptions,
887
- timeoutMs: overrides?.timeoutMs ?? config.timeoutMs,
888
- envs: overrides?.envs ?? config.envs,
889
- metadata: overrides?.metadata ?? config.metadata
811
+ timeoutMs: params?.lifecycle?.ttlMs,
812
+ envs: params?.envs,
813
+ metadata: params?.metadata
890
814
  });
891
815
  const connectSandbox = async (sandboxId) => {
892
816
  const { Sandbox } = await loadE2B();
@@ -910,7 +834,7 @@ function createE2BSandboxClient(config = {}) {
910
834
  },
911
835
  async createSandbox(params) {
912
836
  const { Sandbox } = await loadE2B();
913
- const template = params?.template ?? config.template;
837
+ const template = params?.template;
914
838
  const options = toCreateOptions(params);
915
839
  return { sandboxId: (template !== void 0 ? await Sandbox.create(template, options) : await Sandbox.create(options)).sandboxId };
916
840
  },
@@ -965,11 +889,28 @@ function createMemorySandboxSessionStore() {
965
889
 
966
890
  //#endregion
967
891
  //#region src/sandbox/validate.ts
968
- /** Validates `sandboxPlugin` configuration. */
969
- function validateSandboxPluginConfig(config) {
892
+ const hasLifecycleValues = (lifecycle) => Boolean(lifecycle?.ttlMs !== void 0 || lifecycle?.idleStopMs !== void 0 || lifecycle?.archiveAfterMs !== void 0 || lifecycle?.deleteAfterMs !== void 0);
893
+ function validateSandboxCreateParams(clientProvider, params) {
894
+ const provider = clientProvider?.trim().toLowerCase();
895
+ if (!provider) return;
896
+ if (provider === "daytona") {
897
+ if (params.lifecycle?.ttlMs !== void 0) throw createValidationError$1("`lifecycle.ttlMs` is not supported by the Daytona sandbox client. Use `startupTimeoutMs` for creation readiness and Daytona lifecycle fields like `idleStopMs`, `archiveAfterMs`, or `deleteAfterMs` instead.", "plugins.sandbox.createConfig.lifecycle.ttlMs");
898
+ return;
899
+ }
900
+ if (provider === "e2b") {
901
+ if (params.startupTimeoutMs !== void 0) throw createValidationError$1("`startupTimeoutMs` is not supported by the E2B sandbox client. Use `lifecycle.ttlMs` to control sandbox lifetime.", "plugins.sandbox.createConfig.startupTimeoutMs");
902
+ if (params.lifecycle?.idleStopMs !== void 0 || params.lifecycle?.archiveAfterMs !== void 0 || params.lifecycle?.deleteAfterMs !== void 0) throw createValidationError$1("`lifecycle.idleStopMs`, `lifecycle.archiveAfterMs`, and `lifecycle.deleteAfterMs` are not supported by the E2B sandbox client.", "plugins.sandbox.createConfig.lifecycle");
903
+ return;
904
+ }
905
+ if (hasLifecycleValues(params.lifecycle) || params.startupTimeoutMs !== void 0) return;
906
+ }
907
+ /** Validates `sandbox` configuration. */
908
+ function validateSandboxConfig(config) {
970
909
  const client = config.client;
971
- if (!client || typeof client !== "object") throw createValidationError$1("`sandboxPlugin` requires a `client`.", "plugins.sandboxPlugin");
972
- if (config.prefix !== void 0 && config.prefix.trim().length === 0) throw createValidationError$1("`sandboxPlugin` requires `prefix` to be a non-empty string when provided.", "plugins.sandboxPlugin");
910
+ if (!client || typeof client !== "object") throw createValidationError$1("`sandbox` requires a `client`.", "plugins.sandbox");
911
+ if (config.prefix !== void 0 && config.prefix.trim().length === 0) throw createValidationError$1("`sandbox` requires `prefix` to be a non-empty string when provided.", "plugins.sandbox");
912
+ if (config.createConfig) validateSandboxCreateParams(config.client.provider, config.createConfig);
913
+ if (config.createDefaults) validateSandboxCreateParams(config.client.provider, config.createDefaults);
973
914
  }
974
915
 
975
916
  //#endregion
@@ -983,10 +924,29 @@ const createValidationError = (message, at, context) => BetterAgentError.fromCod
983
924
  ...context !== void 0 ? { context } : {},
984
925
  trace: [{ at }]
985
926
  });
927
+ const resolveCreateParams = (overrides, createConfig, createDefaults) => ({
928
+ template: createConfig?.template ?? overrides?.template ?? createDefaults?.template,
929
+ startupTimeoutMs: createConfig?.startupTimeoutMs ?? overrides?.startupTimeoutMs ?? createDefaults?.startupTimeoutMs,
930
+ envs: createDefaults?.envs || overrides?.envs || createConfig?.envs ? {
931
+ ...createDefaults?.envs ?? {},
932
+ ...overrides?.envs ?? {},
933
+ ...createConfig?.envs ?? {}
934
+ } : void 0,
935
+ metadata: createDefaults?.metadata || overrides?.metadata || createConfig?.metadata ? {
936
+ ...createDefaults?.metadata ?? {},
937
+ ...overrides?.metadata ?? {},
938
+ ...createConfig?.metadata ?? {}
939
+ } : void 0,
940
+ lifecycle: createDefaults?.lifecycle || overrides?.lifecycle || createConfig?.lifecycle ? {
941
+ ...createDefaults?.lifecycle ?? {},
942
+ ...overrides?.lifecycle ?? {},
943
+ ...createConfig?.lifecycle ?? {}
944
+ } : void 0
945
+ });
986
946
  /**
987
947
  * Adds sandbox tools.
988
948
  *
989
- * By default, the plugin reuses one sandbox per `conversationId`.
949
+ * By default, the plugin reuses one sandbox per `threadId`.
990
950
  * Use `sessionKey` to customize reuse, or return `null`/`undefined`
991
951
  * to disable reuse for a specific tool call.
992
952
  *
@@ -994,31 +954,33 @@ const createValidationError = (message, at, context) => BetterAgentError.fromCod
994
954
  *
995
955
  * @example
996
956
  * ```ts
997
- * import { sandboxPlugin, createE2BSandboxClient } from "@better-agent/plugins";
957
+ * import { sandbox, createE2BSandboxClient } from "@better-agent/plugins";
998
958
  *
999
- * const plugin = sandboxPlugin({
959
+ * const plugin = sandbox({
1000
960
  * client: createE2BSandboxClient({
1001
961
  * apiKey: process.env.E2B_API_KEY,
1002
962
  * }),
1003
- * defaults: {
963
+ * createConfig: {
1004
964
  * template: "base",
1005
- * timeoutMs: 10 * 60_000,
965
+ * },
966
+ * createDefaults: {
967
+ * startupTimeoutMs: 90_000,
1006
968
  * },
1007
969
  * });
1008
970
  * ```
1009
971
  */
1010
- const sandboxPlugin = (config) => {
1011
- validateSandboxPluginConfig(config);
1012
- const sandboxClient = config.client;
1013
- const store = config.store ?? createMemorySandboxSessionStore();
1014
- const prefix = trimToUndefined(config.prefix) ?? "sandbox";
972
+ const sandbox = (pluginConfig) => {
973
+ validateSandboxConfig(pluginConfig);
974
+ const sandboxClient = pluginConfig.client;
975
+ const store = pluginConfig.store ?? createMemorySandboxSessionStore();
976
+ const prefix = trimToUndefined(pluginConfig.prefix) ?? "sandbox";
1015
977
  const nameFor = (suffix) => `${prefix}_${suffix}`;
1016
978
  const getSessionPolicy = (ctx, toolName) => {
1017
- if (config.sessionKey) {
1018
- const custom = trimToUndefined(config.sessionKey({
979
+ if (pluginConfig.sessionKey) {
980
+ const custom = trimToUndefined(pluginConfig.sessionKey({
1019
981
  runId: ctx.runId,
1020
- agentName: ctx.agentName,
1021
- ...ctx.conversationId !== void 0 ? { conversationId: ctx.conversationId } : {},
982
+ agentName: ctx.agentName ?? "",
983
+ ...ctx.threadId !== void 0 ? { threadId: ctx.threadId } : {},
1022
984
  toolName
1023
985
  }) ?? void 0);
1024
986
  return custom !== void 0 ? {
@@ -1026,25 +988,22 @@ const sandboxPlugin = (config) => {
1026
988
  sessionKey: custom
1027
989
  } : { kind: "disabled" };
1028
990
  }
1029
- return ctx.conversationId ? {
991
+ return ctx.threadId ? {
1030
992
  kind: "managed",
1031
- sessionKey: `conversation:${ctx.conversationId}`
993
+ sessionKey: `thread:${ctx.threadId}`
1032
994
  } : { kind: "disabled" };
1033
995
  };
1034
996
  const getExplicitSandboxId = (value) => {
1035
997
  if (value === void 0) return;
1036
998
  const trimmed = value.trim();
1037
- if (!trimmed) throw createValidationError("`sandboxId` must be a non-empty string when provided.", "plugins.sandboxPlugin.sandboxId");
999
+ if (!trimmed) throw createValidationError("`sandboxId` must be a non-empty string when provided.", "plugins.sandbox.sandboxId");
1038
1000
  return trimmed;
1039
1001
  };
1040
1002
  const createSandbox = async (params, ctx, toolName) => {
1041
1003
  const sessionPolicy = getSessionPolicy(ctx, toolName);
1042
- const created = await sandboxClient.createSandbox({
1043
- template: params?.template ?? config.defaults?.template,
1044
- timeoutMs: params?.timeoutMs ?? config.defaults?.timeoutMs,
1045
- envs: params?.envs ?? config.defaults?.envs,
1046
- metadata: params?.metadata ?? config.defaults?.metadata
1047
- });
1004
+ const resolvedParams = resolveCreateParams(params, pluginConfig.createConfig, pluginConfig.createDefaults);
1005
+ validateSandboxCreateParams(sandboxClient.provider, resolvedParams);
1006
+ const created = await sandboxClient.createSandbox(resolvedParams);
1048
1007
  if (sessionPolicy.kind === "managed") await store.set(sessionPolicy.sessionKey, created.sandboxId);
1049
1008
  return {
1050
1009
  sandboxId: created.sandboxId,
@@ -1067,10 +1026,9 @@ const sandboxPlugin = (config) => {
1067
1026
  created: false
1068
1027
  };
1069
1028
  }
1070
- if (!params.createIfMissing) throw createValidationError(`No sandbox is available for this ${params.ctx.conversationId ? "conversation" : "run"}. Create one first with '${nameFor("create")}' or pass a sandboxId explicitly.`, "plugins.sandboxPlugin.resolveSandbox.missing", {
1029
+ if (!params.createIfMissing) throw createValidationError(`No sandbox is available for this ${params.ctx.threadId ? "thread" : "run"}. Create one first with '${nameFor("create")}' or pass a sandboxId explicitly.`, "plugins.sandbox.resolveSandbox.missing", {
1071
1030
  runId: params.ctx.runId,
1072
- agentName: params.ctx.agentName,
1073
- conversationId: params.ctx.conversationId,
1031
+ threadId: params.ctx.threadId,
1074
1032
  toolName: params.toolName
1075
1033
  });
1076
1034
  return await createSandbox(params.createParams, params.ctx, params.toolName);
@@ -1088,7 +1046,7 @@ const sandboxPlugin = (config) => {
1088
1046
  properties: {
1089
1047
  forceNew: { type: "boolean" },
1090
1048
  template: { type: "string" },
1091
- timeoutMs: {
1049
+ startupTimeoutMs: {
1092
1050
  type: "number",
1093
1051
  exclusiveMinimum: 0
1094
1052
  },
@@ -1099,6 +1057,28 @@ const sandboxPlugin = (config) => {
1099
1057
  metadata: {
1100
1058
  type: "object",
1101
1059
  additionalProperties: { type: "string" }
1060
+ },
1061
+ lifecycle: {
1062
+ type: "object",
1063
+ properties: {
1064
+ ttlMs: {
1065
+ type: "number",
1066
+ exclusiveMinimum: 0
1067
+ },
1068
+ idleStopMs: {
1069
+ type: "number",
1070
+ exclusiveMinimum: 0
1071
+ },
1072
+ archiveAfterMs: {
1073
+ type: "number",
1074
+ exclusiveMinimum: 0
1075
+ },
1076
+ deleteAfterMs: {
1077
+ type: "number",
1078
+ exclusiveMinimum: 0
1079
+ }
1080
+ },
1081
+ additionalProperties: false
1102
1082
  }
1103
1083
  },
1104
1084
  additionalProperties: false
@@ -1108,12 +1088,16 @@ const sandboxPlugin = (config) => {
1108
1088
  type: "string",
1109
1089
  minLength: 1
1110
1090
  };
1111
- const tools = [
1112
- defineTool({
1113
- name: nameFor("create"),
1114
- description: "Create a sandbox, or reuse the current conversation sandbox unless forceNew is true.",
1115
- schema: createSchema
1116
- }).server(async (input, ctx) => {
1091
+ const envsSchema = {
1092
+ type: "object",
1093
+ additionalProperties: { type: "string" }
1094
+ };
1095
+ const createTool = defineTool({
1096
+ name: nameFor("create"),
1097
+ description: "Create a sandbox, or reuse the current thread sandbox unless forceNew is true.",
1098
+ target: "server",
1099
+ inputSchema: createSchema,
1100
+ execute: async (input, ctx) => {
1117
1101
  if (!input.forceNew) {
1118
1102
  const sessionPolicy = getSessionPolicy(ctx, nameFor("create"));
1119
1103
  if (sessionPolicy.kind === "managed") {
@@ -1128,9 +1112,10 @@ const sandboxPlugin = (config) => {
1128
1112
  }
1129
1113
  const resolved = await createSandbox({
1130
1114
  template: input.template,
1131
- timeoutMs: input.timeoutMs,
1115
+ startupTimeoutMs: input.startupTimeoutMs,
1132
1116
  envs: input.envs,
1133
- metadata: input.metadata
1117
+ metadata: input.metadata,
1118
+ lifecycle: input.lifecycle
1134
1119
  }, ctx, nameFor("create"));
1135
1120
  return {
1136
1121
  sandboxId: resolved.sandboxId,
@@ -1138,33 +1123,32 @@ const sandboxPlugin = (config) => {
1138
1123
  created: true,
1139
1124
  ...resolved.sessionKey !== void 0 ? { sessionKey: resolved.sessionKey } : {}
1140
1125
  };
1141
- }),
1142
- defineTool({
1143
- name: nameFor("exec"),
1144
- description: "Run one shell command inside a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1145
- schema: {
1146
- type: "object",
1147
- properties: {
1148
- sandboxId: sandboxIdSchema,
1149
- cmd: {
1150
- type: "string",
1151
- minLength: 1
1152
- },
1153
- cwd: { type: "string" },
1154
- timeoutMs: {
1155
- type: "number",
1156
- exclusiveMinimum: 0
1157
- },
1158
- envs: {
1159
- type: "object",
1160
- additionalProperties: { type: "string" }
1161
- }
1126
+ }
1127
+ });
1128
+ const execTool = defineTool({
1129
+ name: nameFor("exec"),
1130
+ description: "Run one shell command inside a sandbox. Creates a sandbox automatically when none exists for the thread.",
1131
+ target: "server",
1132
+ inputSchema: {
1133
+ type: "object",
1134
+ properties: {
1135
+ sandboxId: sandboxIdSchema,
1136
+ cmd: {
1137
+ type: "string",
1138
+ minLength: 1
1162
1139
  },
1163
- required: ["cmd"],
1164
- additionalProperties: false
1140
+ cwd: { type: "string" },
1141
+ timeoutMs: {
1142
+ type: "number",
1143
+ exclusiveMinimum: 0
1144
+ },
1145
+ envs: envsSchema
1165
1146
  },
1166
- approval: config.approvals?.exec
1167
- }).server(async (input, ctx) => {
1147
+ required: ["cmd"],
1148
+ additionalProperties: false
1149
+ },
1150
+ approval: pluginConfig.approvals?.exec,
1151
+ execute: async (input, ctx) => {
1168
1152
  const resolved = await resolveSandbox({
1169
1153
  sandboxId: input.sandboxId,
1170
1154
  createIfMissing: true,
@@ -1183,20 +1167,22 @@ const sandboxPlugin = (config) => {
1183
1167
  createdSandbox: resolved.created,
1184
1168
  ...result
1185
1169
  };
1186
- }),
1187
- defineTool({
1188
- name: nameFor("read_file"),
1189
- description: "Read one text file from a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1190
- schema: {
1191
- type: "object",
1192
- properties: {
1193
- sandboxId: sandboxIdSchema,
1194
- path: pathSchema
1195
- },
1196
- required: ["path"],
1197
- additionalProperties: false
1198
- }
1199
- }).server(async (input, ctx) => {
1170
+ }
1171
+ });
1172
+ const readFileTool = defineTool({
1173
+ name: nameFor("read_file"),
1174
+ description: "Read one text file from a sandbox. Creates a sandbox automatically when none exists for the thread.",
1175
+ target: "server",
1176
+ inputSchema: {
1177
+ type: "object",
1178
+ properties: {
1179
+ sandboxId: sandboxIdSchema,
1180
+ path: pathSchema
1181
+ },
1182
+ required: ["path"],
1183
+ additionalProperties: false
1184
+ },
1185
+ execute: async (input, ctx) => {
1200
1186
  const resolved = await resolveSandbox({
1201
1187
  sandboxId: input.sandboxId,
1202
1188
  createIfMissing: true,
@@ -1211,22 +1197,24 @@ const sandboxPlugin = (config) => {
1211
1197
  path: input.path
1212
1198
  })
1213
1199
  };
1214
- }),
1215
- defineTool({
1216
- name: nameFor("write_file"),
1217
- description: "Write one text file into a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1218
- schema: {
1219
- type: "object",
1220
- properties: {
1221
- sandboxId: sandboxIdSchema,
1222
- path: pathSchema,
1223
- content: { type: "string" }
1224
- },
1225
- required: ["path", "content"],
1226
- additionalProperties: false
1200
+ }
1201
+ });
1202
+ const writeFileTool = defineTool({
1203
+ name: nameFor("write_file"),
1204
+ description: "Write one text file into a sandbox. Creates a sandbox automatically when none exists for the thread.",
1205
+ target: "server",
1206
+ inputSchema: {
1207
+ type: "object",
1208
+ properties: {
1209
+ sandboxId: sandboxIdSchema,
1210
+ path: pathSchema,
1211
+ content: { type: "string" }
1227
1212
  },
1228
- approval: config.approvals?.writeFile
1229
- }).server(async (input, ctx) => {
1213
+ required: ["path", "content"],
1214
+ additionalProperties: false
1215
+ },
1216
+ approval: pluginConfig.approvals?.writeFile,
1217
+ execute: async (input, ctx) => {
1230
1218
  const resolved = await resolveSandbox({
1231
1219
  sandboxId: input.sandboxId,
1232
1220
  createIfMissing: true,
@@ -1243,20 +1231,22 @@ const sandboxPlugin = (config) => {
1243
1231
  createdSandbox: resolved.created,
1244
1232
  path: result.path
1245
1233
  };
1246
- }),
1247
- defineTool({
1248
- name: nameFor("list_files"),
1249
- description: "List directory entries inside a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1250
- schema: {
1251
- type: "object",
1252
- properties: {
1253
- sandboxId: sandboxIdSchema,
1254
- path: pathSchema
1255
- },
1256
- required: ["path"],
1257
- additionalProperties: false
1258
- }
1259
- }).server(async (input, ctx) => {
1234
+ }
1235
+ });
1236
+ const listFilesTool = defineTool({
1237
+ name: nameFor("list_files"),
1238
+ description: "List directory entries inside a sandbox. Creates a sandbox automatically when none exists for the thread.",
1239
+ target: "server",
1240
+ inputSchema: {
1241
+ type: "object",
1242
+ properties: {
1243
+ sandboxId: sandboxIdSchema,
1244
+ path: pathSchema
1245
+ },
1246
+ required: ["path"],
1247
+ additionalProperties: false
1248
+ },
1249
+ execute: async (input, ctx) => {
1260
1250
  const resolved = await resolveSandbox({
1261
1251
  sandboxId: input.sandboxId,
1262
1252
  createIfMissing: true,
@@ -1271,20 +1261,22 @@ const sandboxPlugin = (config) => {
1271
1261
  path: input.path
1272
1262
  })
1273
1263
  };
1274
- }),
1275
- defineTool({
1276
- name: nameFor("make_dir"),
1277
- description: "Create a directory inside a sandbox. Creates a sandbox automatically when none exists for the conversation.",
1278
- schema: {
1279
- type: "object",
1280
- properties: {
1281
- sandboxId: sandboxIdSchema,
1282
- path: pathSchema
1283
- },
1284
- required: ["path"],
1285
- additionalProperties: false
1286
- }
1287
- }).server(async (input, ctx) => {
1264
+ }
1265
+ });
1266
+ const makeDirTool = defineTool({
1267
+ name: nameFor("make_dir"),
1268
+ description: "Create a directory inside a sandbox. Creates a sandbox automatically when none exists for the thread.",
1269
+ target: "server",
1270
+ inputSchema: {
1271
+ type: "object",
1272
+ properties: {
1273
+ sandboxId: sandboxIdSchema,
1274
+ path: pathSchema
1275
+ },
1276
+ required: ["path"],
1277
+ additionalProperties: false
1278
+ },
1279
+ execute: async (input, ctx) => {
1288
1280
  const resolved = await resolveSandbox({
1289
1281
  sandboxId: input.sandboxId,
1290
1282
  createIfMissing: true,
@@ -1300,21 +1292,23 @@ const sandboxPlugin = (config) => {
1300
1292
  path: input.path,
1301
1293
  created: result.created
1302
1294
  };
1303
- }),
1304
- defineTool({
1305
- name: nameFor("remove_path"),
1306
- description: "Remove a file or directory from a sandbox.",
1307
- schema: {
1308
- type: "object",
1309
- properties: {
1310
- sandboxId: sandboxIdSchema,
1311
- path: pathSchema
1312
- },
1313
- required: ["path"],
1314
- additionalProperties: false
1295
+ }
1296
+ });
1297
+ const removePathTool = defineTool({
1298
+ name: nameFor("remove_path"),
1299
+ description: "Remove a file or directory from a sandbox.",
1300
+ target: "server",
1301
+ inputSchema: {
1302
+ type: "object",
1303
+ properties: {
1304
+ sandboxId: sandboxIdSchema,
1305
+ path: pathSchema
1315
1306
  },
1316
- approval: config.approvals?.removePath
1317
- }).server(async (input, ctx) => {
1307
+ required: ["path"],
1308
+ additionalProperties: false
1309
+ },
1310
+ approval: pluginConfig.approvals?.removePath,
1311
+ execute: async (input, ctx) => {
1318
1312
  const resolved = await resolveSandbox({
1319
1313
  sandboxId: input.sandboxId,
1320
1314
  createIfMissing: false,
@@ -1330,24 +1324,26 @@ const sandboxPlugin = (config) => {
1330
1324
  removed: true,
1331
1325
  path: input.path
1332
1326
  };
1333
- }),
1334
- defineTool({
1335
- name: nameFor("get_host"),
1336
- description: "Expose one sandbox port as a host URL so callers can reach an app running inside the sandbox.",
1337
- schema: {
1338
- type: "object",
1339
- properties: {
1340
- sandboxId: sandboxIdSchema,
1341
- port: {
1342
- type: "number",
1343
- minimum: 1,
1344
- maximum: 65535
1345
- }
1346
- },
1347
- required: ["port"],
1348
- additionalProperties: false
1349
- }
1350
- }).server(async (input, ctx) => {
1327
+ }
1328
+ });
1329
+ const getHostTool = defineTool({
1330
+ name: nameFor("get_host"),
1331
+ description: "Expose one sandbox port as a host URL so callers can reach an app running inside the sandbox.",
1332
+ target: "server",
1333
+ inputSchema: {
1334
+ type: "object",
1335
+ properties: {
1336
+ sandboxId: sandboxIdSchema,
1337
+ port: {
1338
+ type: "number",
1339
+ minimum: 1,
1340
+ maximum: 65535
1341
+ }
1342
+ },
1343
+ required: ["port"],
1344
+ additionalProperties: false
1345
+ },
1346
+ execute: async (input, ctx) => {
1351
1347
  const resolved = await resolveSandbox({
1352
1348
  sandboxId: input.sandboxId,
1353
1349
  createIfMissing: false,
@@ -1366,17 +1362,19 @@ const sandboxPlugin = (config) => {
1366
1362
  host,
1367
1363
  ...preview?.token !== void 0 ? { token: preview.token } : {}
1368
1364
  };
1369
- }),
1370
- defineTool({
1371
- name: nameFor("kill"),
1372
- description: "Terminate a sandbox and clear the current conversation sandbox when applicable.",
1373
- schema: {
1374
- type: "object",
1375
- properties: { sandboxId: sandboxIdSchema },
1376
- additionalProperties: false
1377
- },
1378
- approval: config.approvals?.killSandbox
1379
- }).server(async (input, ctx) => {
1365
+ }
1366
+ });
1367
+ const killTool = defineTool({
1368
+ name: nameFor("kill"),
1369
+ description: "Terminate a sandbox and clear the current thread sandbox when applicable.",
1370
+ target: "server",
1371
+ inputSchema: {
1372
+ type: "object",
1373
+ properties: { sandboxId: sandboxIdSchema },
1374
+ additionalProperties: false
1375
+ },
1376
+ approval: pluginConfig.approvals?.killSandbox,
1377
+ execute: async (input, ctx) => {
1380
1378
  const resolved = await resolveSandbox({
1381
1379
  sandboxId: input.sandboxId,
1382
1380
  createIfMissing: false,
@@ -1390,14 +1388,26 @@ const sandboxPlugin = (config) => {
1390
1388
  killed: true,
1391
1389
  ...clearedSessionKey !== void 0 ? { clearedSessionKey } : {}
1392
1390
  };
1393
- })
1391
+ }
1392
+ });
1393
+ const toAnyTool = (tool) => tool;
1394
+ const tools = [
1395
+ toAnyTool(createTool),
1396
+ toAnyTool(execTool),
1397
+ toAnyTool(readFileTool),
1398
+ toAnyTool(writeFileTool),
1399
+ toAnyTool(listFilesTool),
1400
+ toAnyTool(makeDirTool),
1401
+ toAnyTool(removePathTool),
1402
+ toAnyTool(getHostTool),
1403
+ toAnyTool(killTool)
1394
1404
  ];
1395
1405
  return {
1396
- id: config.id ?? "sandbox",
1406
+ id: pluginConfig.id ?? "sandbox",
1397
1407
  tools
1398
1408
  };
1399
1409
  };
1400
1410
 
1401
1411
  //#endregion
1402
- export { authPlugin, createDaytonaSandboxClient, createE2BSandboxClient, createMemorySandboxSessionStore, ipAllowlistPlugin, loggingPlugin, rateLimitPlugin, sandboxPlugin };
1412
+ export { createDaytonaSandboxClient, createE2BSandboxClient, createMemorySandboxSessionStore, ipAllowlist, logging, rateLimit, sandbox };
1403
1413
  //# sourceMappingURL=index.mjs.map