@tangle-network/sandbox 1.0.190 → 1.0.191

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 (57) hide show
  1. package/dist/agent/index.d.ts +4 -4
  2. package/dist/agent/index.js +9 -6
  3. package/dist/agent/preview.d.ts +1 -1
  4. package/dist/agent/preview.js +1 -1
  5. package/dist/{agent-run-outcome-Cst4px_l.d.ts → agent-run-outcome-BO5paRhX.d.ts} +2 -2
  6. package/dist/{api-request-CZlFy3ku.js → api-request-BvpTwQKs.js} +1 -1
  7. package/dist/{browser-interactive-control-controller-DHIS8edi.js → browser-interactive-control-controller-BYbNLahH.js} +1 -1
  8. package/dist/{browser-interactive-control-controller-DIvMQyer.d.ts → browser-interactive-control-controller-KOK6qn2s.d.ts} +1 -1
  9. package/dist/{chunked-upload-vAE2hVcP.js → chunked-upload-D5ytV_s6.js} +9 -13
  10. package/dist/{chunked-upload-file-HLSriv3V.js → chunked-upload-file-DL1-AqRp.js} +2 -2
  11. package/dist/{client-BERYosgI.js → client-XjkuWhog.js} +159 -109
  12. package/dist/{client-C4SnCCpF.d.ts → client-yWdLuj_P.d.ts} +5 -5
  13. package/dist/collaboration/index.js +1 -1
  14. package/dist/{collaboration-CmT6Ygiv.js → collaboration-Bbr45lyk.js} +3 -3
  15. package/dist/{core-BT-0QZT-.js → core-BU1I2KvV.js} +13 -7
  16. package/dist/core.d.ts +6 -6
  17. package/dist/core.js +5 -5
  18. package/dist/errors-Mzq10WZq.js +583 -0
  19. package/dist/{file-download-BXgu9BNh.js → file-download-CKOxcATB.js} +1 -1
  20. package/dist/{index-DDIymnCZ.d.ts → index-DKsOu5sq.d.ts} +5 -5
  21. package/dist/index.d.ts +9 -9
  22. package/dist/index.js +10 -10
  23. package/dist/intelligence/index.js +9 -5
  24. package/dist/interactive-control.d.ts +1 -1
  25. package/dist/interactive-control.js +1 -1
  26. package/dist/{interactive-controller-DXGWBdYK.d.ts → interactive-controller-YyZEsoB0.d.ts} +3 -3
  27. package/dist/{interactive-controller-C876Pjaj.js → interactive-controller-jr3_74Pq.js} +90 -57
  28. package/dist/lib/prepared-evidence.js +1 -1
  29. package/dist/lib/prepared-process.js +1 -1
  30. package/dist/{lines-Br3ygqhP.d.ts → lines-CCHDUfQg.d.ts} +1 -1
  31. package/dist/{lines-DxHJhxCe.js → lines-DrG0WOW6.js} +1 -1
  32. package/dist/lines.d.ts +1 -1
  33. package/dist/lines.js +1 -1
  34. package/dist/{mcp-local-D7MpwsW0.js → mcp-local-Dler9R_s.js} +3 -3
  35. package/dist/{prepared-evidence-CP1MmeCQ.js → prepared-evidence-4prvV0YK.js} +1 -1
  36. package/dist/{prepared-process-BWVz93cT.js → prepared-process-BSpZXDDw.js} +1 -1
  37. package/dist/{preview-DUxqnKSk.d.ts → preview-I1z42VGY.d.ts} +3 -3
  38. package/dist/{public-templates-DQiblzzK.d.ts → public-templates-Cw6gmWJD.d.ts} +2 -2
  39. package/dist/public-templates.d.ts +1 -1
  40. package/dist/public-templates.js +7 -4
  41. package/dist/{raw-evidence-archive-CgbWa4es.js → raw-evidence-archive-BD37zHN5.js} +2 -2
  42. package/dist/runtime.d.ts +5 -5
  43. package/dist/runtime.js +9 -7
  44. package/dist/{sandbox-B_Xbc9wR.js → sandbox-Dtb-_btd.js} +317 -221
  45. package/dist/select-seat-command.d.ts +1 -1
  46. package/dist/select-seat-command.js +5 -2
  47. package/dist/session-gateway/index.js +1 -1
  48. package/dist/tangle/index.d.ts +1 -1
  49. package/dist/tangle/index.js +1 -1
  50. package/dist/{tangle-Sg9ORVAA.js → tangle-fs17QlcY.js} +2 -2
  51. package/dist/{terminal-stream-types-1YKRrkZB.d.ts → terminal-stream-types-BqNv5EuG.d.ts} +280 -305
  52. package/dist/{types-B-UsXSi6.d.ts → types-CiuGb4lZ.d.ts} +4 -2
  53. package/package.json +6 -6
  54. package/dist/errors-CaX0a5Ry.js +0 -636
  55. /package/dist/{abort-signal-PgWPMn_e.js → abort-signal-Cu4F2VVz.js} +0 -0
  56. /package/dist/{file-transfer-DTHgLccV.js → file-transfer-Cr9Ossea.js} +0 -0
  57. /package/dist/{retry-after-header-Bt-XvU-x.js → retry-after-header-DUdCC0KJ.js} +0 -0
@@ -1,13 +1,13 @@
1
- import { A as normalizeRuntimeBackendConfig, D as applyRequestedModel, E as isBrowserRuntime, H as backendTypeSchema, N as withProfileHarness, O as assertCurrentBackendType, P as withoutCreateOnlyModelFields, S as encodePromptForWire, o as requestInteractiveSessionToken } from "./interactive-controller-C876Pjaj.js";
1
+ import { A as normalizeRuntimeBackendConfig, D as applyRequestedModel, E as isBrowserRuntime, H as backendTypeSchema, N as withProfileHarness, O as assertCurrentBackendType, P as withoutCreateOnlyModelFields, S as encodePromptForWire, o as requestInteractiveSessionToken } from "./interactive-controller-jr3_74Pq.js";
2
2
  import { r as agentProfileSchema } from "./profile-schema-C1KpT-wS.js";
3
- import { A as normalizeSandboxCreateReceipt, B as CAPACITY_ETA_WAIT_MAX_MS, D as validateDeferredProfileFileMounts, E as splitInlineProfileSkills, F as normalizeConnection, G as extendableDeadline, I as normalizeEgressPolicyFields, J as isDeployWindowCreateError, K as generateIdempotencyKey, L as normalizeProxySecretReceipt, N as dateOf, O as isRetryableProfileFileMaterializeFailure, P as parseLifecycleTimes, R as normalizeSandboxShape, T as splitInlineProfileFileMounts, U as DEPLOY_WINDOW_RETRY_MS, W as createRetryDelayMs, X as isRetryableCreateError, Y as isRateLimitCreateError, d as sandboxLedgerUsd, j as normalizeStartupDiagnostics, k as normalizeCaptureProof, n as createSandboxInstanceFromResponse, o as exportTraceBundle, q as isCapacityShortageCreateError, z as CAPACITY_ETA_MARGIN_MS } from "./sandbox-B_Xbc9wR.js";
4
- import { l as SANDBOX_AGENT_HOME, t as isInsideReservedDir } from "./file-transfer-DTHgLccV.js";
5
- import { E as ValidationError, S as ServerError, T as TimeoutError, _ as PartialFailureError, a as AuthError, g as NotFoundError, h as NetworkError, i as readResponseJson, n as parseErrorResponse, r as parseResponseError, x as SandboxError } from "./errors-CaX0a5Ry.js";
6
- import { r as combineAbortSignals, t as abortableSleep } from "./abort-signal-PgWPMn_e.js";
7
- import { n as fetchSandboxApi } from "./api-request-CZlFy3ku.js";
8
- import { n as fetchWithKeepAlive } from "./keep-alive-fetch-C2lFwrMA.js";
3
+ import { A as normalizeSandboxCreateReceipt, B as CAPACITY_ETA_WAIT_MAX_MS, D as validateDeferredProfileFileMounts, E as splitInlineProfileSkills, F as normalizeConnection, G as extendableDeadline, I as normalizeEgressPolicyFields, J as isDeployWindowCreateError, K as generateIdempotencyKey, L as normalizeProxySecretReceipt, N as dateOf, O as isRetryableProfileFileMaterializeFailure, P as parseLifecycleTimes, R as normalizeSandboxShape, T as splitInlineProfileFileMounts, U as DEPLOY_WINDOW_RETRY_MS, W as createRetryDelayMs, X as isRetryableCreateError, Y as isRateLimitCreateError, d as sandboxLedgerUsd, j as normalizeStartupDiagnostics, k as normalizeCaptureProof, n as createSandboxInstanceFromResponse, o as exportTraceBundle, q as isCapacityShortageCreateError, z as CAPACITY_ETA_MARGIN_MS } from "./sandbox-Dtb-_btd.js";
4
+ import { C as ServerError, D as ValidationError, E as TimeoutError, S as SandboxError, _ as NotFoundError, a as readResponseJson, g as NetworkError, i as parseResponseError, o as retryWhen, r as parseErrorResponse, s as AuthError, v as PartialFailureError } from "./errors-Mzq10WZq.js";
5
+ import { r as combineAbortSignals, t as abortableSleep } from "./abort-signal-Cu4F2VVz.js";
6
+ import { l as SANDBOX_AGENT_HOME, t as isInsideReservedDir } from "./file-transfer-Cr9Ossea.js";
7
+ import { n as fetchSandboxApi } from "./api-request-BvpTwQKs.js";
8
+ import { n as fetchWithKeepAlive, t as emitTiming } from "./keep-alive-fetch-C2lFwrMA.js";
9
9
  import { t as parseSSEStream } from "./sse-parser-B4aH89Nq.js";
