@claxedo/sandbox-manager 0.10.1 → 0.10.3

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 (42) hide show
  1. package/README.md +100 -30
  2. package/dist/checkpoint-manager.mjs +19 -0
  3. package/dist/contract.d.ts +40 -17
  4. package/dist/driver-billing.d.ts +23 -0
  5. package/dist/driver-catalog.mjs +57 -3
  6. package/dist/driver-ensure-input.d.ts +22 -0
  7. package/dist/drivers/agent37-client.d.ts +87 -0
  8. package/dist/drivers/agent37.d.ts +25 -0
  9. package/dist/drivers/agent37.mjs +888 -0
  10. package/dist/drivers/boat-client.d.ts +4 -4
  11. package/dist/drivers/boat-machine.d.ts +80 -0
  12. package/dist/drivers/boat-preparation.test-support.d.ts +2 -0
  13. package/dist/drivers/boat-template.mjs +196 -92
  14. package/dist/drivers/boat.d.ts +0 -17
  15. package/dist/drivers/boat.mjs +200 -108
  16. package/dist/drivers/cloudflare.d.ts +5 -9
  17. package/dist/drivers/cloudflare.mjs +71 -7
  18. package/dist/drivers/docker.mjs +57 -2
  19. package/dist/drivers/local-brokering.d.ts +12 -0
  20. package/dist/drivers/local-brokering.mjs +99 -24
  21. package/dist/drivers/modal.mjs +57 -2
  22. package/dist/drivers/transient-error.d.ts +0 -1
  23. package/dist/drivers/vercel.mjs +61 -3
  24. package/dist/egress-policy.d.ts +33 -32
  25. package/dist/egress-warnings.d.ts +4 -6
  26. package/dist/hosted-network-policy.d.ts +10 -64
  27. package/dist/hosted-network-policy.mjs +1 -1
  28. package/dist/image-name.d.ts +0 -1
  29. package/dist/image-name.mjs +5 -0
  30. package/dist/image.d.ts +1 -0
  31. package/dist/image.mjs +6 -0
  32. package/dist/index.mjs +180 -112
  33. package/dist/json.d.ts +1 -10
  34. package/dist/lease-failure.d.ts +20 -1
  35. package/dist/lease-types.d.ts +1 -10
  36. package/dist/runtime-env.d.ts +6 -0
  37. package/dist/runtime-env.mjs +5 -0
  38. package/dist/runtime-version.d.ts +25 -7
  39. package/dist/runtime-version.mjs +13 -2
  40. package/dist/start-admission.d.ts +32 -0
  41. package/dist/start-telemetry.d.ts +5 -3
  42. package/package.json +12 -4
@@ -12,6 +12,27 @@ var boatBilling = {
12
12
  large: { size: "large", vcpu: 8, memoryGb: 16, usdPerHour: 0.072 }
13
13
  }
14
14
  };
15
+ var AGENT37_USD_PER_MONTH = { vcpu: 0.8, memoryGb: 0.7, diskGb: 0.09 };
16
+ var AGENT37_MONTH_HOURS = 730;
17
+ var agent37MachineShapes = {
18
+ small: { cpu: 2, memory: 4, disk: 10 },
19
+ default: { cpu: 4, memory: 8, disk: 20 },
20
+ large: { cpu: 4, memory: 16, disk: 40 }
21
+ };
22
+ function agent37Rate(shape) {
23
+ const usdPerMonth = shape.cpu * AGENT37_USD_PER_MONTH.vcpu + shape.memory * AGENT37_USD_PER_MONTH.memoryGb + shape.disk * AGENT37_USD_PER_MONTH.diskGb;
24
+ return { size: `${shape.cpu} vCPU / ${shape.memory} GB`, vcpu: shape.cpu, memoryGb: shape.memory, usdPerHour: usdPerMonth / AGENT37_MONTH_HOURS };
25
+ }
26
+ var agent37Billing = {
27
+ source: { url: "https://www.agent37.com/docs/agents-api/billing", readOn: "2026-11-11" },
28
+ meter: "Agent37 meters an instance per minute at a monthly rate over 730 hours: $0.80 per vCPU, $0.70 per GB of memory and $0.09 per GB of disk while it runs, and the disk alone while it is stopped.",
29
+ rates: {
30
+ small: agent37Rate(agent37MachineShapes.small),
31
+ default: agent37Rate(agent37MachineShapes.default),
32
+ large: agent37Rate(agent37MachineShapes.large)
33
+ },
34
+ excludes: `the disk of a stopped instance, billed at $${AGENT37_USD_PER_MONTH.diskGb} per GB a month until the instance is deleted`
35
+ };
15
36
  var CLOUDFLARE_USD_PER_GIB_SECOND_MEMORY = 25e-7;
16
37
  var CLOUDFLARE_USD_PER_GB_SECOND_DISK = 7e-8;
17
38
  var CLOUDFLARE_USD_PER_VCPU_SECOND_ACTIVE = 2e-5;
