@tangle-network/sandbox 0.9.6 → 0.10.0

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/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { a as collectAgentResponseText, c as buildTraceExportPayload, d as toOtelJson, h as serializeForSidecar, i as applySandboxEventText, l as exportTraceBundle, m as normalizeRuntimeBackendConfig, n as SandboxSession, o as getSandboxEventText, p as parseSSEStream, r as InteractiveSessionHandle, t as SandboxInstance, u as otelTraceIdForTangleTrace } from "./sandbox-UQrmff8d.js";
2
- import { a as PartialFailureError, c as ServerError, d as ValidationError, i as NotFoundError, l as StateError, n as CapabilityError, o as QuotaError, r as NetworkError, s as SandboxError, t as AuthError, u as TimeoutError } from "./errors-DZsfJUuc.js";
3
- import { a as DEFAULT_SANDBOX_SIZE, c as resolveSandboxResources, i as SandboxFleetClient, l as sandboxResourcesForSize, n as SandboxClient, o as SANDBOX_SIZE_PRESETS, r as SandboxFleet, s as SANDBOX_SIZE_PRESET_NAMES, t as IntelligenceClient } from "./client-vBERsxhj.js";
4
- import { a as CollaborationClient, i as parseCollaborationDocumentId, n as buildCollaborationDocumentId, r as normalizeCollaborationPath, t as CollaborationFileBridge } from "./collaboration-BxlfZ2Uh.js";
5
- import { t as TangleSandboxClient } from "./tangle-DRzRB6z1.js";
1
+ import { _ as normalizeRuntimeBackendConfig, a as InteractiveSessionHandle, c as getSandboxEventText, d as exportTraceBundle, f as otelTraceIdForTangleTrace, g as parseSSEStream, h as encodeTextForWire, i as SandboxSession, o as applySandboxEventText, p as toOtelJson, r as SandboxTaskSession, s as collectAgentResponseText, t as SandboxInstance, u as buildTraceExportPayload, v as serializeForSidecar } from "./sandbox-CeimsfC8.js";
2
+ import { a as NotFoundError, c as SandboxError, d as TimeoutError, f as ValidationError, i as NetworkError, l as ServerError, n as CapabilityError, o as PartialFailureError, r as EgressProxyRecoveryError, s as QuotaError, t as AuthError, u as StateError } from "./errors-DSz87Rkk.js";
3
+ import { a as DEFAULT_SANDBOX_SIZE, c as resolveSandboxResources, i as SandboxFleetClient, l as sandboxResourcesForSize, n as SandboxClient, o as SANDBOX_SIZE_PRESETS, r as SandboxFleet, s as SANDBOX_SIZE_PRESET_NAMES, t as IntelligenceClient } from "./client-InX98Zxk.js";
4
+ import { a as CollaborationClient, i as parseCollaborationDocumentId, n as buildCollaborationDocumentId, r as normalizeCollaborationPath, t as CollaborationFileBridge } from "./collaboration-D17lnOJX.js";
5
+ import { t as TangleSandboxClient } from "./tangle-DayyZe-i.js";
6
6
  import { defineAgentProfile, defineGitHubResource, defineInlineResource, mergeAgentProfiles } from "@tangle-network/agent-interface";
7
7
  //#region src/confidential.ts
