@tangle-network/sandbox 0.1.2 → 0.2.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/README.md +490 -2
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +271 -1
- package/dist/client-Uve6A5C6.js +2280 -0
- package/dist/collaboration/index.d.ts +1 -1
- package/dist/collaboration/index.js +2 -1
- package/dist/collaboration-CRyb5e8F.js +201 -0
- package/dist/core.d.ts +3 -3
- package/dist/core.js +4 -1
- package/dist/errors-BI75IXOM.d.ts +1177 -0
- package/dist/errors-CljiGR__.js +262 -0
- package/dist/{index-BuS8nl3b.d.ts → index-CCsA3S0D.d.ts} +6 -1
- package/dist/{index-t7xkzv0U.d.ts → index-DhNGZ0h4.d.ts} +3 -3
- package/dist/{index-gA-oRjOi.d.ts → index-Dpj1oB5i.d.ts} +35 -4
- package/dist/index.d.ts +109 -62
- package/dist/index.js +825 -1
- package/dist/openai/index.d.ts +642 -0
- package/dist/openai/index.js +1721 -0
- package/dist/platform-integrations.d.ts +2 -0
- package/dist/platform-integrations.js +2 -0
- package/dist/{sandbox-BvZ0-Iv7.d.ts → sandbox-aBpWqler.d.ts} +1528 -41
- package/dist/sandbox-ksXTNlo-.js +3394 -0
- package/dist/session-gateway/index.js +667 -1
- package/dist/tangle/index.d.ts +1 -1
- package/dist/tangle/index.js +2 -1
- package/dist/tangle-DQ05paN7.js +826 -0
- package/package.json +93 -34
- package/LICENSE +0 -11
- package/dist/client-CcRvqt85.js +0 -1
- package/dist/collaboration-CVvhPU8M.js +0 -1
- package/dist/errors-AIT8qikt.d.ts +0 -491
- package/dist/errors-CdMTv7uG.js +0 -1
- package/dist/sandbox-D1JnQIJx.js +0 -1
- package/dist/tangle-CSb9rjAh.js +0 -1
package/README.md
CHANGED
|
@@ -50,9 +50,11 @@ await box.delete();
|
|
|
50
50
|
- **AI Agent Tasks** - Multi-turn agent execution with automatic tool use
|
|
51
51
|
- **Snapshots** - Save and restore sandbox state
|
|
52
52
|
- **BYOS3** - Bring your own S3 storage for snapshots
|
|
53
|
-
- **
|
|
53
|
+
- **Fleets** - Coordinated multi-machine workloads with policy, workspace snapshots, and parallel dispatch
|
|
54
|
+
- **Intelligence Reports** - Deterministic or agentic post-hoc analysis of sandbox or fleet evidence (fleet reports can refine to a single dispatch via `subject.dispatchId`)
|
|
54
55
|
- **Event Streaming** - Real-time SSE streams for agent events
|
|
55
56
|
- **Collaboration Foundations** - Token issuance and document identity helpers for collaborative editing
|
|
57
|
+
- **Trace Intelligence** - Export raw sandbox and fleet traces, embedded intelligence envelope, timing metrics, and OTEL JSON to customer-owned observability systems
|
|
56
58
|
|
|
57
59
|
## Collaboration Foundations
|
|
58
60
|
|
|
@@ -110,6 +112,64 @@ const bootstrap = await collab.bootstrap({
|
|
|
110
112
|
|
|
111
113
|
The bridge and client are SDK-side primitives. Product/backend endpoints and CRDT runtime integration still need to be wired by the application.
|
|
112
114
|
|
|
115
|
+
## Trace Intelligence
|
|
116
|
+
|
|
117
|
+
The platform exposes two distinct intelligence primitives. Use the right one for the job.
|
|
118
|
+
|
|
119
|
+
| Primitive | What you get | Billable | API call |
|
|
120
|
+
|---|---|---|---|
|
|
121
|
+
| **Embedded envelope** | Inline summary in a trace/dispatch response: signals, recommended actions, fanout timings, dispatch failure classes | No (`billing.billable: false`) | `trace({ includeIntelligence: true })`, `intelligence()`, dispatch responses |
|
|
122
|
+
| **Intelligence Report** | A pollable job over sandbox or fleet evidence; fleet reports can refine to a single dispatch via `subject.dispatchId`. Runs in `deterministic` or `agentic` mode against a budget. | `deterministic`: free. `agentic`: billed against `budget.maxUsd` | `intelligence.createReport(...)`, see [Intelligence Reports](#intelligence-reports) |
|
|
123
|
+
|
|
124
|
+
### Embedded envelope (free)
|
|
125
|
+
|
|
126
|
+
The embedded envelope is opt-in on `trace()` calls (`includeIntelligence: true`) and always included on dispatch responses (because dispatch already pays the analysis cost as part of producing the result).
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const boxBundle = await box.trace(); // { trace } only
|
|
130
|
+
const boxInsights = await box.intelligence(); // envelope only
|
|
131
|
+
const boxBundleWithInsights = await box.trace({ includeIntelligence: true });
|
|
132
|
+
|
|
133
|
+
const run = await fleet.dispatchExecDetailed("pytest -q", {
|
|
134
|
+
machines: ["worker-1", "worker-2"],
|
|
135
|
+
});
|
|
136
|
+
console.log(run.results);
|
|
137
|
+
console.log(run.intelligence.signals); // always present on dispatch
|
|
138
|
+
console.log(run.intelligence.recommendedActions);
|
|
139
|
+
|
|
140
|
+
const fleetBundle = await fleet.trace(); // { trace } only
|
|
141
|
+
const fleetInsights = await fleet.intelligence(); // envelope only
|
|
142
|
+
const fleetBundleWithInsights = await fleet.trace({ includeIntelligence: true });
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Sandbox traces cover lifecycle, runtime, usage, timing, and current health snapshots. Fleet traces add machine lifecycle, workspace state, dispatch results, fanout timings, and critical path. The embedded envelope tells you what to inspect next: reliability, parallelism efficiency for fleets, dispatch failure classes, resource attribution, timing bottlenecks, and recommended actions.
|
|
146
|
+
|
|
147
|
+
The embedded envelope is deterministic platform analysis. It reports `billing.billable: false` and `billing.costUsd: 0`; customers are not charged for generating these envelopes.
|
|
148
|
+
|
|
149
|
+
### Exporting traces to your observability stack
|
|
150
|
+
|
|
151
|
+
Use `format: "tangle"` to preserve the native envelope, or `format: "otel-json"` for OpenTelemetry-style collectors and platforms that accept OTLP JSON.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
await box.exportTrace({
|
|
155
|
+
url: "https://collector.example.com/traces",
|
|
156
|
+
headers: { Authorization: `Bearer ${process.env.OBSERVABILITY_TOKEN}` },
|
|
157
|
+
format: "otel-json",
|
|
158
|
+
serviceName: "research-agent",
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
await fleet.exportTrace({
|
|
162
|
+
url: "https://collector.example.com/traces",
|
|
163
|
+
headers: { Authorization: `Bearer ${process.env.OBSERVABILITY_TOKEN}` },
|
|
164
|
+
format: "otel-json",
|
|
165
|
+
serviceName: "research-agent",
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
For Braintrust, Lemma, Raindrop, Langfuse, Datadog, or a custom warehouse, keep this as a customer-owned sink or webhook. Tangle does not need their vendor credentials: fetch `box.trace()` or `fleet.trace()` and send the raw bundle through their SDK/API, or point `exportTrace()` at a small ingest endpoint that transforms it into the vendor's preferred schema.
|
|
170
|
+
|
|
171
|
+
Agent tools should expose both `trace` and `intelligence` actions on `manageSandboxes`. `trace` returns the full raw bundle for downstream analysis; `intelligence` returns the compact agent-readable next-step summary.
|
|
172
|
+
|
|
113
173
|
## Core Concepts
|
|
114
174
|
|
|
115
175
|
### Sandboxes
|
|
@@ -229,7 +289,7 @@ console.log(`Compute: ${usage.computeMinutes} minutes`);
|
|
|
229
289
|
|
|
230
290
|
#### `client.runBatch(tasks, options?)`
|
|
231
291
|
|
|
232
|
-
Run tasks across
|
|
292
|
+
Run ad-hoc tasks across freshly-provisioned sandboxes in parallel. Use this when the work is one-shot and the sandboxes do not need to share a workspace or be addressable by stable `machineId`. For coordinated multi-machine workloads with policy enforcement, workspace sharing, dispatch buffering, and intelligence reports, see [Fleets](#fleets).
|
|
233
293
|
|
|
234
294
|
```typescript
|
|
235
295
|
const result = await client.runBatch([
|
|
@@ -244,6 +304,14 @@ const result = await client.runBatch([
|
|
|
244
304
|
console.log(`Success rate: ${result.successRate}%`);
|
|
245
305
|
```
|
|
246
306
|
|
|
307
|
+
#### `client.fleets`
|
|
308
|
+
|
|
309
|
+
Fleet client — see [Fleets](#fleets) for the full surface (`create`, `createWithCoordinator`, `list`, `delete`, `estimateCost`, `capabilities`, `operations`, `reconcile`, `reapExpired`).
|
|
310
|
+
|
|
311
|
+
#### `client.intelligence`
|
|
312
|
+
|
|
313
|
+
Intelligence report client — see [Intelligence Reports](#intelligence-reports) for the full surface (`createReport`, `createAgenticReport`, `getReport`, `listReports`, `waitForReport`).
|
|
314
|
+
|
|
247
315
|
### Sandbox Instance
|
|
248
316
|
|
|
249
317
|
After creating or retrieving a sandbox, you get a `SandboxInstance` with these methods:
|
|
@@ -288,6 +356,7 @@ Each sandbox runs one AI backend. Pass `backend.type` to choose it:
|
|
|
288
356
|
| `opencode` | [OpenCode](https://github.com/anomalyco/opencode) | Default. Multi-provider, profile system, MCP support |
|
|
289
357
|
| `claude-code` | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | Anthropic-native. Needs ANTHROPIC_API_KEY |
|
|
290
358
|
| `codex` | [Codex CLI](https://github.com/openai/codex) | OpenAI-native. Needs OPENAI_API_KEY |
|
|
359
|
+
| `cursor` | [Cursor Agent SDK](https://cursor.com/changelog/sdk-release) | Cursor-native local/cloud agent. Needs CURSOR_API_KEY |
|
|
291
360
|
| `amp` | [AMP](https://sourcegraph.com/amp) | Sourcegraph AMP agent |
|
|
292
361
|
| `factory-droids` | [Factory](https://factory.ai) | Factory Droid agent |
|
|
293
362
|
|
|
@@ -302,6 +371,52 @@ await box.prompt("Audit this repo", {
|
|
|
302
371
|
backend: { type: "codex", profile: "browser-codex-fast" },
|
|
303
372
|
});
|
|
304
373
|
|
|
374
|
+
// Use Cursor Agent SDK
|
|
375
|
+
await box.prompt("Implement this change", {
|
|
376
|
+
backend: {
|
|
377
|
+
type: "cursor",
|
|
378
|
+
model: {
|
|
379
|
+
model: "composer-2",
|
|
380
|
+
apiKey: process.env.CURSOR_API_KEY,
|
|
381
|
+
},
|
|
382
|
+
profile: {
|
|
383
|
+
name: "cursor-release-agent",
|
|
384
|
+
prompt: {
|
|
385
|
+
systemPrompt:
|
|
386
|
+
"Use repo rules, configured MCP servers, skills, and subagents when relevant.",
|
|
387
|
+
},
|
|
388
|
+
mcp: {
|
|
389
|
+
docs: { transport: "sse", url: "https://docs.example.com/sse" },
|
|
390
|
+
},
|
|
391
|
+
subagents: {
|
|
392
|
+
reviewer: {
|
|
393
|
+
description: "Reviews changes for correctness and missing tests.",
|
|
394
|
+
prompt: "Review the current diff. Return only blocking findings.",
|
|
395
|
+
model: "composer-2",
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
resources: {
|
|
399
|
+
instructions: "Run focused tests before reporting completion.",
|
|
400
|
+
skills: [
|
|
401
|
+
{
|
|
402
|
+
kind: "inline",
|
|
403
|
+
name: "release-check",
|
|
404
|
+
content:
|
|
405
|
+
"---\nname: release-check\ndescription: Validate release readiness.\n---\nRun typecheck and focused tests.",
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
},
|
|
409
|
+
extensions: {
|
|
410
|
+
cursor: {
|
|
411
|
+
runtime: "local",
|
|
412
|
+
local: { settingSources: ["project", "user"] },
|
|
413
|
+
force: true,
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
|
|
305
420
|
// Use OpenCode with an inline profile
|
|
306
421
|
await box.prompt("Audit this repo", {
|
|
307
422
|
backend: {
|
|
@@ -332,6 +447,36 @@ await box.prompt("Analyze this", {
|
|
|
332
447
|
|
|
333
448
|
The SDK serializes `backend.profile` into the required wire format automatically.
|
|
334
449
|
|
|
450
|
+
Cursor profiles map portable MCP, resources, skills, subagents, hooks, permissions,
|
|
451
|
+
and Cursor-native `extensions.cursor` fields into the Cursor Agent SDK. Local
|
|
452
|
+
Cursor runs materialize `.cursor/*` project files inside the sandbox workspace.
|
|
453
|
+
Cloud Cursor runs fail closed when given uncommitted local resources that cannot
|
|
454
|
+
be delivered to the remote Cursor workspace.
|
|
455
|
+
|
|
456
|
+
Provider-native metadata is available through `box.backend` when the backend
|
|
457
|
+
SDK exposes it:
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
const account = await box.backend.account();
|
|
461
|
+
const models = await box.backend.models();
|
|
462
|
+
const repositories = await box.backend.repositories();
|
|
463
|
+
const agents = await box.backend.agents({ limit: 20 });
|
|
464
|
+
const agent = await box.backend.agent(agents.items[0].agentId);
|
|
465
|
+
const runs = await box.backend.runs(agent.agentId, { limit: 20 });
|
|
466
|
+
const run = await box.backend.run(runs.items[0].id, {
|
|
467
|
+
agentId: agent.agentId,
|
|
468
|
+
});
|
|
469
|
+
const messages = await box.backend.agentMessages(agent.agentId, { limit: 50 });
|
|
470
|
+
const artifacts = await box.backend.artifacts("active-session-id");
|
|
471
|
+
const bytes = await box.backend.downloadArtifact(
|
|
472
|
+
"active-session-id",
|
|
473
|
+
artifacts[0].path,
|
|
474
|
+
);
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Unsupported provider-control methods return the backend error; the SDK does not
|
|
478
|
+
fabricate catalog, run, or artifact data for backends that do not expose it.
|
|
479
|
+
|
|
335
480
|
#### `box.task(message, options?)`
|
|
336
481
|
|
|
337
482
|
Run a multi-turn agent task. The agent keeps working until completion.
|
|
@@ -551,6 +696,317 @@ box.expiresAt // Date | undefined
|
|
|
551
696
|
box.error // Error message if failed
|
|
552
697
|
```
|
|
553
698
|
|
|
699
|
+
## Fleets
|
|
700
|
+
|
|
701
|
+
A **fleet** is a coordinated group of sandboxes that runs one logical workload across many machines. Fleets are the canonical primitive for parallel work, distributed simulation, multi-agent experiments, or any task that needs more than one sandbox under one lifecycle.
|
|
702
|
+
|
|
703
|
+
Fleets give you:
|
|
704
|
+
|
|
705
|
+
- A single id (`fleetId`) plus a stable machine id (`machineId`) per member that you choose
|
|
706
|
+
- Policy enforcement (CPU / memory / storage / accelerator caps, allowed drivers / images / templates, max spend, max concurrent creates) — checked client-side **before** sandboxes are provisioned
|
|
707
|
+
- Per-dispatch parallelism, retries, timeouts, idempotency, cancellation, and result buffering
|
|
708
|
+
- Shared workspace modes (`isolated`, `shared`) with cross-machine snapshot, restore, and reconcile
|
|
709
|
+
- Fleet-scoped telemetry: usage, cost estimate, trace bundle, embedded intelligence envelope, and full intelligence reports
|
|
710
|
+
- Scoped tokens for handing a fleet to a downstream service without leaking the parent API key
|
|
711
|
+
|
|
712
|
+
### Create a fleet
|
|
713
|
+
|
|
714
|
+
There are two creation shapes. Use `create` when every machine is symmetric, and `createWithCoordinator` when one machine acts as orchestrator over a pool of workers.
|
|
715
|
+
|
|
716
|
+
```typescript
|
|
717
|
+
// Symmetric fleet
|
|
718
|
+
const fleet = await client.fleets.create({
|
|
719
|
+
defaults: {
|
|
720
|
+
image: "python:3.12",
|
|
721
|
+
maxLifetimeSeconds: 60 * 60,
|
|
722
|
+
},
|
|
723
|
+
policy: {
|
|
724
|
+
maxMachines: 4,
|
|
725
|
+
maxConcurrentCreates: 2,
|
|
726
|
+
maxTotalCpu: 8,
|
|
727
|
+
maxTotalMemoryMb: 16_384,
|
|
728
|
+
maxSpendUsd: 5,
|
|
729
|
+
allowAccelerators: false,
|
|
730
|
+
},
|
|
731
|
+
machines: [
|
|
732
|
+
{ machineId: "worker-1", resources: { cpuCores: 2, memoryMB: 4096 } },
|
|
733
|
+
{ machineId: "worker-2", resources: { cpuCores: 2, memoryMB: 4096 } },
|
|
734
|
+
],
|
|
735
|
+
workspace: { mode: "isolated" },
|
|
736
|
+
metadata: { experiment: "react-19-bump" },
|
|
737
|
+
idempotencyKey: "exp-react-19-2025-05-18",
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// Coordinator + workers
|
|
741
|
+
const cluster = await client.fleets.createWithCoordinator({
|
|
742
|
+
defaults: { image: "python:3.12" },
|
|
743
|
+
coordinator: { resources: { cpuCores: 1, memoryMB: 1024 } },
|
|
744
|
+
workers: [
|
|
745
|
+
{ machineId: "worker-1", resources: { cpuCores: 2, memoryMB: 4096 } },
|
|
746
|
+
{ machineId: "worker-2", resources: { cpuCores: 2, memoryMB: 4096 } },
|
|
747
|
+
],
|
|
748
|
+
});
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
`createWithCoordinator` is sugar over `create`: it injects a `coordinator` machine with `role: "coordinator"` and tags the workers `role: "worker"` in metadata. After creation both shapes return a `SandboxFleet` instance.
|
|
752
|
+
|
|
753
|
+
### Dispatch across machines
|
|
754
|
+
|
|
755
|
+
```typescript
|
|
756
|
+
// Fire and collect — returns FleetExecDispatchResult[]
|
|
757
|
+
const results = await fleet.dispatchExec("pytest -q", {
|
|
758
|
+
machines: fleet.ids, // default: every machine
|
|
759
|
+
maxConcurrent: 2,
|
|
760
|
+
timeoutMs: 60_000,
|
|
761
|
+
retry: { attempts: 2, backoffMs: 1_000 },
|
|
762
|
+
dispatchId: "pytest-run-1", // idempotent — same id replays the same dispatch
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
// Detailed — returns the full response including dispatchId, durationMs,
|
|
766
|
+
// trace, and the embedded intelligence envelope (always present on dispatch).
|
|
767
|
+
const detailed = await fleet.dispatchExecDetailed("pytest -q");
|
|
768
|
+
console.log(detailed.intelligence.signals);
|
|
769
|
+
|
|
770
|
+
// Prompt variant — runs an agent prompt on each selected machine
|
|
771
|
+
const promptResults = await fleet.dispatchPrompt(
|
|
772
|
+
"Summarize what changed in this branch and why.",
|
|
773
|
+
{ machines: ["worker-1"], model: "anthropic/claude-sonnet-4-20250514" },
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
// Stream events as they happen
|
|
777
|
+
for await (const event of fleet.dispatchExecStream("npm test", {
|
|
778
|
+
machines: fleet.ids,
|
|
779
|
+
})) {
|
|
780
|
+
if (event.type === "result") console.log(event.data);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Read previously-buffered results by dispatchId
|
|
784
|
+
const buffered = await fleet.dispatchResults("pytest-run-1", {
|
|
785
|
+
limit: 100,
|
|
786
|
+
machines: ["worker-1", "worker-2"],
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
// Cancel an in-flight dispatch
|
|
790
|
+
await fleet.cancelDispatch("pytest-run-1", "abandoning experiment");
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### Single-machine helpers
|
|
794
|
+
|
|
795
|
+
When you want to talk to one machine in the fleet directly:
|
|
796
|
+
|
|
797
|
+
```typescript
|
|
798
|
+
const result = await fleet.exec("worker-1", "echo hello");
|
|
799
|
+
const reply = await fleet.prompt("worker-1", "What is the structure?");
|
|
800
|
+
const box = await fleet.sandbox("worker-1"); // full SandboxInstance
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
### Workspace snapshots (shared workspace mode)
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
const snap = await fleet.createWorkspaceSnapshot();
|
|
807
|
+
await fleet.restoreWorkspaceSnapshot(snap.snapshotId);
|
|
808
|
+
await fleet.reconcileWorkspace();
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
### Dynamic topology
|
|
812
|
+
|
|
813
|
+
```typescript
|
|
814
|
+
await fleet.attachMachine({
|
|
815
|
+
machineId: "worker-3",
|
|
816
|
+
sandboxId: "sbx_abc", // existing sandbox to bind into the fleet
|
|
817
|
+
role: "worker",
|
|
818
|
+
});
|
|
819
|
+
await fleet.detachMachine("worker-3");
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
### Artifacts, usage, cost, tokens
|
|
823
|
+
|
|
824
|
+
```typescript
|
|
825
|
+
const artifacts = await fleet.collectArtifacts([
|
|
826
|
+
{ machineId: "worker-1", path: "/workspace/report.json", maxBytes: 1_000_000 },
|
|
827
|
+
]);
|
|
828
|
+
|
|
829
|
+
const usage = await fleet.usage(); // current usage rollup
|
|
830
|
+
const estimate = await fleet.cost(); // cost estimate
|
|
831
|
+
const manifest = await fleet.manifest(); // machine manifest as persisted
|
|
832
|
+
|
|
833
|
+
// Scoped token — hand to a downstream service without leaking the parent key
|
|
834
|
+
const token = await fleet.createToken({ expiresInSeconds: 3600 });
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
### Pre-flight cost estimate (without creating)
|
|
838
|
+
|
|
839
|
+
```typescript
|
|
840
|
+
const preEstimate = await client.fleets.estimateCost({
|
|
841
|
+
defaults: { image: "python:3.12" },
|
|
842
|
+
policy: { maxMachines: 4, maxSpendUsd: 5 },
|
|
843
|
+
machines: [
|
|
844
|
+
{ machineId: "worker-1", resources: { cpuCores: 2, memoryMB: 4096 } },
|
|
845
|
+
{ machineId: "worker-2", resources: { cpuCores: 2, memoryMB: 4096 } },
|
|
846
|
+
],
|
|
847
|
+
});
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
### Operations
|
|
851
|
+
|
|
852
|
+
```typescript
|
|
853
|
+
const caps = await client.fleets.capabilities(); // which drivers / templates / images
|
|
854
|
+
const ops = await client.fleets.operations(); // operations summary
|
|
855
|
+
const recon = await client.fleets.reconcile(); // reconcile drift
|
|
856
|
+
const reaped = await client.fleets.reapExpired(); // sweep expired fleets
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
### Lookup and delete
|
|
860
|
+
|
|
861
|
+
```typescript
|
|
862
|
+
const found = await client.fleets.list({ fleetId: "fleet_abc" });
|
|
863
|
+
await client.fleets.delete("fleet_abc", { continueOnError: true });
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
### Single-shot batches without fleets
|
|
867
|
+
|
|
868
|
+
For one-shot, ad-hoc parallel work that does not need fleet-level policy / workspaces / dispatch buffering / intelligence reports, `client.runBatch(tasks, options)` is the simpler primitive. New code that needs more than one sandbox under one logical lifecycle should reach for fleets.
|
|
869
|
+
|
|
870
|
+
## Intelligence Reports
|
|
871
|
+
|
|
872
|
+
The **Intelligence Reports** API generates structured post-hoc analyses over two subject types: a single **sandbox** or a single **fleet**. A fleet subject can optionally be narrowed to one dispatch within the fleet via `subject.dispatchId`. Two modes:
|
|
873
|
+
|
|
874
|
+
- **`deterministic`** (default) — platform-side rule-based analysis. Free. Returns immediately or near-immediately. Surfaces lifecycle, runtime, plan-headroom, and resource-density signals derived directly from your trace evidence.
|
|
875
|
+
- **`agentic`** — runs the **Tangle Trace Analyst**, an LLM-driven reasoning loop, over your OTLP trace evidence. Returns findings with evidence references, recommended actions, and a validation plan. **Billed** against `budget.maxUsd`; the platform never spends past the budget you set. Async (returns a job; poll for terminal state).
|
|
876
|
+
|
|
877
|
+
The dedicated client lives on `client.intelligence`:
|
|
878
|
+
|
|
879
|
+
```typescript
|
|
880
|
+
import { Sandbox } from "@tangle-network/sandbox";
|
|
881
|
+
import type { IntelligenceClient } from "@tangle-network/sandbox";
|
|
882
|
+
|
|
883
|
+
const client = new Sandbox({ apiKey, baseUrl });
|
|
884
|
+
const intelligence: IntelligenceClient = client.intelligence;
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
### Create a report
|
|
888
|
+
|
|
889
|
+
```typescript
|
|
890
|
+
// Deterministic — over a single sandbox
|
|
891
|
+
const det = await client.intelligence.createReport({
|
|
892
|
+
subject: { type: "sandbox", id: box.id },
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
// Deterministic — over a fleet
|
|
896
|
+
await client.intelligence.createReport({
|
|
897
|
+
subject: { type: "fleet", id: fleet.fleetId },
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
// Deterministic — narrowed to one dispatch within a fleet
|
|
901
|
+
await client.intelligence.createReport({
|
|
902
|
+
subject: {
|
|
903
|
+
type: "fleet",
|
|
904
|
+
id: fleet.fleetId,
|
|
905
|
+
dispatchId: "pytest-run-1",
|
|
906
|
+
},
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
// Agentic — billed against the budget
|
|
910
|
+
const agentic = await client.intelligence.createReport({
|
|
911
|
+
subject: { type: "fleet", id: fleet.fleetId },
|
|
912
|
+
mode: "agentic",
|
|
913
|
+
budget: { billTo: "customer", maxUsd: 5 },
|
|
914
|
+
acknowledgeCost: true,
|
|
915
|
+
metadata: { experiment: "react-19-bump" },
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
// Shorthand for the agentic + budget pattern
|
|
919
|
+
const shortcut = await client.intelligence.createAgenticReport({
|
|
920
|
+
subject: { type: "sandbox", id: box.id },
|
|
921
|
+
maxUsd: 2,
|
|
922
|
+
});
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
### Poll a report to completion
|
|
926
|
+
|
|
927
|
+
Agentic reports return `status: "pending"` immediately. Either poll yourself with `getReport`, or use the built-in `waitForReport`:
|
|
928
|
+
|
|
929
|
+
```typescript
|
|
930
|
+
const job = await client.intelligence.createAgenticReport({
|
|
931
|
+
subject: { type: "fleet", id: fleet.fleetId },
|
|
932
|
+
maxUsd: 5,
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
const completed = await client.intelligence.waitForReport(job.jobId, {
|
|
936
|
+
timeoutMs: 5 * 60 * 1000,
|
|
937
|
+
pollMs: 2_000,
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
if (completed.status === "completed") {
|
|
941
|
+
console.log(completed.findings);
|
|
942
|
+
console.log(completed.recommendedActions);
|
|
943
|
+
}
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
### List existing reports
|
|
947
|
+
|
|
948
|
+
```typescript
|
|
949
|
+
const recent = await client.intelligence.listReports({
|
|
950
|
+
subjectType: "fleet",
|
|
951
|
+
subjectId: fleet.fleetId,
|
|
952
|
+
limit: 20,
|
|
953
|
+
});
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
### Per-subject shortcuts
|
|
957
|
+
|
|
958
|
+
`SandboxInstance` and `SandboxFleet` expose convenience wrappers so you don't have to thread `subject` manually:
|
|
959
|
+
|
|
960
|
+
```typescript
|
|
961
|
+
await box.createIntelligenceReport({ mode: "deterministic" });
|
|
962
|
+
await box.createAgenticIntelligenceReport({ maxUsd: 2 });
|
|
963
|
+
|
|
964
|
+
await fleet.createIntelligenceReport({ mode: "deterministic" });
|
|
965
|
+
await fleet.createAgenticIntelligenceReport({ maxUsd: 5 });
|
|
966
|
+
|
|
967
|
+
// Fleet helpers accept the v2 refinement fields directly:
|
|
968
|
+
await fleet.createIntelligenceReport({
|
|
969
|
+
mode: "deterministic",
|
|
970
|
+
dispatchId: "pytest-run-1",
|
|
971
|
+
});
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
Both wrappers post to `POST /v1/intelligence/reports` with the right `subject` filled in.
|
|
975
|
+
|
|
976
|
+
### Time windows and baselines
|
|
977
|
+
|
|
978
|
+
Every report can be narrowed by a time window and compared against a same-type baseline. The analyzer rejects mixed-type comparisons because the delta would be meaningless.
|
|
979
|
+
|
|
980
|
+
```typescript
|
|
981
|
+
// Bound the analysis to a one-hour window.
|
|
982
|
+
await fleet.createIntelligenceReport({
|
|
983
|
+
window: { since: Date.now() - 60 * 60 * 1000 },
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
// Compare two dispatches of the same fleet against each other.
|
|
987
|
+
await fleet.createIntelligenceReport({
|
|
988
|
+
dispatchId: "run-after",
|
|
989
|
+
compareTo: { type: "fleet", id: fleet.fleetId, dispatchId: "run-before" },
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
// Sandbox baseline.
|
|
993
|
+
await box.createIntelligenceReport({
|
|
994
|
+
compareTo: { type: "sandbox", id: previousBox.id },
|
|
995
|
+
});
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
### Cost before commit
|
|
999
|
+
|
|
1000
|
+
Estimate cost without creating a report. Subject ownership is verified the same way as `createReport`, so the endpoint never becomes an existence oracle for foreign subjects.
|
|
1001
|
+
|
|
1002
|
+
```typescript
|
|
1003
|
+
const estimate = await client.intelligence.estimateReport({
|
|
1004
|
+
subject: { type: "fleet", id: fleet.fleetId, dispatchId: "pytest-run-1" },
|
|
1005
|
+
mode: "agentic",
|
|
1006
|
+
});
|
|
1007
|
+
console.log(`Would cost ${estimate.costUsd} USD (${estimate.reason})`);
|
|
1008
|
+
```
|
|
1009
|
+
|
|
554
1010
|
## Error Handling
|
|
555
1011
|
|
|
556
1012
|
```typescript
|
|
@@ -602,6 +1058,38 @@ import type {
|
|
|
602
1058
|
BatchResult,
|
|
603
1059
|
BatchOptions,
|
|
604
1060
|
UsageInfo,
|
|
1061
|
+
// Fleets
|
|
1062
|
+
CreateSandboxFleetOptions,
|
|
1063
|
+
CreateSandboxFleetWithCoordinatorOptions,
|
|
1064
|
+
SandboxFleetMachineSpec,
|
|
1065
|
+
SandboxFleetInfo,
|
|
1066
|
+
SandboxFleetManifest,
|
|
1067
|
+
SandboxFleetUsage,
|
|
1068
|
+
SandboxFleetCostEstimate,
|
|
1069
|
+
SandboxFleetToken,
|
|
1070
|
+
SandboxFleetTraceBundle,
|
|
1071
|
+
SandboxFleetTraceOptions,
|
|
1072
|
+
SandboxFleetDispatchResponse,
|
|
1073
|
+
FleetExecDispatchOptions,
|
|
1074
|
+
FleetExecDispatchResult,
|
|
1075
|
+
FleetPromptDispatchOptions,
|
|
1076
|
+
FleetPromptDispatchResult,
|
|
1077
|
+
FleetDispatchResultBuffer,
|
|
1078
|
+
FleetDispatchResultBufferOptions,
|
|
1079
|
+
FleetDispatchStreamOptions,
|
|
1080
|
+
FleetDispatchCancelResult,
|
|
1081
|
+
FleetMachineId,
|
|
1082
|
+
// Intelligence Reports
|
|
1083
|
+
IntelligenceReport,
|
|
1084
|
+
IntelligenceReportBudget,
|
|
1085
|
+
CreateIntelligenceReportOptions,
|
|
1086
|
+
} from "@tangle-network/sandbox";
|
|
1087
|
+
|
|
1088
|
+
// Concrete classes — useful when you need to reference the type itself
|
|
1089
|
+
import {
|
|
1090
|
+
IntelligenceClient,
|
|
1091
|
+
SandboxFleet,
|
|
1092
|
+
SandboxFleetClient,
|
|
605
1093
|
} from "@tangle-network/sandbox";
|
|
606
1094
|
```
|
|
607
1095
|
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
export { AnyTokenPayload, BatchScopedTokenPayload, CollaborationAccess, CollaborationTokenPayload, IssueCollaborationTokenOptions, ProductTokenIssuer, ProjectScopedTokenPayload, ReadTokenPayload, SessionScopedTokenPayload, TokenScope,
|
|
1
|
+
import { _ as ReadTokenPayload, a as issueCollaborationToken, c as issueSessionScopedToken, d as AnyTokenPayload, f as BatchScopedTokenPayload, g as ProjectScopedTokenPayload, h as IssueCollaborationTokenOptions, i as issueBatchScopedToken, l as unsafeDecodeToken, m as CollaborationTokenPayload, n as getTokenTTL, o as issueProjectScopedToken, p as CollaborationAccess, r as isTokenExpiringSoon, s as issueReadToken, t as ProductTokenIssuer, u as verifyToken, v as SessionScopedTokenPayload, y as TokenScope } from "../index-Dpj1oB5i.js";
|
|
2
|
+
export { AnyTokenPayload, BatchScopedTokenPayload, CollaborationAccess, CollaborationTokenPayload, IssueCollaborationTokenOptions, ProductTokenIssuer, ProjectScopedTokenPayload, ReadTokenPayload, SessionScopedTokenPayload, TokenScope, getTokenTTL, isTokenExpiringSoon, issueBatchScopedToken, issueCollaborationToken, issueProjectScopedToken, issueReadToken, issueSessionScopedToken, unsafeDecodeToken, verifyToken };
|