10
- import { t as LinesClient } from "./lines-DxHJhxCe.js";
10
+ import { t as LinesClient } from "./lines-DrG0WOW6.js";
11
11
  import { PublicTemplatesClient } from "./public-templates.js";
12
12
  import { z } from "zod";
13
13
  //#region ../../../packages/runtime-contracts/dist/git-policy.js
@@ -760,11 +760,12 @@ function validateBatchRunRequest(request) {
760
760
  const issue = rootIssue ? requestValidationIssue(rootIssue, request.persistent === true) : void 0;
761
761
  if (!issue) throw new ValidationError("Invalid batch request");
762
762
  const path = formatIssuePath(issue.path.length === 0 && issue.code === "unrecognized_keys" ? [issue.keys?.[0] ?? "request"] : issue.path);
763
- throw new ValidationError(path ? `${path}: ${issue.message}` : issue.message, path ? { [path]: issue.message } : void 0);
763
+ throw new ValidationError(path ? `${path}: ${issue.message}` : issue.message, { fields: path ? { [path]: issue.message } : void 0 });
764
764
  }
765
765
  function mapBatchStreamError(error) {
766
766
  if (!(error instanceof BatchStreamError)) throw error;
767
- if (error.kind === "failed") throw new ServerError(error.message, 500, {
767
+ if (error.kind === "failed") throw new ServerError(error.message, {
768
+ status: 500,
768
769
  endpoint: BATCH_RUN_PATH,
769
770
  origin: "sandbox-api"
770
771
  });
@@ -887,7 +888,7 @@ var IntelligenceClient = class {
887
888
  while (true) {
888
889
  const report = await this.getReport(jobId);
889
890
  if (report.status === "completed" || report.status === "failed") return report;
890
- if (Date.now() >= deadline) throw new TimeoutError(options.timeoutMs ?? 6e4);
891
+ if (Date.now() >= deadline) throw new TimeoutError(`Request timed out after ${options.timeoutMs ?? 6e4}ms`, { timeoutMs: options.timeoutMs ?? 6e4 });
891
892
  await new Promise((resolve) => setTimeout(resolve, Math.min(pollMs, Math.max(0, deadline - Date.now()))));
892
893
  }
893
894
  }
@@ -936,10 +937,12 @@ const backendRegistryResponseSchema = z.object({
936
937
  timestamp: z.string().datetime()
937
938
  });
938
939
  function invalidBackendRegistry(message) {
939
- throw new ServerError(message, 502, {
940
+ throw new ServerError(message, {
941
+ status: 502,
940
942
  origin: "sandbox-api",
941
- endpoint: "/v1/backends"
942
- }, "INVALID_BACKEND_REGISTRY");
943
+ endpoint: "/v1/backends",
944
+ code: "INVALID_BACKEND_REGISTRY"
945
+ });
943
946
  }
944
947
  /**
945
948
  * Validate required registry fields and ignore fields from newer API versions.
@@ -1293,7 +1296,7 @@ var SandboxTransport = class {
1293
1296
  }
1294
1297
  /** Fetch a Platform API path (e.g. the agent-profile catalog) with this key. */
1295
1298
  async platformFetch(path, options) {
1296
- if (!this.platformUrl) throw new ValidationError("platformUrl is required for Platform calls when baseUrl is not https://sandbox.tangle.tools", { platformUrl: "required" });
1299
+ if (!this.platformUrl) throw new ValidationError("platformUrl is required for Platform calls when baseUrl is not https://sandbox.tangle.tools", { fields: { platformUrl: "required" } });
1297
1300
  const headers = new Headers(options?.headers);
1298
1301
  if (!headers.has("Authorization")) headers.set("Authorization", `Bearer ${this.#apiKey}`);
1299
1302
  return fetch(`${this.platformUrl}${path}`, {
@@ -1309,8 +1312,9 @@ var SandboxTransport = class {
1309
1312
  apiKey: this.#apiKey,
1310
1313
  timeoutMs: this.timeoutMs,
1311
1314
  signal: this.signal,
1312
- timeoutError: (ms) => new TimeoutError(ms),
1313
- networkError: (err) => new NetworkError(`Failed to connect to Sandbox API: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0, {
1315
+ timeoutError: (ms) => new TimeoutError(`Request timed out after ${ms}ms`, { timeoutMs: ms }),
1316
+ networkError: (err) => new NetworkError(`Failed to connect to Sandbox API: ${err instanceof Error ? err.message : String(err)}`, {
1317
+ cause: err instanceof Error ? err : void 0,
1314
1318
  endpoint: path,
1315
1319
  origin: "sandbox-api"
1316
1320
  }),
@@ -1387,10 +1391,12 @@ const OUTCOMES = new Set([
1387
1391
  "failed"
1388
1392
  ]);
1389
1393
  function invalidDeleteMatching(message) {
1390
- throw new ServerError(`Sandbox API answered delete-matching with ${message}`, 502, {
1394
+ throw new ServerError(`Sandbox API answered delete-matching with ${message}`, {
1395
+ status: 502,
1391
1396
  origin: "sandbox-api",
1392
- endpoint: DELETE_MATCHING_PATH
1393
- }, "INVALID_DELETE_MATCHING_RESPONSE");
1397
+ endpoint: DELETE_MATCHING_PATH,
1398
+ code: "INVALID_DELETE_MATCHING_RESPONSE"
1399
+ });
1394
1400
  }
1395
1401
  function isRecord(value) {
1396
1402
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1479,7 +1485,9 @@ var SandboxManagement = class extends SandboxTransport {
1479
1485
  */
1480
1486
  async waitForRunning(id, options) {
1481
1487
  const instance = await this.get(id);
1482
- if (!instance) throw new NotFoundError("Sandbox", id, {
1488
+ if (!instance) throw new NotFoundError(`Sandbox not found: ${id}`, {
1489
+ resourceType: "Sandbox",
1490
+ resourceId: id,
1483
1491
  endpoint: `/v1/sandboxes/${id}`,
1484
1492
  origin: "sandbox-api"
1485
1493
  });
@@ -1651,7 +1659,10 @@ var SandboxManagement = class extends SandboxTransport {
1651
1659
  */
1652
1660
  async shapes() {
1653
1661
  const data = await readResponseJson(await this.fetch("/v1/shapes"));
1654
- if (!Array.isArray(data?.presets) || typeof data.default !== "string" || !data.ceiling || typeof data.ceiling.cpuCores !== "number" || typeof data.ceiling.memoryMB !== "number" || typeof data.ceiling.diskGB !== "number") throw new SandboxError("GET /v1/shapes returned no usable shape catalogue", "INVALID_RESPONSE", 502);
1662
+ if (!Array.isArray(data?.presets) || typeof data.default !== "string" || !data.ceiling || typeof data.ceiling.cpuCores !== "number" || typeof data.ceiling.memoryMB !== "number" || typeof data.ceiling.diskGB !== "number") throw new SandboxError("GET /v1/shapes returned no usable shape catalogue", {
1663
+ code: "INVALID_RESPONSE",
1664
+ status: 502
1665
+ });
1655
1666
  return data;
1656
1667
  }
1657
1668
  /**
@@ -1892,7 +1903,11 @@ var SandboxCreation = class extends SandboxManagement {
1892
1903
  const spentBudget = elapsedMs >= budgetMs;
1893
1904
  const preamble = spentBudget ? `Sandbox create did not complete within ${budgetMs}ms. ` : `Sandbox create ended after ${elapsedMs}ms of its ${budgetMs}ms budget. `;
1894
1905
  const earlyExit = spentBudget || lastServerError?.retryAfterMs === void 0 ? "" : `The server asked for a ${Math.ceil(lastServerError.retryAfterMs / 1e3)}s wait, which the remaining budget cannot cover. `;
1895
- return new TimeoutError(budgetMs, preamble + (lastServerError ? `The last attempt failed with ${lastServerError.code}${lastServerError.status === void 0 ? "" : ` (HTTP ${lastServerError.status})`}: ${lastServerError.message}. ` : "") + earlyExit + `The provision may still be running server-side; retry create() with idempotencyKey="${idempotencyKey}" to join it.`, metadata, lastServerError);
1906
+ return new TimeoutError(preamble + (lastServerError ? `The last attempt failed with ${lastServerError.code}${lastServerError.status === void 0 ? "" : ` (HTTP ${lastServerError.status})`}: ${lastServerError.message}. ` : "") + earlyExit + `The provision may still be running server-side; retry create() with idempotencyKey="${idempotencyKey}" to join it.`, {
1907
+ timeoutMs: budgetMs,
1908
+ ...metadata,
1909
+ cause: lastServerError
1910
+ });
1896
1911
  };
1897
1912
  const sleepBeforeRetry = async (delayMs) => {
1898
1913
  try {
@@ -1903,6 +1918,13 @@ var SandboxCreation = class extends SandboxManagement {
1903
1918
  }
1904
1919
  };
1905
1920
  const waitBeforeRetry = async (cause, failedAttempt) => {
1921
+ if (cause instanceof SandboxError) emitTiming(this.onTiming, {
1922
+ name: "sdk.retry",
1923
+ durMs: 0,
1924
+ desc: `${cause.wireCode} retry-after=${cause.retryAfterMs ?? "none"}`,
1925
+ path: "/v1/sandboxes",
1926
+ status: cause.status
1927
+ });
1906
1928
  if (isPacedCreateError(cause)) {
1907
1929
  const delayMs = Math.max(createRetryDelayMs(cause, failedAttempt), 250);
1908
1930
  if (delayMs >= deployWindowDeadline - Date.now()) throw deployWindowDeadline === deadline ? createTimeoutError() : cause;
@@ -1911,7 +1933,7 @@ var SandboxCreation = class extends SandboxManagement {
1911
1933
  }
1912
1934
  const delayMs = createRetryDelayMs(cause, failedAttempt);
1913
1935
  if (cause instanceof SandboxError && delayMs >= deadline - Date.now()) {
1914
- if (proxyTimedOutCreate && cause.code === "IDEMPOTENT_CREATE_IN_PROGRESS") throw createTimeoutError();
1936
+ if (proxyTimedOutCreate && cause.wireCode === "IDEMPOTENT_CREATE_IN_PROGRESS") throw createTimeoutError();
1915
1937
  throw cause;
1916
1938
  }
1917
1939
  await sleepBeforeRetry(delayMs);
@@ -1922,7 +1944,7 @@ var SandboxCreation = class extends SandboxManagement {
1922
1944
  if (!isRetryableCreateError(error)) return true;
1923
1945
  if (options?.retry?.capacity === false && error instanceof SandboxError && error.code === "CAPACITY_EXHAUSTED") return true;
1924
1946
  if (isPacedCreateError(error)) return false;
1925
- if (proxyTimedOutCreate && error instanceof SandboxError && error.code === "IDEMPOTENT_CREATE_IN_PROGRESS") return false;
1947
+ if (proxyTimedOutCreate && error instanceof SandboxError && error.wireCode === "IDEMPOTENT_CREATE_IN_PROGRESS") return false;
1926
1948
  shortBudgetFailures += 1;
1927
1949
  return shortBudgetFailures >= 5;
1928
1950
  };
@@ -1949,7 +1971,7 @@ var SandboxCreation = class extends SandboxManagement {
1949
1971
  const error = parseErrorResponse(response.status, body, { allPreviousCreateAttemptsRefused }, response.headers);
1950
1972
  if (response.status === 524) proxyTimedOutCreate = true;
1951
1973
  allPreviousCreateAttemptsRefused = error.createFailureDisposition === "not-admitted";
1952
- if (error.code === "SANDBOX_CREATE_RECONCILING" && lastServerError && lastServerError.code !== "SANDBOX_CREATE_RECONCILING") throw lastServerError;
1974
+ if (error.wireCode === "SANDBOX_CREATE_RECONCILING" && lastServerError && lastServerError.wireCode !== "SANDBOX_CREATE_RECONCILING") throw lastServerError;
1953
1975
  lastServerError = error;
1954
1976
  if (requestOptions?.timeoutMs === void 0 && isCapacityShortageCreateError(error) && error.etaSeconds) {
1955
1977
  const until = Math.min(startedAt + CAPACITY_ETA_WAIT_MAX_MS, Date.now() + error.etaSeconds * 1e3 + CAPACITY_ETA_MARGIN_MS);
@@ -1968,24 +1990,28 @@ var SandboxCreation = class extends SandboxManagement {
1968
1990
  const data = await response.json();
1969
1991
  const info = this.parseInfo(data);
1970
1992
  const injectedSecrets = data.createReceipt?.injectedSecrets;
1971
- if (options?.requireNativeSessionCapture && (!info.captureProof || !info.createReceipt?.captureProof || JSON.stringify(info.captureProof) !== JSON.stringify(info.createReceipt.captureProof))) throw new PartialFailureError("The API did not confirm native session capture for the created container.", "NATIVE_CAPTURE_UNVERIFIED", 502, {
1993
+ if (options?.requireNativeSessionCapture && (!info.captureProof || !info.createReceipt?.captureProof || JSON.stringify(info.captureProof) !== JSON.stringify(info.createReceipt.captureProof))) throw new PartialFailureError("The API did not confirm native session capture for the created container.", {
1994
+ code: "NATIVE_CAPTURE_UNVERIFIED",
1995
+ status: 502,
1972
1996
  origin: "sandbox-api",
1973
- endpoint: "/v1/sandboxes"
1974
- }, [{
1975
- sandboxId: info.id,
1976
- idempotencyKey
1977
- }], void 0, {
1997
+ endpoint: "/v1/sandboxes",
1998
+ failures: [{
1999
+ sandboxId: info.id,
2000
+ idempotencyKey
2001
+ }],
1978
2002
  allocationId: info.id,
1979
2003
  idempotencyKey,
1980
2004
  createReceipt: info.createReceipt ?? null
1981
2005
  });
1982
- if (options?.ownerContext === "isolated" && (info.createReceipt?.ownerContext !== "isolated" || injectedSecrets !== void 0 && (!Array.isArray(injectedSecrets) || injectedSecrets.length !== 0))) throw new PartialFailureError("The host did not confirm isolated owner context. No post-create work was started. Inspect the recorded sandbox and retry identity before allocating a replacement.", "SANDBOX_ISOLATION_UNVERIFIED", 502, {
2006
+ if (options?.ownerContext === "isolated" && (info.createReceipt?.ownerContext !== "isolated" || injectedSecrets !== void 0 && (!Array.isArray(injectedSecrets) || injectedSecrets.length !== 0))) throw new PartialFailureError("The host did not confirm isolated owner context. No post-create work was started. Inspect the recorded sandbox and retry identity before allocating a replacement.", {
2007
+ code: "SANDBOX_ISOLATION_UNVERIFIED",
2008
+ status: 502,
1983
2009
  origin: "sandbox-api",
1984
- endpoint: "/v1/sandboxes"
1985
- }, [{
1986
- sandboxId: info.id,
1987
- idempotencyKey
1988
- }], void 0, {
2010
+ endpoint: "/v1/sandboxes",
2011
+ failures: [{
2012
+ sandboxId: info.id,
2013
+ idempotencyKey
2014
+ }],
1989
2015
  allocationId: info.id,
1990
2016
  idempotencyKey,
1991
2017
  createReceipt: info.createReceipt ?? null
@@ -1999,13 +2025,16 @@ var SandboxCreation = class extends SandboxManagement {
1999
2025
  signal: requestOptions?.signal
2000
2026
  });
2001
2027
  } catch (cause) {
2002
- throw new PartialFailureError(`Sandbox ${info.id} did not become ready: ${cause instanceof Error ? cause.message : String(cause)}`, "SANDBOX_CREATE_READINESS_FAILED", 502, {
2028
+ throw new PartialFailureError(`Sandbox ${info.id} did not become ready: ${cause instanceof Error ? cause.message : String(cause)}`, {
2029
+ code: "SANDBOX_CREATE_READINESS_FAILED",
2030
+ status: 502,
2003
2031
  origin: "sandbox-api",
2004
- endpoint: "/v1/sandboxes"
2005
- }, [{
2006
- sandboxId: info.id,
2007
- idempotencyKey
2008
- }], cause, {
2032
+ endpoint: "/v1/sandboxes",
2033
+ failures: [{
2034
+ sandboxId: info.id,
2035
+ idempotencyKey
2036
+ }],
2037
+ cause,
2009
2038
  allocationId: info.id,
2010
2039
  idempotencyKey,
2011
2040
  createReceipt: info.createReceipt ?? null
@@ -2020,7 +2049,11 @@ var SandboxCreation = class extends SandboxManagement {
2020
2049
  console.warn(`[sandbox.create] ${message}`);
2021
2050
  return instance;
2022
2051
  }
2023
- throw new PartialFailureError(message, "PROFILE_FILES_MATERIALIZE_INTERRUPTED", 502, void 0, paths.map((path) => ({ path })));
2052
+ throw new PartialFailureError(message, {
2053
+ code: "PROFILE_FILES_MATERIALIZE_INTERRUPTED",
2054
+ status: 502,
2055
+ failures: paths.map((path) => ({ path }))
2056
+ });
2024
2057
  }
2025
2058
  try {
2026
2059
  await instance.materializeProfileFiles(resolvedBackend.profile, void 0, "replace");
@@ -2032,7 +2065,12 @@ var SandboxCreation = class extends SandboxManagement {
2032
2065
  return instance;
2033
2066
  }
2034
2067
  const retryable = isRetryableProfileFileMaterializeFailure(err);
2035
- throw new PartialFailureError(message, retryable ? "PROFILE_FILES_MATERIALIZE_INTERRUPTED" : "PROFILE_FILES_MATERIALIZE_FAILED", retryable ? 502 : 422, void 0, paths.map((path) => ({ path })), err);
2068
+ throw new PartialFailureError(message, {
2069
+ code: retryable ? "PROFILE_FILES_MATERIALIZE_INTERRUPTED" : "PROFILE_FILES_MATERIALIZE_FAILED",
2070
+ status: retryable ? 502 : 422,
2071
+ failures: paths.map((path) => ({ path })),
2072
+ cause: err
2073
+ });
2036
2074
  }
2037
2075
  }
2038
2076
  if (deferredProfileForPriming !== void 0) try {
@@ -2114,7 +2152,7 @@ var SandboxBatch = class extends SandboxCreation {
2114
2152
  const parsedIdempotencyKey = rawIdempotencyKey === void 0 ? void 0 : batchIdempotencyKeySchema.safeParse(rawIdempotencyKey);
2115
2153
  if (parsedIdempotencyKey && !parsedIdempotencyKey.success) {
2116
2154
  const issue = parsedIdempotencyKey.error.issues[0];
2117
- throw new ValidationError(`idempotencyKey: ${issue?.message ?? "Invalid batch idempotency key"}`, { idempotencyKey: issue?.message ?? "Invalid value" });
2155
+ throw new ValidationError(`idempotencyKey: ${issue?.message ?? "Invalid batch idempotency key"}`, { fields: { idempotencyKey: issue?.message ?? "Invalid value" } });
2118
2156
  }
2119
2157
  let response;
2120
2158
  try {
@@ -2131,7 +2169,8 @@ var SandboxBatch = class extends SandboxCreation {
2131
2169
  });
2132
2170
  } catch (err) {
2133
2171
  if (userSignal?.aborted) throw err instanceof Error && err.name === "AbortError" ? err : new DOMException("Batch aborted", "AbortError");
2134
- throw new NetworkError(`Failed to connect to Sandbox API: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0, {
2172
+ throw new NetworkError(`Failed to connect to Sandbox API: ${err instanceof Error ? err.message : String(err)}`, {
2173
+ cause: err instanceof Error ? err : void 0,
2135
2174
  endpoint: BATCH_RUN_PATH,
2136
2175
  origin: "sandbox-api"
2137
2176
  });
@@ -2194,7 +2233,10 @@ var SecretsManagerImpl = class {
2194
2233
  }
2195
2234
  async delete(name, options) {
2196
2235
  const response = await this.client.fetch(`/v1/secrets/${encodeURIComponent(name)}${options?.expectedUpdatedAt ? `?${new URLSearchParams({ expectedUpdatedAt: options.expectedUpdatedAt })}` : ""}`, { method: "DELETE" });
2197
- if (response.status === 404) throw new NotFoundError("Secret", name);
2236
+ if (response.status === 404) throw new NotFoundError(`Secret not found: ${name}`, {
2237
+ resourceType: "Secret",
2238
+ resourceId: name
2239
+ });
2198
2240
  if (!response.ok) throw await parseResponseError(response);
2199
2241
  }
2200
2242
  };
@@ -2221,7 +2263,10 @@ var SshKeysManagerImpl = class {
2221
2263
  }
2222
2264
  async delete(id) {
2223
2265
  const response = await this.client.fetch(`/v1/ssh-keys/${encodeURIComponent(id)}`, { method: "DELETE" });
2224
- if (response.status === 404) throw new NotFoundError("SSH key", id);
2266
+ if (response.status === 404) throw new NotFoundError(`SSH key not found: ${id}`, {
2267
+ resourceType: "SSH key",
2268
+ resourceId: id
2269
+ });
2225
2270
  if (!response.ok) throw await parseResponseError(response);
2226
2271
  }
2227
2272
  };
@@ -2399,20 +2444,20 @@ function createFleetId() {
2399
2444
  throw new ValidationError("Fleet id generation requires global crypto");
2400
2445
  }
2401
2446
  function assertSafeId(value, label) {
2402
- if (value.length === 0 || value.length > 64 || !machineIdPattern.test(value)) throw new ValidationError(`${label} must be 1-64 characters and contain only letters, numbers, '.', '_' or '-'`, { [label]: "invalid" });
2447
+ if (value.length === 0 || value.length > 64 || !machineIdPattern.test(value)) throw new ValidationError(`${label} must be 1-64 characters and contain only letters, numbers, '.', '_' or '-'`, { fields: { [label]: "invalid" } });
2403
2448
  }
2404
2449
  function assertUniqueMachineIds(machines) {
2405
2450
  const seen = /* @__PURE__ */ new Set();
2406
2451
  for (const machine of machines) {
2407
2452
  assertSafeId(machine.machineId, "machineId");
2408
- if (seen.has(machine.machineId)) throw new ValidationError(`Duplicate machineId '${machine.machineId}' in fleet create request`, { machineId: "duplicate" });
2453
+ if (seen.has(machine.machineId)) throw new ValidationError(`Duplicate machineId '${machine.machineId}' in fleet create request`, { fields: { machineId: "duplicate" } });
2409
2454
  seen.add(machine.machineId);
2410
2455
  }
2411
2456
  }
2412
2457
  function assertNoReservedMetadata(metadata, label) {
2413
2458
  if (!metadata) return;
2414
2459
  const reserved = Object.keys(metadata).find((key) => key.startsWith(RESERVED_METADATA_PREFIX));
2415
- if (reserved) throw new ValidationError(`${label} metadata key '${reserved}' is reserved by Tangle`, { metadata: "reserved" });
2460
+ if (reserved) throw new ValidationError(`${label} metadata key '${reserved}' is reserved by Tangle`, { fields: { metadata: "reserved" } });
2416
2461
  }
2417
2462
  function mergeCreateOptions(defaults, machine) {
2418
2463
  const { machineId: _machineId, metadata: _metadata, name: _name, role: _role, ...rest } = machine;
@@ -2465,9 +2510,9 @@ function mergeGpuLeaseOptions(defaults, machine) {
2465
2510
  function assertPolicy(machines, defaults, policy) {
2466
2511
  const maxMachines = policy?.maxMachines ?? machines.length;
2467
2512
  if (!Number.isInteger(maxMachines) || maxMachines <= 0) throw new ValidationError("policy.maxMachines must be a positive integer");
2468
- if (machines.length > maxMachines) throw new ValidationError(`Fleet create requested ${machines.length} machines but policy.maxMachines is ${maxMachines}`, { "policy.maxMachines": "exceeded" });
2469
- if (policy?.maxConcurrentCreates !== void 0 && (!Number.isInteger(policy.maxConcurrentCreates) || policy.maxConcurrentCreates <= 0)) throw new ValidationError("policy.maxConcurrentCreates must be a positive integer", { "policy.maxConcurrentCreates": "invalid" });
2470
- if (policy?.maxSpendUsd !== void 0 && (!Number.isFinite(policy.maxSpendUsd) || policy.maxSpendUsd < 0)) throw new ValidationError("policy.maxSpendUsd must be non-negative", { "policy.maxSpendUsd": "invalid" });
2513
+ if (machines.length > maxMachines) throw new ValidationError(`Fleet create requested ${machines.length} machines but policy.maxMachines is ${maxMachines}`, { fields: { "policy.maxMachines": "exceeded" } });
2514
+ if (policy?.maxConcurrentCreates !== void 0 && (!Number.isInteger(policy.maxConcurrentCreates) || policy.maxConcurrentCreates <= 0)) throw new ValidationError("policy.maxConcurrentCreates must be a positive integer", { fields: { "policy.maxConcurrentCreates": "invalid" } });
2515
+ if (policy?.maxSpendUsd !== void 0 && (!Number.isFinite(policy.maxSpendUsd) || policy.maxSpendUsd < 0)) throw new ValidationError("policy.maxSpendUsd must be non-negative", { fields: { "policy.maxSpendUsd": "invalid" } });
2471
2516
  let totalCpu = 0;
2472
2517
  let totalMemoryMb = 0;
2473
2518
  let totalStorageMb = 0;
@@ -2482,13 +2527,13 @@ function assertPolicy(machines, defaults, policy) {
2482
2527
  totalAccelerators += resources?.accelerator?.count ?? (resources?.accelerator ? 1 : 0);
2483
2528
  if (options.gpuLease) totalGpuLeaseMaxSpendUsd += options.gpuLease.maxSpendUsd;
2484
2529
  }
2485
- if (policy?.maxTotalCpu !== void 0 && totalCpu > policy.maxTotalCpu) throw new ValidationError(`Fleet create requested ${totalCpu} CPU cores but policy.maxTotalCpu is ${policy.maxTotalCpu}`, { "policy.maxTotalCpu": "exceeded" });
2486
- if (policy?.maxTotalMemoryMb !== void 0 && totalMemoryMb > policy.maxTotalMemoryMb) throw new ValidationError(`Fleet create requested ${totalMemoryMb} MB RAM but policy.maxTotalMemoryMb is ${policy.maxTotalMemoryMb}`, { "policy.maxTotalMemoryMb": "exceeded" });
2487
- if (policy?.maxTotalStorageMb !== void 0 && totalStorageMb > policy.maxTotalStorageMb) throw new ValidationError(`Fleet create requested ${totalStorageMb} MB storage but policy.maxTotalStorageMb is ${policy.maxTotalStorageMb}`, { "policy.maxTotalStorageMb": "exceeded" });
2488
- if (policy?.maxTotalAccelerators !== void 0 && totalAccelerators > policy.maxTotalAccelerators) throw new ValidationError(`Fleet create requested ${totalAccelerators} accelerator devices but policy.maxTotalAccelerators is ${policy.maxTotalAccelerators}`, { "policy.maxTotalAccelerators": "exceeded" });
2489
- if (policy?.maxSpendUsd !== void 0 && totalGpuLeaseMaxSpendUsd > policy.maxSpendUsd) throw new ValidationError(`Fleet GPU lease max spend $${totalGpuLeaseMaxSpendUsd} exceeds policy.maxSpendUsd $${policy.maxSpendUsd}`, { "policy.maxSpendUsd": "exceeded" });
2530
+ if (policy?.maxTotalCpu !== void 0 && totalCpu > policy.maxTotalCpu) throw new ValidationError(`Fleet create requested ${totalCpu} CPU cores but policy.maxTotalCpu is ${policy.maxTotalCpu}`, { fields: { "policy.maxTotalCpu": "exceeded" } });
2531
+ if (policy?.maxTotalMemoryMb !== void 0 && totalMemoryMb > policy.maxTotalMemoryMb) throw new ValidationError(`Fleet create requested ${totalMemoryMb} MB RAM but policy.maxTotalMemoryMb is ${policy.maxTotalMemoryMb}`, { fields: { "policy.maxTotalMemoryMb": "exceeded" } });
2532
+ if (policy?.maxTotalStorageMb !== void 0 && totalStorageMb > policy.maxTotalStorageMb) throw new ValidationError(`Fleet create requested ${totalStorageMb} MB storage but policy.maxTotalStorageMb is ${policy.maxTotalStorageMb}`, { fields: { "policy.maxTotalStorageMb": "exceeded" } });
2533
+ if (policy?.maxTotalAccelerators !== void 0 && totalAccelerators > policy.maxTotalAccelerators) throw new ValidationError(`Fleet create requested ${totalAccelerators} accelerator devices but policy.maxTotalAccelerators is ${policy.maxTotalAccelerators}`, { fields: { "policy.maxTotalAccelerators": "exceeded" } });
2534
+ if (policy?.maxSpendUsd !== void 0 && totalGpuLeaseMaxSpendUsd > policy.maxSpendUsd) throw new ValidationError(`Fleet GPU lease max spend $${totalGpuLeaseMaxSpendUsd} exceeds policy.maxSpendUsd $${policy.maxSpendUsd}`, { fields: { "policy.maxSpendUsd": "exceeded" } });
2490
2535
  const maxLifetimeSeconds = Math.max(...machines.map((machine) => mergeCreateOptions(defaults, machine).maxLifetimeSeconds ?? 3600));
2491
- if (policy?.maxLifetimeSeconds !== void 0 && maxLifetimeSeconds > policy.maxLifetimeSeconds) throw new ValidationError(`Fleet create requested ${maxLifetimeSeconds}s max lifetime but policy.maxLifetimeSeconds is ${policy.maxLifetimeSeconds}`, { "policy.maxLifetimeSeconds": "exceeded" });
2536
+ if (policy?.maxLifetimeSeconds !== void 0 && maxLifetimeSeconds > policy.maxLifetimeSeconds) throw new ValidationError(`Fleet create requested ${maxLifetimeSeconds}s max lifetime but policy.maxLifetimeSeconds is ${policy.maxLifetimeSeconds}`, { fields: { "policy.maxLifetimeSeconds": "exceeded" } });
2492
2537
  for (const machine of machines) {
2493
2538
  const options = mergeCreateOptions(defaults, machine);
2494
2539
  assertMachineDescriptorPolicy(machine.machineId, options, policy);
@@ -2496,18 +2541,18 @@ function assertPolicy(machines, defaults, policy) {
2496
2541
  }
2497
2542
  function assertMachineDescriptorPolicy(machineId, options, policy) {
2498
2543
  const driverType = options.driver?.type;
2499
- if (policy?.allowedDrivers && driverType && !policy.allowedDrivers.includes(driverType)) throw new ValidationError(`Fleet machine '${machineId}' requested driver '${driverType}' outside policy.allowedDrivers`, { "policy.allowedDrivers": "exceeded" });
2544
+ if (policy?.allowedDrivers && driverType && !policy.allowedDrivers.includes(driverType)) throw new ValidationError(`Fleet machine '${machineId}' requested driver '${driverType}' outside policy.allowedDrivers`, { fields: { "policy.allowedDrivers": "exceeded" } });
2500
2545
  const environment = options.environment;
2501
- if (policy?.allowedEnvironments && environment && !policy.allowedEnvironments.includes(environment)) throw new ValidationError(`Fleet machine '${machineId}' requested environment '${environment}' outside policy.allowedEnvironments`, { "policy.allowedEnvironments": "exceeded" });
2546
+ if (policy?.allowedEnvironments && environment && !policy.allowedEnvironments.includes(environment)) throw new ValidationError(`Fleet machine '${machineId}' requested environment '${environment}' outside policy.allowedEnvironments`, { fields: { "policy.allowedEnvironments": "exceeded" } });
2502
2547
  const templateId = options.templateId ?? options.publicTemplateId;
2503
- if (policy?.allowedTemplateIds && templateId && !policy.allowedTemplateIds.includes(templateId)) throw new ValidationError(`Fleet machine '${machineId}' requested template '${templateId}' outside policy.allowedTemplateIds`, { "policy.allowedTemplateIds": "exceeded" });
2504
- if (policy?.allowAccelerators === false && (options.resources?.accelerator?.count ?? (options.resources?.accelerator ? 1 : 0)) > 0) throw new ValidationError(`Fleet machine '${machineId}' requested accelerators but policy.allowAccelerators is false`, { "policy.allowAccelerators": "exceeded" });
2548
+ if (policy?.allowedTemplateIds && templateId && !policy.allowedTemplateIds.includes(templateId)) throw new ValidationError(`Fleet machine '${machineId}' requested template '${templateId}' outside policy.allowedTemplateIds`, { fields: { "policy.allowedTemplateIds": "exceeded" } });
2549
+ if (policy?.allowAccelerators === false && (options.resources?.accelerator?.count ?? (options.resources?.accelerator ? 1 : 0)) > 0) throw new ValidationError(`Fleet machine '${machineId}' requested accelerators but policy.allowAccelerators is false`, { fields: { "policy.allowAccelerators": "exceeded" } });
2505
2550
  const acceleratorCount = options.resources?.accelerator?.count ?? (options.resources?.accelerator ? 1 : 0);
2506
- if (acceleratorCount > 0 && !options.gpuLease) throw new ValidationError(`Fleet machine '${machineId}' requested accelerators but gpuLease is missing`, { gpuLease: "required" });
2507
- if (acceleratorCount === 0 && options.gpuLease) throw new ValidationError(`Fleet machine '${machineId}' set gpuLease without resources.accelerator`, { gpuLease: "unexpected" });
2551
+ if (acceleratorCount > 0 && !options.gpuLease) throw new ValidationError(`Fleet machine '${machineId}' requested accelerators but gpuLease is missing`, { fields: { gpuLease: "required" } });
2552
+ if (acceleratorCount === 0 && options.gpuLease) throw new ValidationError(`Fleet machine '${machineId}' set gpuLease without resources.accelerator`, { fields: { gpuLease: "unexpected" } });
2508
2553
  if (options.gpuLease) {
2509
- if (!Number.isFinite(options.gpuLease.maxSpendUsd) || options.gpuLease.maxSpendUsd <= 0) throw new ValidationError(`Fleet machine '${machineId}' gpuLease.maxSpendUsd must be positive`, { "gpuLease.maxSpendUsd": "invalid" });
2510
- if (!Number.isInteger(options.gpuLease.maxLifetimeSeconds) || options.gpuLease.maxLifetimeSeconds <= 0) throw new ValidationError(`Fleet machine '${machineId}' gpuLease.maxLifetimeSeconds must be a positive integer`, { "gpuLease.maxLifetimeSeconds": "invalid" });
2554
+ if (!Number.isFinite(options.gpuLease.maxSpendUsd) || options.gpuLease.maxSpendUsd <= 0) throw new ValidationError(`Fleet machine '${machineId}' gpuLease.maxSpendUsd must be positive`, { fields: { "gpuLease.maxSpendUsd": "invalid" } });
2555
+ if (!Number.isInteger(options.gpuLease.maxLifetimeSeconds) || options.gpuLease.maxLifetimeSeconds <= 0) throw new ValidationError(`Fleet machine '${machineId}' gpuLease.maxLifetimeSeconds must be a positive integer`, { fields: { "gpuLease.maxLifetimeSeconds": "invalid" } });
2511
2556
  }
2512
2557
  }
2513
2558
  function calculateResourceTotals(machines, defaults) {
@@ -2624,13 +2669,13 @@ async function mapFleetItems(items, concurrency, fn) {
2624
2669
  }
2625
2670
  function assertArtifactPath(path) {
2626
2671
  const home = SANDBOX_AGENT_HOME.split("/");
2627
- if (path.split("/").slice(0, home.length).join("/") !== "/home/agent") throw new ValidationError(`Fleet artifact path '${path}' must be under ${SANDBOX_AGENT_HOME}`, { path: "outside_workspace" });
2628
- if (path.includes("\0") || path.split("/").includes("..")) throw new ValidationError(`Fleet artifact path '${path}' must not contain traversal segments`, { path: "invalid" });
2672
+ if (path.split("/").slice(0, home.length).join("/") !== "/home/agent") throw new ValidationError(`Fleet artifact path '${path}' must be under ${SANDBOX_AGENT_HOME}`, { fields: { path: "outside_workspace" } });
2673
+ if (path.includes("\0") || path.split("/").includes("..")) throw new ValidationError(`Fleet artifact path '${path}' must not contain traversal segments`, { fields: { path: "invalid" } });
2629
2674
  }
2630
2675
  function assertArtifactSize(content, artifact) {
2631
2676
  const maxBytes = artifact.maxBytes ?? 10485760;
2632
- if (!Number.isFinite(maxBytes) || maxBytes <= 0) throw new ValidationError("artifact.maxBytes must be a positive number", { maxBytes: "invalid" });
2633
- if (new TextEncoder().encode(content).byteLength > maxBytes) throw new ValidationError(`Fleet artifact '${artifact.path}' exceeded ${maxBytes} bytes`, { path: "too_large" });
2677
+ if (!Number.isFinite(maxBytes) || maxBytes <= 0) throw new ValidationError("artifact.maxBytes must be a positive number", { fields: { maxBytes: "invalid" } });
2678
+ if (new TextEncoder().encode(content).byteLength > maxBytes) throw new ValidationError(`Fleet artifact '${artifact.path}' exceeded ${maxBytes} bytes`, { fields: { path: "too_large" } });
2634
2679
  }
2635
2680
  function getMachineRole(info) {
2636
2681
  const value = info.metadata?.[MACHINE_ROLE_META];
@@ -2646,7 +2691,7 @@ function getAgentSpecs(info) {
2646
2691
  try {
2647
2692
  return JSON.parse(value);
2648
2693
  } catch {
2649
- throw new ValidationError(`Fleet sandbox ${info.id} has unreadable agent specs`, { metadata: "invalid" });
2694
+ throw new ValidationError(`Fleet sandbox ${info.id} has unreadable agent specs`, { fields: { metadata: "invalid" } });
2650
2695
  }
2651
2696
  }
2652
2697
  /** The per-turn backend for an agent: only what its spec sets. */
@@ -2667,7 +2712,7 @@ function agentBackend(spec) {
2667
2712
  */
2668
2713
  function withoutModelSecrets(model, agentId) {
2669
2714
  const { apiKey, authFiles, ...rest } = model;
2670
- if (agentId !== void 0 && (apiKey !== void 0 || authFiles !== void 0)) throw new ValidationError(`Fleet agent '${agentId}' model carries a secret; name the sandbox env var that holds it with apiKeyEnv`, { model: "secret" });
2715
+ if (agentId !== void 0 && (apiKey !== void 0 || authFiles !== void 0)) throw new ValidationError(`Fleet agent '${agentId}' model carries a secret; name the sandbox env var that holds it with apiKeyEnv`, { fields: { model: "secret" } });
2671
2716
  return rest;
2672
2717
  }
2673
2718
  function isProfileRef(profile) {
@@ -2721,7 +2766,7 @@ var FleetClientTransport = class {
2721
2766
  const body = await response.text();
2722
2767
  if (!response.ok) throw parseErrorResponse(response.status, body, void 0, response.headers);
2723
2768
  const data = JSON.parse(body);
2724
- if (!data.data?.id || !data.data.key) throw new ValidationError("Platform minted a fleet key without an id", { key: "invalid" });
2769
+ if (!data.data?.id || !data.data.key) throw new ValidationError("Platform minted a fleet key without an id", { fields: { key: "invalid" } });
2725
2770
  return {
2726
2771
  id: data.data.id,
2727
2772
  key: data.data.key
@@ -2734,7 +2779,7 @@ var FleetClientTransport = class {
2734
2779
  */
2735
2780
  hostFor(fleetId) {
2736
2781
  const key = this.#fleetKeys.get(fleetId);
2737
- if (!key) throw new ValidationError(`Fleet ${fleetId} is capped; drive its agents from the client that created it, so every charge counts against the cap`, { fleet: "key_unavailable" });
2782
+ if (!key) throw new ValidationError(`Fleet ${fleetId} is capped; drive its agents from the client that created it, so every charge counts against the cap`, { fields: { fleet: "key_unavailable" } });
2738
2783
  return this.client.withApiKey(key.key);
2739
2784
  }
2740
2785
  /** The budgeted key id a fleet's sandboxes carry, read from one of them. */
@@ -2769,7 +2814,7 @@ var FleetClientTransport = class {
2769
2814
  spentUsd,
2770
2815
  inFlightUsd,
2771
2816
  reservedForChildrenUsd
2772
- ].every(Number.isFinite)) throw new ValidationError(`Platform key ${keyId} returned no readable budget`, { key: "invalid" });
2817
+ ].every(Number.isFinite)) throw new ValidationError(`Platform key ${keyId} returned no readable budget`, { fields: { key: "invalid" } });
2773
2818
  const remainingUsd = capUsd - spentUsd - inFlightUsd - reservedForChildrenUsd;
2774
2819
  return {
2775
2820
  capUsd,
@@ -2819,10 +2864,9 @@ var FleetClientTransport = class {
2819
2864
  await this.deleteMachines(machines);
2820
2865
  return;
2821
2866
  }
2822
- if (!(response.status >= 500 || response.status === 429) || attempt >= DELETE_RETRY_DELAYS_MS.length) {
2823
- const body = await response.text().catch(() => "");
2824
- throw parseErrorResponse(response.status, body, void 0, response.headers);
2825
- }
2867
+ const body = await response.text().catch(() => "");
2868
+ const error = parseErrorResponse(response.status, body, void 0, response.headers);
2869
+ if (!(retryWhen(error) && (error.status >= 500 || error.status === 429)) || attempt >= DELETE_RETRY_DELAYS_MS.length) throw error;
2826
2870
  await new Promise((resolve) => setTimeout(resolve, DELETE_RETRY_DELAYS_MS[attempt]));
2827
2871
  }
2828
2872
  }
@@ -3164,20 +3208,20 @@ function resolveAgentsPerSandbox(placement) {
3164
3208
  if (placement === "dedicated") return 1;
3165
3209
  if (placement === "auto") return void 0;
3166
3210
  const count = placement.agentsPerSandbox;
3167
- if (!Number.isSafeInteger(count) || count < 1) throw new ValidationError("placement.agentsPerSandbox must be a positive integer", { "placement.agentsPerSandbox": "invalid" });
3211
+ if (!Number.isSafeInteger(count) || count < 1) throw new ValidationError("placement.agentsPerSandbox must be a positive integer", { fields: { "placement.agentsPerSandbox": "invalid" } });
3168
3212
  return count;
3169
3213
  }
3170
3214
  function assertMemory(memoryMB) {
3171
- if (!Number.isFinite(memoryMB) || memoryMB <= 0) throw new ValidationError("Sandbox memoryMB must be a positive number", { memoryMB: "invalid" });
3215
+ if (!Number.isFinite(memoryMB) || memoryMB <= 0) throw new ValidationError("Sandbox memoryMB must be a positive number", { fields: { memoryMB: "invalid" } });
3172
3216
  }
3173
3217
  /** Expand `agents: N` or an agent list into specs with ids and placement set. */
3174
3218
  function normalizeAgentSpecs(agents) {
3175
3219
  const specs = typeof agents === "number" ? Array.from({ length: assertAgentCount(agents) }, () => ({})) : [...agents];
3176
- if (specs.length === 0) throw new ValidationError("A fleet needs at least one agent", { agents: "empty" });
3220
+ if (specs.length === 0) throw new ValidationError("A fleet needs at least one agent", { fields: { agents: "empty" } });
3177
3221
  const seen = /* @__PURE__ */ new Set();
3178
3222
  return specs.map((spec, index) => {
3179
3223
  const agentId = spec.agentId ?? `agent-${index}`;
3180
- if (seen.has(agentId)) throw new ValidationError(`Duplicate agentId '${agentId}'`, { agentId: "duplicate" });
3224
+ if (seen.has(agentId)) throw new ValidationError(`Duplicate agentId '${agentId}'`, { fields: { agentId: "duplicate" } });
3181
3225
  seen.add(agentId);
3182
3226
  return {
3183
3227
  ...spec,
@@ -3187,7 +3231,7 @@ function normalizeAgentSpecs(agents) {
3187
3231
  });
3188
3232
  }
3189
3233
  function assertAgentCount(count) {
3190
- if (!Number.isSafeInteger(count) || count < 1) throw new ValidationError("agents must be a positive integer", { agents: "invalid" });
3234
+ if (!Number.isSafeInteger(count) || count < 1) throw new ValidationError("agents must be a positive integer", { fields: { agents: "invalid" } });
3191
3235
  return count;
3192
3236
  }
3193
3237
  //#endregion
@@ -3401,11 +3445,11 @@ var SandboxFleet = class {
3401
3445
  async *run(message, options = {}) {
3402
3446
  const startedAt = Date.now();
3403
3447
  const targets = options.agents ? options.agents.map((agentId) => this.agent(agentId)) : this.agents;
3404
- if (targets.length === 0) throw new ValidationError("Fleet run selected no agents", { agents: "empty" });
3448
+ if (targets.length === 0) throw new ValidationError("Fleet run selected no agents", { fields: { agents: "empty" } });
3405
3449
  const specs = this.agentSpecs();
3406
3450
  const messageFor = (agent) => {
3407
3451
  const resolved = (typeof message === "function" ? message(agent) : message) ?? specs[agent.agentId]?.task;
3408
- if (resolved === void 0) throw new ValidationError(`Fleet agent '${agent.agentId}' has no task and the run passed no message`, { message: "required" });
3452
+ if (resolved === void 0) throw new ValidationError(`Fleet agent '${agent.agentId}' has no task and the run passed no message`, { fields: { message: "required" } });
3409
3453
  return resolved;
3410
3454
  };
3411
3455
  const messages = new Map(targets.map((agent) => [agent.agentId, messageFor(agent)]));
@@ -3559,7 +3603,7 @@ var SandboxFleet = class {
3559
3603
  * Only a capped fleet has a key; an uncapped fleet has nothing to meter.
3560
3604
  */
3561
3605
  async spend() {
3562
- if (!this.keyId) throw new ValidationError(`Fleet ${this.fleetId} has no spend cap, so it has no metered key`, { policy: "uncapped" });
3606
+ if (!this.keyId) throw new ValidationError(`Fleet ${this.fleetId} has no spend cap, so it has no metered key`, { fields: { policy: "uncapped" } });
3563
3607
  return this.client.fleets.keySpend(this.keyId);
3564
3608
  }
3565
3609
  /**
@@ -3718,7 +3762,7 @@ var SandboxFleet = class {
3718
3762
  * refuses it: its turns run through `run()`, `runAll()` or `prompt()`.
3719
3763
  */
3720
3764
  assertUnbudgetedDispatch() {
3721
- if (this.keyId) throw new ValidationError(`Fleet ${this.fleetId} has a budget; prompt dispatch would bill outside it. Use run(), runAll() or prompt(machineId, …)`, { fleet: "budgeted" });
3765
+ if (this.keyId) throw new ValidationError(`Fleet ${this.fleetId} has a budget; prompt dispatch would bill outside it. Use run(), runAll() or prompt(machineId, …)`, { fields: { fleet: "budgeted" } });
3722
3766
  }
3723
3767
  /** @internal */
3724
3768
  coldStartGate(machineId) {
@@ -3732,13 +3776,13 @@ var SandboxFleet = class {
3732
3776
  sandboxFor(machineId, options) {
3733
3777
  const machine = this.get(machineId);
3734
3778
  if (options?.control) return this.client.get(machine.id).then((box) => {
3735
- if (!box) throw new ValidationError(`Fleet machine '${machineId}' no longer exists`, { machineId: "not_found" });
3779
+ if (!box) throw new ValidationError(`Fleet machine '${machineId}' no longer exists`, { fields: { machineId: "not_found" } });
3736
3780
  return box;
3737
3781
  });
3738
3782
  const cached = this.boxes.get(machineId);
3739
3783
  if (cached) return cached;
3740
3784
  const pending = (this.keyId ? this.client.fleets.hostFor(this.fleetId) : this.client).get(machine.id).then((box) => {
3741
- if (!box) throw new ValidationError(`Fleet machine '${machineId}' no longer exists`, { machineId: "not_found" });
3785
+ if (!box) throw new ValidationError(`Fleet machine '${machineId}' no longer exists`, { fields: { machineId: "not_found" } });
3742
3786
  return box;
3743
3787
  });
3744
3788
  pending.catch(() => this.boxes.delete(machineId));
@@ -3757,7 +3801,7 @@ var SandboxFleetClient = class extends FleetClientTransport {
3757
3801
  assertSafeId(input.parent.fleetId, "parent.fleetId");
3758
3802
  assertSafeId(input.parent.agentId, "parent.agentId");
3759
3803
  }
3760
- if (input.budgetUsd !== void 0 && (!Number.isFinite(input.budgetUsd) || input.budgetUsd <= 0)) throw new ValidationError("budgetUsd must be a positive number", { budgetUsd: "invalid" });
3804
+ if (input.budgetUsd !== void 0 && (!Number.isFinite(input.budgetUsd) || input.budgetUsd <= 0)) throw new ValidationError("budgetUsd must be a positive number", { fields: { budgetUsd: "invalid" } });
3761
3805
  const options = {
3762
3806
  ...input,
3763
3807
  machines,
@@ -3771,7 +3815,7 @@ var SandboxFleetClient = class extends FleetClientTransport {
3771
3815
  for (const machine of options.machines) assertNoReservedMetadata(machine.metadata, `Fleet machine '${machine.machineId}'`);
3772
3816
  assertPolicy(options.machines, options.defaults, options.policy);
3773
3817
  const maxConcurrentCreates = resolveMaxConcurrentCreates(options.maxConcurrentCreates);
3774
- if (options.policy?.maxConcurrentCreates !== void 0 && maxConcurrentCreates > options.policy.maxConcurrentCreates) throw new ValidationError(`Fleet create requested ${maxConcurrentCreates} concurrent creates but policy.maxConcurrentCreates is ${options.policy.maxConcurrentCreates}`, { "policy.maxConcurrentCreates": "exceeded" });
3818
+ if (options.policy?.maxConcurrentCreates !== void 0 && maxConcurrentCreates > options.policy.maxConcurrentCreates) throw new ValidationError(`Fleet create requested ${maxConcurrentCreates} concurrent creates but policy.maxConcurrentCreates is ${options.policy.maxConcurrentCreates}`, { fields: { "policy.maxConcurrentCreates": "exceeded" } });
3775
3819
  if (options.policy?.maxSpendUsd !== void 0) {
3776
3820
  const estimate = await this.estimateCost({
3777
3821
  defaults: options.defaults,
@@ -3779,16 +3823,16 @@ var SandboxFleetClient = class extends FleetClientTransport {
3779
3823
  policy: options.policy,
3780
3824
  maxConcurrentCreates
3781
3825
  });
3782
- if (estimate.estimatedMaxLifetimeUsd > options.policy.maxSpendUsd) throw new ValidationError(`Fleet estimated max lifetime cost $${estimate.estimatedMaxLifetimeUsd} exceeds policy.maxSpendUsd $${options.policy.maxSpendUsd}`, { "policy.maxSpendUsd": "exceeded" });
3826
+ if (estimate.estimatedMaxLifetimeUsd > options.policy.maxSpendUsd) throw new ValidationError(`Fleet estimated max lifetime cost $${estimate.estimatedMaxLifetimeUsd} exceeds policy.maxSpendUsd $${options.policy.maxSpendUsd}`, { fields: { "policy.maxSpendUsd": "exceeded" } });
3783
3827
  }
3784
3828
  let fleetKey;
3785
3829
  const capUsd = options.budgetUsd;
3786
3830
  const parentKey = options.parent ? fleetTransportKeys(this).get(options.parent.fleetId) : void 0;
3787
3831
  if (options.parent && !parentKey) {
3788
3832
  const parentFleet = await this.getServerFleet(options.parent.fleetId);
3789
- if (await this.budgetKeyIdOf(parentFleet) !== void 0) throw new ValidationError(`Parent fleet ${options.parent.fleetId} has a budget; create its child fleets with the client that created it`, { parent: "key_unavailable" });
3833
+ if (await this.budgetKeyIdOf(parentFleet) !== void 0) throw new ValidationError(`Parent fleet ${options.parent.fleetId} has a budget; create its child fleets with the client that created it`, { fields: { parent: "key_unavailable" } });
3790
3834
  }
3791
- if (parentKey && capUsd === void 0) throw new ValidationError(`Parent fleet ${options.parent?.fleetId} has a budget, so its child fleets must set budgetUsd`, { budgetUsd: "required" });
3835
+ if (parentKey && capUsd === void 0) throw new ValidationError(`Parent fleet ${options.parent?.fleetId} has a budget, so its child fleets must set budgetUsd`, { fields: { budgetUsd: "required" } });
3792
3836
  if (capUsd !== void 0) fleetKey = await this.mintFleetKey(fleetId, capUsd, Math.max(...machines.map((machine) => mergeCreateOptions(options.defaults, machine).maxLifetimeSeconds ?? 3600)), parentKey?.key);
3793
3837
  const host = fleetKey ? this.client.withApiKey(fleetKey.key) : this.client;
3794
3838
  const releaseKey = async () => {
@@ -3821,7 +3865,7 @@ var SandboxFleetClient = class extends FleetClientTransport {
3821
3865
  try {
3822
3866
  record = await this.persistFleet(host, fleetId, created, options);
3823
3867
  const recorded = new Set((record?.machines ?? []).map((machine) => machine.sandboxId));
3824
- if (record?.machines !== void 0 && created.some((machine) => !recorded.has(machine.sandbox.id))) throw new ValidationError(`Fleet ${fleetId} already exists from an earlier create with the same idempotency key; load it with fleets.list({ fleetId })`, { fleetId: "exists" });
3868
+ if (record?.machines !== void 0 && created.some((machine) => !recorded.has(machine.sandbox.id))) throw new ValidationError(`Fleet ${fleetId} already exists from an earlier create with the same idempotency key; load it with fleets.list({ fleetId })`, { fields: { fleetId: "exists" } });
3825
3869
  } catch (err) {
3826
3870
  if (options.cleanupOnFailure !== false) {
3827
3871
  await Promise.allSettled(created.map(async (machine) => {
@@ -3848,7 +3892,7 @@ var SandboxFleetClient = class extends FleetClientTransport {
3848
3892
  * the Platform catalog here, before any sandbox exists.
3849
3893
  */
3850
3894
  async resolveFleetMachines(options) {
3851
- if (options.agents !== void 0 && options.machines !== void 0) throw new ValidationError("Set either machines or agents, not both", { agents: "conflicts_with_machines" });
3895
+ if (options.agents !== void 0 && options.machines !== void 0) throw new ValidationError("Set either machines or agents, not both", { fields: { agents: "conflicts_with_machines" } });
3852
3896
  if (options.agents === void 0) {
3853
3897
  const machines = options.machines ?? [];
3854
3898
  if (machines.length === 0) throw new ValidationError("Fleet create requires machines or agents");
@@ -3907,8 +3951,8 @@ var SandboxFleetClient = class extends FleetClientTransport {
3907
3951
  if (!response.ok) throw await parseResponseError(response);
3908
3952
  const data = await response.json();
3909
3953
  const definition = data.data?.definition;
3910
- if (!definition) throw new ValidationError(`Saved profile '${ref.profileId}' returned no definition`, { profile: "invalid" });
3911
- if (ref.revision !== void 0 && data.data?.revision !== ref.revision) throw new ValidationError(`Saved profile '${ref.profileId}' is at revision ${data.data?.revision ?? "unknown"}, not ${ref.revision}`, { profile: "revision_mismatch" });
3954
+ if (!definition) throw new ValidationError(`Saved profile '${ref.profileId}' returned no definition`, { fields: { profile: "invalid" } });
3955
+ if (ref.revision !== void 0 && data.data?.revision !== ref.revision) throw new ValidationError(`Saved profile '${ref.profileId}' is at revision ${data.data?.revision ?? "unknown"}, not ${ref.revision}`, { fields: { profile: "revision_mismatch" } });
3912
3956
  return definition;
3913
3957
  }
3914
3958
  async createWithCoordinator(options) {
@@ -3994,7 +4038,7 @@ var SandboxFleetClient = class extends FleetClientTransport {
3994
4038
  async hydrateServerFleet(fleet, options) {
3995
4039
  const machines = await Promise.all(fleet.machines.map(async (machine) => {
3996
4040
  const box = await this.client.get(machine.sandboxId);
3997
- if (!box) throw new ValidationError(`Fleet machine '${machine.machineId}' points at missing sandbox '${machine.sandboxId}'`, { machineId: "missing_sandbox" });
4041
+ if (!box) throw new ValidationError(`Fleet machine '${machine.machineId}' points at missing sandbox '${machine.sandboxId}'`, { fields: { machineId: "missing_sandbox" } });
3998
4042
  return {
3999
4043
  machineId: machine.machineId,
4000
4044
  sandbox: box.toJSON(),
@@ -4067,10 +4111,13 @@ var SandboxFleetClient = class extends FleetClientTransport {
4067
4111
  */
4068
4112
  var InstanceRestartingError = class extends SandboxError {
4069
4113
  constructor(message, instance, failure, retryAfterMs) {
4070
- super(message, "INSTANCE_RESTARTING", 503, { retryAfterMs });
4114
+ super(message, {
4115
+ code: "INSTANCE_RESTARTING",
4116
+ status: 503,
4117
+ retryAfterMs
4118
+ });
4071
4119
  this.instance = instance;
4072
4120
  this.failure = failure;
4073
- this.name = "InstanceRestartingError";
4074
4121
  }
4075
4122
  };
4076
4123
  /**
@@ -4185,7 +4232,10 @@ var InstancesClient = class {
4185
4232
  const running = await this.start(options, record, record.sandboxId);
4186
4233
  if (running) return running;
4187
4234
  }
4188
- throw new SandboxError(`Instance ${options.key} changed on every attempt; retry`, "INSTANCE_CONTENDED", 409);
4235
+ throw new SandboxError(`Instance ${options.key} changed on every attempt; retry`, {
4236
+ code: "INSTANCE_CONTENDED",
4237
+ status: 409
4238
+ });
4189
4239
  }
4190
4240
  async create(options, generation) {
4191
4241
  const box = await this.client.createIsolated({
@@ -4335,7 +4385,7 @@ var RunsClient = class {
4335
4385
  }
4336
4386
  /** Start a run. Returns at once with its id; it executes on Platform. */
4337
4387
  async create(options) {
4338
- if (!options.prompt) throw new ValidationError("prompt is required", { prompt: "required" });
4388
+ if (!options.prompt) throw new ValidationError("prompt is required", { fields: { prompt: "required" } });
4339
4389
  return platformData(await this.http.platformFetch("/v1/runs", {
4340
4390
  method: "POST",
4341
4391
  headers: { "Content-Type": "application/json" },
@@ -4360,7 +4410,7 @@ var RunsClient = class {
4360
4410
  for (;;) {
4361
4411
  const record = await this.get(id, options.signal ? { signal: options.signal } : {});
4362
4412
  if (SETTLED.has(record.status)) return record;
4363
- if (Date.now() + interval > deadline) throw new TimeoutError(timeoutMs, `run ${id} did not settle within ${timeoutMs}ms (last status ${record.status}); cancel it with client.runs.cancel("${id}")`);
4413
+ if (Date.now() + interval > deadline) throw new TimeoutError(`run ${id} did not settle within ${timeoutMs}ms (last status ${record.status}); cancel it with client.runs.cancel("${id}")`, { timeoutMs });
4364
4414
  await abortableSleep(interval, options.signal);
4365
4415
  }
4366
4416
  }