@claxedo/sandbox-manager 0.10.2 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -29
- package/dist/checkpoint-manager.mjs +11 -0
- package/dist/contract.d.ts +28 -16
- package/dist/driver-billing.d.ts +23 -0
- package/dist/driver-catalog.d.ts +1 -2
- package/dist/driver-catalog.mjs +61 -11
- package/dist/driver-ensure-input.d.ts +3 -2
- package/dist/drivers/agent37-client.d.ts +87 -0
- package/dist/drivers/agent37.d.ts +25 -0
- package/dist/drivers/agent37.mjs +886 -0
- package/dist/drivers/boat-client.d.ts +5 -5
- package/dist/drivers/boat-machine.d.ts +7 -17
- package/dist/drivers/boat-template.mjs +87 -35
- package/dist/drivers/boat.d.ts +0 -5
- package/dist/drivers/boat.mjs +81 -26
- package/dist/drivers/cloudflare.d.ts +5 -9
- package/dist/drivers/cloudflare.mjs +97 -43
- package/dist/drivers/docker.mjs +70 -19
- package/dist/drivers/local-brokering.mjs +90 -18
- package/dist/drivers/modal.mjs +56 -11
- package/dist/drivers/transient-error.d.ts +0 -1
- package/dist/drivers/vercel.mjs +65 -15
- package/dist/egress-policy.d.ts +33 -32
- package/dist/egress-warnings.d.ts +4 -6
- package/dist/hosted-network-policy.d.ts +10 -64
- package/dist/hosted-network-policy.mjs +1 -1
- package/dist/image-name.d.ts +0 -1
- package/dist/index.mjs +103 -78
- package/dist/json.d.ts +1 -13
- package/dist/lease-failure.d.ts +3 -3
- package/dist/lease-types.d.ts +1 -10
- package/dist/runtime-env.d.ts +6 -7
- package/dist/runtime-env.mjs +5 -10
- package/dist/runtime-version.d.ts +4 -0
- package/dist/runtime-version.mjs +6 -0
- package/dist/start-admission.d.ts +32 -0
- package/package.json +12 -4
|
@@ -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",
|
|
@@ -55,6 +48,8 @@ var CLAXEDO_TOOL_NAMES = [
|
|
|
55
48
|
"ui_guide"
|
|
56
49
|
];
|
|
57
50
|
var CLAXEDO_TOOL_NAME_SET = new Set(CLAXEDO_TOOL_NAMES);
|
|
51
|
+
var PROMPT_ATTACHMENT_MAX_BYTES = 32 * 1024 * 1024;
|
|
52
|
+
var PROMPT_ATTACHMENT_UPLOAD_MAX_BODY_BYTES = Math.ceil(PROMPT_ATTACHMENT_MAX_BYTES / 3) * 4 + 64 * 1024;
|
|
58
53
|
var CREDENTIAL_BROKER_ERRORS = {
|
|
59
54
|
binding_route_required: {
|
|
60
55
|
fault: "harness",
|
|
@@ -157,17 +152,17 @@ var AGENT_PRESENTATION_EVENT_TYPES = Object.keys(AGENT_PRESENTATION_EVENT_TYPE_R
|
|
|
157
152
|
var mode = (id, name, description, level) => ({
|
|
158
153
|
id,
|
|
159
154
|
name,
|
|
160
|
-
description,
|
|
155
|
+
...description ? { description } : {},
|
|
161
156
|
...level ? { level } : {}
|
|
162
157
|
});
|
|
163
158
|
var CLAUDE_PERMISSION_MODES = {
|
|
164
159
|
modes: [
|
|
165
|
-
mode("default", "Default", "
|
|
166
|
-
mode("acceptEdits", "Accept edits"
|
|
167
|
-
mode("auto", "Auto", "
|
|
168
|
-
mode("plan", "Plan", "
|
|
169
|
-
mode("dontAsk", "Don't ask", "
|
|
170
|
-
mode("bypassPermissions", "Bypass permissions", "
|
|
160
|
+
mode("default", "Default", "Asks before dangerous operations", "ask"),
|
|
161
|
+
mode("acceptEdits", "Accept edits"),
|
|
162
|
+
mode("auto", "Auto", "A model decides which actions to allow", "auto"),
|
|
163
|
+
mode("plan", "Plan", "Runs no tools"),
|
|
164
|
+
mode("dontAsk", "Don't ask", "Denies anything not pre-approved"),
|
|
165
|
+
mode("bypassPermissions", "Bypass permissions", "Runs everything without asking", "full")
|
|
171
166
|
],
|
|
172
167
|
appliesFrom: "immediate",
|
|
173
168
|
defaultModeId: "auto"
|
|
@@ -180,9 +175,18 @@ var CURSOR_PERMISSION_MODES = {
|
|
|
180
175
|
],
|
|
181
176
|
appliesFrom: "next-turn"
|
|
182
177
|
};
|
|
178
|
+
var ANTIGRAVITY_PERMISSION_MODES = {
|
|
179
|
+
modes: [
|
|
180
|
+
mode("default", "Default", "Ask before edits and commands, per your permission settings.", "ask"),
|
|
181
|
+
mode("accept-edits", "Accept edits", "Approve file edits in the workspace without asking."),
|
|
182
|
+
mode("plan", "Plan")
|
|
183
|
+
],
|
|
184
|
+
appliesFrom: "immediate",
|
|
185
|
+
defaultModeId: "default"
|
|
186
|
+
};
|
|
183
187
|
var CODEX_PERMISSION_MODES = {
|
|
184
188
|
modes: [
|
|
185
|
-
mode("read-only", "Read only", "Never asks;
|
|
189
|
+
mode("read-only", "Read only", "Never asks; writes fail", "ask"),
|
|
186
190
|
mode("workspace-write", "Workspace write", "Runs commands inside the workspace without asking; escalates outside it", "auto"),
|
|
187
191
|
mode("untrusted", "Untrusted", "Only trusted commands run without asking; anything else escalates"),
|
|
188
192
|
mode("full-access", "Full access", "Never asks; full filesystem and network access", "full")
|
|
@@ -226,6 +230,15 @@ var AGENT_HARNESS_DEFINITIONS = [
|
|
|
226
230
|
authSlot: "cursor",
|
|
227
231
|
cloudHost: "workspace-runtime"
|
|
228
232
|
},
|
|
233
|
+
{
|
|
234
|
+
key: "antigravity",
|
|
235
|
+
id: "antigravity",
|
|
236
|
+
access: "native",
|
|
237
|
+
label: "Antigravity",
|
|
238
|
+
authEnv: "GEMINI_API_KEY",
|
|
239
|
+
authSlot: "google",
|
|
240
|
+
cloudHost: "workspace-runtime"
|
|
241
|
+
},
|
|
229
242
|
{
|
|
230
243
|
key: "pi",
|
|
231
244
|
id: "pi",
|
|
@@ -246,7 +259,7 @@ var AGENT_HARNESS_DEFINITIONS = [
|
|
|
246
259
|
}
|
|
247
260
|
];
|
|
248
261
|
var AGENT_HARNESS_KEYS = AGENT_HARNESS_DEFINITIONS.map((item) => item.key);
|
|
249
|
-
var HARNESS_IDS = ["claude", "codex", "cursor"];
|
|
262
|
+
var HARNESS_IDS = ["claude", "codex", "cursor", "antigravity"];
|
|
250
263
|
var HARNESS_TABLE = {
|
|
251
264
|
claude: {
|
|
252
265
|
label: "Claude Code",
|
|
@@ -276,6 +289,15 @@ var HARNESS_TABLE = {
|
|
|
276
289
|
machineLoginServes: ["cursor-acp"],
|
|
277
290
|
permissionModes: CURSOR_PERMISSION_MODES,
|
|
278
291
|
defaultModelId: "auto"
|
|
292
|
+
},
|
|
293
|
+
antigravity: {
|
|
294
|
+
label: "Antigravity",
|
|
295
|
+
vendor: "Google",
|
|
296
|
+
providerIds: ["antigravity", "google"],
|
|
297
|
+
connectProvider: "antigravity",
|
|
298
|
+
vendorProvider: "google",
|
|
299
|
+
machineLoginServes: ["antigravity"],
|
|
300
|
+
permissionModes: ANTIGRAVITY_PERMISSION_MODES
|
|
279
301
|
}
|
|
280
302
|
};
|
|
281
303
|
function harnessBindingIds(harness) {
|
|
@@ -451,6 +473,27 @@ var boatBilling = {
|
|
|
451
473
|
large: { size: "large", vcpu: 8, memoryGb: 16, usdPerHour: 0.072 }
|
|
452
474
|
}
|
|
453
475
|
};
|
|
476
|
+
var AGENT37_USD_PER_MONTH = { vcpu: 0.8, memoryGb: 0.7, diskGb: 0.09 };
|
|
477
|
+
var AGENT37_MONTH_HOURS = 730;
|
|
478
|
+
var agent37MachineShapes = {
|
|
479
|
+
small: { cpu: 2, memory: 4, disk: 10 },
|
|
480
|
+
default: { cpu: 4, memory: 8, disk: 20 },
|
|
481
|
+
large: { cpu: 4, memory: 16, disk: 40 }
|
|
482
|
+
};
|
|
483
|
+
function agent37Rate(shape) {
|
|
484
|
+
const usdPerMonth = shape.cpu * AGENT37_USD_PER_MONTH.vcpu + shape.memory * AGENT37_USD_PER_MONTH.memoryGb + shape.disk * AGENT37_USD_PER_MONTH.diskGb;
|
|
485
|
+
return { size: `${shape.cpu} vCPU / ${shape.memory} GB`, vcpu: shape.cpu, memoryGb: shape.memory, usdPerHour: usdPerMonth / AGENT37_MONTH_HOURS };
|
|
486
|
+
}
|
|
487
|
+
var agent37Billing = {
|
|
488
|
+
source: { url: "https://www.agent37.com/docs/agents-api/billing", readOn: "2026-11-11" },
|
|
489
|
+
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.",
|
|
490
|
+
rates: {
|
|
491
|
+
small: agent37Rate(agent37MachineShapes.small),
|
|
492
|
+
default: agent37Rate(agent37MachineShapes.default),
|
|
493
|
+
large: agent37Rate(agent37MachineShapes.large)
|
|
494
|
+
},
|
|
495
|
+
excludes: `the disk of a stopped instance, billed at $${AGENT37_USD_PER_MONTH.diskGb} per GB a month until the instance is deleted`
|
|
496
|
+
};
|
|
454
497
|
var CLOUDFLARE_USD_PER_GIB_SECOND_MEMORY = 25e-7;
|
|
455
498
|
var CLOUDFLARE_USD_PER_GB_SECOND_DISK = 7e-8;
|
|
456
499
|
var CLOUDFLARE_USD_PER_VCPU_SECOND_ACTIVE = 2e-5;
|
|
@@ -485,6 +528,7 @@ var sandboxDriverCredentialFields = {
|
|
|
485
528
|
{ key: "worker_url", label: "Worker URL" }
|
|
486
529
|
],
|
|
487
530
|
boat: [{ key: "api_key", label: "API Key", secret: true }],
|
|
531
|
+
agent37: [{ key: "api_key", label: "API Key", secret: true }],
|
|
488
532
|
docker: [{ key: "image", label: "Image" }]
|
|
489
533
|
};
|
|
490
534
|
var sandboxDriverLabels = {
|
|
@@ -492,6 +536,7 @@ var sandboxDriverLabels = {
|
|
|
492
536
|
vercel: "Vercel",
|
|
493
537
|
cloudflare: "Cloudflare",
|
|
494
538
|
boat: "Boat",
|
|
539
|
+
agent37: "Agent37",
|
|
495
540
|
docker: "Docker"
|
|
496
541
|
};
|
|
497
542
|
|
|
@@ -586,8 +631,8 @@ var sandboxDriverCatalog = {
|
|
|
586
631
|
hostWatchesIdle: true,
|
|
587
632
|
targetAccess: "relay",
|
|
588
633
|
secretBrokering: "native",
|
|
589
|
-
//
|
|
590
|
-
egressControl: "
|
|
634
|
+
// The sandbox Worker starts the container without internet and routes each allowed host through itself; a Worker that cannot confirm that refuses the start.
|
|
635
|
+
egressControl: "hosts",
|
|
591
636
|
// A container snapshot holds the whole root filesystem; the platform keeps it 30 days past its last restore and offers no deletion.
|
|
592
637
|
persistence: {
|
|
593
638
|
resume: "replacement-restore",
|
|
@@ -625,6 +670,33 @@ var sandboxDriverCatalog = {
|
|
|
625
670
|
machineClasses: ["small", "default", "large"]
|
|
626
671
|
}
|
|
627
672
|
},
|
|
673
|
+
agent37: {
|
|
674
|
+
id: "agent37",
|
|
675
|
+
label: sandboxDriverLabels.agent37,
|
|
676
|
+
credentialFields: sandboxDriverCredentialFields.agent37,
|
|
677
|
+
runtimeNetwork: { controlPlane: "direct", relay: "configured" },
|
|
678
|
+
runtimeEnv: "callback",
|
|
679
|
+
preparedImage: true,
|
|
680
|
+
billing: agent37Billing,
|
|
681
|
+
metadata: {
|
|
682
|
+
driverRunsIn: ["worker", "node"],
|
|
683
|
+
hostStopBehavior: "suspends-host",
|
|
684
|
+
hostResumeBehavior: "same-host",
|
|
685
|
+
targetAccess: "relay",
|
|
686
|
+
secretBrokering: "none",
|
|
687
|
+
// An instance's network policy lists domains, reached over HTTPS on 443 alone, and IPv4 ranges; private ranges stay blocked whatever it lists.
|
|
688
|
+
egressControl: "hosts-and-cidrs",
|
|
689
|
+
persistence: {
|
|
690
|
+
resume: "same-sandbox",
|
|
691
|
+
capture: "none",
|
|
692
|
+
clone: false,
|
|
693
|
+
captureSource: "not-applicable",
|
|
694
|
+
retention: "provider-managed",
|
|
695
|
+
restoreMount: "same-resource"
|
|
696
|
+
},
|
|
697
|
+
machineClasses: ["small", "default", "large"]
|
|
698
|
+
}
|
|
699
|
+
},
|
|
628
700
|
docker: {
|
|
629
701
|
id: "docker",
|
|
630
702
|
label: sandboxDriverLabels.docker,
|
package/dist/drivers/modal.mjs
CHANGED
|
@@ -114,6 +114,27 @@ var boatBilling = {
|
|
|
114
114
|
large: { size: "large", vcpu: 8, memoryGb: 16, usdPerHour: 0.072 }
|
|
115
115
|
}
|
|
116
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
|
+
};
|
|
117
138
|
var CLOUDFLARE_USD_PER_GIB_SECOND_MEMORY = 25e-7;
|
|
118
139
|
var CLOUDFLARE_USD_PER_GB_SECOND_DISK = 7e-8;
|
|
119
140
|
var CLOUDFLARE_USD_PER_VCPU_SECOND_ACTIVE = 2e-5;
|
|
@@ -148,6 +169,7 @@ var sandboxDriverCredentialFields = {
|
|
|
148
169
|
{ key: "worker_url", label: "Worker URL" }
|
|
149
170
|
],
|
|
150
171
|
boat: [{ key: "api_key", label: "API Key", secret: true }],
|
|
172
|
+
agent37: [{ key: "api_key", label: "API Key", secret: true }],
|
|
151
173
|
docker: [{ key: "image", label: "Image" }]
|
|
152
174
|
};
|
|
153
175
|
var sandboxDriverLabels = {
|
|
@@ -155,6 +177,7 @@ var sandboxDriverLabels = {
|
|
|
155
177
|
vercel: "Vercel",
|
|
156
178
|
cloudflare: "Cloudflare",
|
|
157
179
|
boat: "Boat",
|
|
180
|
+
agent37: "Agent37",
|
|
158
181
|
docker: "Docker"
|
|
159
182
|
};
|
|
160
183
|
|
|
@@ -225,8 +248,8 @@ var sandboxDriverCatalog = {
|
|
|
225
248
|
hostWatchesIdle: true,
|
|
226
249
|
targetAccess: "relay",
|
|
227
250
|
secretBrokering: "native",
|
|
228
|
-
//
|
|
229
|
-
egressControl: "
|
|
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",
|
|
230
253
|
// A container snapshot holds the whole root filesystem; the platform keeps it 30 days past its last restore and offers no deletion.
|
|
231
254
|
persistence: {
|
|
232
255
|
resume: "replacement-restore",
|
|
@@ -264,6 +287,33 @@ var sandboxDriverCatalog = {
|
|
|
264
287
|
machineClasses: ["small", "default", "large"]
|
|
265
288
|
}
|
|
266
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
|
+
},
|
|
267
317
|
docker: {
|
|
268
318
|
id: "docker",
|
|
269
319
|
label: sandboxDriverLabels.docker,
|
|
@@ -297,10 +347,8 @@ function isRecord(value) {
|
|
|
297
347
|
function asRecord(value) {
|
|
298
348
|
return isRecord(value) ? value : void 0;
|
|
299
349
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
function num(input) {
|
|
303
|
-
return typeof input === "number" ? input : void 0;
|
|
350
|
+
function asNumber(value) {
|
|
351
|
+
return typeof value === "number" ? value : void 0;
|
|
304
352
|
}
|
|
305
353
|
|
|
306
354
|
// src/drivers/transient-error.ts
|
|
@@ -313,7 +361,7 @@ function driverErrorSignals(err) {
|
|
|
313
361
|
return {
|
|
314
362
|
// `response.status` is the axios shape; `statusCode` is what SDK-native
|
|
315
363
|
// error classes expose instead.
|
|
316
|
-
status:
|
|
364
|
+
status: asNumber(nested.status) ?? asNumber(shaped.status) ?? asNumber(shaped.statusCode),
|
|
317
365
|
text: `${textOf(shaped.code)} ${textOf(shaped.name)} ${textOf(shaped.message)}`.toLowerCase()
|
|
318
366
|
};
|
|
319
367
|
}
|
|
@@ -339,9 +387,6 @@ function nameFor(workspaceId) {
|
|
|
339
387
|
return `claxedo-${workspaceId}`;
|
|
340
388
|
}
|
|
341
389
|
var TRANSIENT_MARKERS = ["timeout", "unavailable", "deadline"];
|
|
342
|
-
function transientDriverError(err) {
|
|
343
|
-
return isTransientDriverError(err, TRANSIENT_MARKERS);
|
|
344
|
-
}
|
|
345
390
|
function createModalSandboxDriver(options) {
|
|
346
391
|
let defaultClient;
|
|
347
392
|
function resolveClient() {
|
|
@@ -439,7 +484,7 @@ function createModalSandboxDriver(options) {
|
|
|
439
484
|
...network(input)
|
|
440
485
|
}
|
|
441
486
|
).catch((err) => {
|
|
442
|
-
if (
|
|
487
|
+
if (isTransientDriverError(err, TRANSIENT_MARKERS)) return void 0;
|
|
443
488
|
throw err;
|
|
444
489
|
});
|
|
445
490
|
if (!sandbox) return { provisioning: true, retryAfterMs: TRANSIENT_DRIVER_RETRY_MS };
|
package/dist/drivers/vercel.mjs
CHANGED
|
@@ -101,6 +101,27 @@ var boatBilling = {
|
|
|
101
101
|
large: { size: "large", vcpu: 8, memoryGb: 16, usdPerHour: 0.072 }
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
|
+
var AGENT37_USD_PER_MONTH = { vcpu: 0.8, memoryGb: 0.7, diskGb: 0.09 };
|
|
105
|
+
var AGENT37_MONTH_HOURS = 730;
|
|
106
|
+
var agent37MachineShapes = {
|
|
107
|
+
small: { cpu: 2, memory: 4, disk: 10 },
|
|
108
|
+
default: { cpu: 4, memory: 8, disk: 20 },
|
|
109
|
+
large: { cpu: 4, memory: 16, disk: 40 }
|
|
110
|
+
};
|
|
111
|
+
function agent37Rate(shape) {
|
|
112
|
+
const usdPerMonth = shape.cpu * AGENT37_USD_PER_MONTH.vcpu + shape.memory * AGENT37_USD_PER_MONTH.memoryGb + shape.disk * AGENT37_USD_PER_MONTH.diskGb;
|
|
113
|
+
return { size: `${shape.cpu} vCPU / ${shape.memory} GB`, vcpu: shape.cpu, memoryGb: shape.memory, usdPerHour: usdPerMonth / AGENT37_MONTH_HOURS };
|
|
114
|
+
}
|
|
115
|
+
var agent37Billing = {
|
|
116
|
+
source: { url: "https://www.agent37.com/docs/agents-api/billing", readOn: "2026-11-11" },
|
|
117
|
+
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.",
|
|
118
|
+
rates: {
|
|
119
|
+
small: agent37Rate(agent37MachineShapes.small),
|
|
120
|
+
default: agent37Rate(agent37MachineShapes.default),
|
|
121
|
+
large: agent37Rate(agent37MachineShapes.large)
|
|
122
|
+
},
|
|
123
|
+
excludes: `the disk of a stopped instance, billed at $${AGENT37_USD_PER_MONTH.diskGb} per GB a month until the instance is deleted`
|
|
124
|
+
};
|
|
104
125
|
var CLOUDFLARE_USD_PER_GIB_SECOND_MEMORY = 25e-7;
|
|
105
126
|
var CLOUDFLARE_USD_PER_GB_SECOND_DISK = 7e-8;
|
|
106
127
|
var CLOUDFLARE_USD_PER_VCPU_SECOND_ACTIVE = 2e-5;
|
|
@@ -135,6 +156,7 @@ var sandboxDriverCredentialFields = {
|
|
|
135
156
|
{ key: "worker_url", label: "Worker URL" }
|
|
136
157
|
],
|
|
137
158
|
boat: [{ key: "api_key", label: "API Key", secret: true }],
|
|
159
|
+
agent37: [{ key: "api_key", label: "API Key", secret: true }],
|
|
138
160
|
docker: [{ key: "image", label: "Image" }]
|
|
139
161
|
};
|
|
140
162
|
var sandboxDriverLabels = {
|
|
@@ -142,6 +164,7 @@ var sandboxDriverLabels = {
|
|
|
142
164
|
vercel: "Vercel",
|
|
143
165
|
cloudflare: "Cloudflare",
|
|
144
166
|
boat: "Boat",
|
|
167
|
+
agent37: "Agent37",
|
|
145
168
|
docker: "Docker"
|
|
146
169
|
};
|
|
147
170
|
|
|
@@ -212,8 +235,8 @@ var sandboxDriverCatalog = {
|
|
|
212
235
|
hostWatchesIdle: true,
|
|
213
236
|
targetAccess: "relay",
|
|
214
237
|
secretBrokering: "native",
|
|
215
|
-
//
|
|
216
|
-
egressControl: "
|
|
238
|
+
// The sandbox Worker starts the container without internet and routes each allowed host through itself; a Worker that cannot confirm that refuses the start.
|
|
239
|
+
egressControl: "hosts",
|
|
217
240
|
// A container snapshot holds the whole root filesystem; the platform keeps it 30 days past its last restore and offers no deletion.
|
|
218
241
|
persistence: {
|
|
219
242
|
resume: "replacement-restore",
|
|
@@ -251,6 +274,33 @@ var sandboxDriverCatalog = {
|
|
|
251
274
|
machineClasses: ["small", "default", "large"]
|
|
252
275
|
}
|
|
253
276
|
},
|
|
277
|
+
agent37: {
|
|
278
|
+
id: "agent37",
|
|
279
|
+
label: sandboxDriverLabels.agent37,
|
|
280
|
+
credentialFields: sandboxDriverCredentialFields.agent37,
|
|
281
|
+
runtimeNetwork: { controlPlane: "direct", relay: "configured" },
|
|
282
|
+
runtimeEnv: "callback",
|
|
283
|
+
preparedImage: true,
|
|
284
|
+
billing: agent37Billing,
|
|
285
|
+
metadata: {
|
|
286
|
+
driverRunsIn: ["worker", "node"],
|
|
287
|
+
hostStopBehavior: "suspends-host",
|
|
288
|
+
hostResumeBehavior: "same-host",
|
|
289
|
+
targetAccess: "relay",
|
|
290
|
+
secretBrokering: "none",
|
|
291
|
+
// An instance's network policy lists domains, reached over HTTPS on 443 alone, and IPv4 ranges; private ranges stay blocked whatever it lists.
|
|
292
|
+
egressControl: "hosts-and-cidrs",
|
|
293
|
+
persistence: {
|
|
294
|
+
resume: "same-sandbox",
|
|
295
|
+
capture: "none",
|
|
296
|
+
clone: false,
|
|
297
|
+
captureSource: "not-applicable",
|
|
298
|
+
retention: "provider-managed",
|
|
299
|
+
restoreMount: "same-resource"
|
|
300
|
+
},
|
|
301
|
+
machineClasses: ["small", "default", "large"]
|
|
302
|
+
}
|
|
303
|
+
},
|
|
254
304
|
docker: {
|
|
255
305
|
id: "docker",
|
|
256
306
|
label: sandboxDriverLabels.docker,
|
|
@@ -284,10 +334,8 @@ function isRecord(value) {
|
|
|
284
334
|
function asRecord(value) {
|
|
285
335
|
return isRecord(value) ? value : void 0;
|
|
286
336
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
function num(input) {
|
|
290
|
-
return typeof input === "number" ? input : void 0;
|
|
337
|
+
function asNumber(value) {
|
|
338
|
+
return typeof value === "number" ? value : void 0;
|
|
291
339
|
}
|
|
292
340
|
|
|
293
341
|
// src/drivers/transient-error.ts
|
|
@@ -300,7 +348,7 @@ function driverErrorSignals(err) {
|
|
|
300
348
|
return {
|
|
301
349
|
// `response.status` is the axios shape; `statusCode` is what SDK-native
|
|
302
350
|
// error classes expose instead.
|
|
303
|
-
status:
|
|
351
|
+
status: asNumber(nested.status) ?? asNumber(shaped.status) ?? asNumber(shaped.statusCode),
|
|
304
352
|
text: `${textOf(shaped.code)} ${textOf(shaped.name)} ${textOf(shaped.message)}`.toLowerCase()
|
|
305
353
|
};
|
|
306
354
|
}
|
|
@@ -329,7 +377,8 @@ var HARNESS_RELEASE_AGE_EXCLUSIONS = `printf '%s\\n' ${[
|
|
|
329
377
|
"@anthropic-ai/claude-*",
|
|
330
378
|
"@cursor/sdk*",
|
|
331
379
|
"@earendil-works/*",
|
|
332
|
-
"@openai/codex"
|
|
380
|
+
"@openai/codex",
|
|
381
|
+
"@opencode/*"
|
|
333
382
|
].map((name) => `'min-release-age-exclude[]=${name}'`).join(" ")} >> "$HOME/.npmrc"`;
|
|
334
383
|
var DEFAULT_RUNTIME_COMMAND = "/usr/local/bin/workspace-runtime";
|
|
335
384
|
var DEFAULT_WORKSPACE_DIR = "/workspace";
|
|
@@ -351,9 +400,6 @@ function nameFor(workspaceId) {
|
|
|
351
400
|
return `claxedo-${workspaceId}`;
|
|
352
401
|
}
|
|
353
402
|
var TRANSIENT_MARKERS = ["timeout", "unavailable", "deadline", "pending"];
|
|
354
|
-
function transientDriverError(err) {
|
|
355
|
-
return isTransientDriverError(err, TRANSIENT_MARKERS);
|
|
356
|
-
}
|
|
357
403
|
function network(input) {
|
|
358
404
|
if (!input.net || input.net.mode === "allow-all") return void 0;
|
|
359
405
|
const hosts = input.net.hosts ?? [];
|
|
@@ -488,21 +534,25 @@ function createVercelSandboxDriver(options) {
|
|
|
488
534
|
await runSetup(builder, "sudo dnf install -y git make gcc-c++ python3 > /dev/null 2>&1");
|
|
489
535
|
await runSetup(builder, [
|
|
490
536
|
HARNESS_RELEASE_AGE_EXCLUSIONS,
|
|
491
|
-
"npm i -g --min-release-age=2 tsx@4.22.3
|
|
537
|
+
"npm i -g --min-release-age=2 tsx@4.22.3 @anthropic-ai/claude-code@2.1.323 @openai/codex@0.165.0",
|
|
538
|
+
"sudo mkdir -p /etc/codex",
|
|
539
|
+
"printf 'check_for_update_on_startup = false\\n' | sudo tee /etc/codex/config.toml > /dev/null",
|
|
492
540
|
"curl https://cursor.com/install -fsS | bash",
|
|
493
541
|
"curl -fsSL https://ampcode.com/install.sh | bash",
|
|
494
542
|
"curl -fsSL https://app.factory.ai/cli | sh",
|
|
543
|
+
"curl -fsSL https://antigravity.google/cli/install.sh | bash",
|
|
495
544
|
"sudo ln -sf $HOME/.local/bin/agent /usr/local/bin/agent",
|
|
496
545
|
"sudo ln -sf $HOME/.local/bin/cursor-agent /usr/local/bin/cursor-agent",
|
|
497
546
|
"sudo ln -sf $HOME/.local/bin/amp /usr/local/bin/amp",
|
|
498
547
|
"sudo ln -sf $HOME/.local/bin/droid /usr/local/bin/droid",
|
|
499
|
-
"
|
|
548
|
+
"sudo ln -sf $HOME/.local/bin/agy /usr/local/bin/agy",
|
|
500
549
|
"command -v claude",
|
|
501
550
|
"command -v codex",
|
|
502
551
|
"command -v agent",
|
|
503
552
|
"command -v cursor-agent",
|
|
504
553
|
"command -v amp",
|
|
505
|
-
"command -v droid"
|
|
554
|
+
"command -v droid",
|
|
555
|
+
"command -v agy"
|
|
506
556
|
].join(" && "));
|
|
507
557
|
await runSetup(builder, [
|
|
508
558
|
`sudo mkdir -p ${shell(RUNTIME_DIR)}`,
|
|
@@ -578,7 +628,7 @@ function createVercelSandboxDriver(options) {
|
|
|
578
628
|
env,
|
|
579
629
|
...createPolicy ? { networkPolicy: createPolicy } : {}
|
|
580
630
|
}).catch((err) => {
|
|
581
|
-
if (
|
|
631
|
+
if (isTransientDriverError(err, TRANSIENT_MARKERS)) return void 0;
|
|
582
632
|
throw err;
|
|
583
633
|
});
|
|
584
634
|
if (!sandbox) return { provisioning: true, retryAfterMs: TRANSIENT_DRIVER_RETRY_MS };
|
package/dist/egress-policy.d.ts
CHANGED
|
@@ -8,26 +8,32 @@ export declare function sandboxNetworkPolicyWithBrokeredHosts(policy: SandboxNet
|
|
|
8
8
|
/** @see SandboxDriverMetadata.egressControl */
|
|
9
9
|
export type SandboxEgressControl = "none" | "hosts" | "hosts-and-cidrs";
|
|
10
10
|
/**
|
|
11
|
-
* What
|
|
12
|
-
*
|
|
11
|
+
* What a start does with a restricted policy on a driver that cannot contain
|
|
12
|
+
* egress: `"refuse"` the start, or `"allow"` the sandbox to run with the policy
|
|
13
|
+
* withheld and the gap reported. Refusing is the default; allowing is the
|
|
14
|
+
* workspace owner's explicit, auditable decision.
|
|
15
|
+
*/
|
|
16
|
+
export type SandboxUncontainedEgress = "refuse" | "allow";
|
|
17
|
+
/**
|
|
18
|
+
* What the manager does with a caller's `net` for a given driver.
|
|
13
19
|
*
|
|
14
20
|
* - `"enforce"` — the driver gets the policy verbatim and contains the
|
|
15
21
|
* sandbox. Also the answer when the caller asked for no containment
|
|
16
22
|
* (absent / `allow-all`), which is nothing to enforce.
|
|
17
|
-
* - `"withhold"` — the driver declares `egressControl: "none"
|
|
18
|
-
*
|
|
19
|
-
* drivers throw on a restricted policy (docker, modal, boat)
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* particular policy's encoding (a
|
|
27
|
-
* allowlist).
|
|
28
|
-
* on an otherwise-enforcing driver
|
|
29
|
-
* weaken the enforcing path
|
|
30
|
-
* hosts, so no production path reaches it.
|
|
23
|
+
* - `"withhold"` — the driver declares `egressControl: "none"` and the
|
|
24
|
+
* start allows uncontained egress. The policy is NOT handed down: the
|
|
25
|
+
* `"none"` drivers throw on a restricted policy (docker, modal, boat), so
|
|
26
|
+
* withholding at the manager keeps their throws as their own last line of
|
|
27
|
+
* defence. The sandbox comes up with UNRESTRICTED egress, which is a real
|
|
28
|
+
* exposure, so the manager warns (see `onEgressUnenforced`).
|
|
29
|
+
* - `"refuse"` — either the driver declares `egressControl: "none"` and the
|
|
30
|
+
* start does not allow uncontained egress (`sandbox_egress_uncontained`,
|
|
31
|
+
* the default), or the driver HAS egress control but cannot express this
|
|
32
|
+
* particular policy's encoding (`sandbox_egress_policy_unenforceable`: a
|
|
33
|
+
* hosts-only driver handed an address-only allowlist). The second is a
|
|
34
|
+
* composition mistake on an otherwise-enforcing driver; degrading it to
|
|
35
|
+
* "withhold" would weaken the enforcing path, and `hostedSandboxNetworkPolicy`
|
|
36
|
+
* only ever emits hosts, so no production path reaches it.
|
|
31
37
|
*
|
|
32
38
|
* Pure, like `validateSandboxPersistenceCapabilities`, so the manager, the
|
|
33
39
|
* composition layer, and tests can all ask the same question.
|
|
@@ -39,22 +45,18 @@ export type SandboxEgressDisposition = {
|
|
|
39
45
|
reason: "sandbox_egress_uncontained";
|
|
40
46
|
} | {
|
|
41
47
|
action: "refuse";
|
|
42
|
-
reason: "sandbox_egress_policy_unenforceable";
|
|
48
|
+
reason: "sandbox_egress_uncontained" | "sandbox_egress_policy_unenforceable";
|
|
43
49
|
};
|
|
44
50
|
/** @see SandboxEgressDisposition */
|
|
45
|
-
export declare function sandboxEgressDisposition(control: SandboxEgressControl, net: SandboxNetworkPolicy | undefined): SandboxEgressDisposition;
|
|
51
|
+
export declare function sandboxEgressDisposition(control: SandboxEgressControl, net: SandboxNetworkPolicy | undefined, uncontained?: SandboxUncontainedEgress): SandboxEgressDisposition;
|
|
46
52
|
/**
|
|
47
|
-
* Emitted
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* - `"composition"` — once per `createSandboxManager` per driver, at boot.
|
|
53
|
-
* - `"ensure"` — every time a caller's policy is actually withheld, naming the
|
|
54
|
-
* workspace and the allowlist that did not take effect.
|
|
53
|
+
* Emitted the first time per driver per isolate that a start which allows
|
|
54
|
+
* uncontained egress has its restricted policy withheld because the driver
|
|
55
|
+
* declares no egress control: an unrestricted sandbox that nobody was told
|
|
56
|
+
* about is the exposure, so the degrade is never silent. A start that refuses
|
|
57
|
+
* uncontained egress emits none: it never runs.
|
|
55
58
|
*/
|
|
56
59
|
export type SandboxEgressUnenforcedEvent = {
|
|
57
|
-
phase: "composition" | "ensure";
|
|
58
60
|
reason: "sandbox_egress_uncontained";
|
|
59
61
|
/** `SandboxDriver.id` of the composed driver. */
|
|
60
62
|
driver: string;
|
|
@@ -62,12 +64,11 @@ export type SandboxEgressUnenforcedEvent = {
|
|
|
62
64
|
egressControl: SandboxEgressControl;
|
|
63
65
|
/** Ready-to-log sentence. Sinks that only forward text can use this alone. */
|
|
64
66
|
message: string;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
/** The start's placement labels. Present on `"ensure"`. */
|
|
67
|
+
workspaceId: string;
|
|
68
|
+
/** The start's placement labels. */
|
|
68
69
|
labels?: Record<string, string>;
|
|
69
|
-
/** The allowlist the caller asked for and did NOT get.
|
|
70
|
-
requested
|
|
70
|
+
/** The allowlist the caller asked for and did NOT get. */
|
|
71
|
+
requested: SandboxNetworkPolicy;
|
|
71
72
|
};
|
|
72
73
|
/**
|
|
73
74
|
* A credential the SANDBOX must be able to USE for outbound requests but must
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import type { SandboxEgressControl, SandboxEgressUnenforcedEvent, SandboxNetworkPolicy } from "./egress-policy";
|
|
2
2
|
/**
|
|
3
|
-
* The
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* wherever the process starts, so an operator learns of it BEFORE the first
|
|
7
|
-
* workspace exists. `withheld` is the start half, raised once per driver per
|
|
8
|
-
* isolate from the first start whose policy the driver could not enforce.
|
|
3
|
+
* The warning that a start runs with unrestricted egress, raised once per
|
|
4
|
+
* driver per isolate from the first start whose policy the driver could not
|
|
5
|
+
* enforce. A refused start raises none: its refusal is that start's own answer.
|
|
9
6
|
*/
|
|
10
7
|
export declare function createEgressWarnings(input: {
|
|
11
8
|
driver: {
|
|
@@ -20,3 +17,4 @@ export declare function createEgressWarnings(input: {
|
|
|
20
17
|
requested: SandboxNetworkPolicy;
|
|
21
18
|
}): void;
|
|
22
19
|
};
|
|
20
|
+
export type EgressWarnings = ReturnType<typeof createEgressWarnings>;
|