@@ -31,7 +52,7 @@ var cloudflareBilling = {
31
52
  };
32
53
 
33
54
  // ../sandbox-contract/dist/index.mjs
34
- var sandboxCheckpointUnusableCauses = ["expired", "runtime_outdated", "runtime_modified", "not_restorable"];
55
+ var sandboxCheckpointUnusableCauses = ["expired", "runtime_modified", "not_restorable"];
35
56
  function isSandboxCheckpointUnusableCause(value) {
36
57
  return sandboxCheckpointUnusableCauses.some((cause) => cause === value);
37
58
  }
@@ -50,6 +71,7 @@ var sandboxDriverCredentialFields = {
50
71
  { key: "worker_url", label: "Worker URL" }
51
72
  ],
52
73
  boat: [{ key: "api_key", label: "API Key", secret: true }],
74
+ agent37: [{ key: "api_key", label: "API Key", secret: true }],
53
75
  docker: [{ key: "image", label: "Image" }]
54
76
  };
55
77
  var sandboxDriverLabels = {
@@ -57,6 +79,7 @@ var sandboxDriverLabels = {
57
79
  vercel: "Vercel",
58
80
  cloudflare: "Cloudflare",
59
81
  boat: "Boat",
82
+ agent37: "Agent37",
60
83
  docker: "Docker"
61
84
  };
62
85
  function cloudflareWorkerBaseUrl(input) {
@@ -139,8 +162,8 @@ var sandboxDriverCatalog = {
139
162
  hostWatchesIdle: true,
140
163
  targetAccess: "relay",
141
164
  secretBrokering: "native",
142
- // Credential handlers do not restrict unrelated destinations.
143
- egressControl: "none",
165
+ // The sandbox Worker starts the container without internet and routes each allowed host through itself; a Worker that cannot confirm that refuses the start.
166
+ egressControl: "hosts",
144
167
  // A container snapshot holds the whole root filesystem; the platform keeps it 30 days past its last restore and offers no deletion.
145
168
  persistence: {
146
169
  resume: "replacement-restore",
@@ -178,6 +201,33 @@ var sandboxDriverCatalog = {
178
201
  machineClasses: ["small", "default", "large"]
179
202
  }
180
203
  },
204
+ agent37: {
205
+ id: "agent37",
206
+ label: sandboxDriverLabels.agent37,
207
+ credentialFields: sandboxDriverCredentialFields.agent37,
208
+ runtimeNetwork: { controlPlane: "direct", relay: "configured" },
209
+ runtimeEnv: "callback",
210
+ preparedImage: true,
211
+ billing: agent37Billing,
212
+ metadata: {
213
+ driverRunsIn: ["worker", "node"],
214
+ hostStopBehavior: "suspends-host",
215
+ hostResumeBehavior: "same-host",
216
+ targetAccess: "relay",
217
+ secretBrokering: "none",
218
+ // An instance's network policy lists domains, reached over HTTPS on 443 alone, and IPv4 ranges; private ranges stay blocked whatever it lists.
219
+ egressControl: "hosts-and-cidrs",
220
+ persistence: {
221
+ resume: "same-sandbox",
222
+ capture: "none",
223
+ clone: false,
224
+ captureSource: "not-applicable",
225
+ retention: "provider-managed",
226
+ restoreMount: "same-resource"
227
+ },
228
+ machineClasses: ["small", "default", "large"]
229
+ }
230
+ },
181
231
  docker: {
182
232
  id: "docker",
183
233
  label: sandboxDriverLabels.docker,
@@ -311,7 +361,11 @@ function workspaceRuntimeSourceEnv(input) {
311
361
  }
312
362
 
313
363
  // src/runtime-version.ts
314
- var MINIMUM_RESTORABLE_WORKSPACE_RUNTIME_VERSION = "0.5.2";
364
+ var SERVED_WORKSPACE_RUNTIME_FLOOR = {
365
+ version: "0.10.1",
366
+ image: "ghcr.io/kyashrathore/claxedo-sandbox:workspace-runtime-0-10-1-40706b7356-agent-plugins-v8"
367
+ };
368
+ var MINIMUM_SERVED_WORKSPACE_RUNTIME_VERSION = SERVED_WORKSPACE_RUNTIME_FLOOR.version;
315
369
 
316
370
  // src/drivers/cloudflare.ts
317
371
  function registryEntries(input) {
@@ -437,10 +491,15 @@ function createCloudflareSandboxDriver(options) {
437
491
  };
438
492
  });
439
493
  }
494
+ function networkPolicy(input) {
495
+ if (!input.net || input.net.mode === "allow-all") return void 0;
496
+ return { hosts: [...new Set(input.net.hosts ?? [])] };
497
+ }
440
498
  async function ensureHost(input) {
441
499
  const sandboxId = sandboxIdFor(input.workspaceId);
442
500
  const hostId = sandboxId;
443
501
  const egress = egressRegistrations(input);
502
+ const net = networkPolicy(input);
444
503
  const response = await call(
445
504
  sandboxId,
446
505
  "ensure-runtime",
@@ -452,8 +511,9 @@ function createCloudflareSandboxDriver(options) {
452
511
  // what makes this driver's `list()` (and therefore GC) possible at all.
453
512
  // Not container env — labels are control-plane metadata.
454
513
  labels: input.labels,
455
- ...input.bootSource?.kind === "driver-snapshot" ? { restore: { snapshotId: input.bootSource.snapshotId }, minimumRuntimeVersion: MINIMUM_RESTORABLE_WORKSPACE_RUNTIME_VERSION } : {},
456
- ...egress ? { egress } : {}
514
+ ...input.bootSource?.kind === "driver-snapshot" ? { restore: { snapshotId: input.bootSource.snapshotId }, minimumRuntimeVersion: MINIMUM_SERVED_WORKSPACE_RUNTIME_VERSION } : {},
515
+ ...egress ? { egress } : {},
516
+ ...net ? { net } : {}
457
517
  }
458
518
  ).catch((error) => {
459
519
  if (error instanceof Error && error.name === "TimeoutError") return void 0;
@@ -473,6 +533,10 @@ function createCloudflareSandboxDriver(options) {
473
533
  const reason = `Cloudflare ensure-runtime failed (${status}): ${text(data.error) ?? "no runtime url"}`;
474
534
  throw status === 401 || status === 403 ? new SandboxProviderKeyRefusedError(reason) : new Error(reason);
475
535
  }
536
+ if (net && asRecord(data.egress)?.contained !== true) {
537
+ await call(sandboxId, "stop", { epoch: input.epoch }).catch(() => void 0);
538
+ throw new Error("Cloudflare sandbox Worker did not confirm egress containment; deploy the current sandbox Worker (scripts/sandbox/cloudflare-worker)");
539
+ }
476
540
  const targetOut = {
477
541
  workspaceId: input.workspaceId,
478
542
  sandboxId,
@@ -558,7 +622,7 @@ function createCloudflareSandboxDriver(options) {
558
622
  hostWatchesIdle: true,
559
623
  targetAccess: "relay",
560
624
  secretBrokering: "native",
561
- egressControl: "none",
625
+ egressControl: "hosts",
562
626
  persistence: sandboxDriverCatalog.cloudflare.metadata.persistence
563
627
  },
564
628
  ensureHost,
@@ -91,6 +91,11 @@ var DEFAULT_WORKSPACE_RUNTIME_VERSION = "0.5.2";
91
91
  function workspaceRuntimeVersion() {
92
92
  return process.env.WORKSPACE_RUNTIME_VERSION || DEFAULT_WORKSPACE_RUNTIME_VERSION;
93
93
  }
94
+ var SERVED_WORKSPACE_RUNTIME_FLOOR = {
95
+ version: "0.10.1",
96
+ image: "ghcr.io/kyashrathore/claxedo-sandbox:workspace-runtime-0-10-1-40706b7356-agent-plugins-v8"
97
+ };
98
+ var MINIMUM_SERVED_WORKSPACE_RUNTIME_VERSION = SERVED_WORKSPACE_RUNTIME_FLOOR.version;
94
99
 
95
100
  // src/image-name.ts
96
101
  var SNAPSHOT_SCHEMA_VERSION = 8;
@@ -133,6 +138,27 @@ var boatBilling = {
133
138
  large: { size: "large", vcpu: 8, memoryGb: 16, usdPerHour: 0.072 }
134
139
  }
135
140
  };
141
+ var AGENT37_USD_PER_MONTH = { vcpu: 0.8, memoryGb: 0.7, diskGb: 0.09 };
142
+ var AGENT37_MONTH_HOURS = 730;
143
+ var agent37MachineShapes = {
144
+ small: { cpu: 2, memory: 4, disk: 10 },
145
+ default: { cpu: 4, memory: 8, disk: 20 },
146
+ large: { cpu: 4, memory: 16, disk: 40 }
147
+ };
148
+ function agent37Rate(shape) {
149
+ const usdPerMonth = shape.cpu * AGENT37_USD_PER_MONTH.vcpu + shape.memory * AGENT37_USD_PER_MONTH.memoryGb + shape.disk * AGENT37_USD_PER_MONTH.diskGb;
150
+ return { size: `${shape.cpu} vCPU / ${shape.memory} GB`, vcpu: shape.cpu, memoryGb: shape.memory, usdPerHour: usdPerMonth / AGENT37_MONTH_HOURS };
151
+ }
152
+ var agent37Billing = {
153
+ source: { url: "https://www.agent37.com/docs/agents-api/billing", readOn: "2026-11-11" },
154
+ meter: "Agent37 meters an instance per minute at a monthly rate over 730 hours: $0.80 per vCPU, $0.70 per GB of memory and $0.09 per GB of disk while it runs, and the disk alone while it is stopped.",
155
+ rates: {
156
+ small: agent37Rate(agent37MachineShapes.small),
157
+ default: agent37Rate(agent37MachineShapes.default),
158
+ large: agent37Rate(agent37MachineShapes.large)
159
+ },
160
+ excludes: `the disk of a stopped instance, billed at $${AGENT37_USD_PER_MONTH.diskGb} per GB a month until the instance is deleted`
161
+ };
136
162
  var CLOUDFLARE_USD_PER_GIB_SECOND_MEMORY = 25e-7;
137
163
  var CLOUDFLARE_USD_PER_GB_SECOND_DISK = 7e-8;
138
164
  var CLOUDFLARE_USD_PER_VCPU_SECOND_ACTIVE = 2e-5;
@@ -167,6 +193,7 @@ var sandboxDriverCredentialFields = {
167
193
  { key: "worker_url", label: "Worker URL" }
168
194
  ],
169
195
  boat: [{ key: "api_key", label: "API Key", secret: true }],
196
+ agent37: [{ key: "api_key", label: "API Key", secret: true }],
170
197
  docker: [{ key: "image", label: "Image" }]
171
198
  };
172
199
  var sandboxDriverLabels = {
@@ -174,6 +201,7 @@ var sandboxDriverLabels = {
174
201
  vercel: "Vercel",
175
202
  cloudflare: "Cloudflare",
176
203
  boat: "Boat",
204
+ agent37: "Agent37",
177
205
  docker: "Docker"
178
206
  };
179
207
 
@@ -244,8 +272,8 @@ var sandboxDriverCatalog = {
244
272
  hostWatchesIdle: true,
245
273
  targetAccess: "relay",
246
274
  secretBrokering: "native",
247
- // Credential handlers do not restrict unrelated destinations.
248
- egressControl: "none",
275
+ // The sandbox Worker starts the container without internet and routes each allowed host through itself; a Worker that cannot confirm that refuses the start.
276
+ egressControl: "hosts",
249
277
  // A container snapshot holds the whole root filesystem; the platform keeps it 30 days past its last restore and offers no deletion.
250
278
  persistence: {
251
279
  resume: "replacement-restore",
@@ -283,6 +311,33 @@ var sandboxDriverCatalog = {
283
311
  machineClasses: ["small", "default", "large"]
284
312
  }
285
313
  },
314
+ agent37: {
315
+ id: "agent37",
316
+ label: sandboxDriverLabels.agent37,
317
+ credentialFields: sandboxDriverCredentialFields.agent37,
318
+ runtimeNetwork: { controlPlane: "direct", relay: "configured" },
319
+ runtimeEnv: "callback",
320
+ preparedImage: true,
321
+ billing: agent37Billing,
322
+ metadata: {
323
+ driverRunsIn: ["worker", "node"],
324
+ hostStopBehavior: "suspends-host",
325
+ hostResumeBehavior: "same-host",
326
+ targetAccess: "relay",
327
+ secretBrokering: "none",
328
+ // An instance's network policy lists domains, reached over HTTPS on 443 alone, and IPv4 ranges; private ranges stay blocked whatever it lists.
329
+ egressControl: "hosts-and-cidrs",
330
+ persistence: {
331
+ resume: "same-sandbox",
332
+ capture: "none",
333
+ clone: false,
334
+ captureSource: "not-applicable",
335
+ retention: "provider-managed",
336
+ restoreMount: "same-resource"
337
+ },
338
+ machineClasses: ["small", "default", "large"]
339
+ }
340
+ },
286
341
  docker: {
287
342
  id: "docker",
288
343
  label: sandboxDriverLabels.docker,
@@ -1,9 +1,21 @@
1
1
  import type { SandboxDriver } from "../contract";
2
2
  import { localBrokeringTestDriverCatalogEntry } from "../driver-catalog";
3
+ /**
4
+ * A published runtime image to run instead of `executable`: Docker on the host
5
+ * network, with the paths the runtime reads mounted where they are, and no
6
+ * sandbox-exec network policy, so it runs on Linux. The version-skew suite
7
+ * runs the control plane against the oldest image it still serves this way.
8
+ */
9
+ export type LocalBrokeringContainer = {
10
+ image: string;
11
+ command: string;
12
+ mounts: readonly string[];
13
+ };
3
14
  export type LocalBrokeringDriverOptions = {
4
15
  root: string;
5
16
  executable: string;
6
17
  args: string[];
18
+ container?: LocalBrokeringContainer;
7
19
  allowedOrigins: readonly string[];
8
20
  directOrigins: readonly string[];
9
21
  upstreams?: Readonly<Record<string, string>>;
@@ -15,11 +15,6 @@ function asRecord(value) {
15
15
  return isRecord(value) ? value : void 0;
16
16
  }
17
17
  var CLAXEDO_TOOL_NAMES = [
18
- "task_list",
19
- "task_get",
20
- "task_create",
21
- "task_edit",
22
- "task_start",
23
18
  "session_create",
24
19
  "sessions_list",
25
20
  "session_get",
@@ -30,8 +25,6 @@ var CLAXEDO_TOOL_NAMES = [
30
25
  "session_rename",
31
26
  "session_delete",
32
27
  "session_changes",
33
- "documents_list",
34
- "documents_open",
35
28
  "subagent_capabilities",
36
29
  "create_subagent",
37
30
  "subagent_status",
@@ -157,17 +150,17 @@ var AGENT_PRESENTATION_EVENT_TYPES = Object.keys(AGENT_PRESENTATION_EVENT_TYPE_R
157
150
  var mode = (id, name, description, level) => ({
158
151
  id,
159
152
  name,
160
- description,
153
+ ...description ? { description } : {},
161
154
  ...level ? { level } : {}
162
155
  });
163
156
  var CLAUDE_PERMISSION_MODES = {
164
157
  modes: [
165
- mode("default", "Default", "Standard behavior, prompts for dangerous operations", "ask"),
166
- mode("acceptEdits", "Accept edits", "Auto-accept file edit operations"),
167
- mode("auto", "Auto", "Use a model classifier to approve/deny permission prompts", "auto"),
168
- mode("plan", "Plan", "Planning mode, no actual tool execution"),
169
- mode("dontAsk", "Don't ask", "Don't prompt for permissions, deny if not pre-approved"),
170
- mode("bypassPermissions", "Bypass permissions", "Bypass all permission checks", "full")
158
+ mode("default", "Default", "Asks before dangerous operations", "ask"),
159
+ mode("acceptEdits", "Accept edits"),
160
+ mode("auto", "Auto", "A model decides which actions to allow", "auto"),
161
+ mode("plan", "Plan", "Runs no tools"),
162
+ mode("dontAsk", "Don't ask", "Denies anything not pre-approved"),
163
+ mode("bypassPermissions", "Bypass permissions", "Runs everything without asking", "full")
171
164
  ],
172
165
  appliesFrom: "immediate",
173
166
  defaultModeId: "auto"
@@ -180,9 +173,18 @@ var CURSOR_PERMISSION_MODES = {
180
173
  ],
181
174
  appliesFrom: "next-turn"
182
175
  };
176
+ var ANTIGRAVITY_PERMISSION_MODES = {
177
+ modes: [
178
+ mode("default", "Default", "Ask before edits and commands, per your permission settings.", "ask"),
179
+ mode("accept-edits", "Accept edits", "Approve file edits in the workspace without asking."),
180
+ mode("plan", "Plan")
181
+ ],
182
+ appliesFrom: "immediate",
183
+ defaultModeId: "default"
184
+ };
183
185
  var CODEX_PERMISSION_MODES = {
184
186
  modes: [
185
- mode("read-only", "Read only", "Never asks; the sandbox permits reads only, so writes fail", "ask"),
187
+ mode("read-only", "Read only", "Never asks; writes fail", "ask"),
186
188
  mode("workspace-write", "Workspace write", "Runs commands inside the workspace without asking; escalates outside it", "auto"),
187
189
  mode("untrusted", "Untrusted", "Only trusted commands run without asking; anything else escalates"),
188
190
  mode("full-access", "Full access", "Never asks; full filesystem and network access", "full")
@@ -226,6 +228,15 @@ var AGENT_HARNESS_DEFINITIONS = [
226
228
  authSlot: "cursor",
227
229
  cloudHost: "workspace-runtime"
228
230
  },
231
+ {
232
+ key: "antigravity",
233
+ id: "antigravity",
234
+ access: "native",
235
+ label: "Antigravity",
236
+ authEnv: "GEMINI_API_KEY",
237
+ authSlot: "google",
238
+ cloudHost: "workspace-runtime"
239
+ },
229
240
  {
230
241
  key: "pi",
231
242
  id: "pi",
@@ -246,7 +257,7 @@ var AGENT_HARNESS_DEFINITIONS = [
246
257
  }
247
258
  ];
248
259
  var AGENT_HARNESS_KEYS = AGENT_HARNESS_DEFINITIONS.map((item) => item.key);
249
- var HARNESS_IDS = ["claude", "codex", "cursor"];
260
+ var HARNESS_IDS = ["claude", "codex", "cursor", "antigravity"];
250
261
  var HARNESS_TABLE = {
251
262
  claude: {
252
263
  label: "Claude Code",
@@ -276,6 +287,15 @@ var HARNESS_TABLE = {
276
287
  machineLoginServes: ["cursor-acp"],
277
288
  permissionModes: CURSOR_PERMISSION_MODES,
278
289
  defaultModelId: "auto"
290
+ },
291
+ antigravity: {
292
+ label: "Antigravity",
293
+ vendor: "Google",
294
+ providerIds: ["antigravity", "google"],
295
+ connectProvider: "antigravity",
296
+ vendorProvider: "google",
297
+ machineLoginServes: ["antigravity"],
298
+ permissionModes: ANTIGRAVITY_PERMISSION_MODES
279
299
  }
280
300
  };
281
301
  function harnessBindingIds(harness) {
@@ -451,6 +471,27 @@ var boatBilling = {
451
471
  large: { size: "large", vcpu: 8, memoryGb: 16, usdPerHour: 0.072 }
452
472
  }
453
473
  };
474
+ var AGENT37_USD_PER_MONTH = { vcpu: 0.8, memoryGb: 0.7, diskGb: 0.09 };
475
+ var AGENT37_MONTH_HOURS = 730;
476
+ var agent37MachineShapes = {
477
+ small: { cpu: 2, memory: 4, disk: 10 },
478
+ default: { cpu: 4, memory: 8, disk: 20 },
479
+ large: { cpu: 4, memory: 16, disk: 40 }
480
+ };
481
+ function agent37Rate(shape) {
482
+ const usdPerMonth = shape.cpu * AGENT37_USD_PER_MONTH.vcpu + shape.memory * AGENT37_USD_PER_MONTH.memoryGb + shape.disk * AGENT37_USD_PER_MONTH.diskGb;
483
+ return { size: `${shape.cpu} vCPU / ${shape.memory} GB`, vcpu: shape.cpu, memoryGb: shape.memory, usdPerHour: usdPerMonth / AGENT37_MONTH_HOURS };
484
+ }
485
+ var agent37Billing = {
486
+ source: { url: "https://www.agent37.com/docs/agents-api/billing", readOn: "2026-11-11" },
487
+ meter: "Agent37 meters an instance per minute at a monthly rate over 730 hours: $0.80 per vCPU, $0.70 per GB of memory and $0.09 per GB of disk while it runs, and the disk alone while it is stopped.",
488
+ rates: {
489
+ small: agent37Rate(agent37MachineShapes.small),
490
+ default: agent37Rate(agent37MachineShapes.default),
491
+ large: agent37Rate(agent37MachineShapes.large)
492
+ },
493
+ excludes: `the disk of a stopped instance, billed at $${AGENT37_USD_PER_MONTH.diskGb} per GB a month until the instance is deleted`
494
+ };
454
495
  var CLOUDFLARE_USD_PER_GIB_SECOND_MEMORY = 25e-7;
455
496
  var CLOUDFLARE_USD_PER_GB_SECOND_DISK = 7e-8;
456
497
  var CLOUDFLARE_USD_PER_VCPU_SECOND_ACTIVE = 2e-5;
@@ -485,6 +526,7 @@ var sandboxDriverCredentialFields = {
485
526
  { key: "worker_url", label: "Worker URL" }
486
527
  ],
487
528
  boat: [{ key: "api_key", label: "API Key", secret: true }],
529
+ agent37: [{ key: "api_key", label: "API Key", secret: true }],
488
530
  docker: [{ key: "image", label: "Image" }]
489
531
  };
490
532
  var sandboxDriverLabels = {
@@ -492,6 +534,7 @@ var sandboxDriverLabels = {
492
534
  vercel: "Vercel",
493
535
  cloudflare: "Cloudflare",
494
536
  boat: "Boat",
537
+ agent37: "Agent37",
495
538
  docker: "Docker"
496
539
  };
497
540
 
@@ -586,8 +629,8 @@ var sandboxDriverCatalog = {
586
629
  hostWatchesIdle: true,
587
630
  targetAccess: "relay",
588
631
  secretBrokering: "native",
589
- // Credential handlers do not restrict unrelated destinations.
590
- egressControl: "none",
632
+ // The sandbox Worker starts the container without internet and routes each allowed host through itself; a Worker that cannot confirm that refuses the start.
633
+ egressControl: "hosts",
591
634
  // A container snapshot holds the whole root filesystem; the platform keeps it 30 days past its last restore and offers no deletion.
592
635
  persistence: {
593
636
  resume: "replacement-restore",
@@ -625,6 +668,33 @@ var sandboxDriverCatalog = {
625
668
  machineClasses: ["small", "default", "large"]
626
669
  }
627
670
  },
671
+ agent37: {
672
+ id: "agent37",
673
+ label: sandboxDriverLabels.agent37,
674
+ credentialFields: sandboxDriverCredentialFields.agent37,
675
+ runtimeNetwork: { controlPlane: "direct", relay: "configured" },
676
+ runtimeEnv: "callback",
677
+ preparedImage: true,
678
+ billing: agent37Billing,
679
+ metadata: {
680
+ driverRunsIn: ["worker", "node"],
681
+ hostStopBehavior: "suspends-host",
682
+ hostResumeBehavior: "same-host",
683
+ targetAccess: "relay",
684
+ secretBrokering: "none",
685
+ // An instance's network policy lists domains, reached over HTTPS on 443 alone, and IPv4 ranges; private ranges stay blocked whatever it lists.
686
+ egressControl: "hosts-and-cidrs",
687
+ persistence: {
688
+ resume: "same-sandbox",
689
+ capture: "none",
690
+ clone: false,
691
+ captureSource: "not-applicable",
692
+ retention: "provider-managed",
693
+ restoreMount: "same-resource"
694
+ },
695
+ machineClasses: ["small", "default", "large"]
696
+ }
697
+ },
628
698
  docker: {
629
699
  id: "docker",
630
700
  label: sandboxDriverLabels.docker,
@@ -793,6 +863,7 @@ ${lines}` : ""}`);
793
863
  }
794
864
  async function closeHost(host) {
795
865
  const child = host.child;
866
+ if (host.container) execFileSync("docker", ["rm", "-f", host.container], { stdio: "ignore" });
796
867
  if (child && child.exitCode === null) {
797
868
  child.kill("SIGTERM");
798
869
  await new Promise((resolve) => child.once("exit", () => resolve()));
@@ -802,8 +873,14 @@ async function closeHost(host) {
802
873
  await new Promise((resolve) => host.proxy.close(() => resolve()));
803
874
  await fs.rm(host.directory, { recursive: true, force: true });
804
875
  }
876
+ function containerRun(container, name, input) {
877
+ const user = `${process.getuid?.() ?? 0}:${process.getgid?.() ?? 0}`;
878
+ const mounts = [.../* @__PURE__ */ new Set([input.root, ...container.mounts])].flatMap((mount) => ["-v", `${mount}:${mount}`]);
879
+ const env = Object.keys(input.env).filter((key) => key !== "PATH").flatMap((key) => ["-e", key]);
880
+ return { name, args: ["run", "--rm", "--name", name, "--network", "host", "--user", user, ...mounts, ...env, "-w", input.workspace, "--entrypoint", container.command, container.image] };
881
+ }
805
882
  function createLocalBrokeringSandboxDriver(options) {
806
- if (process.platform !== "darwin") throw new Error("local brokering test driver requires macOS sandbox-exec network policy");
883
+ if (process.platform !== "darwin" && !options.container) throw new Error("local brokering test driver requires macOS sandbox-exec network policy, or a runtime container");
807
884
  const authority = createTestAuthority(options.root);
808
885
  const hosts = /* @__PURE__ */ new Map();
809
886
  const allowedOrigins = new Set(options.allowedOrigins.map((origin) => new URL(origin).origin));
@@ -976,11 +1053,9 @@ function createLocalBrokeringSandboxDriver(options) {
976
1053
  const log = openSync(logFile, "w", 384);
977
1054
  let child;
978
1055
  try {
979
- child = spawn("/usr/bin/sandbox-exec", ["-p", networkPolicy, options.executable, ...options.args], {
980
- cwd: workspace,
981
- env,
982
- stdio: ["ignore", log, log]
983
- });
1056
+ const container = options.container ? containerRun(options.container, `claxedo-local-${target.sandboxId}`, { root: options.root, workspace, env }) : void 0;
1057
+ if (container) host.container = container.name;
1058
+ child = container ? spawn("docker", container.args, { env: { ...process.env, ...env }, stdio: ["ignore", log, log] }) : spawn("/usr/bin/sandbox-exec", ["-p", networkPolicy, options.executable, ...options.args], { cwd: workspace, env, stdio: ["ignore", log, log] });
984
1059
  } finally {
985
1060
  closeSync(log);
986
1061
  }
@@ -73,6 +73,11 @@ var DEFAULT_WORKSPACE_RUNTIME_VERSION = "0.5.2";
73
73
  function workspaceRuntimeVersion() {
74
74
  return process.env.WORKSPACE_RUNTIME_VERSION || DEFAULT_WORKSPACE_RUNTIME_VERSION;
75
75
  }
76
+ var SERVED_WORKSPACE_RUNTIME_FLOOR = {
77
+ version: "0.10.1",
78
+ image: "ghcr.io/kyashrathore/claxedo-sandbox:workspace-runtime-0-10-1-40706b7356-agent-plugins-v8"
79
+ };
80
+ var MINIMUM_SERVED_WORKSPACE_RUNTIME_VERSION = SERVED_WORKSPACE_RUNTIME_FLOOR.version;
76
81
 
77
82
  // src/image-name.ts
78
83
  var SNAPSHOT_SCHEMA_VERSION = 8;
@@ -109,6 +114,27 @@ var boatBilling = {
109
114
  large: { size: "large", vcpu: 8, memoryGb: 16, usdPerHour: 0.072 }
110
115
  }
111
116
  };
117
+ var AGENT37_USD_PER_MONTH = { vcpu: 0.8, memoryGb: 0.7, diskGb: 0.09 };
118
+ var AGENT37_MONTH_HOURS = 730;
119
+ var agent37MachineShapes = {
120
+ small: { cpu: 2, memory: 4, disk: 10 },
121
+ default: { cpu: 4, memory: 8, disk: 20 },
122
+ large: { cpu: 4, memory: 16, disk: 40 }
123
+ };
124
+ function agent37Rate(shape) {
125
+ const usdPerMonth = shape.cpu * AGENT37_USD_PER_MONTH.vcpu + shape.memory * AGENT37_USD_PER_MONTH.memoryGb + shape.disk * AGENT37_USD_PER_MONTH.diskGb;
126
+ return { size: `${shape.cpu} vCPU / ${shape.memory} GB`, vcpu: shape.cpu, memoryGb: shape.memory, usdPerHour: usdPerMonth / AGENT37_MONTH_HOURS };
127
+ }
128
+ var agent37Billing = {
129
+ source: { url: "https://www.agent37.com/docs/agents-api/billing", readOn: "2026-11-11" },
130
+ meter: "Agent37 meters an instance per minute at a monthly rate over 730 hours: $0.80 per vCPU, $0.70 per GB of memory and $0.09 per GB of disk while it runs, and the disk alone while it is stopped.",
131
+ rates: {
132
+ small: agent37Rate(agent37MachineShapes.small),
133
+ default: agent37Rate(agent37MachineShapes.default),
134
+ large: agent37Rate(agent37MachineShapes.large)
135
+ },
136
+ excludes: `the disk of a stopped instance, billed at $${AGENT37_USD_PER_MONTH.diskGb} per GB a month until the instance is deleted`
137
+ };
112
138
  var CLOUDFLARE_USD_PER_GIB_SECOND_MEMORY = 25e-7;
113
139
  var CLOUDFLARE_USD_PER_GB_SECOND_DISK = 7e-8;
114
140
  var CLOUDFLARE_USD_PER_VCPU_SECOND_ACTIVE = 2e-5;
@@ -143,6 +169,7 @@ var sandboxDriverCredentialFields = {
143
169
  { key: "worker_url", label: "Worker URL" }
144
170
  ],
145
171
  boat: [{ key: "api_key", label: "API Key", secret: true }],
172
+ agent37: [{ key: "api_key", label: "API Key", secret: true }],
146
173
  docker: [{ key: "image", label: "Image" }]
147
174
  };
148
175
  var sandboxDriverLabels = {
@@ -150,6 +177,7 @@ var sandboxDriverLabels = {
150
177
  vercel: "Vercel",
151
178
  cloudflare: "Cloudflare",
152
179
  boat: "Boat",
180
+ agent37: "Agent37",
153
181
  docker: "Docker"
154
182
  };
155
183
 
@@ -220,8 +248,8 @@ var sandboxDriverCatalog = {
220
248
  hostWatchesIdle: true,
221
249
  targetAccess: "relay",
222
250
  secretBrokering: "native",
223
- // Credential handlers do not restrict unrelated destinations.
224
- egressControl: "none",
251
+ // The sandbox Worker starts the container without internet and routes each allowed host through itself; a Worker that cannot confirm that refuses the start.
252
+ egressControl: "hosts",
225
253
  // A container snapshot holds the whole root filesystem; the platform keeps it 30 days past its last restore and offers no deletion.
226
254
  persistence: {
227
255
  resume: "replacement-restore",
@@ -259,6 +287,33 @@ var sandboxDriverCatalog = {
259
287
  machineClasses: ["small", "default", "large"]
260
288
  }
261
289
  },
290
+ agent37: {
291
+ id: "agent37",
292
+ label: sandboxDriverLabels.agent37,
293
+ credentialFields: sandboxDriverCredentialFields.agent37,
294
+ runtimeNetwork: { controlPlane: "direct", relay: "configured" },
295
+ runtimeEnv: "callback",
296
+ preparedImage: true,
297
+ billing: agent37Billing,
298
+ metadata: {
299
+ driverRunsIn: ["worker", "node"],
300
+ hostStopBehavior: "suspends-host",
301
+ hostResumeBehavior: "same-host",
302
+ targetAccess: "relay",
303
+ secretBrokering: "none",
304
+ // An instance's network policy lists domains, reached over HTTPS on 443 alone, and IPv4 ranges; private ranges stay blocked whatever it lists.
305
+ egressControl: "hosts-and-cidrs",
306
+ persistence: {
307
+ resume: "same-sandbox",
308
+ capture: "none",
309
+ clone: false,
310
+ captureSource: "not-applicable",
311
+ retention: "provider-managed",
312
+ restoreMount: "same-resource"
313
+ },
314
+ machineClasses: ["small", "default", "large"]
315
+ }
316
+ },
262
317
  docker: {
263
318
  id: "docker",
264
319
  label: sandboxDriverLabels.docker,
@@ -1,4 +1,3 @@
1
- /** The status and lower-cased text a driver error carries, if it carries any. */
2
1
  export declare function driverErrorSignals(err: unknown): {
3
2
  status: number | undefined;
4
3
  text: string;