@tangle-network/sandbox 1.0.189 → 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.
- package/README.md +7 -0
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +9 -6
- package/dist/agent/preview.d.ts +1 -1
- package/dist/agent/preview.js +1 -1
- package/dist/{agent-run-outcome-Cst4px_l.d.ts → agent-run-outcome-BO5paRhX.d.ts} +2 -2
- package/dist/{api-request-D4Ln_0F5.js → api-request-BvpTwQKs.js} +4 -1
- package/dist/{browser-interactive-control-controller-DHIS8edi.js → browser-interactive-control-controller-BYbNLahH.js} +1 -1
- package/dist/{browser-interactive-control-controller-DIvMQyer.d.ts → browser-interactive-control-controller-KOK6qn2s.d.ts} +1 -1
- package/dist/{chunked-upload-vAE2hVcP.js → chunked-upload-D5ytV_s6.js} +9 -13
- package/dist/{chunked-upload-file-HLSriv3V.js → chunked-upload-file-DL1-AqRp.js} +2 -2
- package/dist/{client-CwWS3a-i.js → client-XjkuWhog.js} +159 -109
- package/dist/{client-C4SnCCpF.d.ts → client-yWdLuj_P.d.ts} +5 -5
- package/dist/collaboration/index.js +1 -1
- package/dist/{collaboration-CmT6Ygiv.js → collaboration-Bbr45lyk.js} +3 -3
- package/dist/{core-BT-0QZT-.js → core-BU1I2KvV.js} +13 -7
- package/dist/core.d.ts +6 -6
- package/dist/core.js +5 -5
- package/dist/errors-Mzq10WZq.js +583 -0
- package/dist/{file-download-BXgu9BNh.js → file-download-CKOxcATB.js} +1 -1
- package/dist/{index-DDIymnCZ.d.ts → index-DKsOu5sq.d.ts} +5 -5
- package/dist/index.d.ts +9 -9
- package/dist/index.js +10 -10
- package/dist/intelligence/index.js +9 -5
- package/dist/interactive-control.d.ts +1 -1
- package/dist/interactive-control.js +1 -1
- package/dist/{interactive-controller-DXGWBdYK.d.ts → interactive-controller-YyZEsoB0.d.ts} +3 -3
- package/dist/{interactive-controller-C876Pjaj.js → interactive-controller-jr3_74Pq.js} +90 -57
- package/dist/lib/prepared-evidence.js +1 -1
- package/dist/lib/prepared-process.js +1 -1
- package/dist/{lines-Br3ygqhP.d.ts → lines-CCHDUfQg.d.ts} +1 -1
- package/dist/{lines-DxHJhxCe.js → lines-DrG0WOW6.js} +1 -1
- package/dist/lines.d.ts +1 -1
- package/dist/lines.js +1 -1
- package/dist/{mcp-local-Cqf4Lpp0.js → mcp-local-Dler9R_s.js} +3 -10
- package/dist/{prepared-evidence-CP1MmeCQ.js → prepared-evidence-4prvV0YK.js} +1 -1
- package/dist/{prepared-process-BWVz93cT.js → prepared-process-BSpZXDDw.js} +1 -1
- package/dist/{preview-DUxqnKSk.d.ts → preview-I1z42VGY.d.ts} +3 -3
- package/dist/{public-templates-DQiblzzK.d.ts → public-templates-Cw6gmWJD.d.ts} +2 -2
- package/dist/public-templates.d.ts +1 -1
- package/dist/public-templates.js +7 -4
- package/dist/{raw-evidence-archive-CgbWa4es.js → raw-evidence-archive-BD37zHN5.js} +2 -2
- package/dist/runtime.d.ts +5 -5
- package/dist/runtime.js +9 -7
- package/dist/{sandbox-B_Xbc9wR.js → sandbox-Dtb-_btd.js} +317 -221
- package/dist/select-seat-command.d.ts +1 -1
- package/dist/select-seat-command.js +5 -2
- package/dist/session-gateway/index.js +1 -1
- package/dist/tangle/index.d.ts +1 -1
- package/dist/tangle/index.js +1 -1
- package/dist/{tangle-Sg9ORVAA.js → tangle-fs17QlcY.js} +2 -2
- package/dist/{terminal-stream-types-1YKRrkZB.d.ts → terminal-stream-types-BqNv5EuG.d.ts} +280 -305
- package/dist/{types-B-UsXSi6.d.ts → types-CiuGb4lZ.d.ts} +4 -2
- package/package.json +6 -6
- package/dist/errors-CaX0a5Ry.js +0 -636
- /package/dist/{abort-signal-PgWPMn_e.js → abort-signal-Cu4F2VVz.js} +0 -0
- /package/dist/{file-transfer-DTHgLccV.js → file-transfer-Cr9Ossea.js} +0 -0
- /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-
|
|
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-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
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-
|
|
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,
|
|
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,
|
|
940
|
+
throw new ServerError(message, {
|
|
941
|
+
status: 502,
|
|
940
942
|
origin: "sandbox-api",
|
|
941
|
-
endpoint: "/v1/backends"
|
|
942
|
-
|
|
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)}`,
|
|
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}`,
|
|
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
|
-
|
|
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(
|
|
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",
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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.",
|
|
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
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
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.",
|
|
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
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
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)}`,
|
|
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
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
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,
|
|
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,
|
|
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)}`,
|
|
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(
|
|
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(
|
|
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
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
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,
|
|
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`,
|
|
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(
|
|
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
|
}
|