@tangle-network/sandbox 0.1.1 → 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 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
- - **Batch Execution** - Run tasks across multiple sandboxes in parallel
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 multiple sandboxes in parallel.
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
 
@@ -1,2 +1,2 @@
1
- import { _ as SessionScopedTokenPayload, a as issueBatchScopedToken, c as issueReadToken, d as BatchScopedTokenPayload, f as CollaborationAccess, g as ReadTokenPayload, h as ProjectScopedTokenPayload, i as isTokenExpiringSoon, l as issueSessionScopedToken, m as IssueCollaborationTokenOptions, n as decodeToken, o as issueCollaborationToken, p as CollaborationTokenPayload, r as getTokenTTL, s as issueProjectScopedToken, t as ProductTokenIssuer, u as AnyTokenPayload, v as TokenScope } from "../index-gA-oRjOi.js";
2
- export { AnyTokenPayload, BatchScopedTokenPayload, CollaborationAccess, CollaborationTokenPayload, IssueCollaborationTokenOptions, ProductTokenIssuer, ProjectScopedTokenPayload, ReadTokenPayload, SessionScopedTokenPayload, TokenScope, decodeToken, getTokenTTL, isTokenExpiringSoon, issueBatchScopedToken, issueCollaborationToken, issueProjectScopedToken, issueReadToken, issueSessionScopedToken };
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 };