@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,21 +1,21 @@
|
|
|
1
|
-
import { A as normalizeRuntimeBackendConfig, C as encodeTextForWire, D as applyRequestedModel, F as withoutRuntimeSecrets, I as createAgentRunOutcomeTracker, L as deriveAgentResultOutcome, M as requestedSecretProfilePublicDigest, O as assertCurrentBackendType, S as encodePromptForWire, T as canUseNodeHttp2, W as normalizeEffectiveBackend, _ as isTraceId, a as assertScopedTokenActive, b as resolveDispatchRunIdentity, c as SandboxRuntimeApi, d as appendSessionIdQuery, f as callHeaders, g as TRACE_ID_HEADER, h as isRetryableCredentialRefusal, i as terminalUpgradeHeadersSupported, j as requestedPublicProfileDigest, k as mergeRuntimeBackendConfig, l as normalizeSessionInfo, m as assertFileWritePreconditions, n as InteractiveSessionController, p as sessionHeaders, r as TerminalStream, s as requestScopedToken, u as sessionMessageRequestBody, v as mintTraceId, w as runtimeFilesystemPath, x as validateAcceptedDispatchRunRef, y as resolveTraceId } from "./interactive-controller-
|
|
1
|
+
import { A as normalizeRuntimeBackendConfig, C as encodeTextForWire, D as applyRequestedModel, F as withoutRuntimeSecrets, I as createAgentRunOutcomeTracker, L as deriveAgentResultOutcome, M as requestedSecretProfilePublicDigest, O as assertCurrentBackendType, S as encodePromptForWire, T as canUseNodeHttp2, W as normalizeEffectiveBackend, _ as isTraceId, a as assertScopedTokenActive, b as resolveDispatchRunIdentity, c as SandboxRuntimeApi, d as appendSessionIdQuery, f as callHeaders, g as TRACE_ID_HEADER, h as isRetryableCredentialRefusal, i as terminalUpgradeHeadersSupported, j as requestedPublicProfileDigest, k as mergeRuntimeBackendConfig, l as normalizeSessionInfo, m as assertFileWritePreconditions, n as InteractiveSessionController, p as sessionHeaders, r as TerminalStream, s as requestScopedToken, u as sessionMessageRequestBody, v as mintTraceId, w as runtimeFilesystemPath, x as validateAcceptedDispatchRunRef, y as resolveTraceId } from "./interactive-controller-jr3_74Pq.js";
|
|
2
2
|
import { C as boundedIdentifierSchema, E as boundedStringSchema, g as sha256DigestSchema$1, o as canonicalCandidateDigest$1, s as canonicalCandidateJson$1, u as isCanonicalJsonValue$1 } from "./harness-DMaNgn0S.js";
|
|
3
3
|
import { t as sha256 } from "./sha2-vu4U-x2L.js";
|
|
4
|
-
import { A as agentInteractiveSessionStatusMatchesRef, B as PlanContinuationSchema, C as AgentInteractiveSessionStopAcknowledgementSchema, D as agentInteractiveSessionControlClaimMatchesRef, E as agentInteractiveSessionControlClaimIsNewer, F as AgentRunCancellationAcknowledgementSchema, G as InteractionResponseSchema, H as InteractionBindingSchema, I as AgentRunCancellationRequestSchema, L as agentRunCancellationAcknowledgementMatchesRequest, M as agentInteractiveDimensionSchema, N as sameAgentExactRun, O as agentInteractiveSessionPromptAcknowledgementMatchesCommand, P as AgentExactRunControlRefSchema, S as AgentInteractiveSessionStatusSchema, T as agentInteractiveSessionControlClaimAcknowledgementMatchesRequest, U as InteractionRequestSchema, V as InteractionAcknowledgementSchema, W as InteractionResponseCommandSchema, _ as AgentInteractiveSessionControlClaimRequestSchema, b as AgentInteractiveSessionPromptCommandSchema, d as interactiveSessionControlClaimResponseSchema, f as interactiveSessionControlReleaseResponseSchema, g as AgentInteractiveSessionControlClaimAcknowledgementSchema, h as AgentInteractiveSessionAttachSchema, j as agentInteractiveSessionStopAcknowledgementMatchesCommand, l as createInteractiveControlHolderId, m as interactiveSessionStatusResponseSchema, p as interactiveSessionControlValidationResponseSchema, v as AgentInteractiveSessionControlClaimSchema, w as AgentInteractiveSessionStopCommandSchema, x as AgentInteractiveSessionRefSchema, y as AgentInteractiveSessionPromptAcknowledgementSchema } from "./browser-interactive-control-controller-
|
|
4
|
+
import { A as agentInteractiveSessionStatusMatchesRef, B as PlanContinuationSchema, C as AgentInteractiveSessionStopAcknowledgementSchema, D as agentInteractiveSessionControlClaimMatchesRef, E as agentInteractiveSessionControlClaimIsNewer, F as AgentRunCancellationAcknowledgementSchema, G as InteractionResponseSchema, H as InteractionBindingSchema, I as AgentRunCancellationRequestSchema, L as agentRunCancellationAcknowledgementMatchesRequest, M as agentInteractiveDimensionSchema, N as sameAgentExactRun, O as agentInteractiveSessionPromptAcknowledgementMatchesCommand, P as AgentExactRunControlRefSchema, S as AgentInteractiveSessionStatusSchema, T as agentInteractiveSessionControlClaimAcknowledgementMatchesRequest, U as InteractionRequestSchema, V as InteractionAcknowledgementSchema, W as InteractionResponseCommandSchema, _ as AgentInteractiveSessionControlClaimRequestSchema, b as AgentInteractiveSessionPromptCommandSchema, d as interactiveSessionControlClaimResponseSchema, f as interactiveSessionControlReleaseResponseSchema, g as AgentInteractiveSessionControlClaimAcknowledgementSchema, h as AgentInteractiveSessionAttachSchema, j as agentInteractiveSessionStopAcknowledgementMatchesCommand, l as createInteractiveControlHolderId, m as interactiveSessionStatusResponseSchema, p as interactiveSessionControlValidationResponseSchema, v as AgentInteractiveSessionControlClaimSchema, w as AgentInteractiveSessionStopCommandSchema, x as AgentInteractiveSessionRefSchema, y as AgentInteractiveSessionPromptAcknowledgementSchema } from "./browser-interactive-control-controller-BYbNLahH.js";
|
|
5
5
|
import { a as ProxySecretReceiptSchema, o as canonicalAgentProfileValue, r as agentProfileSchema } from "./profile-schema-C1KpT-wS.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { C as ServerError, D as ValidationError, E as TimeoutError, S as SandboxError, T as StreamInterruptedError, _ as NotFoundError, a as readResponseJson, d as EdgeNotReadyError, g as NetworkError, i as parseResponseError, l as CapabilityError, n as isTransientWrite, o as retryWhen, r as parseErrorResponse, s as AuthError, t as TerminalStreamError, w as StateError, y as ProfileFileMountPathError } from "./errors-Mzq10WZq.js";
|
|
7
|
+
import { i as delayUntilAborted, n as awaitWithAbort, r as combineAbortSignals, t as abortableSleep } from "./abort-signal-Cu4F2VVz.js";
|
|
8
|
+
import { a as SANDBOX_PROXY_REQUEST_MAX_BYTES, c as PROFILE_FILE_MOUNT_OUTSIDE_ROOTS_CODE, d as isProfileFileMountOutsideRoots, f as profileFileMountOutsideRootsMessage, l as SANDBOX_AGENT_HOME, o as FILE_OPERATION_LIMITS, u as SANDBOX_FILE_API_ABSOLUTE_ROOTS } from "./file-transfer-Cr9Ossea.js";
|
|
9
9
|
import { t as rewrapResponse } from "./response-rewrap-Cjoxh2wZ.js";
|
|
10
10
|
import { n as fetchWithKeepAlive, t as emitTiming } from "./keep-alive-fetch-C2lFwrMA.js";
|
|
11
11
|
import { t as SIDECAR_PROXY_ERROR_HEADER } from "./sidecar-proxy-error-DTJbOAWL.js";
|
|
12
12
|
import { t as parseSSEStream } from "./sse-parser-B4aH89Nq.js";
|
|
13
|
-
import { s as uploadChunked } from "./chunked-upload-
|
|
14
|
-
import { t as runPreparedProcess } from "./prepared-process-
|
|
13
|
+
import { s as uploadChunked } from "./chunked-upload-D5ytV_s6.js";
|
|
14
|
+
import { t as runPreparedProcess } from "./prepared-process-BSpZXDDw.js";
|
|
15
15
|
import { a as isBackendType, r as cliAuthReferenceSchema } from "./cli-auth-bundle-C6DtK7GX-BbbiAn-Z.js";
|
|
16
16
|
import "./cli-auth-bundle-CMJWI99l.js";
|
|
17
|
-
import { n as SandboxLines } from "./lines-
|
|
18
|
-
import { t as downloadFile } from "./file-download-
|
|
17
|
+
import { n as SandboxLines } from "./lines-DrG0WOW6.js";
|
|
18
|
+
import { t as downloadFile } from "./file-download-CKOxcATB.js";
|
|
19
19
|
import { z } from "zod";
|
|
20
20
|
import { createHash } from "node:crypto";
|
|
21
21
|
import { constants } from "node:fs";
|
|
@@ -259,16 +259,6 @@ const TerminalReplayWindowSchema = z.strictObject({
|
|
|
259
259
|
const DEFAULT_CREATE_TIMEOUT_MS = 12e4;
|
|
260
260
|
const DEFAULT_GPU_CREATE_TIMEOUT_MS = 900 * 1e3;
|
|
261
261
|
const CREATE_RETRY_MAX_DELAY_MS = 4e3;
|
|
262
|
-
const RETRYABLE_CREATE_STATUS_CODES = new Set([
|
|
263
|
-
408,
|
|
264
|
-
425,
|
|
265
|
-
429,
|
|
266
|
-
500,
|
|
267
|
-
502,
|
|
268
|
-
503,
|
|
269
|
-
504,
|
|
270
|
-
524
|
|
271
|
-
]);
|
|
272
262
|
/**
|
|
273
263
|
* 503 classes that mean "the cell cannot place a create right now, ask
|
|
274
264
|
* again", not "this create failed". A full fleet answers `CAPACITY_EXHAUSTED`
|
|
@@ -342,15 +332,14 @@ function extendableDeadline(atMs) {
|
|
|
342
332
|
};
|
|
343
333
|
}
|
|
344
334
|
function isDeployWindowCreateError(cause) {
|
|
345
|
-
return cause instanceof SandboxError && cause.status === 503 && DEPLOY_WINDOW_RETRY_CODES.has(cause.
|
|
335
|
+
return cause instanceof SandboxError && cause.status === 503 && DEPLOY_WINDOW_RETRY_CODES.has(cause.wireCode);
|
|
346
336
|
}
|
|
347
337
|
/** A no-room refusal allocated nothing, so a create giving up on one reports this typed 503. */
|
|
348
|
-
const isCapacityShortageCreateError = (cause) => isDeployWindowCreateError(cause) && (cause.
|
|
338
|
+
const isCapacityShortageCreateError = (cause) => isDeployWindowCreateError(cause) && (cause.wireCode === "CAPACITY_EXHAUSTED" || cause.wireCode === "HOST_PRESSURE");
|
|
349
339
|
/**
|
|
350
340
|
* The plan rate limiter's 429 (`RATE_LIMIT_EXCEEDED`, or the platform's own
|
|
351
|
-
* limiter)
|
|
352
|
-
*
|
|
353
|
-
* Worker management request that limiter counts (the free plan allows 60 a
|
|
341
|
+
* limiter); a quota 429 is final in the catalog. Every paced re-POST above is
|
|
342
|
+
* a Worker management request that limiter counts (the free plan allows 60 a
|
|
354
343
|
* minute, and one create retrying every 2 s is 30 of them), so in a full
|
|
355
344
|
* fleet two concurrent creates meet it. Answered right after a capacity
|
|
356
345
|
* refusal on the same key, it is part of that pacing, not a new failure: it
|
|
@@ -358,18 +347,25 @@ const isCapacityShortageCreateError = (cause) => isDeployWindowCreateError(cause
|
|
|
358
347
|
* the attempt budget.
|
|
359
348
|
*/
|
|
360
349
|
function isRateLimitCreateError(cause) {
|
|
361
|
-
return cause
|
|
350
|
+
return retryWhen(cause) && cause.status === 429;
|
|
362
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Create retries what the catalog marks retryable (a 524 is a `TIMEOUT`, so a
|
|
354
|
+
* key that a proxy cut off is joined), except a 409, which create never
|
|
355
|
+
* retried, and a customer-floor hold: the cell keeps that room for customers
|
|
356
|
+
* and a re-POST meets the same hold.
|
|
357
|
+
*/
|
|
363
358
|
function isRetryableCreateError(cause) {
|
|
364
|
-
|
|
365
|
-
if (cause instanceof GpuProviderNotConfiguredError) return false;
|
|
366
|
-
if (cause instanceof CompliancePlacementError) return false;
|
|
367
|
-
if (cause instanceof SandboxError && cause.capacityHold) return false;
|
|
368
|
-
if (cause instanceof QuotaError && NON_RETRYABLE_CREATE_QUOTA_REASONS.has(cause.quotaType.toUpperCase())) return false;
|
|
369
|
-
return cause instanceof SandboxError && cause.status !== void 0 && RETRYABLE_CREATE_STATUS_CODES.has(cause.status);
|
|
359
|
+
return retryWhen(cause) && cause.status !== 409 && !cause.capacityHold;
|
|
370
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* Longest wait on an `IDEMPOTENT_CREATE_PROBE_FAILED` `Retry-After`: the
|
|
363
|
+
* probe answers again within seconds, and an upstream 11.8 s hint ended a
|
|
364
|
+
* create on 10-26 instead of re-probing.
|
|
365
|
+
*/
|
|
366
|
+
const PROBE_FAILED_MAX_DELAY_MS = 5e3;
|
|
371
367
|
function createRetryDelayMs(cause, failedAttempt) {
|
|
372
|
-
if (cause instanceof SandboxError && cause.retryAfterMs !== void 0) return cause.retryAfterMs;
|
|
368
|
+
if (cause instanceof SandboxError && cause.retryAfterMs !== void 0) return cause.wireCode === "IDEMPOTENT_CREATE_PROBE_FAILED" ? Math.min(cause.retryAfterMs, PROBE_FAILED_MAX_DELAY_MS) : cause.retryAfterMs;
|
|
373
369
|
return Math.min(250 * 2 ** Math.max(0, failedAttempt - 1), CREATE_RETRY_MAX_DELAY_MS);
|
|
374
370
|
}
|
|
375
371
|
function generateIdempotencyKey() {
|
|
@@ -466,7 +462,7 @@ function normalizeEgressPolicyFields(data) {
|
|
|
466
462
|
function normalizeProxySecretReceipt(raw, sandboxId) {
|
|
467
463
|
if (raw === void 0) return void 0;
|
|
468
464
|
const receipt = ProxySecretReceiptSchema.parse(raw);
|
|
469
|
-
if (receipt.sandboxId !== sandboxId) throw new ServerError("Proxy secret receipt names another sandbox", 502);
|
|
465
|
+
if (receipt.sandboxId !== sandboxId) throw new ServerError("Proxy secret receipt names another sandbox", { status: 502 });
|
|
470
466
|
return receipt;
|
|
471
467
|
}
|
|
472
468
|
//#endregion
|
|
@@ -1089,7 +1085,7 @@ var SandboxInfoCapabilities = class {
|
|
|
1089
1085
|
/** Read and apply a lifecycle answer, which carries the complete sandbox state. */
|
|
1090
1086
|
async readLifecycleResponse(response) {
|
|
1091
1087
|
const data = await readResponseJson(response);
|
|
1092
|
-
if (!this.applyLifecycleResponse(data)) throw new ServerError("Sandbox API lifecycle answer did not carry this sandbox's state", 502);
|
|
1088
|
+
if (!this.applyLifecycleResponse(data)) throw new ServerError("Sandbox API lifecycle answer did not carry this sandbox's state", { status: 502 });
|
|
1093
1089
|
return data;
|
|
1094
1090
|
}
|
|
1095
1091
|
applyLifecycleResponse(data) {
|
|
@@ -1127,11 +1123,17 @@ var SandboxInfoCapabilities = class {
|
|
|
1127
1123
|
if (this.status !== "running" || this.filesystemIncarnationReadiness !== "ready") return "not-running";
|
|
1128
1124
|
}
|
|
1129
1125
|
assertRunnable() {
|
|
1130
|
-
if (this.status !== "running") throw new StateError(`Sandbox is not running (status: ${this.status})`,
|
|
1126
|
+
if (this.status !== "running") throw new StateError(`Sandbox is not running (status: ${this.status})`, {
|
|
1127
|
+
currentState: this.status,
|
|
1128
|
+
requiredState: "running"
|
|
1129
|
+
});
|
|
1131
1130
|
this.assertFilesystemIncarnationReady();
|
|
1132
1131
|
}
|
|
1133
1132
|
assertFilesystemIncarnationReady() {
|
|
1134
|
-
if (this.filesystemIncarnationReadiness !== "ready") throw new StateError("Sandbox filesystem incarnation is not ready",
|
|
1133
|
+
if (this.filesystemIncarnationReadiness !== "ready") throw new StateError("Sandbox filesystem incarnation is not ready", {
|
|
1134
|
+
currentState: this.status,
|
|
1135
|
+
requiredState: "running with a ready filesystem incarnation"
|
|
1136
|
+
});
|
|
1135
1137
|
}
|
|
1136
1138
|
/** Authenticated runtime request relative to this sandbox; preserves the runtime error
|
|
1137
1139
|
* and request context. */
|
|
@@ -1188,7 +1190,8 @@ var SandboxInfoCapabilities = class {
|
|
|
1188
1190
|
} catch (err) {
|
|
1189
1191
|
options?.signal?.throwIfAborted();
|
|
1190
1192
|
if (err instanceof StateError) throw err;
|
|
1191
|
-
throw new NetworkError(`Failed to connect to sandbox: ${err instanceof Error ? err.message : String(err)}`,
|
|
1193
|
+
throw new NetworkError(`Failed to connect to sandbox: ${err instanceof Error ? err.message : String(err)}`, {
|
|
1194
|
+
cause: err instanceof Error ? err : void 0,
|
|
1192
1195
|
endpoint: path,
|
|
1193
1196
|
origin: "runtime"
|
|
1194
1197
|
});
|
|
@@ -3424,15 +3427,6 @@ const PROFILE_FILE_MOUNT_RETRY_MAX_MS = 2e3;
|
|
|
3424
3427
|
* platform restart, which the caller is better told about.
|
|
3425
3428
|
*/
|
|
3426
3429
|
const PROFILE_FILE_MOUNT_WRITE_RETRY_BUDGET_MS = 1e4;
|
|
3427
|
-
/**
|
|
3428
|
-
* Answers that decided nothing about the write: the gateway or proxy in front
|
|
3429
|
-
* of the sandbox runtime answered for it, so the write may never have run.
|
|
3430
|
-
*/
|
|
3431
|
-
const UNDECIDED_WRITE_STATUSES = new Set([
|
|
3432
|
-
502,
|
|
3433
|
-
503,
|
|
3434
|
-
504
|
|
3435
|
-
]);
|
|
3436
3430
|
const waitForProfileFileMount = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3437
3431
|
var ProfileFileMountExecTimeoutError = class extends Error {
|
|
3438
3432
|
constructor(timeoutMs) {
|
|
@@ -3561,9 +3555,12 @@ function isRetryableProfileFileMaterializeFailure(err) {
|
|
|
3561
3555
|
for (let link = err; link && typeof link === "object";) {
|
|
3562
3556
|
if (seen.has(link)) break;
|
|
3563
3557
|
seen.add(link);
|
|
3564
|
-
if (link instanceof ProfileFileMountExecTimeoutError
|
|
3558
|
+
if (link instanceof ProfileFileMountExecTimeoutError) return true;
|
|
3565
3559
|
const status = errorStatus(link);
|
|
3566
|
-
if (
|
|
3560
|
+
if (link instanceof SandboxError) {
|
|
3561
|
+
if (link.retryable) return true;
|
|
3562
|
+
decided = true;
|
|
3563
|
+
} else if (status !== void 0) {
|
|
3567
3564
|
if (status >= 500 || UNSETTLED_HTTP_STATUSES.has(status)) return true;
|
|
3568
3565
|
if (status >= 400) decided = true;
|
|
3569
3566
|
}
|
|
@@ -3583,12 +3580,6 @@ function isRetryableProfileFileMaterializeFailure(err) {
|
|
|
3583
3580
|
}
|
|
3584
3581
|
return !decided;
|
|
3585
3582
|
}
|
|
3586
|
-
function isUndecidedWriteFailure(err) {
|
|
3587
|
-
if (err instanceof NetworkError) return true;
|
|
3588
|
-
if (!err || typeof err !== "object") return false;
|
|
3589
|
-
const status = errorStatus(err);
|
|
3590
|
-
return status !== void 0 && UNDECIDED_WRITE_STATUSES.has(status);
|
|
3591
|
-
}
|
|
3592
3583
|
/**
|
|
3593
3584
|
* Run one file API batch write, sending it again after an undecided answer.
|
|
3594
3585
|
*
|
|
@@ -3603,7 +3594,7 @@ async function writeBatchRidingOutUndecided(write, maxRetries) {
|
|
|
3603
3594
|
await write();
|
|
3604
3595
|
return;
|
|
3605
3596
|
} catch (err) {
|
|
3606
|
-
if (!
|
|
3597
|
+
if (!(retryWhen(err) && (err instanceof NetworkError || err.status >= 502 && !(err instanceof TimeoutError))) || attempt >= maxRetries) throw err;
|
|
3607
3598
|
const waitMs = retryAfterMs(err) ?? Math.min(250 * 2 ** attempt, 2e3);
|
|
3608
3599
|
if (Date.now() - startedAt + waitMs >= PROFILE_FILE_MOUNT_WRITE_RETRY_BUDGET_MS) throw err;
|
|
3609
3600
|
await waitForProfileFileMount(waitMs);
|
|
@@ -3660,14 +3651,21 @@ function validateDeferredProfileFileMounts(files, options = {}) {
|
|
|
3660
3651
|
validateProfileFilePath(mount.path);
|
|
3661
3652
|
if (isProfileFileMountOutsideRoots(mount.path)) {
|
|
3662
3653
|
const message = `materializeProfileFileMounts: ${profileFileMountOutsideRootsMessage(mount.path)}`;
|
|
3663
|
-
if (options.failOnError !== false) throw new ProfileFileMountPathError(
|
|
3654
|
+
if (options.failOnError !== false) throw new ProfileFileMountPathError(message, {
|
|
3655
|
+
path: mount.path,
|
|
3656
|
+
code: PROFILE_FILE_MOUNT_OUTSIDE_ROOTS_CODE
|
|
3657
|
+
});
|
|
3664
3658
|
console.warn(`[sandbox.profile] dropped profile file mount (resources.failOnError is false): ${message}`);
|
|
3665
3659
|
continue;
|
|
3666
3660
|
}
|
|
3667
3661
|
const path = (mount.path.startsWith("~/") ? `${PROFILE_AGENT_HOME}/${mount.path.slice(2)}` : mount.path).replace(/\/+/g, "/");
|
|
3668
3662
|
const same = paths.get(path);
|
|
3669
3663
|
if (same === void 0) kept.push(mount);
|
|
3670
|
-
else if (inlineContent(same) !== inlineContent(mount) || isExecutableProfileFile(same) !== isExecutableProfileFile(mount)) throw new ProfileFileMountPathError(
|
|
3664
|
+
else if (inlineContent(same) !== inlineContent(mount) || isExecutableProfileFile(same) !== isExecutableProfileFile(mount)) throw new ProfileFileMountPathError(`materializeProfileFileMounts: ${same.path} and ${mount.path} name one file with different content or mode`, {
|
|
3665
|
+
path: mount.path,
|
|
3666
|
+
code: "PROFILE_FILE_CONFLICT",
|
|
3667
|
+
status: 409
|
|
3668
|
+
});
|
|
3671
3669
|
paths.set(path, mount);
|
|
3672
3670
|
}
|
|
3673
3671
|
return kept;
|
|
@@ -4057,7 +4055,11 @@ async function materializeAbsentProfileFiles(box, files, options) {
|
|
|
4057
4055
|
const conflict = /^PROFILE_FILE_CONFLICT (.+)$/m.exec(stderr)?.[1];
|
|
4058
4056
|
if (conflict !== void 0) {
|
|
4059
4057
|
const paths = JSON.parse(conflict);
|
|
4060
|
-
throw new ProfileFileMountPathError(
|
|
4058
|
+
throw new ProfileFileMountPathError(`Profile file ${paths.join(" and ")} conflicts (changed in the sandbox and the profile, or one target with two contents or modes); refusing to replace it`, {
|
|
4059
|
+
path: paths[0],
|
|
4060
|
+
code: "PROFILE_FILE_CONFLICT",
|
|
4061
|
+
status: 409
|
|
4062
|
+
});
|
|
4061
4063
|
}
|
|
4062
4064
|
throw new ProfileFileMaterializeError(`materializeProfileFileMounts: ${stderr}`, {
|
|
4063
4065
|
exitCode: applied.exitCode,
|
|
@@ -4133,7 +4135,10 @@ var ProfileFileDelivery = class {
|
|
|
4133
4135
|
if (incarnation === void 0 || this.delivered?.incarnation !== incarnation || this.delivered.fingerprint !== bundle) {
|
|
4134
4136
|
this.delivered = void 0;
|
|
4135
4137
|
delivery = await this.deliverProfileFiles(box, deferredFiles, bundle, existingFiles);
|
|
4136
|
-
if (box.filesystemIncarnationId !== incarnation) throw new StateError("Filesystem incarnation changed during profile file delivery",
|
|
4138
|
+
if (box.filesystemIncarnationId !== incarnation) throw new StateError("Filesystem incarnation changed during profile file delivery", {
|
|
4139
|
+
currentState: box.filesystemIncarnationId ?? "unknown",
|
|
4140
|
+
requiredState: incarnation ?? "unknown"
|
|
4141
|
+
});
|
|
4137
4142
|
if (incarnation !== void 0) this.delivered = {
|
|
4138
4143
|
incarnation,
|
|
4139
4144
|
fingerprint: bundle
|
|
@@ -4244,8 +4249,7 @@ const DEFAULT_WAIT_TIMEOUT_MS = 10 * 6e4;
|
|
|
4244
4249
|
var SandboxOperationFailedError = class extends SandboxError {
|
|
4245
4250
|
operation;
|
|
4246
4251
|
constructor(operation) {
|
|
4247
|
-
super(operation.error?.message ?? `Sandbox ${operation.kind} operation ${operation.id} failed`, operation.error?.code ?? "OPERATION_FAILED");
|
|
4248
|
-
this.name = "SandboxOperationFailedError";
|
|
4252
|
+
super(operation.error?.message ?? `Sandbox ${operation.kind} operation ${operation.id} failed`, { code: operation.error?.code ?? "OPERATION_FAILED" });
|
|
4249
4253
|
this.operation = operation;
|
|
4250
4254
|
}
|
|
4251
4255
|
};
|
|
@@ -4273,7 +4277,10 @@ function parseSnapshot(value) {
|
|
|
4273
4277
|
};
|
|
4274
4278
|
}
|
|
4275
4279
|
function contractViolation(message) {
|
|
4276
|
-
return new SandboxError(message,
|
|
4280
|
+
return new SandboxError(message, {
|
|
4281
|
+
code: "UPSTREAM_CONTRACT_VIOLATION",
|
|
4282
|
+
status: 502
|
|
4283
|
+
});
|
|
4277
4284
|
}
|
|
4278
4285
|
/**
|
|
4279
4286
|
* Parse the API's operation body; throws on a body that is not one. With
|
|
@@ -4393,7 +4400,7 @@ var SandboxOperation = class SandboxOperation {
|
|
|
4393
4400
|
let delayMs = 0;
|
|
4394
4401
|
let info;
|
|
4395
4402
|
while (!info || !isTerminalOperation(info)) {
|
|
4396
|
-
if (Date.now() - startedAt + delayMs > timeoutMs) throw new TimeoutError(
|
|
4403
|
+
if (Date.now() - startedAt + delayMs > timeoutMs) throw new TimeoutError(`Timed out waiting for ${this.#info.kind} operation ${this.#info.id} (last state ${this.#info.state})`, { timeoutMs });
|
|
4397
4404
|
signal?.throwIfAborted();
|
|
4398
4405
|
if (delayMs > 0) await delayUntilAborted(delayMs, signal);
|
|
4399
4406
|
const next = Math.min(Math.max(delayMs * 2, FIRST_POLL_MS), MAX_POLL_MS);
|
|
@@ -4401,8 +4408,7 @@ var SandboxOperation = class SandboxOperation {
|
|
|
4401
4408
|
info = await this.refresh({ signal });
|
|
4402
4409
|
delayMs = next;
|
|
4403
4410
|
} catch (error) {
|
|
4404
|
-
if (!(error instanceof
|
|
4405
|
-
if (error.status !== 429 && error.status !== 503) throw error;
|
|
4411
|
+
if (!retryWhen(error) || error instanceof NetworkError || error.status !== 429 && error.status !== 503) throw error;
|
|
4406
4412
|
delayMs = Math.max(error.retryAfterMs ?? 0, next);
|
|
4407
4413
|
}
|
|
4408
4414
|
}
|
|
@@ -4730,7 +4736,8 @@ var SandboxBackendCapabilities = class extends SandboxGitCapabilities {
|
|
|
4730
4736
|
async backendControlData(path) {
|
|
4731
4737
|
await this.ensureRunning();
|
|
4732
4738
|
const payload = await readResponseJson(await this.runtimeFetch(path, { method: "GET" }));
|
|
4733
|
-
if (!payload || typeof payload !== "object" || !("data" in payload)) throw new ServerError("Backend control response missing data",
|
|
4739
|
+
if (!payload || typeof payload !== "object" || !("data" in payload)) throw new ServerError("Backend control response missing data", {
|
|
4740
|
+
status: 502,
|
|
4734
4741
|
endpoint: path,
|
|
4735
4742
|
origin: "runtime"
|
|
4736
4743
|
});
|
|
@@ -4791,7 +4798,8 @@ var SandboxBackendCapabilities = class extends SandboxGitCapabilities {
|
|
|
4791
4798
|
await this.ensureRunning();
|
|
4792
4799
|
const search = new URLSearchParams({ path });
|
|
4793
4800
|
const payload = await readResponseJson(await this.runtimeFetch(`/config/artifacts/${encodeURIComponent(sessionId)}/download?${search.toString()}`, { method: "GET" }));
|
|
4794
|
-
if (!payload || typeof payload.base64 !== "string") throw new ServerError("Backend artifact download response missing base64",
|
|
4801
|
+
if (!payload || typeof payload.base64 !== "string") throw new ServerError("Backend artifact download response missing base64", {
|
|
4802
|
+
status: 502,
|
|
4795
4803
|
endpoint: `/config/artifacts/${encodeURIComponent(sessionId)}/download`,
|
|
4796
4804
|
origin: "runtime"
|
|
4797
4805
|
});
|
|
@@ -5157,12 +5165,14 @@ var SandboxPreviewCapabilities = class extends SandboxGpuCapabilities {
|
|
|
5157
5165
|
const startTime = Date.now();
|
|
5158
5166
|
let lastProbeCode;
|
|
5159
5167
|
while (true) {
|
|
5160
|
-
if (options?.signal?.aborted) throw new TimeoutError(
|
|
5168
|
+
if (options?.signal?.aborted) throw new TimeoutError("Aborted", {
|
|
5169
|
+
timeoutMs: 0,
|
|
5161
5170
|
origin: "runtime",
|
|
5162
5171
|
endpoint
|
|
5163
5172
|
});
|
|
5164
5173
|
const remainingBeforeCheckMs = timeoutMs - (Date.now() - startTime);
|
|
5165
|
-
if (remainingBeforeCheckMs < 0) throw new TimeoutError(
|
|
5174
|
+
if (remainingBeforeCheckMs < 0) throw new TimeoutError(`Timed out waiting for preview link ${previewId} to become ready${lastProbeCode ? ` (${lastProbeCode})` : ""}`, {
|
|
5175
|
+
timeoutMs,
|
|
5166
5176
|
origin: "runtime",
|
|
5167
5177
|
endpoint
|
|
5168
5178
|
});
|
|
@@ -5171,35 +5181,43 @@ var SandboxPreviewCapabilities = class extends SandboxGpuCapabilities {
|
|
|
5171
5181
|
try {
|
|
5172
5182
|
previewLink = await this.previewLinkGet(previewId, requestSignal);
|
|
5173
5183
|
} catch (error) {
|
|
5174
|
-
if (options?.signal?.aborted) throw new TimeoutError(
|
|
5184
|
+
if (options?.signal?.aborted) throw new TimeoutError("Aborted", {
|
|
5185
|
+
timeoutMs: 0,
|
|
5175
5186
|
origin: "runtime",
|
|
5176
5187
|
endpoint
|
|
5177
5188
|
});
|
|
5178
|
-
if (timeoutMs > 0 && Date.now() - startTime >= timeoutMs) throw new TimeoutError(
|
|
5189
|
+
if (timeoutMs > 0 && Date.now() - startTime >= timeoutMs) throw new TimeoutError(`Timed out waiting for preview link ${previewId} to become ready${lastProbeCode ? ` (${lastProbeCode})` : ""}`, {
|
|
5190
|
+
timeoutMs,
|
|
5179
5191
|
origin: "runtime",
|
|
5180
5192
|
endpoint
|
|
5181
5193
|
});
|
|
5182
5194
|
throw error;
|
|
5183
5195
|
}
|
|
5184
5196
|
lastProbeCode = previewLink.lastProbe?.code;
|
|
5185
|
-
if (options?.signal?.aborted) throw new TimeoutError(
|
|
5197
|
+
if (options?.signal?.aborted) throw new TimeoutError("Aborted", {
|
|
5198
|
+
timeoutMs: 0,
|
|
5186
5199
|
origin: "runtime",
|
|
5187
5200
|
endpoint
|
|
5188
5201
|
});
|
|
5189
5202
|
if (previewLink.status === "ready") return previewLink;
|
|
5190
|
-
if (previewLink.status === "error" || previewLink.status === "disabled") throw new StateError(`Preview link cannot become ready (status: ${previewLink.status})`,
|
|
5203
|
+
if (previewLink.status === "error" || previewLink.status === "disabled") throw new StateError(`Preview link cannot become ready (status: ${previewLink.status})`, {
|
|
5204
|
+
currentState: previewLink.status,
|
|
5205
|
+
requiredState: "ready",
|
|
5191
5206
|
origin: "runtime",
|
|
5192
|
-
endpoint
|
|
5193
|
-
|
|
5207
|
+
endpoint,
|
|
5208
|
+
code: lastProbeCode ?? "PREVIEW_LINK_UNAVAILABLE"
|
|
5209
|
+
});
|
|
5194
5210
|
const remainingAfterCheckMs = timeoutMs - (Date.now() - startTime);
|
|
5195
|
-
if (remainingAfterCheckMs <= 0) throw new TimeoutError(
|
|
5211
|
+
if (remainingAfterCheckMs <= 0) throw new TimeoutError(`Timed out waiting for preview link ${previewId} to become ready${lastProbeCode ? ` (${lastProbeCode})` : ""}`, {
|
|
5212
|
+
timeoutMs,
|
|
5196
5213
|
origin: "runtime",
|
|
5197
5214
|
endpoint
|
|
5198
5215
|
});
|
|
5199
5216
|
try {
|
|
5200
5217
|
await delayUntilAborted(Math.min(pollIntervalMs, remainingAfterCheckMs), options?.signal);
|
|
5201
5218
|
} catch {
|
|
5202
|
-
throw new TimeoutError(
|
|
5219
|
+
throw new TimeoutError("Aborted", {
|
|
5220
|
+
timeoutMs: 0,
|
|
5203
5221
|
origin: "runtime",
|
|
5204
5222
|
endpoint
|
|
5205
5223
|
});
|
|
@@ -5365,8 +5383,8 @@ var SandboxLifecycleEventsCapabilities = class extends SandboxPreviewCapabilitie
|
|
|
5365
5383
|
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
5366
5384
|
const signal = options?.signal ? combineAbortSignals([options.signal, timeoutSignal]) : timeoutSignal;
|
|
5367
5385
|
const rethrowWaitError = (error) => {
|
|
5368
|
-
if (timeoutSignal.aborted) throw new TimeoutError(
|
|
5369
|
-
if (options?.signal?.aborted) throw new TimeoutError(
|
|
5386
|
+
if (timeoutSignal.aborted) throw new TimeoutError(`Timed out waiting for sandbox to reach ${statuses.join(" or ")}`, { timeoutMs });
|
|
5387
|
+
if (options?.signal?.aborted) throw new TimeoutError("Aborted", { timeoutMs: 0 });
|
|
5370
5388
|
throw error;
|
|
5371
5389
|
};
|
|
5372
5390
|
if (options?.onProgress && (this.status === "provisioning" || this.status === "pending")) try {
|
|
@@ -5380,8 +5398,8 @@ var SandboxLifecycleEventsCapabilities = class extends SandboxPreviewCapabilitie
|
|
|
5380
5398
|
if (signal.aborted) rethrowWaitError(signal.reason);
|
|
5381
5399
|
await this.refreshFor("poll", signal).catch(rethrowWaitError);
|
|
5382
5400
|
if (this.hasReachedWaitTarget(statuses)) return;
|
|
5383
|
-
if (this.status === "failed") throw new StateError(this.error ?? "Sandbox failed", this.status);
|
|
5384
|
-
if (Date.now() - startTime > timeoutMs) throw new TimeoutError(
|
|
5401
|
+
if (this.status === "failed") throw new StateError(this.error ?? "Sandbox failed", { currentState: this.status });
|
|
5402
|
+
if (Date.now() - startTime > timeoutMs) throw new TimeoutError(`Timed out waiting for sandbox to reach ${statuses.join(" or ")}`, { timeoutMs });
|
|
5385
5403
|
await delayUntilAborted(pollIntervalMs, signal).catch(rethrowWaitError);
|
|
5386
5404
|
}
|
|
5387
5405
|
}
|
|
@@ -5395,7 +5413,7 @@ var SandboxLifecycleEventsCapabilities = class extends SandboxPreviewCapabilitie
|
|
|
5395
5413
|
if (signal) {
|
|
5396
5414
|
if (signal.aborted) {
|
|
5397
5415
|
clearTimeout(timeout);
|
|
5398
|
-
throw new TimeoutError(
|
|
5416
|
+
throw new TimeoutError("Aborted", { timeoutMs: 0 });
|
|
5399
5417
|
}
|
|
5400
5418
|
signal.addEventListener("abort", () => abortController.abort(), { once: true });
|
|
5401
5419
|
}
|
|
@@ -5420,17 +5438,17 @@ var SandboxLifecycleEventsCapabilities = class extends SandboxPreviewCapabilitie
|
|
|
5420
5438
|
});
|
|
5421
5439
|
} else if (event.type === "provision_failed") {
|
|
5422
5440
|
await this.refresh(abortController.signal);
|
|
5423
|
-
throw new StateError(event.data.error ?? "Provisioning failed", this.status);
|
|
5441
|
+
throw new StateError(event.data.error ?? "Provisioning failed", { currentState: this.status });
|
|
5424
5442
|
} else if (event.type === "provision_complete" || event.type === "status_change") {
|
|
5425
5443
|
await this.refreshFor("poll", abortController.signal);
|
|
5426
5444
|
if (this.hasReachedWaitTarget(statuses)) return true;
|
|
5427
|
-
if (this.status === "failed") throw new StateError(this.error ?? "Sandbox failed", this.status);
|
|
5445
|
+
if (this.status === "failed") throw new StateError(this.error ?? "Sandbox failed", { currentState: this.status });
|
|
5428
5446
|
}
|
|
5429
5447
|
await this.refreshFor("poll", abortController.signal);
|
|
5430
5448
|
if (this.hasReachedWaitTarget(statuses)) return true;
|
|
5431
|
-
if (this.status === "failed") throw new StateError(this.error ?? "Sandbox failed", this.status);
|
|
5449
|
+
if (this.status === "failed") throw new StateError(this.error ?? "Sandbox failed", { currentState: this.status });
|
|
5432
5450
|
if (statuses.includes("running") && this.status === "running") return false;
|
|
5433
|
-
throw new TimeoutError(
|
|
5451
|
+
throw new TimeoutError(`SSE stream ended before sandbox reached ${statuses.join(" or ")}`, { timeoutMs });
|
|
5434
5452
|
} finally {
|
|
5435
5453
|
clearTimeout(timeout);
|
|
5436
5454
|
abortController.abort();
|
|
@@ -5450,7 +5468,9 @@ var SandboxLifecycleEventsCapabilities = class extends SandboxPreviewCapabilitie
|
|
|
5450
5468
|
*/
|
|
5451
5469
|
consumerConnectionReady() {
|
|
5452
5470
|
const connection = this.connection;
|
|
5453
|
-
if (connection?.edgeStatus === "failed") throw new EdgeNotReadyError(connection.edgeError || `Sandbox ${this.id} public edge failed to attach`,
|
|
5471
|
+
if (connection?.edgeStatus === "failed") throw new EdgeNotReadyError(connection.edgeError || `Sandbox ${this.id} public edge failed to attach`, {
|
|
5472
|
+
edgeStatus: "failed",
|
|
5473
|
+
status: 502,
|
|
5454
5474
|
origin: "sandbox-api",
|
|
5455
5475
|
endpoint: `/v1/sandboxes/${this.id}`
|
|
5456
5476
|
});
|
|
@@ -5532,7 +5552,10 @@ var SandboxLifecycleCapabilities = class extends SandboxLifecycleEventsCapabilit
|
|
|
5532
5552
|
if (diagnostics) local.info.startupDiagnostics = diagnostics;
|
|
5533
5553
|
}
|
|
5534
5554
|
async delete(options) {
|
|
5535
|
-
if (options?.wait === false && options.until) throw new SandboxError("delete({ wait: false }) cannot also wait until removed",
|
|
5555
|
+
if (options?.wait === false && options.until) throw new SandboxError("delete({ wait: false }) cannot also wait until removed", {
|
|
5556
|
+
code: "INVALID_OPTIONS",
|
|
5557
|
+
status: 400
|
|
5558
|
+
});
|
|
5536
5559
|
options?.signal?.throwIfAborted();
|
|
5537
5560
|
const path = `/v1/sandboxes/${this.id}?wait=false${options?.until === "removed" ? "&until=removed" : ""}`;
|
|
5538
5561
|
let sawLifecycleConflict = false;
|
|
@@ -5590,17 +5613,12 @@ var SandboxLifecycleCapabilities = class extends SandboxLifecycleEventsCapabilit
|
|
|
5590
5613
|
}
|
|
5591
5614
|
};
|
|
5592
5615
|
/**
|
|
5593
|
-
* The platform's typed refusal when one sandbox already has a lifecycle
|
|
5594
|
-
* operation in flight: HTTP 409 from `withSandboxLifecycleClaim`
|
|
5595
|
-
* (products/sandbox/api/src/routes/sandbox-lifecycle-claim.ts).
|
|
5596
|
-
*/
|
|
5597
|
-
const LIFECYCLE_CONFLICT_CODE = "SANDBOX_LIFECYCLE_IN_PROGRESS";
|
|
5598
|
-
/**
|
|
5599
5616
|
* How long a lifecycle call re-attempts a refusal that only asks it to wait.
|
|
5600
5617
|
* `resume`, `stop` and `delete` share one lease per sandbox, `lifecycle-<id>`,
|
|
5601
5618
|
* held past a response while its deferred cleanup runs (the workflow smoke
|
|
5602
|
-
* hand-rolls the same retry), so a second call in that window is refused 409
|
|
5603
|
-
* 2 of 2
|
|
5619
|
+
* hand-rolls the same retry), so a second call in that window is refused 409
|
|
5620
|
+
* `SANDBOX_LIFECYCLE_IN_PROGRESS` by `withSandboxLifecycleClaim`; 2 of 2
|
|
5621
|
+
* provider-seam runs (agent-sdk#280) lost a completed turn to it. A
|
|
5604
5622
|
* lease-store or storage outage answers 503 (us1 2026-11-01: 16/16 child
|
|
5605
5623
|
* deletes failed through a Platform deploy that had removed every child). The
|
|
5606
5624
|
* ceiling (~11 s) is short: a longer conflict is a real concurrent operation.
|
|
@@ -5611,7 +5629,7 @@ const LIFECYCLE_CONFLICT_MAX_DELAY_MS = 4e3;
|
|
|
5611
5629
|
/** A lease-store or storage outage the API answers 503 before or after its effect: a retry is safe. */
|
|
5612
5630
|
const LIFECYCLE_OUTAGE_CODES = new Set(["SANDBOX_LIFECYCLE_COORDINATION_UNAVAILABLE", "STORAGE_UNAVAILABLE"]);
|
|
5613
5631
|
function isLifecycleConflict(cause) {
|
|
5614
|
-
return cause instanceof SandboxError && (cause.status === 409 && cause.
|
|
5632
|
+
return cause instanceof SandboxError && (cause.status === 409 && cause.wireCode === "SANDBOX_LIFECYCLE_IN_PROGRESS" || cause.status === 503 && LIFECYCLE_OUTAGE_CODES.has(cause.wireCode));
|
|
5615
5633
|
}
|
|
5616
5634
|
function lifecycleConflictDelayMs(cause, failedAttempt) {
|
|
5617
5635
|
if (cause instanceof SandboxError && cause.retryAfterMs !== void 0) return cause.retryAfterMs;
|
|
@@ -5661,7 +5679,7 @@ function usableTimeoutMs(timeoutMs) {
|
|
|
5661
5679
|
function usableExecutionTtlMs(ttlMs) {
|
|
5662
5680
|
if (ttlMs === void 0) return void 0;
|
|
5663
5681
|
const got = timerMsViolation(ttlMs);
|
|
5664
|
-
if (got !== void 0) throw new ValidationError(`ttlMs must be an integer between 1 and ${MAX_TIMER_MS}ms; got ${got}`, { ttlMs: "Must be a positive integer of milliseconds" });
|
|
5682
|
+
if (got !== void 0) throw new ValidationError(`ttlMs must be an integer between 1 and ${MAX_TIMER_MS}ms; got ${got}`, { fields: { ttlMs: "Must be a positive integer of milliseconds" } });
|
|
5665
5683
|
return ttlMs;
|
|
5666
5684
|
}
|
|
5667
5685
|
/**
|
|
@@ -6582,7 +6600,7 @@ function assertActiveInteractiveControlClaim(control) {
|
|
|
6582
6600
|
if (!Number.isFinite(expiresAt) || expiresAt <= Date.now()) throw new Error("Interactive control claim is stale");
|
|
6583
6601
|
}
|
|
6584
6602
|
function assertInteractiveRefBelongsToSandbox(ref, sandboxId) {
|
|
6585
|
-
if (ref.run.environmentId !== sandboxId) throw new ValidationError("Interactive session reference does not belong to this sandbox", { "ref.run.environmentId": `Expected ${sandboxId}` });
|
|
6603
|
+
if (ref.run.environmentId !== sandboxId) throw new ValidationError("Interactive session reference does not belong to this sandbox", { fields: { "ref.run.environmentId": `Expected ${sandboxId}` } });
|
|
6586
6604
|
}
|
|
6587
6605
|
//#endregion
|
|
6588
6606
|
//#region src/sandbox-ledger.ts
|
|
@@ -6624,10 +6642,12 @@ function meterTurn(box, fetch, id) {
|
|
|
6624
6642
|
//#endregion
|
|
6625
6643
|
//#region src/capabilities.ts
|
|
6626
6644
|
function invalidCapabilities(message) {
|
|
6627
|
-
throw new ServerError(`Capability discovery returned ${message}`,
|
|
6645
|
+
throw new ServerError(`Capability discovery returned ${message}`, {
|
|
6646
|
+
status: 502,
|
|
6628
6647
|
origin: "runtime",
|
|
6629
|
-
endpoint: "/capabilities"
|
|
6630
|
-
|
|
6648
|
+
endpoint: "/capabilities",
|
|
6649
|
+
code: "INVALID_CAPABILITY_DOCUMENT"
|
|
6650
|
+
});
|
|
6631
6651
|
}
|
|
6632
6652
|
function readFlagGroup(record, group, keys) {
|
|
6633
6653
|
const value = record[group];
|
|
@@ -6770,7 +6790,8 @@ var SandboxRuntimeOperations = class extends SandboxLifecycleCapabilities {
|
|
|
6770
6790
|
try {
|
|
6771
6791
|
data = JSON.parse(body);
|
|
6772
6792
|
} catch (error) {
|
|
6773
|
-
if (response.ok) throw new NetworkError("Detailed health returned a non-JSON response",
|
|
6793
|
+
if (response.ok) throw new NetworkError("Detailed health returned a non-JSON response", {
|
|
6794
|
+
cause: error instanceof Error ? error : void 0,
|
|
6774
6795
|
endpoint: "/health/detailed",
|
|
6775
6796
|
origin: "runtime"
|
|
6776
6797
|
});
|
|
@@ -6795,9 +6816,9 @@ var SandboxRuntimeOperations = class extends SandboxLifecycleCapabilities {
|
|
|
6795
6816
|
if (!response.ok) throw parseErrorResponse(response.status, await response.text());
|
|
6796
6817
|
const value = await readResponseJson(response);
|
|
6797
6818
|
const pending = value && typeof value === "object" && "proxySecretReceipt" in value ? ProxySecretReceiptSchema.safeParse(value.proxySecretReceipt) : void 0;
|
|
6798
|
-
if (!value || typeof value !== "object" || (pending ? !pending.success || pending.data.status !== "proxy-installed" || pending.data.sandboxId !== this.id || !pending.data.bindings.length || pending.data.bindings.some((binding) => binding.secretRef.key !== cliAuth.secretEnv) || "installed" in value : !("installed" in value) || value.installed !== true) || !("harness" in value) || value.harness !== grant.harness || !("cliAuth" in value)) throw new StateError("Native credential grant receipt is invalid", "unconfirmed");
|
|
6819
|
+
if (!value || typeof value !== "object" || (pending ? !pending.success || pending.data.status !== "proxy-installed" || pending.data.sandboxId !== this.id || !pending.data.bindings.length || pending.data.bindings.some((binding) => binding.secretRef.key !== cliAuth.secretEnv) || "installed" in value : !("installed" in value) || value.installed !== true) || !("harness" in value) || value.harness !== grant.harness || !("cliAuth" in value)) throw new StateError("Native credential grant receipt is invalid", { currentState: "unconfirmed" });
|
|
6799
6820
|
const confirmed = cliAuthReferenceSchema.safeParse(value.cliAuth);
|
|
6800
|
-
if (!confirmed.success || confirmed.data.account !== cliAuth.account || confirmed.data.secretEnv !== cliAuth.secretEnv || confirmed.data.format !== cliAuth.format) throw new StateError("Native credential grant receipt names another reference", "unconfirmed");
|
|
6821
|
+
if (!confirmed.success || confirmed.data.account !== cliAuth.account || confirmed.data.secretEnv !== cliAuth.secretEnv || confirmed.data.format !== cliAuth.format) throw new StateError("Native credential grant receipt names another reference", { currentState: "unconfirmed" });
|
|
6801
6822
|
}
|
|
6802
6823
|
/** Deployed capabilities; newer schemas return null, malformed known schemas or transport failures throw. */
|
|
6803
6824
|
async capabilities() {
|
|
@@ -6856,7 +6877,7 @@ var SandboxCodeOperations = class extends SandboxRuntimeOperations {
|
|
|
6856
6877
|
blocking: true
|
|
6857
6878
|
})
|
|
6858
6879
|
}, timeoutMs + 15e3))).data;
|
|
6859
|
-
if (!result || typeof result.exitCode !== "number" || typeof result.stdout !== "string" || typeof result.stderr !== "string") throw new ServerError("Sandbox process response was malformed", 502);
|
|
6880
|
+
if (!result || typeof result.exitCode !== "number" || typeof result.stdout !== "string" || typeof result.stderr !== "string") throw new ServerError("Sandbox process response was malformed", { status: 502 });
|
|
6860
6881
|
return {
|
|
6861
6882
|
exitCode: result.exitCode,
|
|
6862
6883
|
stdout: result.stdout,
|
|
@@ -7039,7 +7060,7 @@ var SandboxFileOperations = class extends SandboxExecCapabilities {
|
|
|
7039
7060
|
ifAbsent: options?.ifAbsent
|
|
7040
7061
|
})
|
|
7041
7062
|
}));
|
|
7042
|
-
if (!data.data) throw new ServerError("Invalid file write response: missing data", 502);
|
|
7063
|
+
if (!data.data) throw new ServerError("Invalid file write response: missing data", { status: 502 });
|
|
7043
7064
|
this.assertWritePreconditions(data.data, {
|
|
7044
7065
|
expectedHash: options?.ifHash,
|
|
7045
7066
|
ifAbsent: options?.ifAbsent
|
|
@@ -7063,12 +7084,14 @@ var SandboxFileOperations = class extends SandboxExecCapabilities {
|
|
|
7063
7084
|
payload = JSON.parse(await response.text());
|
|
7064
7085
|
} catch (error) {
|
|
7065
7086
|
if (!(error instanceof SyntaxError)) throw error;
|
|
7066
|
-
throw new ServerError("Runtime returned invalid JSON for file read",
|
|
7087
|
+
throw new ServerError("Runtime returned invalid JSON for file read", {
|
|
7088
|
+
status: 502,
|
|
7067
7089
|
origin: "runtime",
|
|
7068
7090
|
endpoint: "/files/read"
|
|
7069
7091
|
});
|
|
7070
7092
|
}
|
|
7071
|
-
if (typeof payload !== "object" || payload === null || !("success" in payload) || payload.success !== true || !("data" in payload) || typeof payload.data !== "object" || payload.data === null || !("content" in payload.data) || typeof payload.data.content !== "string" || !("encoding" in payload.data) || payload.data.encoding !== "utf8") throw new ServerError("Runtime returned a malformed text file response",
|
|
7093
|
+
if (typeof payload !== "object" || payload === null || !("success" in payload) || payload.success !== true || !("data" in payload) || typeof payload.data !== "object" || payload.data === null || !("content" in payload.data) || typeof payload.data.content !== "string" || !("encoding" in payload.data) || payload.data.encoding !== "utf8") throw new ServerError("Runtime returned a malformed text file response", {
|
|
7094
|
+
status: 502,
|
|
7072
7095
|
origin: "runtime",
|
|
7073
7096
|
endpoint: "/files/read"
|
|
7074
7097
|
});
|
|
@@ -7094,12 +7117,12 @@ var SandboxFileWriteCapabilities = class extends SandboxFileOperations {
|
|
|
7094
7117
|
}, response.headers);
|
|
7095
7118
|
}
|
|
7096
7119
|
const payload = await response.json().catch(() => null);
|
|
7097
|
-
if (!payload?.data?.files || typeof payload.data.files !== "object") throw new ServerError("Invalid file batch write response: missing data.files", 502);
|
|
7120
|
+
if (!payload?.data?.files || typeof payload.data.files !== "object") throw new ServerError("Invalid file batch write response: missing data.files", { status: 502 });
|
|
7098
7121
|
for (const file of files) {
|
|
7099
7122
|
const result = payload.data.files[file.path];
|
|
7100
|
-
if (!result || typeof result !== "object") throw new ServerError(`Invalid file batch write response: missing result for ${file.path}`, 502);
|
|
7123
|
+
if (!result || typeof result !== "object") throw new ServerError(`Invalid file batch write response: missing result for ${file.path}`, { status: 502 });
|
|
7101
7124
|
if (result.success === false) {
|
|
7102
|
-
if (typeof result.status !== "number" || typeof result.error !== "string" && (typeof result.error !== "object" || result.error === null)) throw new ServerError(`Invalid file batch write failure for ${file.path}`, 502);
|
|
7125
|
+
if (typeof result.status !== "number" || typeof result.error !== "string" && (typeof result.error !== "object" || result.error === null)) throw new ServerError(`Invalid file batch write failure for ${file.path}`, { status: 502 });
|
|
7103
7126
|
throw parseErrorResponse(result.status, JSON.stringify({
|
|
7104
7127
|
success: false,
|
|
7105
7128
|
error: result.error
|
|
@@ -7108,7 +7131,7 @@ var SandboxFileWriteCapabilities = class extends SandboxFileOperations {
|
|
|
7108
7131
|
path: "/files/write-batch"
|
|
7109
7132
|
}, response.headers);
|
|
7110
7133
|
}
|
|
7111
|
-
if (result.success !== true || typeof result.path !== "string" || typeof result.hash !== "string" || typeof result.size !== "number" || typeof result.mtime !== "number" || result.preconditionsApplied !== void 0 && (!Array.isArray(result.preconditionsApplied) || !result.preconditionsApplied.every((value) => value === "ifHash" || value === "ifAbsent"))) throw new ServerError(`Invalid file batch write result for ${file.path}`, 502);
|
|
7134
|
+
if (result.success !== true || typeof result.path !== "string" || typeof result.hash !== "string" || typeof result.size !== "number" || typeof result.mtime !== "number" || result.preconditionsApplied !== void 0 && (!Array.isArray(result.preconditionsApplied) || !result.preconditionsApplied.every((value) => value === "ifHash" || value === "ifAbsent"))) throw new ServerError(`Invalid file batch write result for ${file.path}`, { status: 502 });
|
|
7112
7135
|
this.assertWritePreconditions({
|
|
7113
7136
|
path: result.path,
|
|
7114
7137
|
hash: result.hash,
|
|
@@ -7135,7 +7158,7 @@ var SandboxFileWriteCapabilities = class extends SandboxFileOperations {
|
|
|
7135
7158
|
});
|
|
7136
7159
|
break;
|
|
7137
7160
|
} catch (err) {
|
|
7138
|
-
if (
|
|
7161
|
+
if (isTransientWrite(err) && attempt < maxRetries) {
|
|
7139
7162
|
const backoff = Math.min(WRITE_MANY_RETRY_BASE_MS * 2 ** attempt, WRITE_MANY_RETRY_MAX_MS);
|
|
7140
7163
|
await delay((err instanceof SandboxError ? err.retryAfterMs : void 0) ?? backoff);
|
|
7141
7164
|
continue;
|
|
@@ -7240,12 +7263,6 @@ function hasDuplicateWriteManyPaths(files) {
|
|
|
7240
7263
|
return new Set(files.map((file) => file.path)).size !== files.length;
|
|
7241
7264
|
}
|
|
7242
7265
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7243
|
-
/** Transient file-write failures worth retrying: rate-limit (quota), 5xx,
|
|
7244
|
-
* transport (network), and request timeouts. A non-transient error (bad path,
|
|
7245
|
-
* auth, validation) fails loud immediately. */
|
|
7246
|
-
function isTransientWriteError(err) {
|
|
7247
|
-
return err instanceof QuotaError || err instanceof ServerError || err instanceof NetworkError || err instanceof TimeoutError;
|
|
7248
|
-
}
|
|
7249
7266
|
//#endregion
|
|
7250
7267
|
//#region src/sandbox-profile.ts
|
|
7251
7268
|
function isProfileNotCachedError(err) {
|
|
@@ -7487,7 +7504,8 @@ var SandboxProfileCapabilities = class extends SandboxFileWriteCapabilities {
|
|
|
7487
7504
|
const receipt = await this.postProfileRevision(backend.profile, endpoint, body);
|
|
7488
7505
|
const digest = /^sha256:[a-f0-9]{64}$/;
|
|
7489
7506
|
const conflicts = readPublicProfileConflicts(receipt.conflicts);
|
|
7490
|
-
if (typeof receipt.workspacePlanDigest !== "string" || !digest.test(receipt.workspacePlanDigest) || typeof receipt.materializedDigest !== "string" || !digest.test(receipt.materializedDigest) || typeof receipt.launchDigest !== "string" || !/^[a-f0-9]{64}$/.test(receipt.launchDigest) || conflicts === void 0) throw new ServerError("Public profile preparation returned an invalid receipt",
|
|
7507
|
+
if (typeof receipt.workspacePlanDigest !== "string" || !digest.test(receipt.workspacePlanDigest) || typeof receipt.materializedDigest !== "string" || !digest.test(receipt.materializedDigest) || typeof receipt.launchDigest !== "string" || !/^[a-f0-9]{64}$/.test(receipt.launchDigest) || conflicts === void 0) throw new ServerError("Public profile preparation returned an invalid receipt", {
|
|
7508
|
+
status: 502,
|
|
7491
7509
|
origin: "runtime",
|
|
7492
7510
|
endpoint
|
|
7493
7511
|
});
|
|
@@ -7519,7 +7537,8 @@ var SandboxProfileCapabilities = class extends SandboxFileWriteCapabilities {
|
|
|
7519
7537
|
secret: true
|
|
7520
7538
|
});
|
|
7521
7539
|
const receipt = await this.postProfileRevision(backend.profile, endpoint, body);
|
|
7522
|
-
if (typeof receipt.workspacePlanDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(receipt.workspacePlanDigest) || typeof receipt.executorReceipt !== "string" || !/^hmac-sha256:[a-f0-9]{64}$/.test(receipt.executorReceipt) || !Array.isArray(receipt.conflicts) || !receipt.conflicts.every((conflict) => typeof conflict === "object" && conflict !== null && typeof conflict.relPath === "string" && typeof conflict.copyRelPath === "string" && (conflict.reason === "existing" || conflict.reason === "symlink"))) throw new ServerError("Secret profile preparation returned an invalid receipt",
|
|
7540
|
+
if (typeof receipt.workspacePlanDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(receipt.workspacePlanDigest) || typeof receipt.executorReceipt !== "string" || !/^hmac-sha256:[a-f0-9]{64}$/.test(receipt.executorReceipt) || !Array.isArray(receipt.conflicts) || !receipt.conflicts.every((conflict) => typeof conflict === "object" && conflict !== null && typeof conflict.relPath === "string" && typeof conflict.copyRelPath === "string" && (conflict.reason === "existing" || conflict.reason === "symlink"))) throw new ServerError("Secret profile preparation returned an invalid receipt", {
|
|
7541
|
+
status: 502,
|
|
7523
7542
|
origin: "runtime",
|
|
7524
7543
|
endpoint
|
|
7525
7544
|
});
|
|
@@ -7700,7 +7719,8 @@ const InteractionListEnvelopeSchema = z.object({
|
|
|
7700
7719
|
data: z.object({ interactions: z.array(InteractionRequestSchema) })
|
|
7701
7720
|
});
|
|
7702
7721
|
function invalidRuntimeResponse(message, response, endpoint) {
|
|
7703
|
-
return new ServerError(message,
|
|
7722
|
+
return new ServerError(message, {
|
|
7723
|
+
status: 502,
|
|
7704
7724
|
origin: "runtime",
|
|
7705
7725
|
endpoint,
|
|
7706
7726
|
sidecarVersion: response.headers.get("x-sidecar-version") ?? void 0,
|
|
@@ -7748,7 +7768,8 @@ async function readInteractionResult(response, endpoint, sessionId, submitted) {
|
|
|
7748
7768
|
}
|
|
7749
7769
|
/** Convert a command result into the shared retry-safe acknowledgement. */
|
|
7750
7770
|
function interactionAcknowledgement(command, result) {
|
|
7751
|
-
if ("interactionId" in result && (result.binding === void 0 || !bindingsEqual(result.binding, command.binding))) throw new ServerError("Sidecar did not return the exact interaction binding for this command",
|
|
7771
|
+
if ("interactionId" in result && (result.binding === void 0 || !bindingsEqual(result.binding, command.binding))) throw new ServerError("Sidecar did not return the exact interaction binding for this command", {
|
|
7772
|
+
status: 502,
|
|
7752
7773
|
origin: "runtime",
|
|
7753
7774
|
endpoint: "/agents/sessions/:id/interactions"
|
|
7754
7775
|
});
|
|
@@ -7776,7 +7797,7 @@ function commandHttpFailure(command, error) {
|
|
|
7776
7797
|
const message = error.message;
|
|
7777
7798
|
switch (error.status) {
|
|
7778
7799
|
case 400: return { acknowledgement: commandAcknowledgement(command, "invalid_response", { message }) };
|
|
7779
|
-
case 404: return { acknowledgement: commandAcknowledgement(command, error.
|
|
7800
|
+
case 404: return { acknowledgement: commandAcknowledgement(command, error.wireCode === "SESSION_NOT_FOUND" ? "unknown_run" : "unknown_interaction", { message }) };
|
|
7780
7801
|
case 408:
|
|
7781
7802
|
case 429: return { acknowledgement: commandAcknowledgement(command, "transport_failure", {
|
|
7782
7803
|
message,
|
|
@@ -7824,7 +7845,7 @@ async function listSandboxInteractions(transport, sessionId, options) {
|
|
|
7824
7845
|
/** Submit a plain response and preserve the server's durable result. */
|
|
7825
7846
|
async function submitSandboxInteractionResponse(transport, sessionId, submitted, options) {
|
|
7826
7847
|
const parsed = InteractionResponseSchema.safeParse(submitted);
|
|
7827
|
-
if (!parsed.success) throw new ValidationError("Invalid interaction response", { response: parsed.error.message });
|
|
7848
|
+
if (!parsed.success) throw new ValidationError("Invalid interaction response", { fields: { response: parsed.error.message } });
|
|
7828
7849
|
await prepareTransport(transport, options?.signal);
|
|
7829
7850
|
const endpoint = interactionEndpoint(sessionId);
|
|
7830
7851
|
return readInteractionResult(await transport.fetch(endpoint, {
|
|
@@ -7837,9 +7858,9 @@ async function submitSandboxInteractionResponse(transport, sessionId, submitted,
|
|
|
7837
7858
|
/** Submit one digest-bound command and return the shared acknowledgement. */
|
|
7838
7859
|
async function submitSandboxInteractionCommand(transport, sessionId, command, options) {
|
|
7839
7860
|
const parsed = InteractionResponseCommandSchema.safeParse(command);
|
|
7840
|
-
if (!parsed.success) throw new ValidationError("Invalid interaction response command", { commandDigest: parsed.error.message });
|
|
7841
|
-
if (parsed.data.binding.sessionId !== sessionId) throw new ValidationError("Interaction response command does not belong to this session", { "binding.sessionId": `Expected ${sessionId}` });
|
|
7842
|
-
if (parsed.data.binding.environmentId !== transport.environmentId) throw new ValidationError("Interaction response command does not belong to this sandbox", { "binding.environmentId": `Expected ${transport.environmentId}` });
|
|
7861
|
+
if (!parsed.success) throw new ValidationError("Invalid interaction response command", { fields: { commandDigest: parsed.error.message } });
|
|
7862
|
+
if (parsed.data.binding.sessionId !== sessionId) throw new ValidationError("Interaction response command does not belong to this session", { fields: { "binding.sessionId": `Expected ${sessionId}` } });
|
|
7863
|
+
if (parsed.data.binding.environmentId !== transport.environmentId) throw new ValidationError("Interaction response command does not belong to this sandbox", { fields: { "binding.environmentId": `Expected ${transport.environmentId}` } });
|
|
7843
7864
|
try {
|
|
7844
7865
|
await prepareTransport(transport, options?.signal);
|
|
7845
7866
|
} catch (error) {
|
|
@@ -7932,7 +7953,8 @@ var SandboxSessionAdmissionCapabilities = class extends SandboxProfileCapabiliti
|
|
|
7932
7953
|
});
|
|
7933
7954
|
if (!response.ok) throw await parseResponseError(response);
|
|
7934
7955
|
const info = normalizeSessionInfo(await response.json());
|
|
7935
|
-
if (!info) throw new ServerError("Session fork response missing session id",
|
|
7956
|
+
if (!info) throw new ServerError("Session fork response missing session id", {
|
|
7957
|
+
status: 502,
|
|
7936
7958
|
origin: "runtime",
|
|
7937
7959
|
endpoint: `/agents/sessions/${encodeURIComponent(id)}/forks`
|
|
7938
7960
|
});
|
|
@@ -7955,7 +7977,7 @@ var SandboxSessionAdmissionCapabilities = class extends SandboxProfileCapabiliti
|
|
|
7955
7977
|
}
|
|
7956
7978
|
/** @internal — invoked by SandboxSession.interrupt(). */
|
|
7957
7979
|
async _interrupt(id, options) {
|
|
7958
|
-
if (options?.executionId !== void 0 && !options.executionId) throw new ValidationError("executionId must not be empty", { executionId: "Expected a non-empty runtime execution id" });
|
|
7980
|
+
if (options?.executionId !== void 0 && !options.executionId) throw new ValidationError("executionId must not be empty", { fields: { executionId: "Expected a non-empty runtime execution id" } });
|
|
7959
7981
|
await this.ensureRunning();
|
|
7960
7982
|
const response = options?.executionId ? await this.runtimeFetch("/agents/run/cancel", {
|
|
7961
7983
|
method: "POST",
|
|
@@ -8000,9 +8022,9 @@ var SandboxSessionAdmissionCapabilities = class extends SandboxProfileCapabiliti
|
|
|
8000
8022
|
async _cancelRun(id, request, options) {
|
|
8001
8023
|
options?.signal?.throwIfAborted();
|
|
8002
8024
|
const parsedRequest = AgentRunCancellationRequestSchema.safeParse(request);
|
|
8003
|
-
if (!parsedRequest.success) throw new ValidationError("Invalid run cancellation request", { requestDigest: parsedRequest.error.message });
|
|
8004
|
-
if (parsedRequest.data.run.sessionId !== id) throw new ValidationError("Cancellation request does not belong to this session", { "run.sessionId": `Expected ${id}` });
|
|
8005
|
-
if (parsedRequest.data.run.environmentId !== this.id) throw new ValidationError("Cancellation request does not belong to this sandbox", { "run.environmentId": `Expected ${this.id}` });
|
|
8025
|
+
if (!parsedRequest.success) throw new ValidationError("Invalid run cancellation request", { fields: { requestDigest: parsedRequest.error.message } });
|
|
8026
|
+
if (parsedRequest.data.run.sessionId !== id) throw new ValidationError("Cancellation request does not belong to this session", { fields: { "run.sessionId": `Expected ${id}` } });
|
|
8027
|
+
if (parsedRequest.data.run.environmentId !== this.id) throw new ValidationError("Cancellation request does not belong to this sandbox", { fields: { "run.environmentId": `Expected ${this.id}` } });
|
|
8006
8028
|
await this.ensureRunning();
|
|
8007
8029
|
options?.signal?.throwIfAborted();
|
|
8008
8030
|
const response = await this.runtimeFetch("/agents/run/cancel", {
|
|
@@ -8017,17 +8039,20 @@ var SandboxSessionAdmissionCapabilities = class extends SandboxProfileCapabiliti
|
|
|
8017
8039
|
try {
|
|
8018
8040
|
raw = JSON.parse(body);
|
|
8019
8041
|
} catch {
|
|
8020
|
-
throw new ServerError("Sidecar returned an invalid cancellation acknowledgement",
|
|
8042
|
+
throw new ServerError("Sidecar returned an invalid cancellation acknowledgement", {
|
|
8043
|
+
status: response.status,
|
|
8021
8044
|
origin: "runtime",
|
|
8022
8045
|
endpoint: "/agents/run/cancel"
|
|
8023
8046
|
});
|
|
8024
8047
|
}
|
|
8025
8048
|
const acknowledgement = AgentRunCancellationAcknowledgementSchema.safeParse(raw);
|
|
8026
|
-
if (!acknowledgement.success) throw new ServerError("Sidecar returned an invalid cancellation acknowledgement",
|
|
8049
|
+
if (!acknowledgement.success) throw new ServerError("Sidecar returned an invalid cancellation acknowledgement", {
|
|
8050
|
+
status: response.status,
|
|
8027
8051
|
origin: "runtime",
|
|
8028
8052
|
endpoint: "/agents/run/cancel"
|
|
8029
8053
|
});
|
|
8030
|
-
if (!agentRunCancellationAcknowledgementMatchesRequest(parsedRequest.data, acknowledgement.data)) throw new ServerError("Sidecar returned a cancellation acknowledgement for another request",
|
|
8054
|
+
if (!agentRunCancellationAcknowledgementMatchesRequest(parsedRequest.data, acknowledgement.data)) throw new ServerError("Sidecar returned a cancellation acknowledgement for another request", {
|
|
8055
|
+
status: response.status,
|
|
8031
8056
|
origin: "runtime",
|
|
8032
8057
|
endpoint: "/agents/run/cancel"
|
|
8033
8058
|
});
|
|
@@ -8049,7 +8074,8 @@ var SandboxSessionAdmissionCapabilities = class extends SandboxProfileCapabiliti
|
|
|
8049
8074
|
const response = await state(this).client.fetch(path);
|
|
8050
8075
|
if (response.status === 404) return null;
|
|
8051
8076
|
const decoded = await readResponseJson(response);
|
|
8052
|
-
if (!decoded.plan || typeof decoded.plan.sourceTurnId !== "string" || !decoded.plan.sourceTurnId.trim()) throw new ServerError("Durable plan response is missing plan or original sourceTurnId",
|
|
8077
|
+
if (!decoded.plan || typeof decoded.plan.sourceTurnId !== "string" || !decoded.plan.sourceTurnId.trim()) throw new ServerError("Durable plan response is missing plan or original sourceTurnId", {
|
|
8078
|
+
status: 502,
|
|
8053
8079
|
endpoint: path,
|
|
8054
8080
|
origin: "control-plane"
|
|
8055
8081
|
});
|
|
@@ -8073,11 +8099,13 @@ var SandboxSessionAdmissionCapabilities = class extends SandboxProfileCapabiliti
|
|
|
8073
8099
|
"started",
|
|
8074
8100
|
"completed",
|
|
8075
8101
|
"failed"
|
|
8076
|
-
].includes(String(followUpState)) || typeof decoded.idempotent !== "boolean") throw new ServerError("Plan decision response is invalid",
|
|
8102
|
+
].includes(String(followUpState)) || typeof decoded.idempotent !== "boolean") throw new ServerError("Plan decision response is invalid", {
|
|
8103
|
+
status: 502,
|
|
8077
8104
|
endpoint: path,
|
|
8078
8105
|
origin: "control-plane"
|
|
8079
8106
|
});
|
|
8080
|
-
if (options.followUp && decoded.followUp.mode !== "caller") throw new ServerError("Server did not acknowledge caller-owned plan follow-up",
|
|
8107
|
+
if (options.followUp && decoded.followUp.mode !== "caller") throw new ServerError("Server did not acknowledge caller-owned plan follow-up", {
|
|
8108
|
+
status: 502,
|
|
8081
8109
|
endpoint: path,
|
|
8082
8110
|
origin: "control-plane"
|
|
8083
8111
|
});
|
|
@@ -8085,7 +8113,8 @@ var SandboxSessionAdmissionCapabilities = class extends SandboxProfileCapabiliti
|
|
|
8085
8113
|
if (decoded.followUp.resume !== void 0) {
|
|
8086
8114
|
const candidate = decoded.followUp.resume;
|
|
8087
8115
|
const continuation = PlanContinuationSchema.safeParse(candidate?.continuation);
|
|
8088
|
-
if (!continuation.success || typeof candidate.sourceTurnId !== "string" || !candidate.sourceTurnId || typeof candidate.credential !== "string" || !candidate.credential || typeof candidate.callbackUrl !== "string" || !/^https?:\/\//.test(candidate.callbackUrl) || typeof candidate.expiresAt !== "string" || !Number.isFinite(Date.parse(candidate.expiresAt))) throw new ServerError("Caller plan admission response is invalid",
|
|
8116
|
+
if (!continuation.success || typeof candidate.sourceTurnId !== "string" || !candidate.sourceTurnId || typeof candidate.credential !== "string" || !candidate.credential || typeof candidate.callbackUrl !== "string" || !/^https?:\/\//.test(candidate.callbackUrl) || typeof candidate.expiresAt !== "string" || !Number.isFinite(Date.parse(candidate.expiresAt))) throw new ServerError("Caller plan admission response is invalid", {
|
|
8117
|
+
status: 502,
|
|
8089
8118
|
endpoint: path,
|
|
8090
8119
|
origin: "control-plane"
|
|
8091
8120
|
});
|
|
@@ -8118,10 +8147,10 @@ function validatePromptDispatchOptions(sandboxId, options) {
|
|
|
8118
8147
|
const validId = (id) => typeof id === "string" && /^[A-Za-z0-9._:-]{1,256}$/u.test(id);
|
|
8119
8148
|
if (!options.sessionId || options.lastEventId !== void 0 || resume === null || typeof resume !== "object" || Object.keys(resume).sort().join(",") !== "harness,nativeSessionId,sourceCheckpointId" || !["claude-code", "codex"].includes(resume.harness) || !validId(resume.nativeSessionId) || !validId(resume.sourceCheckpointId)) throw new ValidationError("Native resume requires an exact harness, native session, source checkpoint, and new session dispatch");
|
|
8120
8149
|
}
|
|
8121
|
-
if (options?.lastEventId && !options.executionId) throw new ValidationError("lastEventId requires executionId so the SDK can replay the correct execution", { executionId: "Required when lastEventId is set" });
|
|
8150
|
+
if (options?.lastEventId && !options.executionId) throw new ValidationError("lastEventId requires executionId so the SDK can replay the correct execution", { fields: { executionId: "Required when lastEventId is set" } });
|
|
8122
8151
|
const runControlRef = options?.runControlRef ? AgentExactRunControlRefSchema.safeParse(options.runControlRef) : void 0;
|
|
8123
|
-
if (runControlRef && !runControlRef.success) throw new ValidationError("Invalid runControlRef", { runControlRef: runControlRef.error.message });
|
|
8124
|
-
if (runControlRef?.success && (runControlRef.data.environmentId !== sandboxId || runControlRef.data.sessionId !== options?.sessionId || runControlRef.data.executionId !== options?.executionId)) throw new ValidationError("runControlRef does not belong to this sandbox dispatch", { runControlRef: "environmentId, sessionId, and executionId must match the dispatch" });
|
|
8152
|
+
if (runControlRef && !runControlRef.success) throw new ValidationError("Invalid runControlRef", { fields: { runControlRef: runControlRef.error.message } });
|
|
8153
|
+
if (runControlRef?.success && (runControlRef.data.environmentId !== sandboxId || runControlRef.data.sessionId !== options?.sessionId || runControlRef.data.executionId !== options?.executionId)) throw new ValidationError("runControlRef does not belong to this sandbox dispatch", { fields: { runControlRef: "environmentId, sessionId, and executionId must match the dispatch" } });
|
|
8125
8154
|
return runControlRef?.success ? runControlRef.data : void 0;
|
|
8126
8155
|
}
|
|
8127
8156
|
var SandboxPromptTransportCapabilities = class extends SandboxSessionAdmissionCapabilities {
|
|
@@ -8141,7 +8170,8 @@ var SandboxPromptTransportCapabilities = class extends SandboxSessionAdmissionCa
|
|
|
8141
8170
|
const streamSignal = timeoutSignal ? options?.signal ? combineAbortSignals([options.signal, timeoutSignal]) : timeoutSignal : options?.signal;
|
|
8142
8171
|
const throwIfTimedOut = () => {
|
|
8143
8172
|
if (!timeoutSignal?.aborted || !timeoutMs) return;
|
|
8144
|
-
throw new TimeoutError(
|
|
8173
|
+
throw new TimeoutError(`Prompt stream timed out after ${timeoutMs}ms`, {
|
|
8174
|
+
timeoutMs,
|
|
8145
8175
|
endpoint: "/agents/run/stream",
|
|
8146
8176
|
origin: "runtime"
|
|
8147
8177
|
});
|
|
@@ -8303,15 +8333,16 @@ var SandboxPromptTransportCapabilities = class extends SandboxSessionAdmissionCa
|
|
|
8303
8333
|
});
|
|
8304
8334
|
const resumeExecutionId = state.executionId;
|
|
8305
8335
|
const interrupted = (reason, detail, cause) => {
|
|
8306
|
-
return new StreamInterruptedError({
|
|
8307
|
-
|
|
8336
|
+
return new StreamInterruptedError(`Prompt stream for execution ${resumeExecutionId} ended before a terminal event and could not be rejoined (${detail}); the execution's outcome is unknown${state.lastReplayStatus !== void 0 ? `; last replay status ${state.lastReplayStatus}` : ""}${state.lastExecutionStatus ? `; last known execution status ${state.lastExecutionStatus}` : ""}`, {
|
|
8337
|
+
origin: "runtime",
|
|
8338
|
+
endpoint: `/agents/run/${resumeExecutionId}/events`,
|
|
8308
8339
|
executionId: resumeExecutionId,
|
|
8309
|
-
|
|
8310
|
-
|
|
8340
|
+
sessionId: state.sessionId,
|
|
8341
|
+
lastEventId: state.lastEventId,
|
|
8311
8342
|
reason,
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8343
|
+
lastReplayStatus: state.lastReplayStatus,
|
|
8344
|
+
lastExecutionStatus: state.lastExecutionStatus,
|
|
8345
|
+
cause
|
|
8315
8346
|
});
|
|
8316
8347
|
};
|
|
8317
8348
|
let outageStartedAt = Date.now();
|
|
@@ -8399,7 +8430,9 @@ function fromStreamErrorEvent(data) {
|
|
|
8399
8430
|
const code = typeof data?.code === "string" && data.code.length > 0 ? data.code : void 0;
|
|
8400
8431
|
if (status === void 0 && code === void 0) return new ServerError(message);
|
|
8401
8432
|
const origin = data?.origin;
|
|
8402
|
-
const error = new SandboxError(message,
|
|
8433
|
+
const error = new SandboxError(message, {
|
|
8434
|
+
code: code ?? "SERVER_ERROR",
|
|
8435
|
+
status,
|
|
8403
8436
|
...origin === "sidecar" || origin === "sandbox-api" || origin === "control-plane" || origin === "runtime" || origin === "unknown" ? { origin } : {},
|
|
8404
8437
|
...typeof data?.endpoint === "string" ? { endpoint: data.endpoint } : {},
|
|
8405
8438
|
...typeof data?.retryAfterMs === "number" && Number.isFinite(data.retryAfterMs) && data.retryAfterMs >= 0 ? { retryAfterMs: data.retryAfterMs } : {}
|
|
@@ -8430,7 +8463,7 @@ function toStreamErrorEvent(err) {
|
|
|
8430
8463
|
type: "error",
|
|
8431
8464
|
data: {
|
|
8432
8465
|
message: err.message,
|
|
8433
|
-
code: err.
|
|
8466
|
+
code: err.wireCode,
|
|
8434
8467
|
name: err.name,
|
|
8435
8468
|
...err.status !== void 0 ? { status: err.status } : {},
|
|
8436
8469
|
...err.origin ? { origin: err.origin } : {},
|
|
@@ -8613,12 +8646,14 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8613
8646
|
if (!response.ok) throw await parseResponseError(response);
|
|
8614
8647
|
const endpoint = `/agents/sessions/${encodeURIComponent(id)}/interactive`;
|
|
8615
8648
|
const parsed = interactiveSessionStartResponseSchema.safeParse(await response.json());
|
|
8616
|
-
if (!parsed.success) throw new ServerError("Interactive session response has an invalid exact identity",
|
|
8649
|
+
if (!parsed.success) throw new ServerError("Interactive session response has an invalid exact identity", {
|
|
8650
|
+
status: 502,
|
|
8617
8651
|
endpoint,
|
|
8618
8652
|
origin: "runtime"
|
|
8619
8653
|
});
|
|
8620
8654
|
const result = parsed.data.data;
|
|
8621
|
-
if (!agentInteractiveSessionRefMatchesStart(exactStart, result.ref) || !agentInteractiveSessionControlClaimMatchesRef(result.ref, result.control)) throw new ServerError("Interactive session response has an invalid exact identity",
|
|
8655
|
+
if (!agentInteractiveSessionRefMatchesStart(exactStart, result.ref) || !agentInteractiveSessionControlClaimMatchesRef(result.ref, result.control)) throw new ServerError("Interactive session response has an invalid exact identity", {
|
|
8656
|
+
status: 502,
|
|
8622
8657
|
endpoint,
|
|
8623
8658
|
origin: "runtime"
|
|
8624
8659
|
});
|
|
@@ -8631,7 +8666,9 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8631
8666
|
if (exactCommand.ref.run.sessionId !== id) throw new ValidationError("Interactive prompt command does not belong to the session");
|
|
8632
8667
|
await this.ensureRunning(options?.signal);
|
|
8633
8668
|
const status = await this._interactiveStatus(id, exactCommand.ref.incarnationId, options);
|
|
8634
|
-
if (!status) throw new NotFoundError(
|
|
8669
|
+
if (!status) throw new NotFoundError(`Interactive session not found: ${id}`, {
|
|
8670
|
+
resourceType: "Interactive session",
|
|
8671
|
+
resourceId: id,
|
|
8635
8672
|
origin: "runtime",
|
|
8636
8673
|
endpoint: `/agents/sessions/${encodeURIComponent(id)}/interactive`
|
|
8637
8674
|
});
|
|
@@ -8643,12 +8680,14 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8643
8680
|
...options?.signal === void 0 ? {} : { signal: options.signal }
|
|
8644
8681
|
});
|
|
8645
8682
|
const parsed = interactiveSessionPromptResponseSchema.safeParse(await readResponseJson(response));
|
|
8646
|
-
if (!parsed.success) throw new ServerError("Interactive prompt acknowledgement is invalid",
|
|
8683
|
+
if (!parsed.success) throw new ServerError("Interactive prompt acknowledgement is invalid", {
|
|
8684
|
+
status: 502,
|
|
8647
8685
|
endpoint,
|
|
8648
8686
|
origin: "runtime"
|
|
8649
8687
|
});
|
|
8650
8688
|
const acknowledgement = parsed.data.data;
|
|
8651
|
-
if (!agentInteractiveSessionPromptAcknowledgementMatchesCommand(exactCommand, acknowledgement)) throw new ServerError("Interactive prompt acknowledgement does not match the command",
|
|
8689
|
+
if (!agentInteractiveSessionPromptAcknowledgementMatchesCommand(exactCommand, acknowledgement)) throw new ServerError("Interactive prompt acknowledgement does not match the command", {
|
|
8690
|
+
status: 502,
|
|
8652
8691
|
endpoint,
|
|
8653
8692
|
origin: "runtime"
|
|
8654
8693
|
});
|
|
@@ -8669,12 +8708,14 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8669
8708
|
...options?.signal === void 0 ? {} : { signal: options.signal }
|
|
8670
8709
|
});
|
|
8671
8710
|
const parsed = interactiveSessionControlClaimResponseSchema.safeParse(await readResponseJson(response));
|
|
8672
|
-
if (!parsed.success) throw new ServerError("Interactive control claim acknowledgement is invalid",
|
|
8711
|
+
if (!parsed.success) throw new ServerError("Interactive control claim acknowledgement is invalid", {
|
|
8712
|
+
status: 502,
|
|
8673
8713
|
endpoint,
|
|
8674
8714
|
origin: "runtime"
|
|
8675
8715
|
});
|
|
8676
8716
|
const acknowledgement = parsed.data.data;
|
|
8677
|
-
if (!agentInteractiveSessionControlClaimAcknowledgementMatchesRequest(exactRequest, acknowledgement)) throw new ServerError("Interactive control claim acknowledgement does not match the request",
|
|
8717
|
+
if (!agentInteractiveSessionControlClaimAcknowledgementMatchesRequest(exactRequest, acknowledgement)) throw new ServerError("Interactive control claim acknowledgement does not match the request", {
|
|
8718
|
+
status: 502,
|
|
8678
8719
|
endpoint,
|
|
8679
8720
|
origin: "runtime"
|
|
8680
8721
|
});
|
|
@@ -8703,7 +8744,8 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8703
8744
|
body: JSON.stringify(claim),
|
|
8704
8745
|
...options?.signal === void 0 ? {} : { signal: options.signal }
|
|
8705
8746
|
});
|
|
8706
|
-
if (!interactiveSessionControlReleaseResponseSchema.safeParse(await readResponseJson(response)).success) throw new ServerError("Interactive control release response is invalid",
|
|
8747
|
+
if (!interactiveSessionControlReleaseResponseSchema.safeParse(await readResponseJson(response)).success) throw new ServerError("Interactive control release response is invalid", {
|
|
8748
|
+
status: 502,
|
|
8707
8749
|
endpoint,
|
|
8708
8750
|
origin: "runtime"
|
|
8709
8751
|
});
|
|
@@ -8720,7 +8762,8 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8720
8762
|
body: JSON.stringify(claim),
|
|
8721
8763
|
...options?.signal === void 0 ? {} : { signal: options.signal }
|
|
8722
8764
|
});
|
|
8723
|
-
if (!interactiveSessionControlValidationResponseSchema.safeParse(await readResponseJson(response)).success) throw new ServerError("Interactive control validation response is invalid",
|
|
8765
|
+
if (!interactiveSessionControlValidationResponseSchema.safeParse(await readResponseJson(response)).success) throw new ServerError("Interactive control validation response is invalid", {
|
|
8766
|
+
status: 502,
|
|
8724
8767
|
endpoint,
|
|
8725
8768
|
origin: "runtime"
|
|
8726
8769
|
});
|
|
@@ -8736,12 +8779,14 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8736
8779
|
});
|
|
8737
8780
|
if (response.status === 404) return null;
|
|
8738
8781
|
const parsed = interactiveSessionStatusResponseSchema.safeParse(await readResponseJson(response));
|
|
8739
|
-
if (!parsed.success) throw new ServerError("Interactive status response has an invalid exact identity",
|
|
8782
|
+
if (!parsed.success) throw new ServerError("Interactive status response has an invalid exact identity", {
|
|
8783
|
+
status: 502,
|
|
8740
8784
|
endpoint,
|
|
8741
8785
|
origin: "runtime"
|
|
8742
8786
|
});
|
|
8743
8787
|
const status = parsed.data.data.status;
|
|
8744
|
-
if (status.ref.run.sessionId !== id || status.ref.incarnationId !== incarnationId) throw new ServerError("Interactive status response has an invalid exact identity",
|
|
8788
|
+
if (status.ref.run.sessionId !== id || status.ref.incarnationId !== incarnationId) throw new ServerError("Interactive status response has an invalid exact identity", {
|
|
8789
|
+
status: 502,
|
|
8745
8790
|
endpoint,
|
|
8746
8791
|
origin: "runtime"
|
|
8747
8792
|
});
|
|
@@ -8782,12 +8827,14 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8782
8827
|
...options?.signal === void 0 ? {} : { signal: options.signal }
|
|
8783
8828
|
});
|
|
8784
8829
|
const parsed = interactiveSessionStopResponseSchema.safeParse(await readResponseJson(response));
|
|
8785
|
-
if (!parsed.success) throw new ServerError("Interactive stop acknowledgement is invalid",
|
|
8830
|
+
if (!parsed.success) throw new ServerError("Interactive stop acknowledgement is invalid", {
|
|
8831
|
+
status: 502,
|
|
8786
8832
|
endpoint,
|
|
8787
8833
|
origin: "runtime"
|
|
8788
8834
|
});
|
|
8789
8835
|
const acknowledgement = parsed.data.data;
|
|
8790
|
-
if (!agentInteractiveSessionStopAcknowledgementMatchesCommand(exactCommand, acknowledgement)) throw new ServerError("Interactive stop acknowledgement does not match the command",
|
|
8836
|
+
if (!agentInteractiveSessionStopAcknowledgementMatchesCommand(exactCommand, acknowledgement)) throw new ServerError("Interactive stop acknowledgement does not match the command", {
|
|
8837
|
+
status: 502,
|
|
8791
8838
|
endpoint,
|
|
8792
8839
|
origin: "runtime"
|
|
8793
8840
|
});
|
|
@@ -8807,12 +8854,14 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8807
8854
|
...options?.signal === void 0 ? {} : { signal: options.signal }
|
|
8808
8855
|
});
|
|
8809
8856
|
const parsed = interactiveSessionStopResponseSchema.safeParse(await readResponseJson(response));
|
|
8810
|
-
if (!parsed.success) throw new ServerError("Interactive lifecycle stop acknowledgement is invalid",
|
|
8857
|
+
if (!parsed.success) throw new ServerError("Interactive lifecycle stop acknowledgement is invalid", {
|
|
8858
|
+
status: 502,
|
|
8811
8859
|
endpoint,
|
|
8812
8860
|
origin: "runtime"
|
|
8813
8861
|
});
|
|
8814
8862
|
const acknowledgement = parsed.data.data;
|
|
8815
|
-
if (canonicalCandidateDigest$1(acknowledgement.ref) !== canonicalCandidateDigest$1(exactRef)) throw new ServerError("Interactive lifecycle stop acknowledgement does not match the exact ref",
|
|
8863
|
+
if (canonicalCandidateDigest$1(acknowledgement.ref) !== canonicalCandidateDigest$1(exactRef)) throw new ServerError("Interactive lifecycle stop acknowledgement does not match the exact ref", {
|
|
8864
|
+
status: 502,
|
|
8816
8865
|
endpoint,
|
|
8817
8866
|
origin: "runtime"
|
|
8818
8867
|
});
|
|
@@ -8824,7 +8873,7 @@ var SandboxInteractiveCapabilities = class extends SandboxPromptCapabilities {
|
|
|
8824
8873
|
var SandboxSessionEvidenceCapabilities = class extends SandboxInteractiveCapabilities {
|
|
8825
8874
|
/** @internal — invoked before the sandbox is deleted. */
|
|
8826
8875
|
async _sessionExportRawEvidence(id, destination, options) {
|
|
8827
|
-
const { exportRawEvidenceDirectory } = await import("./raw-evidence-archive-
|
|
8876
|
+
const { exportRawEvidenceDirectory } = await import("./raw-evidence-archive-BD37zHN5.js");
|
|
8828
8877
|
await this.ensureRunning();
|
|
8829
8878
|
await this.refresh();
|
|
8830
8879
|
return exportRawEvidenceDirectory((signal, archiveId, cancel) => this.runtimeFetch(`/agents/sessions/${encodeURIComponent(id)}/raw-evidence/archive?${new URLSearchParams([
|
|
@@ -8855,7 +8904,7 @@ var SandboxSessionEvidenceCapabilities = class extends SandboxInteractiveCapabil
|
|
|
8855
8904
|
* and the byte and file bounds are verified before the directory appears.
|
|
8856
8905
|
*/
|
|
8857
8906
|
async exportRawEvidence(destination, options) {
|
|
8858
|
-
const { exportRawEvidenceDirectory } = await import("./raw-evidence-archive-
|
|
8907
|
+
const { exportRawEvidenceDirectory } = await import("./raw-evidence-archive-BD37zHN5.js");
|
|
8859
8908
|
await this.ensureRunning();
|
|
8860
8909
|
await this.refresh();
|
|
8861
8910
|
return exportRawEvidenceDirectory((signal, archiveId, cancel) => this.runtimeFetch(`/agents/raw-evidence/archive?${new URLSearchParams({
|
|
@@ -8967,19 +9016,24 @@ var SandboxSessionEvidenceCapabilities = class extends SandboxInteractiveCapabil
|
|
|
8967
9016
|
}
|
|
8968
9017
|
/** @internal — invoked by SandboxSession.result(). */
|
|
8969
9018
|
async _sessionResult(id, options, knownSession) {
|
|
8970
|
-
if (options?.executionId !== void 0 && !options.executionId) throw new ValidationError("executionId must not be empty", { executionId: "Expected a non-empty runtime execution id" });
|
|
9019
|
+
if (options?.executionId !== void 0 && !options.executionId) throw new ValidationError("executionId must not be empty", { fields: { executionId: "Expected a non-empty runtime execution id" } });
|
|
8971
9020
|
const startTime = Date.now();
|
|
8972
9021
|
let executionId = options?.executionId;
|
|
8973
9022
|
if (!executionId) {
|
|
8974
9023
|
const session = knownSession ?? await this._sessionStatus(id);
|
|
8975
|
-
if (!session) throw new NotFoundError(
|
|
9024
|
+
if (!session) throw new NotFoundError(`Session not found: ${id}`, {
|
|
9025
|
+
resourceType: "Session",
|
|
9026
|
+
resourceId: id,
|
|
8976
9027
|
origin: "runtime",
|
|
8977
9028
|
endpoint: `/agents/sessions/${encodeURIComponent(id)}`
|
|
8978
9029
|
});
|
|
8979
|
-
if (!session.latestExecutionId) throw new StateError(`Session ${id} has no execution result to await`,
|
|
9030
|
+
if (!session.latestExecutionId) throw new StateError(`Session ${id} has no execution result to await`, {
|
|
9031
|
+
currentState: session.status,
|
|
9032
|
+
requiredState: "session with an admitted execution",
|
|
8980
9033
|
origin: "runtime",
|
|
8981
|
-
endpoint: `/agents/sessions/${encodeURIComponent(id)}
|
|
8982
|
-
|
|
9034
|
+
endpoint: `/agents/sessions/${encodeURIComponent(id)}`,
|
|
9035
|
+
code: "SESSION_EXECUTION_MISSING"
|
|
9036
|
+
});
|
|
8983
9037
|
executionId = session.latestExecutionId;
|
|
8984
9038
|
}
|
|
8985
9039
|
let resultExecutionId = executionId;
|
|
@@ -9091,10 +9145,12 @@ function readModelCalls(value) {
|
|
|
9091
9145
|
//#endregion
|
|
9092
9146
|
//#region src/completed-turn.ts
|
|
9093
9147
|
function invalidCompletedTurn(message) {
|
|
9094
|
-
throw new ServerError(`Completed-turn lookup returned ${message}`,
|
|
9148
|
+
throw new ServerError(`Completed-turn lookup returned ${message}`, {
|
|
9149
|
+
status: 502,
|
|
9095
9150
|
origin: "runtime",
|
|
9096
|
-
endpoint: "/messages/turns"
|
|
9097
|
-
|
|
9151
|
+
endpoint: "/messages/turns",
|
|
9152
|
+
code: "INVALID_COMPLETED_TURN"
|
|
9153
|
+
});
|
|
9098
9154
|
}
|
|
9099
9155
|
function parseCompletedTurnResult(value, expected) {
|
|
9100
9156
|
if (value === null || typeof value !== "object" || Array.isArray(value)) invalidCompletedTurn("a non-object record");
|
|
@@ -9538,7 +9594,8 @@ var SandboxSessionCapabilities = class extends SandboxSessionEvidenceCapabilitie
|
|
|
9538
9594
|
ctrl.abort();
|
|
9539
9595
|
}
|
|
9540
9596
|
if (!admissionObserved || !resolvedSessionId || !resolvedExecutionId) throw dispatchError ?? new ServerError("dispatchPrompt did not receive an execution admission from the sandbox");
|
|
9541
|
-
if (!resolvedRunControlRef) throw new ServerError("Sandbox admission did not return the exact run reference",
|
|
9597
|
+
if (!resolvedRunControlRef) throw new ServerError("Sandbox admission did not return the exact run reference", {
|
|
9598
|
+
status: 502,
|
|
9542
9599
|
origin: "runtime",
|
|
9543
9600
|
endpoint: "/agents/run/stream"
|
|
9544
9601
|
});
|
|
@@ -9722,7 +9779,11 @@ var SandboxTurnCapabilities = class extends SandboxSessionCapabilities {
|
|
|
9722
9779
|
let checked = firstCheck;
|
|
9723
9780
|
if (!info) {
|
|
9724
9781
|
if (firstCheck.status === "fulfilled") await firstCheck.value.body?.cancel().catch(() => void 0);
|
|
9725
|
-
if (cached) throw new StateError("Retained result has no verifiable conversation admission",
|
|
9782
|
+
if (cached) throw new StateError("Retained result has no verifiable conversation admission", {
|
|
9783
|
+
currentState: "unverified",
|
|
9784
|
+
requiredState: "verified admission",
|
|
9785
|
+
code: "MESSAGE_ADMISSION_UNVERIFIED"
|
|
9786
|
+
});
|
|
9726
9787
|
if (opts.allowDispatch === false) return { state: "not_admitted" };
|
|
9727
9788
|
await this.createSession({
|
|
9728
9789
|
sessionId: opts.sessionId,
|
|
@@ -9740,7 +9801,11 @@ var SandboxTurnCapabilities = class extends SandboxSessionCapabilities {
|
|
|
9740
9801
|
if (body.requestVerified !== true || !Object.hasOwn(body, "receipt")) throw new Error("Runtime did not verify the durable message admission request");
|
|
9741
9802
|
let executionId;
|
|
9742
9803
|
if (body.receipt === null) {
|
|
9743
|
-
if (cached) throw new StateError("Retained result has no matching request evidence",
|
|
9804
|
+
if (cached) throw new StateError("Retained result has no matching request evidence", {
|
|
9805
|
+
currentState: "unverified",
|
|
9806
|
+
requiredState: "verified admission",
|
|
9807
|
+
code: "MESSAGE_ADMISSION_UNVERIFIED"
|
|
9808
|
+
});
|
|
9744
9809
|
if (opts.allowDispatch === false) return { state: "not_admitted" };
|
|
9745
9810
|
if (isSessionInFlight(info)) return { state: "running" };
|
|
9746
9811
|
const receipt = await this.session(opts.sessionId).sendMessage(request, {
|
|
@@ -9816,7 +9881,7 @@ const SNAPSHOT_DELETE_RETAINED_RETRY_MS = 2e3;
|
|
|
9816
9881
|
const HELD_REASONS = new Set(["resume_generation_unconfirmed", "snapshot_delete_in_flight"]);
|
|
9817
9882
|
/** A hold the platform says to wait out, named by the body's typed `reason`. */
|
|
9818
9883
|
function isRetainedHold(cause, reason) {
|
|
9819
|
-
return cause.status === 409 && cause.
|
|
9884
|
+
return cause.status === 409 && cause.wireCode === "RESUME_SNAPSHOT_RETAINED" && typeof reason === "string" && HELD_REASONS.has(reason);
|
|
9820
9885
|
}
|
|
9821
9886
|
function interpretWorkspaceOperationLookup(body, kind) {
|
|
9822
9887
|
const outcome = body?.outcome;
|
|
@@ -9881,7 +9946,7 @@ var SandboxSnapshotCapabilities = class extends SandboxTurnCapabilities {
|
|
|
9881
9946
|
}, { timeoutMs: SNAPSHOT_CREATE_REQUEST_TIMEOUT_MS });
|
|
9882
9947
|
const data = await readResponseJson(response);
|
|
9883
9948
|
const snapshotId = data.snapshotId ?? data.id;
|
|
9884
|
-
if (typeof snapshotId !== "string" || snapshotId.length === 0) throw new ServerError(`Snapshot create returned 200 but body had no snapshot ID: ${JSON.stringify(data).slice(0, 200)}`, response.status);
|
|
9949
|
+
if (typeof snapshotId !== "string" || snapshotId.length === 0) throw new ServerError(`Snapshot create returned 200 but body had no snapshot ID: ${JSON.stringify(data).slice(0, 200)}`, { status: response.status });
|
|
9885
9950
|
const reportedOutcome = data.idempotency?.outcome;
|
|
9886
9951
|
const reportedDigest = data.idempotency?.requestDigest;
|
|
9887
9952
|
const idempotency = (reportedOutcome === "created" || reportedOutcome === "replayed") && typeof reportedDigest === "string" ? {
|
|
@@ -9918,7 +9983,7 @@ var SandboxSnapshotCapabilities = class extends SandboxTurnCapabilities {
|
|
|
9918
9983
|
sandboxId: this.id
|
|
9919
9984
|
}), state(this).client);
|
|
9920
9985
|
const snapshotId = body.snapshotId ?? body.id;
|
|
9921
|
-
if (typeof snapshotId !== "string" || snapshotId.length === 0) throw new ServerError(`Snapshot create returned ${response.status} but body had no snapshot ID: ${JSON.stringify(body).slice(0, 200)}`, response.status);
|
|
9986
|
+
if (typeof snapshotId !== "string" || snapshotId.length === 0) throw new ServerError(`Snapshot create returned ${response.status} but body had no snapshot ID: ${JSON.stringify(body).slice(0, 200)}`, { status: response.status });
|
|
9922
9987
|
return SandboxOperation.completed("snapshot", this.id, { snapshot: {
|
|
9923
9988
|
snapshotId,
|
|
9924
9989
|
createdAt: new Date(typeof body.createdAt === "string" ? body.createdAt : Date.now()),
|
|
@@ -10083,7 +10148,7 @@ var SandboxSnapshotCapabilities = class extends SandboxTurnCapabilities {
|
|
|
10083
10148
|
polledAt = Date.now();
|
|
10084
10149
|
status = await readResponseJson(await state(this).client.fetch(`/v1/sandboxes/${this.id}/snapshots/jobs/${encodeURIComponent(accepted.jobId)}?waitMs=${SNAPSHOT_RESTORE_HOLD_MS}`));
|
|
10085
10150
|
}
|
|
10086
|
-
throw new TimeoutError(SNAPSHOT_RESTORE_TIMEOUT_MS);
|
|
10151
|
+
throw new TimeoutError(`Request timed out after ${SNAPSHOT_RESTORE_TIMEOUT_MS}ms`, { timeoutMs: SNAPSHOT_RESTORE_TIMEOUT_MS });
|
|
10087
10152
|
}
|
|
10088
10153
|
async waitForSnapshotVisible(snapshotId, fallback) {
|
|
10089
10154
|
const deadline = Date.now() + SNAPSHOT_VISIBILITY_TIMEOUT_MS;
|
|
@@ -10110,7 +10175,7 @@ var SandboxSnapshotCapabilities = class extends SandboxTurnCapabilities {
|
|
|
10110
10175
|
sizeBytes: fallback.sizeBytes,
|
|
10111
10176
|
tags: fallback.tags ?? []
|
|
10112
10177
|
};
|
|
10113
|
-
throw lastError instanceof Error ? lastError : new TimeoutError(SNAPSHOT_VISIBILITY_TIMEOUT_MS);
|
|
10178
|
+
throw lastError instanceof Error ? lastError : new TimeoutError(`Request timed out after ${SNAPSHOT_VISIBILITY_TIMEOUT_MS}ms`, { timeoutMs: SNAPSHOT_VISIBILITY_TIMEOUT_MS });
|
|
10114
10179
|
}
|
|
10115
10180
|
/**
|
|
10116
10181
|
* Restore from the latest snapshot in customer-provided storage.
|
|
@@ -10224,7 +10289,7 @@ var SandboxBranchCapabilities = class extends SandboxSnapshotCapabilities {
|
|
|
10224
10289
|
})
|
|
10225
10290
|
});
|
|
10226
10291
|
const data = await readResponseJson(response);
|
|
10227
|
-
if (typeof data.rolloutId !== "string" || data.rolloutId.length === 0) throw new ServerError(`Rollout create returned ${response.status} but body had no rolloutId: ${JSON.stringify(data).slice(0, 200)}`, response.status);
|
|
10292
|
+
if (typeof data.rolloutId !== "string" || data.rolloutId.length === 0) throw new ServerError(`Rollout create returned ${response.status} but body had no rolloutId: ${JSON.stringify(data).slice(0, 200)}`, { status: response.status });
|
|
10228
10293
|
return data;
|
|
10229
10294
|
}
|
|
10230
10295
|
/**
|
|
@@ -10250,10 +10315,10 @@ var SandboxBranchCapabilities = class extends SandboxSnapshotCapabilities {
|
|
|
10250
10315
|
const pollIntervalMs = options?.pollIntervalMs ?? 2e3;
|
|
10251
10316
|
const startTime = Date.now();
|
|
10252
10317
|
while (true) {
|
|
10253
|
-
if (options?.signal?.aborted) throw new TimeoutError(
|
|
10318
|
+
if (options?.signal?.aborted) throw new TimeoutError("Aborted", { timeoutMs: 0 });
|
|
10254
10319
|
const record = await this.getRollout(rolloutId);
|
|
10255
10320
|
if (record.status !== "running") return record;
|
|
10256
|
-
if (Date.now() - startTime > timeoutMs) throw new TimeoutError(
|
|
10321
|
+
if (Date.now() - startTime > timeoutMs) throw new TimeoutError(`Timed out waiting for rollout ${rolloutId} to settle`, { timeoutMs });
|
|
10257
10322
|
await this.sleep(pollIntervalMs);
|
|
10258
10323
|
}
|
|
10259
10324
|
}
|
|
@@ -10286,7 +10351,11 @@ async function fetchTeeSurface(client, sandboxId, surface, init) {
|
|
|
10286
10351
|
const response = await client.fetch(path, init);
|
|
10287
10352
|
if (response.ok) return response;
|
|
10288
10353
|
const body = await response.text();
|
|
10289
|
-
if (response.status === 404 && isRouterNotFound(body)) throw new CapabilityError(`TEE ${surface} is not served by this deployment. The /tee paths are answered by a Tangle confidential operator that intercepts them before the sandbox API; a sandbox reached through the public API has no such route. Point the client at the operator, or create the sandbox with driver.type "tangle" on a deployment that fronts it.`,
|
|
10354
|
+
if (response.status === 404 && isRouterNotFound(body)) throw new CapabilityError(`TEE ${surface} is not served by this deployment. The /tee paths are answered by a Tangle confidential operator that intercepts them before the sandbox API; a sandbox reached through the public API has no such route. Point the client at the operator, or create the sandbox with driver.type "tangle" on a deployment that fronts it.`, {
|
|
10355
|
+
code: "TEE_UNSUPPORTED",
|
|
10356
|
+
status: 404,
|
|
10357
|
+
capability: "tee"
|
|
10358
|
+
});
|
|
10290
10359
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
10291
10360
|
}
|
|
10292
10361
|
//#endregion
|
|
@@ -10424,7 +10493,10 @@ var SandboxAccessCapabilities = class extends SandboxBranchCapabilities {
|
|
|
10424
10493
|
*/
|
|
10425
10494
|
async ssh() {
|
|
10426
10495
|
await this.ensureRunning();
|
|
10427
|
-
if (!this.connection?.ssh) throw new StateError("SSH is not enabled for this sandbox",
|
|
10496
|
+
if (!this.connection?.ssh) throw new StateError("SSH is not enabled for this sandbox", {
|
|
10497
|
+
currentState: this.status,
|
|
10498
|
+
requiredState: "running"
|
|
10499
|
+
});
|
|
10428
10500
|
return this.connection.ssh;
|
|
10429
10501
|
}
|
|
10430
10502
|
async sshCommand() {
|
|
@@ -10735,14 +10807,14 @@ var SandboxEgressOperations = class {
|
|
|
10735
10807
|
const response = await this.host.fetch(`/v1/sandboxes/${encodeURIComponent(this.host.id())}/egress/denials${query}`);
|
|
10736
10808
|
if (!response.ok) throw await parseResponseError(response);
|
|
10737
10809
|
const body = await readEgressJson(response, "Egress denials response was malformed: body is not valid JSON");
|
|
10738
|
-
if (typeof body !== "object" || body === null || Array.isArray(body)) throw new ServerError("Egress denials response was malformed: body is not an object", 502);
|
|
10810
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) throw new ServerError("Egress denials response was malformed: body is not an object", { status: 502 });
|
|
10739
10811
|
const raw = body.denials;
|
|
10740
10812
|
if (raw === void 0 || raw === null) return [];
|
|
10741
|
-
if (!Array.isArray(raw)) throw new ServerError("Egress denials response was malformed: denials is not a list", 502);
|
|
10813
|
+
if (!Array.isArray(raw)) throw new ServerError("Egress denials response was malformed: denials is not a list", { status: 502 });
|
|
10742
10814
|
const denials = [];
|
|
10743
10815
|
for (const [index, entry] of raw.entries()) {
|
|
10744
10816
|
const row = entry;
|
|
10745
|
-
if (typeof row !== "object" || row === null || typeof row.host !== "string" || typeof row.rejectedBy !== "string" || typeof row.count !== "number" || !Number.isInteger(row.count) || row.count < 0 || row.lastAt !== void 0 && row.lastAt !== null && typeof row.lastAt !== "string") throw new ServerError(`Egress denials response was malformed: entry ${index} is not a denial`, 502);
|
|
10817
|
+
if (typeof row !== "object" || row === null || typeof row.host !== "string" || typeof row.rejectedBy !== "string" || typeof row.count !== "number" || !Number.isInteger(row.count) || row.count < 0 || row.lastAt !== void 0 && row.lastAt !== null && typeof row.lastAt !== "string") throw new ServerError(`Egress denials response was malformed: entry ${index} is not a denial`, { status: 502 });
|
|
10746
10818
|
denials.push(row.lastAt === void 0 || row.lastAt === null ? {
|
|
10747
10819
|
host: row.host,
|
|
10748
10820
|
rejectedBy: row.rejectedBy,
|
|
@@ -10772,7 +10844,7 @@ var SandboxEgressOperations = class {
|
|
|
10772
10844
|
const row = typeof usage === "object" && usage !== null && !Array.isArray(usage) ? usage : void 0;
|
|
10773
10845
|
const count = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
10774
10846
|
const usd = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
10775
|
-
if (!row || typeof row.sandboxId !== "string" || typeof row.month !== "string" || row.plan !== "free" && row.plan !== "pro" && row.plan !== "enterprise" || !count(row.bytes) || !usd(row.chargeUsd) || !usd(row.usdPerGb) || !count(row.accountBytes) || !count(row.accountIncludedBytes) || !count(row.accountOverageBytes)) throw new ServerError("Egress usage response was malformed: usage is not a month record", 502);
|
|
10847
|
+
if (!row || typeof row.sandboxId !== "string" || typeof row.month !== "string" || row.plan !== "free" && row.plan !== "pro" && row.plan !== "enterprise" || !count(row.bytes) || !usd(row.chargeUsd) || !usd(row.usdPerGb) || !count(row.accountBytes) || !count(row.accountIncludedBytes) || !count(row.accountOverageBytes)) throw new ServerError("Egress usage response was malformed: usage is not a month record", { status: 502 });
|
|
10776
10848
|
return {
|
|
10777
10849
|
sandboxId: row.sandboxId,
|
|
10778
10850
|
month: row.month,
|
|
@@ -10803,11 +10875,11 @@ var SandboxEgressOperations = class {
|
|
|
10803
10875
|
const response = await this.host.fetch(`/v1/sandboxes/${encodeURIComponent(this.host.id())}/egress/activity${query}`);
|
|
10804
10876
|
if (!response.ok) throw await parseResponseError(response);
|
|
10805
10877
|
const body = await readEgressJson(response, "Egress activity response was malformed: body is not valid JSON");
|
|
10806
|
-
if (typeof body !== "object" || body === null || Array.isArray(body)) throw new ServerError("Egress activity response was malformed: body is not an object", 502);
|
|
10878
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) throw new ServerError("Egress activity response was malformed: body is not an object", { status: 502 });
|
|
10807
10879
|
const raw = body.activity;
|
|
10808
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new ServerError("Egress activity response was malformed: activity is not a record", 502);
|
|
10880
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new ServerError("Egress activity response was malformed: activity is not a record", { status: 502 });
|
|
10809
10881
|
const record = raw;
|
|
10810
|
-
if (typeof record.complete !== "boolean" || typeof record.requestsObserved !== "number" || !Number.isInteger(record.requestsObserved) || record.requestsObserved < 0 || typeof record.unparsedLines !== "number" || !Number.isInteger(record.unparsedLines) || record.unparsedLines < 0 || !Array.isArray(record.destinations) || !Array.isArray(record.incompleteReasons) || !record.incompleteReasons.every((one) => typeof one === "string") || record.complete !== (record.incompleteReasons.length === 0)) throw new ServerError("Egress activity response was malformed: activity is not an activity record", 502);
|
|
10882
|
+
if (typeof record.complete !== "boolean" || typeof record.requestsObserved !== "number" || !Number.isInteger(record.requestsObserved) || record.requestsObserved < 0 || typeof record.unparsedLines !== "number" || !Number.isInteger(record.unparsedLines) || record.unparsedLines < 0 || !Array.isArray(record.destinations) || !Array.isArray(record.incompleteReasons) || !record.incompleteReasons.every((one) => typeof one === "string") || record.complete !== (record.incompleteReasons.length === 0)) throw new ServerError("Egress activity response was malformed: activity is not an activity record", { status: 502 });
|
|
10811
10883
|
const destinations = [];
|
|
10812
10884
|
for (const [index, entry] of record.destinations.entries()) {
|
|
10813
10885
|
const row = entry;
|
|
@@ -10818,7 +10890,7 @@ var SandboxEgressOperations = class {
|
|
|
10818
10890
|
].every((field) => {
|
|
10819
10891
|
const value = row[field];
|
|
10820
10892
|
return value === void 0 || value === null || typeof value === "string";
|
|
10821
|
-
})) throw new ServerError(`Egress activity response was malformed: destination ${index} is not a destination`, 502);
|
|
10893
|
+
})) throw new ServerError(`Egress activity response was malformed: destination ${index} is not a destination`, { status: 502 });
|
|
10822
10894
|
destinations.push({
|
|
10823
10895
|
host: row.host,
|
|
10824
10896
|
action: row.action,
|
|
@@ -10842,7 +10914,7 @@ async function readEgressJson(response, message) {
|
|
|
10842
10914
|
try {
|
|
10843
10915
|
return await response.json();
|
|
10844
10916
|
} catch (cause) {
|
|
10845
|
-
const error = new ServerError(message, 502);
|
|
10917
|
+
const error = new ServerError(message, { status: 502 });
|
|
10846
10918
|
error.cause = cause;
|
|
10847
10919
|
throw error;
|
|
10848
10920
|
}
|
|
@@ -10962,7 +11034,7 @@ var SandboxFilesystemOperations = class extends DeclaredFilesystemOperations {
|
|
|
10962
11034
|
totalBytes: size,
|
|
10963
11035
|
percentage: 0
|
|
10964
11036
|
});
|
|
10965
|
-
const { uploadChunkedFromFile } = await import("./chunked-upload-file-
|
|
11037
|
+
const { uploadChunkedFromFile } = await import("./chunked-upload-file-DL1-AqRp.js");
|
|
10966
11038
|
await uploadChunkedFromFile({ fetch: (path, init) => this.host.runtimeFetch(path, init) }, remotePath, localPath, {
|
|
10967
11039
|
mode: options?.permissions,
|
|
10968
11040
|
onProgress: options?.onProgress ? (progress) => {
|
|
@@ -11087,7 +11159,7 @@ const REFUSED_DIAL_TTL_MS = 5 * 6e4;
|
|
|
11087
11159
|
* the scoped-token dial that follows tells them apart.
|
|
11088
11160
|
*/
|
|
11089
11161
|
function isUpgradeRefusal(error) {
|
|
11090
|
-
return error instanceof TerminalStreamError && error.beforeOpen;
|
|
11162
|
+
return error instanceof TerminalStreamError && error.beforeOpen === true;
|
|
11091
11163
|
}
|
|
11092
11164
|
/** Transport failures before `ready`; a protocol error frame is the terminal's own answer. */
|
|
11093
11165
|
const EDGE_DIAL_FAILURES = new Set([
|
|
@@ -11100,7 +11172,7 @@ const EDGE_DIAL_FAILURES = new Set([
|
|
|
11100
11172
|
* dropped, closed before `ready` or timed out.
|
|
11101
11173
|
*/
|
|
11102
11174
|
function isEdgeDialFailure(error) {
|
|
11103
|
-
return error instanceof TerminalStreamError && (error.beforeOpen || EDGE_DIAL_FAILURES.has(error.
|
|
11175
|
+
return error instanceof TerminalStreamError && (error.beforeOpen || EDGE_DIAL_FAILURES.has(error.wireCode));
|
|
11104
11176
|
}
|
|
11105
11177
|
/**
|
|
11106
11178
|
* Hold the caller's `onClose` until the stream is ready. A dial that is
|
|
@@ -11239,7 +11311,10 @@ var SandboxInteractiveTerminalOperations = class {
|
|
|
11239
11311
|
* (`/v1/sandboxes/{id}/runtime`) that `new URL(absolutePath, base)` would drop.
|
|
11240
11312
|
*/
|
|
11241
11313
|
resolveTerminalStreamUrl(sidecarProxyUrl, streamPath) {
|
|
11242
|
-
if (!streamPath.startsWith("/")) throw new ServerError(`Terminal stream path must be sandbox-relative (got "${streamPath}")`,
|
|
11314
|
+
if (!streamPath.startsWith("/")) throw new ServerError(`Terminal stream path must be sandbox-relative (got "${streamPath}")`, {
|
|
11315
|
+
status: 502,
|
|
11316
|
+
origin: "runtime"
|
|
11317
|
+
});
|
|
11243
11318
|
return `${sidecarProxyUrl.replace(/\/+$/, "")}${streamPath}`;
|
|
11244
11319
|
}
|
|
11245
11320
|
/** @internal — invoked by InteractiveSessionHandle.attach(). */
|
|
@@ -11287,14 +11362,19 @@ var SandboxInteractiveTerminalOperations = class {
|
|
|
11287
11362
|
if (exactRef.run.sessionId !== id) throw new ValidationError("Interactive attach ref does not belong to the session");
|
|
11288
11363
|
assertInteractiveRefBelongsToSandbox(exactRef, this.host.id);
|
|
11289
11364
|
const status = await this.host.interactiveStatus(id, exactRef.incarnationId, options);
|
|
11290
|
-
if (!status) throw new NotFoundError(
|
|
11365
|
+
if (!status) throw new NotFoundError(`Interactive session not found: ${id}`, {
|
|
11366
|
+
resourceType: "Interactive session",
|
|
11367
|
+
resourceId: id,
|
|
11291
11368
|
origin: "runtime",
|
|
11292
11369
|
endpoint: `/agents/sessions/${encodeURIComponent(id)}/interactive`
|
|
11293
11370
|
});
|
|
11294
|
-
if (status.state !== "running") throw new StateError(`Interactive session ${id} has exited; there is no live PTY to attach`,
|
|
11371
|
+
if (status.state !== "running") throw new StateError(`Interactive session ${id} has exited; there is no live PTY to attach`, {
|
|
11372
|
+
currentState: status.state,
|
|
11373
|
+
requiredState: "running",
|
|
11295
11374
|
origin: "runtime",
|
|
11296
|
-
endpoint: `/agents/sessions/${encodeURIComponent(id)}/interactive
|
|
11297
|
-
|
|
11375
|
+
endpoint: `/agents/sessions/${encodeURIComponent(id)}/interactive`,
|
|
11376
|
+
code: "INTERACTIVE_SESSION_EXITED"
|
|
11377
|
+
});
|
|
11298
11378
|
if (canonicalCandidateDigest$1(status.ref) !== canonicalCandidateDigest$1(exactRef)) throw new ValidationError("Interactive attach ref does not match the current session");
|
|
11299
11379
|
return exactRef;
|
|
11300
11380
|
}
|
|
@@ -11533,7 +11613,7 @@ async function readBoundedResponseText(response, signal) {
|
|
|
11533
11613
|
const declaredBytes = Number(response.headers.get("content-length"));
|
|
11534
11614
|
if (Number.isFinite(declaredBytes) && declaredBytes > MAX_WORKSPACE_IMAGE_RESPONSE_BYTES) {
|
|
11535
11615
|
response.body?.cancel();
|
|
11536
|
-
throw new ServerError("Sandbox API returned an oversized workspace image response", 502);
|
|
11616
|
+
throw new ServerError("Sandbox API returned an oversized workspace image response", { status: 502 });
|
|
11537
11617
|
}
|
|
11538
11618
|
if (!response.body) return "";
|
|
11539
11619
|
const reader = response.body.getReader();
|
|
@@ -11551,7 +11631,7 @@ async function readBoundedResponseText(response, signal) {
|
|
|
11551
11631
|
const { done, value } = await Promise.race([reader.read(), aborted]);
|
|
11552
11632
|
if (done) break;
|
|
11553
11633
|
totalBytes += value.byteLength;
|
|
11554
|
-
if (totalBytes > MAX_WORKSPACE_IMAGE_RESPONSE_BYTES) throw new ServerError("Sandbox API returned an oversized workspace image response", 502);
|
|
11634
|
+
if (totalBytes > MAX_WORKSPACE_IMAGE_RESPONSE_BYTES) throw new ServerError("Sandbox API returned an oversized workspace image response", { status: 502 });
|
|
11555
11635
|
chunks.push(value);
|
|
11556
11636
|
}
|
|
11557
11637
|
} catch (error) {
|
|
@@ -11590,7 +11670,8 @@ var WorkspaceImages = class {
|
|
|
11590
11670
|
await awaitWithSignal(this.ensureRunning(), signal);
|
|
11591
11671
|
} catch (error) {
|
|
11592
11672
|
throwIfAborted(options.signal);
|
|
11593
|
-
if (timeoutSignal.aborted || Date.now() >= deadline) throw new TimeoutError(
|
|
11673
|
+
if (timeoutSignal.aborted || Date.now() >= deadline) throw new TimeoutError(`Workspace image publication timed out after ${timeoutMs}ms`, {
|
|
11674
|
+
timeoutMs,
|
|
11594
11675
|
origin: "sandbox-api",
|
|
11595
11676
|
endpoint: path
|
|
11596
11677
|
});
|
|
@@ -11598,7 +11679,11 @@ var WorkspaceImages = class {
|
|
|
11598
11679
|
}
|
|
11599
11680
|
throwIfAborted(options.signal);
|
|
11600
11681
|
const filesystemIncarnationId = this.filesystemIncarnationId();
|
|
11601
|
-
if (!filesystemIncarnationId) throw new StateError("Sandbox filesystem incarnation is not ready",
|
|
11682
|
+
if (!filesystemIncarnationId) throw new StateError("Sandbox filesystem incarnation is not ready", {
|
|
11683
|
+
currentState: "running",
|
|
11684
|
+
requiredState: "running with a ready filesystem incarnation",
|
|
11685
|
+
code: "FILESYSTEM_INCARNATION_NOT_READY"
|
|
11686
|
+
});
|
|
11602
11687
|
const idempotencyKey = options.idempotencyKey ?? createIdempotencyKey();
|
|
11603
11688
|
const body = JSON.stringify({
|
|
11604
11689
|
...input,
|
|
@@ -11606,7 +11691,8 @@ var WorkspaceImages = class {
|
|
|
11606
11691
|
});
|
|
11607
11692
|
for (let attempt = 1; attempt <= WORKSPACE_IMAGE_REQUEST_ATTEMPTS; attempt++) {
|
|
11608
11693
|
const remainingMs = deadline - Date.now();
|
|
11609
|
-
if (remainingMs <= 0) throw new TimeoutError(
|
|
11694
|
+
if (remainingMs <= 0) throw new TimeoutError(`Workspace image publication timed out after ${timeoutMs}ms`, {
|
|
11695
|
+
timeoutMs,
|
|
11610
11696
|
origin: "sandbox-api",
|
|
11611
11697
|
endpoint: path
|
|
11612
11698
|
});
|
|
@@ -11625,13 +11711,15 @@ var WorkspaceImages = class {
|
|
|
11625
11711
|
responseText = await readBoundedResponseText(response, signal);
|
|
11626
11712
|
} catch (error) {
|
|
11627
11713
|
throwIfAborted(options.signal);
|
|
11628
|
-
if (timeoutSignal.aborted || Date.now() >= deadline) throw new TimeoutError(
|
|
11714
|
+
if (timeoutSignal.aborted || Date.now() >= deadline) throw new TimeoutError(`Workspace image publication timed out after ${timeoutMs}ms`, {
|
|
11715
|
+
timeoutMs,
|
|
11629
11716
|
origin: "sandbox-api",
|
|
11630
11717
|
endpoint: path
|
|
11631
11718
|
});
|
|
11632
11719
|
if (error instanceof ServerError) throw error;
|
|
11633
11720
|
if (attempt < WORKSPACE_IMAGE_REQUEST_ATTEMPTS && Date.now() < deadline) continue;
|
|
11634
|
-
throw new NetworkError("Failed to read the workspace image publication response",
|
|
11721
|
+
throw new NetworkError("Failed to read the workspace image publication response", {
|
|
11722
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
11635
11723
|
origin: "sandbox-api",
|
|
11636
11724
|
endpoint: path
|
|
11637
11725
|
});
|
|
@@ -11641,7 +11729,7 @@ var WorkspaceImages = class {
|
|
|
11641
11729
|
method: "POST",
|
|
11642
11730
|
path
|
|
11643
11731
|
}, response.headers);
|
|
11644
|
-
if (error.
|
|
11732
|
+
if (error.wireCode === "IMAGE_PUBLISH_OUTCOME_UNKNOWN" && attempt < WORKSPACE_IMAGE_REQUEST_ATTEMPTS && Date.now() < deadline) continue;
|
|
11645
11733
|
throw error;
|
|
11646
11734
|
}
|
|
11647
11735
|
const parsed = parseWorkspaceImagePublishResultForRepository((() => {
|
|
@@ -11651,13 +11739,15 @@ var WorkspaceImages = class {
|
|
|
11651
11739
|
return;
|
|
11652
11740
|
}
|
|
11653
11741
|
})(), input.repository);
|
|
11654
|
-
if (!parsed) throw new ServerError("Sandbox API returned an invalid workspace image result",
|
|
11742
|
+
if (!parsed) throw new ServerError("Sandbox API returned an invalid workspace image result", {
|
|
11743
|
+
status: 502,
|
|
11655
11744
|
origin: "sandbox-api",
|
|
11656
11745
|
endpoint: path
|
|
11657
11746
|
});
|
|
11658
11747
|
return parsed;
|
|
11659
11748
|
}
|
|
11660
|
-
throw new ServerError("Sandbox API did not return a workspace image result",
|
|
11749
|
+
throw new ServerError("Sandbox API did not return a workspace image result", {
|
|
11750
|
+
status: 502,
|
|
11661
11751
|
origin: "sandbox-api",
|
|
11662
11752
|
endpoint: path
|
|
11663
11753
|
});
|
|
@@ -11820,7 +11910,10 @@ var SandboxInstance = class SandboxInstance extends SandboxEgressCapabilities {
|
|
|
11820
11910
|
*/
|
|
11821
11911
|
direct() {
|
|
11822
11912
|
const data = state(this);
|
|
11823
|
-
if (!this.connection?.hostUrl && !this.connection?.execHost && !this.connection?.runtimeUrl) throw new StateError("Sandbox has no direct runtime URL",
|
|
11913
|
+
if (!this.connection?.hostUrl && !this.connection?.execHost && !this.connection?.runtimeUrl) throw new StateError("Sandbox has no direct runtime URL", {
|
|
11914
|
+
currentState: this.status,
|
|
11915
|
+
requiredState: "running"
|
|
11916
|
+
});
|
|
11824
11917
|
let directInstance;
|
|
11825
11918
|
directInstance = new SandboxInstance(new DirectRuntimeHttpClient(data.client, this.id, () => directInstance.connection, async () => {
|
|
11826
11919
|
await directInstance.refreshFor("bearer-refused");
|
|
@@ -11831,7 +11924,10 @@ var SandboxInstance = class SandboxInstance extends SandboxEgressCapabilities {
|
|
|
11831
11924
|
try {
|
|
11832
11925
|
await directInstance.refreshFor("runtime-refused");
|
|
11833
11926
|
} catch (error) {
|
|
11834
|
-
if (error instanceof NotFoundError) throw new StateError("Sandbox is not running (status: deleted)",
|
|
11927
|
+
if (error instanceof NotFoundError) throw new StateError("Sandbox is not running (status: deleted)", {
|
|
11928
|
+
currentState: "deleted",
|
|
11929
|
+
requiredState: "running"
|
|
11930
|
+
});
|
|
11835
11931
|
return;
|
|
11836
11932
|
}
|
|
11837
11933
|
directInstance.assertRunnable();
|