8
8
  function generateAttestationNonce(bytes = 32) {
@@ -595,6 +595,20 @@ function generateDockerfile(spec) {
595
595
  return lines.join("\n");
596
596
  }
597
597
  //#endregion
598
+ //#region src/types.ts
599
+ const GPU_LEASE_PROVIDER_NAMES = [
600
+ "runpod",
601
+ "vast-ai",
602
+ "lambda-labs",
603
+ "tensordock",
604
+ "paperspace",
605
+ "aws",
606
+ "gcp",
607
+ "azure",
608
+ "crusoe",
609
+ "fluidstack"
610
+ ];
611
+ //#endregion
598
612
  //#region src/manage-sandboxes.ts
599
613
  const MANAGE_SANDBOXES_TOOL_NAME = "manageSandboxes";
600
614
  function createManageSandboxesTool(client, options = {}) {
@@ -605,8 +619,8 @@ function createManageSandboxesTool(client, options = {}) {
605
619
  async execute(input) {
606
620
  switch (input.action) {
607
621
  case "capabilities": return client.fleets.capabilities();
608
- case "create": return client.fleets.create(applyToolPolicy(requireCreateOptions(input), options.policy));
609
- case "createWithCoordinator": return client.fleets.createWithCoordinator(applyToolPolicy(requireCreateWithCoordinatorOptions(input), options.policy));
622
+ case "create": return client.fleets.create(applyFleetGpuPolicy(applyToolPolicy(requireCreateOptions(input), options.policy), options.gpuPolicy));
623
+ case "createWithCoordinator": return client.fleets.createWithCoordinator(applyFleetGpuPolicyToCoordinator(applyToolPolicy(requireCreateWithCoordinatorOptions(input), options.policy), options.gpuPolicy));
610
624
  case "list": return client.fleets.list({ fleetId: requireString(input, "fleetId") });
611
625
  case "delete": return client.fleets.delete(requireString(input, "fleetId"));
612
626
  case "attachMachine": return client.fleets.attachMachine(requireString(input, "fleetId"), requireMachine(input));
@@ -619,6 +633,13 @@ function createManageSandboxesTool(client, options = {}) {
619
633
  case "createWorkspaceSnapshot": return client.fleets.createWorkspaceSnapshot(requireString(input, "fleetId"));
620
634
  case "restoreWorkspaceSnapshot": return client.fleets.restoreWorkspaceSnapshot(requireString(input, "fleetId"), requireString(input, "snapshotId"));
621
635
  case "reconcileWorkspace": return client.fleets.reconcileWorkspace(requireString(input, "fleetId"));
636
+ case "attachGpuLease": {
637
+ const attachOptions = applyGpuToolPolicy(requireGpuAttachOptions(input), options.gpuPolicy);
638
+ return (await requireSandbox(client, requireString(input, "sandboxId"))).gpu.attach(attachOptions);
639
+ }
640
+ case "listGpuLeases": return (await requireSandbox(client, requireString(input, "sandboxId"))).gpu.list();
641
+ case "execGpuLease": return (await requireSandbox(client, requireString(input, "sandboxId"))).gpu.exec(requireString(input, "leaseId"), requireGpuCommand(input));
642
+ case "detachGpuLease": return (await requireSandbox(client, requireString(input, "sandboxId"))).gpu.detach(requireString(input, "leaseId"));
622
643
  default: throw new Error(`Unsupported manageSandboxes action: ${String(input.action)}`);
623
644
  }
624
645
  }
@@ -650,6 +671,77 @@ function applyToolPolicy(options, policy) {
650
671
  policy
651
672
  };
652
673
  }
674
+ function applyFleetGpuPolicy(options, gpuPolicy) {
675
+ return {
676
+ ...options,
677
+ defaults: options.defaults ? applyGpuPolicyToCreateFragment(options.defaults, gpuPolicy, "fleet defaults") : void 0,
678
+ machines: options.machines.map((machine) => applyGpuPolicyToCreateFragment(machine, gpuPolicy, `machine ${machine.machineId}`))
679
+ };
680
+ }
681
+ function applyFleetGpuPolicyToCoordinator(options, gpuPolicy) {
682
+ return {
683
+ ...options,
684
+ coordinator: options.coordinator ? applyGpuPolicyToCreateFragment(options.coordinator, gpuPolicy, "coordinator") : void 0,
685
+ workers: options.workers.map((worker) => applyGpuPolicyToCreateFragment(worker, gpuPolicy, `worker ${worker.machineId}`))
686
+ };
687
+ }
688
+ function applyGpuPolicyToCreateFragment(fragment, gpuPolicy, label) {
689
+ if (fragment.gpuLease !== void 0) throw new Error("manageSandboxes GPU lease budget fields must be supplied by the tool host");
690
+ if (!fragment.resources?.accelerator) return fragment;
691
+ if (!gpuPolicy) throw new Error(`manageSandboxes ${label} requested accelerators but no host-owned GPU policy was supplied`);
692
+ return {
693
+ ...fragment,
694
+ gpuLease: gpuLeaseFromPolicy(gpuPolicy)
695
+ };
696
+ }
697
+ function gpuLeaseFromPolicy(policy) {
698
+ return {
699
+ ...policy.provider ? { provider: policy.provider } : {},
700
+ maxSpendUsd: policy.maxSpendUsd,
701
+ maxLifetimeSeconds: policy.maxLifetimeSeconds,
702
+ ...policy.idleTimeoutSeconds !== void 0 ? { idleTimeoutSeconds: policy.idleTimeoutSeconds } : {}
703
+ };
704
+ }
705
+ async function requireSandbox(client, sandboxId) {
706
+ const box = await client.get(sandboxId);
707
+ if (!box) throw new Error(`Sandbox not found: ${sandboxId}`);
708
+ return box;
709
+ }
710
+ function requireGpuAttachOptions(input) {
711
+ const value = input.options;
712
+ if (!value || typeof value !== "object") throw new Error("manageSandboxes requires options");
713
+ const options = value;
714
+ for (const key of [
715
+ "maxSpendUsd",
716
+ "maxLifetimeSeconds",
717
+ "idleTimeoutSeconds"
718
+ ]) if (key in options && options[key] !== void 0) throw new Error("manageSandboxes GPU lease budget fields must be supplied by the tool host");
719
+ const acceleratorValue = options.accelerator;
720
+ if (!acceleratorValue || typeof acceleratorValue !== "object") throw new Error("manageSandboxes GPU lease attach requires accelerator");
721
+ requireString(acceleratorValue, "kind");
722
+ return value;
723
+ }
724
+ function applyGpuToolPolicy(options, policy) {
725
+ if (!policy) throw new Error("manageSandboxes GPU lease attach requires a host-owned policy");
726
+ const requestedProvider = options.provider;
727
+ if (requestedProvider && policy.allowProviders && !policy.allowProviders.includes(requestedProvider)) throw new Error(`GPU provider ${requestedProvider} is not allowed by policy`);
728
+ const acceleratorCount = options.accelerator.count ?? 1;
729
+ if (policy.maxAcceleratorCount !== void 0 && acceleratorCount > policy.maxAcceleratorCount) throw new Error(`GPU lease requested ${acceleratorCount} accelerator(s) but policy.maxAcceleratorCount is ${policy.maxAcceleratorCount}`);
730
+ const provider = policy.provider ?? requestedProvider;
731
+ return {
732
+ ...options,
733
+ ...provider ? { provider } : {},
734
+ maxSpendUsd: policy.maxSpendUsd,
735
+ maxLifetimeSeconds: policy.maxLifetimeSeconds,
736
+ ...policy.idleTimeoutSeconds !== void 0 ? { idleTimeoutSeconds: policy.idleTimeoutSeconds } : {}
737
+ };
738
+ }
739
+ function requireGpuCommand(input) {
740
+ const value = input.command;
741
+ if (!value || typeof value !== "object") throw new Error("manageSandboxes requires command");
742
+ requireString(value, "command");
743
+ return value;
744
+ }
653
745
  function requireMachine(input) {
654
746
  const value = input.machine;
655
747
  if (!value || typeof value !== "object") throw new Error("manageSandboxes requires machine");
@@ -662,6 +754,135 @@ const fleetId = {
662
754
  type: "string",
663
755
  pattern: "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
664
756
  };
757
+ const accelerator = {
758
+ type: "object",
759
+ additionalProperties: false,
760
+ required: ["kind"],
761
+ properties: {
762
+ kind: {
763
+ type: "string",
764
+ pattern: "^[A-Za-z0-9][A-Za-z0-9._-]*$",
765
+ description: "Accelerator kind such as gpu, nvidia, nvidia-l4, or nvidia-h100."
766
+ },
767
+ count: {
768
+ type: "integer",
769
+ minimum: 1,
770
+ maximum: 16
771
+ },
772
+ memoryMB: {
773
+ type: "integer",
774
+ minimum: 1
775
+ }
776
+ }
777
+ };
778
+ const gpuLeaseAttachOptions = {
779
+ type: "object",
780
+ additionalProperties: false,
781
+ required: ["accelerator"],
782
+ properties: {
783
+ provider: {
784
+ type: "string",
785
+ enum: [...GPU_LEASE_PROVIDER_NAMES]
786
+ },
787
+ accelerator,
788
+ image: {
789
+ type: "string",
790
+ minLength: 1
791
+ },
792
+ env: {
793
+ type: "object",
794
+ additionalProperties: { type: "string" }
795
+ }
796
+ }
797
+ };
798
+ const gpuLeaseCommand = {
799
+ type: "object",
800
+ additionalProperties: false,
801
+ required: ["command"],
802
+ properties: {
803
+ command: {
804
+ type: "string",
805
+ minLength: 1
806
+ },
807
+ timeoutMs: {
808
+ type: "integer",
809
+ minimum: 1,
810
+ maximum: 36e5
811
+ },
812
+ cwd: {
813
+ type: "string",
814
+ minLength: 1
815
+ },
816
+ env: {
817
+ type: "object",
818
+ additionalProperties: { type: "string" }
819
+ }
820
+ }
821
+ };
822
+ const resources = {
823
+ type: "object",
824
+ additionalProperties: false,
825
+ properties: {
826
+ cpuCores: {
827
+ type: "number",
828
+ exclusiveMinimum: 0
829
+ },
830
+ memoryMB: {
831
+ type: "integer",
832
+ minimum: 1
833
+ },
834
+ diskGB: {
835
+ type: "integer",
836
+ minimum: 1
837
+ },
838
+ accelerator
839
+ }
840
+ };
841
+ const driver = {
842
+ type: "object",
843
+ additionalProperties: true,
844
+ properties: {
845
+ type: {
846
+ type: "string",
847
+ enum: [
848
+ "docker",
849
+ "firecracker",
850
+ "host-agent",
851
+ "tangle"
852
+ ]
853
+ },
854
+ runtimeBackend: {
855
+ type: "string",
856
+ enum: ["docker", "firecracker"]
857
+ }
858
+ }
859
+ };
860
+ const fleetMachineSpec = {
861
+ type: "object",
862
+ additionalProperties: true,
863
+ required: ["machineId"],
864
+ properties: {
865
+ machineId: fleetId,
866
+ name: {
867
+ type: "string",
868
+ minLength: 1
869
+ },
870
+ role: {
871
+ type: "string",
872
+ enum: ["coordinator", "worker"]
873
+ },
874
+ image: {
875
+ type: "string",
876
+ minLength: 1
877
+ },
878
+ environment: {
879
+ type: "string",
880
+ minLength: 1
881
+ },
882
+ resources,
883
+ driver
884
+ }
885
+ };
665
886
  const MANAGE_SANDBOXES_PARAMETERS = {
666
887
  type: "object",
667
888
  additionalProperties: false,
@@ -684,16 +905,72 @@ const MANAGE_SANDBOXES_PARAMETERS = {
684
905
  "cost",
685
906
  "createWorkspaceSnapshot",
686
907
  "restoreWorkspaceSnapshot",
687
- "reconcileWorkspace"
908
+ "reconcileWorkspace",
909
+ "attachGpuLease",
910
+ "listGpuLeases",
911
+ "execGpuLease",
912
+ "detachGpuLease"
688
913
  ]
689
914
  },
690
915
  fleetId,
691
916
  machineId: fleetId,
917
+ sandboxId: {
918
+ type: "string",
919
+ minLength: 1,
920
+ maxLength: 128
921
+ },
922
+ leaseId: {
923
+ type: "string",
924
+ minLength: 1,
925
+ maxLength: 128
926
+ },
692
927
  snapshotId: {
693
928
  type: "string",
694
929
  minLength: 1
695
930
  },
696
- options: { type: "object" },
931
+ options: { anyOf: [{
932
+ type: "object",
933
+ additionalProperties: true,
934
+ properties: {
935
+ fleetId,
936
+ defaults: {
937
+ type: "object",
938
+ additionalProperties: true,
939
+ properties: {
940
+ image: {
941
+ type: "string",
942
+ minLength: 1
943
+ },
944
+ environment: {
945
+ type: "string",
946
+ minLength: 1
947
+ },
948
+ resources,
949
+ driver
950
+ }
951
+ },
952
+ machines: {
953
+ type: "array",
954
+ items: fleetMachineSpec,
955
+ minItems: 1
956
+ },
957
+ workers: {
958
+ type: "array",
959
+ items: fleetMachineSpec,
960
+ minItems: 1
961
+ },
962
+ coordinator: fleetMachineSpec,
963
+ workspace: {
964
+ type: "object",
965
+ additionalProperties: true,
966
+ properties: { mode: {
967
+ type: "string",
968
+ enum: ["isolated", "shared"]
969
+ } }
970
+ }
971
+ }
972
+ }, gpuLeaseAttachOptions] },
973
+ command: gpuLeaseCommand,
697
974
  machine: {
698
975
  type: "object",
699
976
  required: ["machineId", "sandboxId"],
@@ -784,6 +1061,281 @@ function buildControlPlaneMcpConfig(options) {
784
1061
  };
785
1062
  }
786
1063
  //#endregion
1064
+ //#region src/profile-file-mounts.ts
1065
+ const PROFILE_FILE_MOUNT_B64_CHUNK_CHARS = 3e3;
1066
+ const PROFILE_FILE_MOUNT_MAX_RETRIES = 4;
1067
+ const PROFILE_FILE_MOUNT_RETRY_BASE_MS = 250;
1068
+ const PROFILE_FILE_MOUNT_RETRY_MAX_MS = 2e3;
1069
+ const PROFILE_FILE_MOUNT_EXEC_TIMEOUT_MS = 3e4;
1070
+ const PROFILE_FILE_MOUNT_PACE_MS = 150;
1071
+ const PROFILE_BIN_DIR_RE = /(^|\/)(s?bin)\//;
1072
+ const DEFAULT_PROFILE_FILE_API_ABSOLUTE_PREFIXES = [
1073
+ ["/home", "agent"].join("/"),
1074
+ ["/home", "user"].join("/"),
1075
+ "/tmp",
1076
+ "/output"
1077
+ ];
1078
+ function inlineContent(mount) {
1079
+ const resource = mount.resource;
1080
+ if (resource.kind !== "inline") throw new Error(`materializeProfileFileMounts: ${mount.path} requires an inline resource`);
1081
+ if (typeof resource.content !== "string") throw new Error(`materializeProfileFileMounts: ${mount.path} inline resource requires string content`);
1082
+ return resource.content;
1083
+ }
1084
+ function splitInlineProfileFileMounts(profile) {
1085
+ const files = profile.resources?.files ?? [];
1086
+ const deferredFiles = [];
1087
+ const keptFiles = [];
1088
+ for (const mount of files) if (mount.resource.kind === "inline") deferredFiles.push(mount);
1089
+ else keptFiles.push(mount);
1090
+ if (deferredFiles.length === 0) return {
1091
+ leanProfile: profile,
1092
+ deferredFiles
1093
+ };
1094
+ return {
1095
+ leanProfile: {
1096
+ ...profile,
1097
+ resources: {
1098
+ ...profile.resources ?? {},
1099
+ files: keptFiles
1100
+ }
1101
+ },
1102
+ deferredFiles
1103
+ };
1104
+ }
1105
+ function shellSingleQuote(value) {
1106
+ return `'${value.replace(/'/g, `'\\''`)}'`;
1107
+ }
1108
+ function shellPath(path) {
1109
+ if (path === "~") return "\"$HOME\"";
1110
+ if (path.startsWith("~/")) {
1111
+ const rest = path.slice(2);
1112
+ return rest ? `"$HOME"/${shellSingleQuote(rest)}` : "\"$HOME\"";
1113
+ }
1114
+ return shellSingleQuote(path);
1115
+ }
1116
+ function validateProfileFilePath(path) {
1117
+ if (!path) throw new Error("materializeProfileFileMounts: file path is required");
1118
+ if (path.endsWith("/")) throw new Error(`materializeProfileFileMounts: refusing directory-shaped file path ${path}`);
1119
+ if (hasControlCharacter(path)) throw new Error(`materializeProfileFileMounts: refusing unsafe file path ${JSON.stringify(path)}`);
1120
+ if (path === "~" || /^~[^/]/.test(path)) throw new Error(`materializeProfileFileMounts: refusing unsupported home path ${path}`);
1121
+ if (hasUnsafeFileApiSegment(path)) throw new Error(`materializeProfileFileMounts: refusing unsafe file path ${path}`);
1122
+ }
1123
+ function hasUnsafeFileApiSegment(path) {
1124
+ return path.split("/").some((segment) => segment === "." || segment === ".." || segment === ".sidecar");
1125
+ }
1126
+ function hasControlCharacter(value) {
1127
+ for (let i = 0; i < value.length; i++) {
1128
+ const code = value.charCodeAt(i);
1129
+ if (code < 32 || code === 127) return true;
1130
+ }
1131
+ return false;
1132
+ }
1133
+ function normalizeAbsolutePrefixes(prefixes) {
1134
+ return prefixes.map((prefix) => {
1135
+ const normalized = prefix.trim().replace(/\/+$/, "");
1136
+ if (!normalized.startsWith("/") || hasControlCharacter(normalized)) throw new Error(`materializeProfileFileMounts: fileApiAbsolutePrefixes must contain absolute paths; received ${JSON.stringify(prefix)}`);
1137
+ if (hasUnsafeFileApiSegment(normalized)) throw new Error(`materializeProfileFileMounts: fileApiAbsolutePrefixes contains unsafe path ${JSON.stringify(prefix)}`);
1138
+ return normalized;
1139
+ });
1140
+ }
1141
+ function isAllowedAbsoluteFileApiPath(path, prefixes) {
1142
+ for (const prefix of prefixes) if (path === prefix || path.startsWith(`${prefix}/`)) return true;
1143
+ return false;
1144
+ }
1145
+ function fileApiTarget(mount, absolutePrefixes) {
1146
+ if (mount.path.startsWith("~/")) return null;
1147
+ if (mount.path.startsWith("/")) return isAllowedAbsoluteFileApiPath(mount.path, absolutePrefixes) ? mount.path : null;
1148
+ if (mount.path.startsWith("~")) return null;
1149
+ if (mount.path.length === 0) return null;
1150
+ return mount.path;
1151
+ }
1152
+ function isExecutableProfileFile(mount) {
1153
+ return mount.executable ?? PROFILE_BIN_DIR_RE.test(mount.path);
1154
+ }
1155
+ function profileFileMode(mount) {
1156
+ return isExecutableProfileFile(mount) ? 493 : void 0;
1157
+ }
1158
+ function fileApiSupportsMode(box) {
1159
+ return box.fs?.supportsWriteMode === true;
1160
+ }
1161
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1162
+ var ProfileFileMountExecTimeoutError = class extends Error {
1163
+ constructor(timeoutMs) {
1164
+ super(`exec exceeded ${timeoutMs}ms`);
1165
+ this.name = "ProfileFileMountExecTimeoutError";
1166
+ }
1167
+ };
1168
+ function execWithTimeout(box, cmd, timeoutMs) {
1169
+ return new Promise((resolve, reject) => {
1170
+ let settled = false;
1171
+ const timer = setTimeout(() => {
1172
+ if (settled) return;
1173
+ settled = true;
1174
+ reject(new ProfileFileMountExecTimeoutError(timeoutMs));
1175
+ }, timeoutMs);
1176
+ box.exec(cmd, { timeoutMs }).then((res) => {
1177
+ if (settled) return;
1178
+ settled = true;
1179
+ clearTimeout(timer);
1180
+ resolve(res);
1181
+ }, (err) => {
1182
+ if (settled) return;
1183
+ settled = true;
1184
+ clearTimeout(timer);
1185
+ reject(err);
1186
+ });
1187
+ });
1188
+ }
1189
+ const TRANSIENT_EXEC_STATUS_CODES = new Set([
1190
+ 408,
1191
+ 409,
1192
+ 425,
1193
+ 429,
1194
+ 500,
1195
+ 502,
1196
+ 503,
1197
+ 504
1198
+ ]);
1199
+ const TRANSIENT_EXEC_CODE_RE = /^(ECONNRESET|ECONNREFUSED|ETIMEDOUT|EPIPE|ECONNABORTED)$/i;
1200
+ const TRANSIENT_EXEC_MESSAGE_RE = /\b(408|409|425|429|500|502|503|504)\b|rate.?limit|too many requests|\bfetch failed\b|network error|connection reset|socket hang up|timed? out|service unavailable|bad gateway|gateway timeout|internal server error|\b(?:sidecar|runtime|exec(?:ution)?|terminal|sandbox|service|command(?:s)?|proxy)\b.{0,80}\bnot ready\b|\bnot ready\b.{0,80}\b(?:sidecar|runtime|exec(?:ution)?|terminal|sandbox|service|command(?:s)?|proxy)\b/i;
1201
+ function errorStatus(err) {
1202
+ const rawStatus = err.status ?? err.statusCode ?? (err.response && typeof err.response === "object" ? err.response.status : void 0);
1203
+ if (typeof rawStatus === "number") return rawStatus;
1204
+ if (typeof rawStatus === "string" && /^\d+$/.test(rawStatus)) return Number(rawStatus);
1205
+ }
1206
+ function retryAfterMs(err, seen = /* @__PURE__ */ new Set()) {
1207
+ if (!err || typeof err !== "object") return void 0;
1208
+ if (seen.has(err)) return void 0;
1209
+ seen.add(err);
1210
+ const e = err;
1211
+ if (typeof e.retryAfterMs === "number") return e.retryAfterMs;
1212
+ return retryAfterMs(e.cause, seen);
1213
+ }
1214
+ function isTransientExecError(err, seen = /* @__PURE__ */ new Set()) {
1215
+ if (!err || typeof err !== "object") return false;
1216
+ if (seen.has(err)) return false;
1217
+ seen.add(err);
1218
+ const e = err;
1219
+ const status = errorStatus(e);
1220
+ if (status !== void 0 && TRANSIENT_EXEC_STATUS_CODES.has(status)) return true;
1221
+ if (typeof e.code === "string") {
1222
+ if (TRANSIENT_EXEC_CODE_RE.test(e.code)) return true;
1223
+ if (/rate.?limit|too.?many.?requests|429|server.?error|service.?unavailable/i.test(e.code)) return true;
1224
+ }
1225
+ if (typeof e.message === "string" && TRANSIENT_EXEC_MESSAGE_RE.test(e.message)) return true;
1226
+ return isTransientExecError(e.cause, seen);
1227
+ }
1228
+ function transientExecError(err) {
1229
+ if (err instanceof ProfileFileMountExecTimeoutError) return { retryable: false };
1230
+ if (isTransientExecError(err)) return {
1231
+ retryable: true,
1232
+ retryAfterMs: retryAfterMs(err)
1233
+ };
1234
+ return { retryable: false };
1235
+ }
1236
+ async function sha256Hex(content) {
1237
+ const bytes = new TextEncoder().encode(content);
1238
+ const webSubtle = globalThis.crypto?.subtle;
1239
+ if (webSubtle) {
1240
+ const digest = await webSubtle.digest("SHA-256", bytes);
1241
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
1242
+ }
1243
+ const { createHash } = await import("node:crypto");
1244
+ return createHash("sha256").update(content, "utf8").digest("hex");
1245
+ }
1246
+ async function execProfileFileMount(box, mount, content, options, execState) {
1247
+ const maxRetries = options.maxRetries ?? PROFILE_FILE_MOUNT_MAX_RETRIES;
1248
+ const path = mount.path;
1249
+ const b64 = encodeTextForWire(content);
1250
+ const b64Chunks = [];
1251
+ for (let i = 0; i < b64.length; i += PROFILE_FILE_MOUNT_B64_CHUNK_CHARS) b64Chunks.push(b64.slice(i, i + PROFILE_FILE_MOUNT_B64_CHUNK_CHARS));
1252
+ const expectedSha256 = await sha256Hex(content);
1253
+ const dir = path.replace(/\/[^/]*$/, "");
1254
+ const executable = isExecutableProfileFile(mount);
1255
+ const q = shellPath(path);
1256
+ const qb64 = shellPath(`${path}.b64`);
1257
+ const qtmp = shellPath(`${path}.tmp`);
1258
+ const qpartPrefix = shellPath(`${path}.b64.part.`);
1259
+ const homeGuard = path.startsWith("~/") ? `[ -n "$HOME" ] || { echo ${shellSingleQuote(`materializeProfileFileMounts: HOME is unset for ${path}`)} >&2; exit 1; }; ` : "";
1260
+ const run = async (cmd) => {
1261
+ for (let attempt = 0;; attempt++) {
1262
+ if (execState.started && options.paceMs > 0) await sleep(options.paceMs);
1263
+ execState.started = true;
1264
+ try {
1265
+ return await execWithTimeout(box, cmd, options.execTimeoutMs);
1266
+ } catch (err) {
1267
+ const { retryable, retryAfterMs: retryAfter } = transientExecError(err);
1268
+ if (retryable && attempt < maxRetries) {
1269
+ const backoff = Math.min(PROFILE_FILE_MOUNT_RETRY_BASE_MS * 2 ** attempt, PROFILE_FILE_MOUNT_RETRY_MAX_MS);
1270
+ await sleep(Math.min(retryAfter ?? backoff, PROFILE_FILE_MOUNT_RETRY_MAX_MS));
1271
+ continue;
1272
+ }
1273
+ throw new Error(`materializeProfileFileMounts: exec failed for ${path}`, { cause: err });
1274
+ }
1275
+ }
1276
+ };
1277
+ const step = async (cmd) => {
1278
+ const result = await run(`${homeGuard}${cmd}`);
1279
+ if (result.exitCode !== 0) throw new Error(`materializeProfileFileMounts: failed to write ${path} (exit ${result.exitCode}): ${result.stderr.slice(0, 500)}`);
1280
+ };
1281
+ await step(dir ? `mkdir -p ${shellPath(dir)}` : ":");
1282
+ for (let i = 0; i < b64Chunks.length; i++) {
1283
+ const slice = b64Chunks[i];
1284
+ if (slice === void 0) continue;
1285
+ await step(`printf '%s' '${slice}' > ${shellPath(`${path}.b64.part.${i}`)}`);
1286
+ }
1287
+ const chmod = executable ? `chmod +x ${q} || exit 1; ` : "";
1288
+ const checksumMismatch = shellSingleQuote(`materializeProfileFileMounts: checksum mismatch for ${path}`);
1289
+ await step(`cleanup() { ${`rm -f ${qb64} ${qtmp}; i=0; while [ "$i" -lt ${b64Chunks.length} ]; do rm -f ${qpartPrefix}$i; i=$((i+1)); done`}; }; trap cleanup EXIT; expected='${expectedSha256}'; if [ -f ${q} ] && [ "$(sha256sum ${q} | awk '{print $1}')" = "$expected" ]; then ${chmod}exit 0; fi; : > ${qb64} && i=0; while [ "$i" -lt ${b64Chunks.length} ]; do cat ${qpartPrefix}$i >> ${qb64} || exit 1; i=$((i+1)); done && base64 -d ${qb64} > ${qtmp} && [ "$(sha256sum ${qtmp} | awk '{print $1}')" = "$expected" ] || { echo ${checksumMismatch} >&2; exit 1; }; mv ${qtmp} ${q} && ${executable ? `chmod +x ${q} && ` : ""}[ "$(sha256sum ${q} | awk '{print $1}')" = "$expected" ] || { echo ${checksumMismatch} >&2; exit 1; }`);
1290
+ }
1291
+ async function materializeProfileFileMounts(box, files, options = {}) {
1292
+ const execTimeoutMs = options.execTimeoutMs ?? PROFILE_FILE_MOUNT_EXEC_TIMEOUT_MS;
1293
+ const paceMs = options.paceMs ?? PROFILE_FILE_MOUNT_PACE_MS;
1294
+ const maxRetries = options.maxRetries ?? PROFILE_FILE_MOUNT_MAX_RETRIES;
1295
+ const absolutePrefixes = normalizeAbsolutePrefixes(options.fileApiAbsolutePrefixes ?? DEFAULT_PROFILE_FILE_API_ABSOLUTE_PREFIXES);
1296
+ const fs = box.fs;
1297
+ const fileApiAvailable = typeof fs?.writeMany === "function";
1298
+ const modeAwareFileApi = fileApiSupportsMode(box);
1299
+ const viaFileApi = [];
1300
+ const viaExec = [];
1301
+ for (const mount of files) {
1302
+ const content = inlineContent(mount);
1303
+ validateProfileFilePath(mount.path);
1304
+ const target = fileApiAvailable ? fileApiTarget(mount, absolutePrefixes) : null;
1305
+ const executable = isExecutableProfileFile(mount);
1306
+ if (target !== null && (!executable || modeAwareFileApi)) {
1307
+ const mode = profileFileMode(mount);
1308
+ viaFileApi.push({
1309
+ path: target,
1310
+ content,
1311
+ ...mode !== void 0 ? { mode } : {}
1312
+ });
1313
+ } else viaExec.push({
1314
+ mount,
1315
+ content
1316
+ });
1317
+ }
1318
+ if (viaFileApi.length > 0) try {
1319
+ await fs?.writeMany(viaFileApi, {
1320
+ paceMs,
1321
+ maxRetries
1322
+ });
1323
+ } catch (err) {
1324
+ throw new Error("materializeProfileFileMounts: file API batch write failed", { cause: err });
1325
+ }
1326
+ const execState = { started: false };
1327
+ for (const item of viaExec) await execProfileFileMount(box, item.mount, item.content, {
1328
+ execTimeoutMs,
1329
+ paceMs,
1330
+ maxRetries
1331
+ }, execState);
1332
+ return {
1333
+ written: viaFileApi.length + viaExec.length,
1334
+ viaFileApi: viaFileApi.length,
1335
+ viaExec: viaExec.length
1336
+ };
1337
+ }
1338
+ //#endregion
787
1339
  //#region src/router-search.ts
788
1340
  const DEFAULT_ROUTER_URL = "https://router.tangle.tools";
789
1341
  async function fetchTangleRouterSearchConfig(options = {}) {
@@ -1177,4 +1729,4 @@ function stripUndefined(value) {
1177
1729
  return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => [k, stripUndefined(v)]));
1178
1730
  }
1179
1731
  //#endregion
1180
- export { AuthError, CONTROL_PLANE_MCP_SERVER_NAME, CapabilityError, CollaborationClient, CollaborationFileBridge, DEFAULT_SANDBOX_SIZE, Image, ImageBuilder, IntelligenceClient, InteractiveSessionHandle, MANAGE_SANDBOXES_PARAMETERS, MANAGE_SANDBOXES_TOOL_NAME, NetworkError, NotFoundError, PartialFailureError, QuotaError, SANDBOX_MCP_SERVER_NAME, SANDBOX_SIZE_PRESETS, SANDBOX_SIZE_PRESET_NAMES, SandboxClient as Sandbox, SandboxClient, SandboxError, SandboxFleet, SandboxFleetClient, SandboxInstance, SandboxSession, ServerError, StateError, TangleSandboxClient, TimeoutError, ValidationError, applySandboxEventText, buildCollaborationDocumentId, buildControlPlaneMcpConfig, buildSandboxMcpConfig, buildTangleRouterEvalMatrixRequest, buildTangleRouterResponsesWebSearchRequest, buildTangleRouterSearchBackend, buildTangleRouterSearchProfile, buildTraceExportPayload, collectAgentResponseText, createConfidentialSandbox, createManageSandboxesTool, defineAgentProfile, defineGitHubResource, defineInlineResource, exportTraceBundle, fetchTangleRouterSearchConfig, generateAttestationNonce, generateDockerfile, getSandboxEventText, mergeAgentProfiles, normalizeCollaborationPath, normalizeRuntimeBackendConfig, otelTraceIdForTangleTrace, parseCollaborationDocumentId, parseSSEStream, resolveSandboxResources, runTangleRouterEvalMatrixInSandbox, sandboxResourcesForSize, serializeForSidecar, startTeeAttestationHeartbeat, toOtelJson };
1732
+ export { AuthError, CONTROL_PLANE_MCP_SERVER_NAME, CapabilityError, CollaborationClient, CollaborationFileBridge, DEFAULT_SANDBOX_SIZE, EgressProxyRecoveryError, GPU_LEASE_PROVIDER_NAMES, Image, ImageBuilder, IntelligenceClient, InteractiveSessionHandle, MANAGE_SANDBOXES_PARAMETERS, MANAGE_SANDBOXES_TOOL_NAME, NetworkError, NotFoundError, PartialFailureError, QuotaError, SANDBOX_MCP_SERVER_NAME, SANDBOX_SIZE_PRESETS, SANDBOX_SIZE_PRESET_NAMES, SandboxClient as Sandbox, SandboxClient, SandboxError, SandboxFleet, SandboxFleetClient, SandboxInstance, SandboxSession, SandboxTaskSession, ServerError, StateError, TangleSandboxClient, TimeoutError, ValidationError, applySandboxEventText, buildCollaborationDocumentId, buildControlPlaneMcpConfig, buildSandboxMcpConfig, buildTangleRouterEvalMatrixRequest, buildTangleRouterResponsesWebSearchRequest, buildTangleRouterSearchBackend, buildTangleRouterSearchProfile, buildTraceExportPayload, collectAgentResponseText, createConfidentialSandbox, createManageSandboxesTool, defineAgentProfile, defineGitHubResource, defineInlineResource, exportTraceBundle, fetchTangleRouterSearchConfig, generateAttestationNonce, generateDockerfile, getSandboxEventText, materializeProfileFileMounts, mergeAgentProfiles, normalizeCollaborationPath, normalizeRuntimeBackendConfig, otelTraceIdForTangleTrace, parseCollaborationDocumentId, parseSSEStream, resolveSandboxResources, runTangleRouterEvalMatrixInSandbox, sandboxResourcesForSize, serializeForSidecar, splitInlineProfileFileMounts, startTeeAttestationHeartbeat, toOtelJson };
@@ -1,4 +1,4 @@
1
- import { f as parseErrorResponse, r as NetworkError, u as TimeoutError } from "../errors-DZsfJUuc.js";
1
+ import { d as TimeoutError, i as NetworkError, p as parseErrorResponse } from "../errors-DSz87Rkk.js";
2
2
  //#region src/intelligence/index.ts
3
3
  /**
4
4
  * `@tangle-network/sandbox/intelligence` — browser-safe typed client for the