@openai/agents-extensions 0.8.4 → 0.9.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.
Files changed (218) hide show
  1. package/dist/ai-sdk-ui/uiMessageStream.d.ts +5 -0
  2. package/dist/ai-sdk-ui/uiMessageStream.js +10 -3
  3. package/dist/ai-sdk-ui/uiMessageStream.js.map +1 -1
  4. package/dist/ai-sdk-ui/uiMessageStream.mjs +9 -3
  5. package/dist/ai-sdk-ui/uiMessageStream.mjs.map +1 -1
  6. package/dist/metadata.js +2 -2
  7. package/dist/metadata.mjs +2 -2
  8. package/dist/sandbox/blaxel/index.d.ts +2 -0
  9. package/dist/sandbox/blaxel/index.js +19 -0
  10. package/dist/sandbox/blaxel/index.js.map +1 -0
  11. package/dist/sandbox/blaxel/index.mjs +3 -0
  12. package/dist/sandbox/blaxel/index.mjs.map +1 -0
  13. package/dist/sandbox/blaxel/mounts.d.ts +101 -0
  14. package/dist/sandbox/blaxel/mounts.js +542 -0
  15. package/dist/sandbox/blaxel/mounts.js.map +1 -0
  16. package/dist/sandbox/blaxel/mounts.mjs +529 -0
  17. package/dist/sandbox/blaxel/mounts.mjs.map +1 -0
  18. package/dist/sandbox/blaxel/sandbox.d.ts +155 -0
  19. package/dist/sandbox/blaxel/sandbox.js +1110 -0
  20. package/dist/sandbox/blaxel/sandbox.js.map +1 -0
  21. package/dist/sandbox/blaxel/sandbox.mjs +1072 -0
  22. package/dist/sandbox/blaxel/sandbox.mjs.map +1 -0
  23. package/dist/sandbox/cloudflare/index.d.ts +2 -0
  24. package/dist/sandbox/cloudflare/index.js +19 -0
  25. package/dist/sandbox/cloudflare/index.js.map +1 -0
  26. package/dist/sandbox/cloudflare/index.mjs +3 -0
  27. package/dist/sandbox/cloudflare/index.mjs.map +1 -0
  28. package/dist/sandbox/cloudflare/mounts.d.ts +28 -0
  29. package/dist/sandbox/cloudflare/mounts.js +150 -0
  30. package/dist/sandbox/cloudflare/mounts.js.map +1 -0
  31. package/dist/sandbox/cloudflare/mounts.mjs +143 -0
  32. package/dist/sandbox/cloudflare/mounts.mjs.map +1 -0
  33. package/dist/sandbox/cloudflare/sandbox.d.ts +91 -0
  34. package/dist/sandbox/cloudflare/sandbox.js +1163 -0
  35. package/dist/sandbox/cloudflare/sandbox.js.map +1 -0
  36. package/dist/sandbox/cloudflare/sandbox.mjs +1125 -0
  37. package/dist/sandbox/cloudflare/sandbox.mjs.map +1 -0
  38. package/dist/sandbox/daytona/index.d.ts +2 -0
  39. package/dist/sandbox/daytona/index.js +19 -0
  40. package/dist/sandbox/daytona/index.js.map +1 -0
  41. package/dist/sandbox/daytona/index.mjs +3 -0
  42. package/dist/sandbox/daytona/index.mjs.map +1 -0
  43. package/dist/sandbox/daytona/mounts.d.ts +11 -0
  44. package/dist/sandbox/daytona/mounts.js +17 -0
  45. package/dist/sandbox/daytona/mounts.js.map +1 -0
  46. package/dist/sandbox/daytona/mounts.mjs +12 -0
  47. package/dist/sandbox/daytona/mounts.mjs.map +1 -0
  48. package/dist/sandbox/daytona/sandbox.d.ts +160 -0
  49. package/dist/sandbox/daytona/sandbox.js +938 -0
  50. package/dist/sandbox/daytona/sandbox.js.map +1 -0
  51. package/dist/sandbox/daytona/sandbox.mjs +900 -0
  52. package/dist/sandbox/daytona/sandbox.mjs.map +1 -0
  53. package/dist/sandbox/e2b/index.d.ts +2 -0
  54. package/dist/sandbox/e2b/index.js +19 -0
  55. package/dist/sandbox/e2b/index.js.map +1 -0
  56. package/dist/sandbox/e2b/index.mjs +3 -0
  57. package/dist/sandbox/e2b/index.mjs.map +1 -0
  58. package/dist/sandbox/e2b/mounts.d.ts +11 -0
  59. package/dist/sandbox/e2b/mounts.js +17 -0
  60. package/dist/sandbox/e2b/mounts.js.map +1 -0
  61. package/dist/sandbox/e2b/mounts.mjs +12 -0
  62. package/dist/sandbox/e2b/mounts.mjs.map +1 -0
  63. package/dist/sandbox/e2b/sandbox.d.ts +169 -0
  64. package/dist/sandbox/e2b/sandbox.js +829 -0
  65. package/dist/sandbox/e2b/sandbox.js.map +1 -0
  66. package/dist/sandbox/e2b/sandbox.mjs +791 -0
  67. package/dist/sandbox/e2b/sandbox.mjs.map +1 -0
  68. package/dist/sandbox/modal/index.d.ts +2 -0
  69. package/dist/sandbox/modal/index.js +19 -0
  70. package/dist/sandbox/modal/index.js.map +1 -0
  71. package/dist/sandbox/modal/index.mjs +3 -0
  72. package/dist/sandbox/modal/index.mjs.map +1 -0
  73. package/dist/sandbox/modal/mounts.d.ts +24 -0
  74. package/dist/sandbox/modal/mounts.js +194 -0
  75. package/dist/sandbox/modal/mounts.js.map +1 -0
  76. package/dist/sandbox/modal/mounts.mjs +188 -0
  77. package/dist/sandbox/modal/mounts.mjs.map +1 -0
  78. package/dist/sandbox/modal/sandbox.d.ts +220 -0
  79. package/dist/sandbox/modal/sandbox.js +1621 -0
  80. package/dist/sandbox/modal/sandbox.js.map +1 -0
  81. package/dist/sandbox/modal/sandbox.mjs +1581 -0
  82. package/dist/sandbox/modal/sandbox.mjs.map +1 -0
  83. package/dist/sandbox/runloop/index.d.ts +2 -0
  84. package/dist/sandbox/runloop/index.js +19 -0
  85. package/dist/sandbox/runloop/index.js.map +1 -0
  86. package/dist/sandbox/runloop/index.mjs +3 -0
  87. package/dist/sandbox/runloop/index.mjs.map +1 -0
  88. package/dist/sandbox/runloop/mounts.d.ts +11 -0
  89. package/dist/sandbox/runloop/mounts.js +17 -0
  90. package/dist/sandbox/runloop/mounts.js.map +1 -0
  91. package/dist/sandbox/runloop/mounts.mjs +12 -0
  92. package/dist/sandbox/runloop/mounts.mjs.map +1 -0
  93. package/dist/sandbox/runloop/sandbox.d.ts +266 -0
  94. package/dist/sandbox/runloop/sandbox.js +1390 -0
  95. package/dist/sandbox/runloop/sandbox.js.map +1 -0
  96. package/dist/sandbox/runloop/sandbox.mjs +1346 -0
  97. package/dist/sandbox/runloop/sandbox.mjs.map +1 -0
  98. package/dist/sandbox/shared/archive.d.ts +34 -0
  99. package/dist/sandbox/shared/archive.js +397 -0
  100. package/dist/sandbox/shared/archive.js.map +1 -0
  101. package/dist/sandbox/shared/archive.mjs +389 -0
  102. package/dist/sandbox/shared/archive.mjs.map +1 -0
  103. package/dist/sandbox/shared/editor.d.ts +17 -0
  104. package/dist/sandbox/shared/editor.js +65 -0
  105. package/dist/sandbox/shared/editor.js.map +1 -0
  106. package/dist/sandbox/shared/editor.mjs +61 -0
  107. package/dist/sandbox/shared/editor.mjs.map +1 -0
  108. package/dist/sandbox/shared/environment.d.ts +1 -0
  109. package/dist/sandbox/shared/environment.js +13 -0
  110. package/dist/sandbox/shared/environment.js.map +1 -0
  111. package/dist/sandbox/shared/environment.mjs +2 -0
  112. package/dist/sandbox/shared/environment.mjs.map +1 -0
  113. package/dist/sandbox/shared/git.d.ts +4 -0
  114. package/dist/sandbox/shared/git.js +20 -0
  115. package/dist/sandbox/shared/git.js.map +1 -0
  116. package/dist/sandbox/shared/git.mjs +17 -0
  117. package/dist/sandbox/shared/git.mjs.map +1 -0
  118. package/dist/sandbox/shared/inContainerMounts.d.ts +40 -0
  119. package/dist/sandbox/shared/inContainerMounts.js +670 -0
  120. package/dist/sandbox/shared/inContainerMounts.js.map +1 -0
  121. package/dist/sandbox/shared/inContainerMounts.mjs +662 -0
  122. package/dist/sandbox/shared/inContainerMounts.mjs.map +1 -0
  123. package/dist/sandbox/shared/index.d.ts +23 -0
  124. package/dist/sandbox/shared/index.js +112 -0
  125. package/dist/sandbox/shared/index.js.map +1 -0
  126. package/dist/sandbox/shared/index.mjs +18 -0
  127. package/dist/sandbox/shared/index.mjs.map +1 -0
  128. package/dist/sandbox/shared/localSources.d.ts +8 -0
  129. package/dist/sandbox/shared/localSources.js +378 -0
  130. package/dist/sandbox/shared/localSources.js.map +1 -0
  131. package/dist/sandbox/shared/localSources.mjs +372 -0
  132. package/dist/sandbox/shared/localSources.mjs.map +1 -0
  133. package/dist/sandbox/shared/manifest.d.ts +38 -0
  134. package/dist/sandbox/shared/manifest.js +254 -0
  135. package/dist/sandbox/shared/manifest.js.map +1 -0
  136. package/dist/sandbox/shared/manifest.mjs +233 -0
  137. package/dist/sandbox/shared/manifest.mjs.map +1 -0
  138. package/dist/sandbox/shared/media.d.ts +1 -0
  139. package/dist/sandbox/shared/media.js +10 -0
  140. package/dist/sandbox/shared/media.js.map +1 -0
  141. package/dist/sandbox/shared/media.mjs +2 -0
  142. package/dist/sandbox/shared/media.mjs.map +1 -0
  143. package/dist/sandbox/shared/metadata.d.ts +7 -0
  144. package/dist/sandbox/shared/metadata.js +87 -0
  145. package/dist/sandbox/shared/metadata.js.map +1 -0
  146. package/dist/sandbox/shared/metadata.mjs +81 -0
  147. package/dist/sandbox/shared/metadata.mjs.map +1 -0
  148. package/dist/sandbox/shared/output.d.ts +1 -0
  149. package/dist/sandbox/shared/output.js +8 -0
  150. package/dist/sandbox/shared/output.js.map +1 -0
  151. package/dist/sandbox/shared/output.mjs +2 -0
  152. package/dist/sandbox/shared/output.mjs.map +1 -0
  153. package/dist/sandbox/shared/paths.d.ts +27 -0
  154. package/dist/sandbox/shared/paths.js +245 -0
  155. package/dist/sandbox/shared/paths.js.map +1 -0
  156. package/dist/sandbox/shared/paths.mjs +236 -0
  157. package/dist/sandbox/shared/paths.mjs.map +1 -0
  158. package/dist/sandbox/shared/ports.d.ts +14 -0
  159. package/dist/sandbox/shared/ports.js +75 -0
  160. package/dist/sandbox/shared/ports.js.map +1 -0
  161. package/dist/sandbox/shared/ports.mjs +69 -0
  162. package/dist/sandbox/shared/ports.mjs.map +1 -0
  163. package/dist/sandbox/shared/process.d.ts +16 -0
  164. package/dist/sandbox/shared/process.js +90 -0
  165. package/dist/sandbox/shared/process.js.map +1 -0
  166. package/dist/sandbox/shared/process.mjs +86 -0
  167. package/dist/sandbox/shared/process.mjs.map +1 -0
  168. package/dist/sandbox/shared/pty.d.ts +81 -0
  169. package/dist/sandbox/shared/pty.js +382 -0
  170. package/dist/sandbox/shared/pty.js.map +1 -0
  171. package/dist/sandbox/shared/pty.mjs +335 -0
  172. package/dist/sandbox/shared/pty.mjs.map +1 -0
  173. package/dist/sandbox/shared/runAs.d.ts +46 -0
  174. package/dist/sandbox/shared/runAs.js +149 -0
  175. package/dist/sandbox/shared/runAs.js.map +1 -0
  176. package/dist/sandbox/shared/runAs.mjs +141 -0
  177. package/dist/sandbox/shared/runAs.mjs.map +1 -0
  178. package/dist/sandbox/shared/session.d.ts +16 -0
  179. package/dist/sandbox/shared/session.js +138 -0
  180. package/dist/sandbox/shared/session.js.map +1 -0
  181. package/dist/sandbox/shared/session.mjs +128 -0
  182. package/dist/sandbox/shared/session.mjs.map +1 -0
  183. package/dist/sandbox/shared/sessionBase.d.ts +90 -0
  184. package/dist/sandbox/shared/sessionBase.js +446 -0
  185. package/dist/sandbox/shared/sessionBase.js.map +1 -0
  186. package/dist/sandbox/shared/sessionBase.mjs +442 -0
  187. package/dist/sandbox/shared/sessionBase.mjs.map +1 -0
  188. package/dist/sandbox/shared/sessionState.d.ts +7 -0
  189. package/dist/sandbox/shared/sessionState.js +35 -0
  190. package/dist/sandbox/shared/sessionState.js.map +1 -0
  191. package/dist/sandbox/shared/sessionState.mjs +31 -0
  192. package/dist/sandbox/shared/sessionState.mjs.map +1 -0
  193. package/dist/sandbox/shared/snapshots.d.ts +9 -0
  194. package/dist/sandbox/shared/snapshots.js +62 -0
  195. package/dist/sandbox/shared/snapshots.js.map +1 -0
  196. package/dist/sandbox/shared/snapshots.mjs +57 -0
  197. package/dist/sandbox/shared/snapshots.mjs.map +1 -0
  198. package/dist/sandbox/shared/typeGuards.d.ts +10 -0
  199. package/dist/sandbox/shared/typeGuards.js +50 -0
  200. package/dist/sandbox/shared/typeGuards.js.map +1 -0
  201. package/dist/sandbox/shared/typeGuards.mjs +38 -0
  202. package/dist/sandbox/shared/typeGuards.mjs.map +1 -0
  203. package/dist/sandbox/shared/types.d.ts +24 -0
  204. package/dist/sandbox/shared/types.js +3 -0
  205. package/dist/sandbox/shared/types.js.map +1 -0
  206. package/dist/sandbox/shared/types.mjs +2 -0
  207. package/dist/sandbox/shared/types.mjs.map +1 -0
  208. package/dist/sandbox/vercel/index.d.ts +1 -0
  209. package/dist/sandbox/vercel/index.js +18 -0
  210. package/dist/sandbox/vercel/index.js.map +1 -0
  211. package/dist/sandbox/vercel/index.mjs +2 -0
  212. package/dist/sandbox/vercel/index.mjs.map +1 -0
  213. package/dist/sandbox/vercel/sandbox.d.ts +135 -0
  214. package/dist/sandbox/vercel/sandbox.js +1001 -0
  215. package/dist/sandbox/vercel/sandbox.js.map +1 -0
  216. package/dist/sandbox/vercel/sandbox.mjs +963 -0
  217. package/dist/sandbox/vercel/sandbox.mjs.map +1 -0
  218. package/package.json +108 -4
@@ -0,0 +1,1621 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ModalSandboxClient = exports.ModalSandboxSession = exports.ModalSandboxSelector = exports.ModalImageSelector = void 0;
37
+ const agents_core_1 = require("@openai/agents-core");
38
+ const sandbox_1 = require("@openai/agents-core/sandbox");
39
+ const internal_1 = require("@openai/agents-core/sandbox/internal");
40
+ const node_path_1 = require("node:path");
41
+ const shared_1 = require("../shared/index.js");
42
+ const localSources_1 = require("../shared/localSources.js");
43
+ const mounts_1 = require("./mounts.js");
44
+ const DEFAULT_MODAL_IMAGE_TAG = 'debian:bookworm-slim';
45
+ const DEFAULT_EXEC_YIELD_TIME_MS = 10_000;
46
+ const DEFAULT_WRITE_STDIN_YIELD_TIME_MS = 250;
47
+ const MAX_ACTIVE_PROCESS_OUTPUT_CHARS = 1024 * 1024;
48
+ const COMPLETED_ACTIVE_PROCESS_RETENTION_MS = 60_000;
49
+ class ModalImageSelector {
50
+ kind;
51
+ value;
52
+ constructor(kind, value) {
53
+ this.kind = kind;
54
+ this.value = value;
55
+ }
56
+ static fromImage(image) {
57
+ return new ModalImageSelector('image', image);
58
+ }
59
+ static fromId(imageId) {
60
+ return new ModalImageSelector('id', imageId);
61
+ }
62
+ static fromTag(imageTag) {
63
+ return new ModalImageSelector('tag', imageTag);
64
+ }
65
+ }
66
+ exports.ModalImageSelector = ModalImageSelector;
67
+ class ModalSandboxSelector {
68
+ kind;
69
+ value;
70
+ constructor(kind, value) {
71
+ this.kind = kind;
72
+ this.value = value;
73
+ }
74
+ static fromSandbox(sandbox) {
75
+ return new ModalSandboxSelector('sandbox', sandbox);
76
+ }
77
+ static fromId(sandboxId) {
78
+ return new ModalSandboxSelector('id', sandboxId);
79
+ }
80
+ }
81
+ exports.ModalSandboxSelector = ModalSandboxSelector;
82
+ class ModalSandboxSession {
83
+ state;
84
+ modal;
85
+ app;
86
+ cloudBucketMountsProvider;
87
+ cloudBucketMounts;
88
+ cloudBucketMountsResolved = false;
89
+ sandbox;
90
+ ownsSandbox;
91
+ closed = false;
92
+ closePromise;
93
+ activeProcesses = new Map();
94
+ remotePathResolver = async (path, options) => await this.resolveRemotePath(path, options);
95
+ concurrencyLimits;
96
+ nextProcessId = 1;
97
+ constructor(args) {
98
+ this.state = args.state;
99
+ this.modal = args.modal;
100
+ this.app = args.app;
101
+ this.sandbox = args.sandbox;
102
+ this.ownsSandbox = args.ownsSandbox ?? args.state.ownsSandbox ?? true;
103
+ this.state.ownsSandbox = this.ownsSandbox;
104
+ this.cloudBucketMounts = args.cloudBucketMounts;
105
+ this.cloudBucketMountsProvider = args.cloudBucketMountsProvider;
106
+ this.concurrencyLimits = args.concurrencyLimits;
107
+ this.cloudBucketMountsResolved =
108
+ args.cloudBucketMounts !== undefined || !args.cloudBucketMountsProvider;
109
+ }
110
+ createEditor(runAs) {
111
+ if (runAs) {
112
+ return (0, shared_1.createRunAsRemoteEditor)({
113
+ providerName: 'ModalSandboxClient',
114
+ providerId: 'modal',
115
+ runAs,
116
+ resolvePath: this.remotePathResolver,
117
+ runCommand: this.runAsCommandRunner.bind(this),
118
+ writer: this.writer(),
119
+ });
120
+ }
121
+ return new shared_1.RemoteSandboxEditor({
122
+ resolvePath: this.remotePathResolver,
123
+ pathExists: async (path) => await this.pathExists(path),
124
+ readText: async (path) => new TextDecoder().decode(await this.readSandboxFile(path)),
125
+ writeText: async (path, content) => {
126
+ await this.writeSandboxFile(path, content);
127
+ },
128
+ deletePath: async (path) => {
129
+ await this.removeSandboxPath(path);
130
+ },
131
+ });
132
+ }
133
+ supportsPty() {
134
+ return true;
135
+ }
136
+ async execCommand(args) {
137
+ const start = Date.now();
138
+ const command = buildShellCommand({
139
+ ...args,
140
+ cmd: (0, shared_1.sandboxUserShellCommand)(args.cmd, args.runAs),
141
+ });
142
+ const process = await this.sandbox.exec(command, {
143
+ mode: 'text',
144
+ workdir: this.resolveWorkdir(args.workdir),
145
+ env: this.state.environment,
146
+ pty: args.tty ?? false,
147
+ stdout: 'pipe',
148
+ stderr: 'pipe',
149
+ });
150
+ const activeProcess = createActiveProcess(process);
151
+ await waitForProcessOrTimeout(activeProcess, args.yieldTimeMs ?? DEFAULT_EXEC_YIELD_TIME_MS);
152
+ if (activeProcess.done) {
153
+ const output = (0, shared_1.truncateOutput)(consumeCommandOutput(activeProcess), args.maxOutputTokens);
154
+ return (0, shared_1.formatExecResponse)({
155
+ output: output.text,
156
+ wallTimeSeconds: (0, shared_1.elapsedSeconds)(start),
157
+ exitCode: activeProcess.exitCode ?? 1,
158
+ originalTokenCount: output.originalTokenCount,
159
+ });
160
+ }
161
+ const sessionId = this.nextProcessId++;
162
+ this.registerActiveProcess(sessionId, activeProcess);
163
+ const output = (0, shared_1.truncateOutput)(consumeCommandOutput(activeProcess), args.maxOutputTokens);
164
+ return (0, shared_1.formatExecResponse)({
165
+ output: output.text,
166
+ wallTimeSeconds: (0, shared_1.elapsedSeconds)(start),
167
+ sessionId,
168
+ originalTokenCount: output.originalTokenCount,
169
+ });
170
+ }
171
+ async writeStdin(args) {
172
+ const activeProcess = this.activeProcesses.get(args.sessionId);
173
+ if (!activeProcess) {
174
+ return (0, shared_1.formatExecResponse)({
175
+ output: `write_stdin failed: session not found: ${args.sessionId}`,
176
+ wallTimeSeconds: 0,
177
+ exitCode: 1,
178
+ });
179
+ }
180
+ const start = Date.now();
181
+ const chars = args.chars ?? '';
182
+ if (chars.length > 0) {
183
+ if (!activeProcess.process.stdin?.writeText) {
184
+ throw new agents_core_1.UserError('Modal sandbox process does not expose stdin for write_stdin().');
185
+ }
186
+ await activeProcess.process.stdin.writeText(chars);
187
+ }
188
+ await waitForProcessOrTimeout(activeProcess, args.yieldTimeMs ?? DEFAULT_WRITE_STDIN_YIELD_TIME_MS);
189
+ const output = (0, shared_1.truncateOutput)(consumeCommandOutput(activeProcess), args.maxOutputTokens);
190
+ if (activeProcess.done) {
191
+ this.deleteActiveProcess(args.sessionId, activeProcess);
192
+ return (0, shared_1.formatExecResponse)({
193
+ output: output.text,
194
+ wallTimeSeconds: (0, shared_1.elapsedSeconds)(start),
195
+ exitCode: activeProcess.exitCode ?? 1,
196
+ });
197
+ }
198
+ return (0, shared_1.formatExecResponse)({
199
+ output: output.text,
200
+ wallTimeSeconds: (0, shared_1.elapsedSeconds)(start),
201
+ sessionId: args.sessionId,
202
+ originalTokenCount: output.originalTokenCount,
203
+ });
204
+ }
205
+ async viewImage(args) {
206
+ const absolutePath = await this.resolveRemotePath(args.path);
207
+ const bytes = args.runAs
208
+ ? await (0, shared_1.readRunAsRemoteFile)({
209
+ providerName: 'ModalSandboxClient',
210
+ providerId: 'modal',
211
+ path: absolutePath,
212
+ runAs: args.runAs,
213
+ runCommand: this.runAsCommandRunner.bind(this),
214
+ })
215
+ : await this.readSandboxFile(absolutePath);
216
+ return (0, shared_1.imageOutputFromBytes)(args.path, bytes);
217
+ }
218
+ async readFile(args) {
219
+ const absolutePath = await this.resolveRemotePath(args.path);
220
+ const bytes = args.runAs
221
+ ? await (0, shared_1.readRunAsRemoteFile)({
222
+ providerName: 'ModalSandboxClient',
223
+ providerId: 'modal',
224
+ path: absolutePath,
225
+ runAs: args.runAs,
226
+ runCommand: this.runAsCommandRunner.bind(this),
227
+ })
228
+ : await this.readSandboxFile(absolutePath);
229
+ if (typeof args.maxBytes === 'number' && bytes.byteLength > args.maxBytes) {
230
+ return bytes.subarray(0, args.maxBytes);
231
+ }
232
+ return bytes;
233
+ }
234
+ async pathExists(path, runAs) {
235
+ const absolutePath = await this.resolveRemotePath(path);
236
+ if (!runAs) {
237
+ const process = await this.sandbox.exec(['test', '-e', absolutePath], {
238
+ mode: 'text',
239
+ workdir: this.state.manifest.root,
240
+ stdout: 'ignore',
241
+ stderr: 'pipe',
242
+ });
243
+ return (await process.wait()) === 0;
244
+ }
245
+ return await (0, shared_1.runAsRemotePathExists)(absolutePath, runAs, this.runAsCommandRunner.bind(this));
246
+ }
247
+ async running() {
248
+ try {
249
+ return (await this.sandbox.poll()) === null;
250
+ }
251
+ catch {
252
+ return false;
253
+ }
254
+ }
255
+ async resolveExposedPort(port) {
256
+ const requestedPort = (0, shared_1.assertConfiguredExposedPort)({
257
+ providerName: 'ModalSandboxClient',
258
+ port,
259
+ configuredPorts: this.state.configuredExposedPorts,
260
+ });
261
+ const cached = (0, shared_1.getCachedExposedPortEndpoint)(this.state, requestedPort);
262
+ if (cached) {
263
+ return cached;
264
+ }
265
+ if (!this.sandbox.tunnels) {
266
+ throw new sandbox_1.SandboxProviderError('ModalSandboxClient exposed port resolution requires Modal sandbox tunnels support.', {
267
+ provider: 'modal',
268
+ port: requestedPort,
269
+ });
270
+ }
271
+ let tunnels;
272
+ try {
273
+ tunnels = await this.sandbox.tunnels(10_000);
274
+ }
275
+ catch (error) {
276
+ throw new sandbox_1.SandboxProviderError(`ModalSandboxClient failed to look up tunnels for exposed port ${requestedPort}.`, {
277
+ provider: 'modal',
278
+ port: requestedPort,
279
+ cause: error instanceof Error ? error.message : String(error),
280
+ });
281
+ }
282
+ const tunnel = tunnels[requestedPort];
283
+ if (typeof tunnel?.host !== 'string' ||
284
+ tunnel.host.length === 0 ||
285
+ typeof tunnel.port !== 'number') {
286
+ throw new sandbox_1.SandboxProviderError(`ModalSandboxClient did not expose port ${requestedPort}.`, {
287
+ provider: 'modal',
288
+ port: requestedPort,
289
+ });
290
+ }
291
+ return (0, shared_1.recordResolvedExposedPortEndpoint)(this.state, requestedPort, {
292
+ host: tunnel.host,
293
+ port: tunnel.port,
294
+ tls: true,
295
+ query: '',
296
+ });
297
+ }
298
+ async materializeEntry(args) {
299
+ (0, shared_1.assertSandboxEntryMetadataSupported)('ModalSandboxClient', args.path, args.entry, shared_1.MOUNT_MANIFEST_METADATA_SUPPORT);
300
+ assertModalLiveEntryMountsUnsupported(args.entry, args.path, this.state.manifest);
301
+ await (0, localSources_1.applyLocalSourceManifestEntryToState)(this.state, args.path, args.entry, 'modal', this.writer(), this.remotePathResolver, this.manifestMaterializationOptions(args.runAs));
302
+ this.invalidateCloudBucketMounts();
303
+ }
304
+ async applyManifest(manifest, runAs) {
305
+ (0, shared_1.assertSandboxManifestMetadataSupported)('ModalSandboxClient', manifest, shared_1.MOUNT_MANIFEST_METADATA_SUPPORT);
306
+ assertModalLiveManifestMountsUnsupported(manifest, this.state.manifest);
307
+ await (0, localSources_1.applyLocalSourceManifestToState)(this.state, manifest, 'modal', this.writer(), this.remotePathResolver, this.manifestMaterializationOptions(runAs));
308
+ this.invalidateCloudBucketMounts();
309
+ }
310
+ async persistWorkspace() {
311
+ if (this.state.workspacePersistence === 'snapshot_filesystem') {
312
+ const archive = await this.persistSnapshotFilesystem();
313
+ if (archive) {
314
+ return archive;
315
+ }
316
+ }
317
+ else if (this.state.workspacePersistence === 'snapshot_directory') {
318
+ const archive = await this.persistSnapshotDirectory();
319
+ if (archive) {
320
+ return archive;
321
+ }
322
+ }
323
+ else {
324
+ (0, shared_1.assertTarWorkspacePersistence)('ModalSandboxClient', this.state.workspacePersistence);
325
+ }
326
+ return await (0, shared_1.persistRemoteWorkspaceTar)({
327
+ providerName: 'ModalSandboxClient',
328
+ manifest: this.state.manifest,
329
+ io: this.archiveIo(),
330
+ });
331
+ }
332
+ async hydrateWorkspace(data) {
333
+ const snapshotRef = (0, shared_1.decodeNativeSnapshotRef)(data);
334
+ if (snapshotRef?.provider === 'modal_snapshot_filesystem') {
335
+ this.assertExpectedSnapshotPersistence(snapshotRef.workspacePersistence ?? 'snapshot_filesystem', 'snapshot_filesystem');
336
+ await this.restoreSnapshotFilesystem(snapshotRef.snapshotId);
337
+ return;
338
+ }
339
+ if (snapshotRef?.provider === 'modal_snapshot_directory') {
340
+ this.assertExpectedSnapshotPersistence(snapshotRef.workspacePersistence ?? 'snapshot_directory', 'snapshot_directory');
341
+ await this.restoreSnapshotDirectory(snapshotRef.snapshotId);
342
+ return;
343
+ }
344
+ if (this.state.workspacePersistence === 'tar') {
345
+ (0, shared_1.assertTarWorkspacePersistence)('ModalSandboxClient', this.state.workspacePersistence);
346
+ }
347
+ await (0, shared_1.hydrateRemoteWorkspaceTar)({
348
+ providerName: 'ModalSandboxClient',
349
+ manifest: this.state.manifest,
350
+ io: this.archiveIo(),
351
+ data,
352
+ });
353
+ }
354
+ async persistSnapshotFilesystem() {
355
+ if (this.nativeSnapshotRequiresTarFallback()) {
356
+ return undefined;
357
+ }
358
+ if (!this.sandbox.snapshotFilesystem) {
359
+ return undefined;
360
+ }
361
+ const snapshotId = snapshotIdFromModalResult(await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'capture snapshot_filesystem', async () => await this.sandbox.snapshotFilesystem(this.state.snapshotFilesystemTimeoutMs), { sandboxId: this.state.sandboxId }));
362
+ if (!snapshotId) {
363
+ throw new sandbox_1.SandboxProviderError('Modal snapshot_filesystem persistence did not return a snapshot id.', {
364
+ provider: 'modal',
365
+ sandboxId: this.state.sandboxId,
366
+ });
367
+ }
368
+ return (0, shared_1.encodeNativeSnapshotRef)({
369
+ provider: 'modal_snapshot_filesystem',
370
+ snapshotId,
371
+ workspacePersistence: 'snapshot_filesystem',
372
+ });
373
+ }
374
+ async persistSnapshotDirectory() {
375
+ if (this.nativeSnapshotRequiresTarFallback()) {
376
+ return undefined;
377
+ }
378
+ if (!this.sandbox.snapshotDirectory) {
379
+ return undefined;
380
+ }
381
+ const snapshotId = snapshotIdFromModalResult(await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'capture snapshot_directory', async () => await withOptionalTimeoutMs(this.sandbox.snapshotDirectory(this.state.manifest.root), this.state.snapshotFilesystemTimeoutMs, 'Modal snapshot_directory persistence timed out.', async (snapshot) => {
382
+ await this.deleteSnapshotDirectoryImage(snapshot);
383
+ }), { sandboxId: this.state.sandboxId }));
384
+ if (!snapshotId) {
385
+ throw new sandbox_1.SandboxProviderError('Modal snapshot_directory persistence did not return a snapshot id.', {
386
+ provider: 'modal',
387
+ sandboxId: this.state.sandboxId,
388
+ });
389
+ }
390
+ return (0, shared_1.encodeNativeSnapshotRef)({
391
+ provider: 'modal_snapshot_directory',
392
+ snapshotId,
393
+ workspacePersistence: 'snapshot_directory',
394
+ });
395
+ }
396
+ nativeSnapshotRequiresTarFallback() {
397
+ return this.state.manifest.ephemeralPersistencePaths().size > 0;
398
+ }
399
+ assertExpectedSnapshotPersistence(actual, expected) {
400
+ if (actual !== expected || this.state.workspacePersistence !== expected) {
401
+ throw new sandbox_1.SandboxProviderError(`Modal snapshot reference uses ${actual}, but this session expects ${this.state.workspacePersistence}.`, {
402
+ provider: 'modal',
403
+ workspacePersistence: this.state.workspacePersistence,
404
+ snapshotWorkspacePersistence: actual,
405
+ });
406
+ }
407
+ }
408
+ async restoreSnapshotFilesystem(snapshotId) {
409
+ const previousSandbox = this.sandbox;
410
+ const previousSandboxId = this.state.sandboxId;
411
+ const previousSandboxOwned = this.ownsSandbox;
412
+ const sandbox = await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'restore snapshot_filesystem', async () => await withOptionalTimeoutMs((async () => {
413
+ const image = maybeSetModalSandboxCmd(await this.modalImageFromId(snapshotId), this.state.useSleepCmd);
414
+ return await this.modal.sandboxes.create(this.app, image, await this.sandboxCreateParams());
415
+ })(), this.state.snapshotFilesystemRestoreTimeoutMs, 'Modal snapshot_filesystem restore timed out.', terminateModalSandboxAfterTimeout), { sandboxId: previousSandboxId, snapshotId });
416
+ if (previousSandboxOwned) {
417
+ try {
418
+ await previousSandbox.terminate();
419
+ }
420
+ catch (error) {
421
+ let replacementTerminateCause;
422
+ try {
423
+ await sandbox.terminate();
424
+ }
425
+ catch (replacementTerminateError) {
426
+ replacementTerminateCause = errorMessage(replacementTerminateError);
427
+ }
428
+ throw new sandbox_1.SandboxProviderError('Modal snapshot_filesystem restore created a replacement sandbox, but terminating the previous sandbox failed.', {
429
+ provider: 'modal',
430
+ sandboxId: previousSandboxId,
431
+ replacementSandboxId: sandbox.sandboxId,
432
+ cause: errorMessage(error),
433
+ ...(replacementTerminateCause ? { replacementTerminateCause } : {}),
434
+ });
435
+ }
436
+ }
437
+ await this.resetActiveProcesses();
438
+ this.sandbox = sandbox;
439
+ this.ownsSandbox = true;
440
+ this.state.sandboxId = sandbox.sandboxId;
441
+ this.state.ownsSandbox = true;
442
+ delete this.state.exposedPorts;
443
+ }
444
+ async restoreSnapshotDirectory(snapshotId) {
445
+ if (!this.sandbox.mountImage) {
446
+ throw new sandbox_1.SandboxProviderError('Modal snapshot_directory restore requires sandbox mountImage(path, image) support.', {
447
+ provider: 'modal',
448
+ sandboxId: this.state.sandboxId,
449
+ snapshotId,
450
+ });
451
+ }
452
+ const sandbox = this.sandbox;
453
+ const ownsSandbox = this.ownsSandbox;
454
+ await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'restore snapshot_directory', async () => {
455
+ if (!ownsSandbox) {
456
+ const image = await withOptionalTimeoutMs(this.modalImageFromId(snapshotId), this.state.snapshotFilesystemRestoreTimeoutMs, 'Modal snapshot_directory restore timed out.');
457
+ await sandbox.mountImage(this.state.manifest.root, image);
458
+ return;
459
+ }
460
+ await withOptionalTimeoutMs((async () => {
461
+ const image = await this.modalImageFromId(snapshotId);
462
+ await sandbox.mountImage(this.state.manifest.root, image);
463
+ })(), this.state.snapshotFilesystemRestoreTimeoutMs, 'Modal snapshot_directory restore timed out.', async () => {
464
+ await terminateModalSandboxAfterTimeout(sandbox);
465
+ });
466
+ }, { sandboxId: this.state.sandboxId, snapshotId });
467
+ }
468
+ async deleteSnapshotDirectoryImage(snapshot) {
469
+ const snapshotId = snapshotIdFromModalResult(snapshot);
470
+ if (!snapshotId || !this.modal.images.delete) {
471
+ return;
472
+ }
473
+ await this.modal.images.delete(snapshotId).catch(() => { });
474
+ }
475
+ async modalImageFromId(snapshotId) {
476
+ return this.modal.images.fromId
477
+ ? await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'look up image', async () => await this.modal.images.fromId(snapshotId), { imageId: snapshotId })
478
+ : { imageId: snapshotId };
479
+ }
480
+ async sandboxCreateParams() {
481
+ const cloudBucketMounts = await this.resolveCloudBucketMounts();
482
+ return {
483
+ workdir: this.state.manifest.root,
484
+ env: this.state.environment,
485
+ ...(typeof this.state.timeoutMs === 'number'
486
+ ? { timeoutMs: this.state.timeoutMs }
487
+ : {}),
488
+ ...(typeof this.state.idleTimeoutMs === 'number'
489
+ ? { idleTimeoutMs: this.state.idleTimeoutMs }
490
+ : {}),
491
+ ...(this.state.gpu ? { gpu: this.state.gpu } : {}),
492
+ ...(this.state.configuredExposedPorts
493
+ ? { encryptedPorts: this.state.configuredExposedPorts }
494
+ : {}),
495
+ ...(cloudBucketMounts ? { cloudBucketMounts } : {}),
496
+ };
497
+ }
498
+ async resolveCloudBucketMounts() {
499
+ if (this.cloudBucketMountsResolved) {
500
+ return this.cloudBucketMounts;
501
+ }
502
+ this.cloudBucketMounts = await this.cloudBucketMountsProvider?.();
503
+ this.cloudBucketMountsResolved = true;
504
+ return this.cloudBucketMounts;
505
+ }
506
+ async close() {
507
+ if (this.closed) {
508
+ return;
509
+ }
510
+ if (this.closePromise) {
511
+ await this.closePromise;
512
+ return;
513
+ }
514
+ this.closePromise = this.closeOnce();
515
+ try {
516
+ await this.closePromise;
517
+ this.closed = true;
518
+ }
519
+ finally {
520
+ if (!this.closed) {
521
+ this.closePromise = undefined;
522
+ }
523
+ }
524
+ }
525
+ async closeOnce() {
526
+ const processes = [...this.activeProcesses.values()];
527
+ const activeProcesses = processes.filter((activeProcess) => !activeProcess.done);
528
+ await Promise.all(activeProcesses.map(async (activeProcess) => {
529
+ if (activeProcess.process.stdin?.close) {
530
+ await activeProcess.process.stdin.close().catch(() => { });
531
+ }
532
+ }));
533
+ if (this.ownsSandbox) {
534
+ await this.sandbox.terminate();
535
+ await Promise.all(activeProcesses.map(async (activeProcess) => {
536
+ await activeProcess.donePromise.catch(() => { });
537
+ }));
538
+ }
539
+ else {
540
+ await Promise.all(activeProcesses.map(async (activeProcess) => {
541
+ await activeProcess.stopOutputPumps?.().catch(() => { });
542
+ }));
543
+ }
544
+ for (const process of processes) {
545
+ clearActiveProcessCleanup(process);
546
+ }
547
+ this.activeProcesses.clear();
548
+ }
549
+ async resetActiveProcesses() {
550
+ const processes = [...this.activeProcesses.values()];
551
+ this.activeProcesses.clear();
552
+ await Promise.all(processes.map(async (activeProcess) => {
553
+ clearActiveProcessCleanup(activeProcess);
554
+ if (!activeProcess.done && activeProcess.process.stdin?.close) {
555
+ await activeProcess.process.stdin.close().catch(() => { });
556
+ }
557
+ if (!activeProcess.done) {
558
+ await activeProcess.stopOutputPumps?.().catch(() => { });
559
+ }
560
+ }));
561
+ }
562
+ manifestMaterializationOptions(runAs) {
563
+ return (0, shared_1.manifestMaterializationOptionsWithRunAs)({
564
+ providerName: 'ModalSandboxClient',
565
+ providerId: 'modal',
566
+ runAs,
567
+ runCommand: this.runAsCommandRunner.bind(this),
568
+ options: {
569
+ materializeMount: async (absolutePath, _entry) => {
570
+ throwModalLiveMountsUnsupported([absolutePath]);
571
+ },
572
+ concurrencyLimits: this.concurrencyLimits,
573
+ },
574
+ support: shared_1.MOUNT_MANIFEST_METADATA_SUPPORT,
575
+ });
576
+ }
577
+ invalidateCloudBucketMounts() {
578
+ if (!this.cloudBucketMountsProvider) {
579
+ return;
580
+ }
581
+ this.cloudBucketMounts = undefined;
582
+ this.cloudBucketMountsResolved = false;
583
+ }
584
+ async shutdown() {
585
+ await this.close();
586
+ }
587
+ async delete() {
588
+ await this.close();
589
+ }
590
+ registerActiveProcess(sessionId, activeProcess) {
591
+ this.activeProcesses.set(sessionId, activeProcess);
592
+ activeProcess.donePromise
593
+ .finally(() => {
594
+ activeProcess.cleanupTimer = setTimeout(() => {
595
+ if (this.activeProcesses.get(sessionId) === activeProcess) {
596
+ this.activeProcesses.delete(sessionId);
597
+ }
598
+ }, COMPLETED_ACTIVE_PROCESS_RETENTION_MS);
599
+ unrefTimer(activeProcess.cleanupTimer);
600
+ })
601
+ .catch(() => { });
602
+ }
603
+ deleteActiveProcess(sessionId, activeProcess) {
604
+ clearActiveProcessCleanup(activeProcess);
605
+ if (this.activeProcesses.get(sessionId) === activeProcess) {
606
+ this.activeProcesses.delete(sessionId);
607
+ }
608
+ }
609
+ resolveAbsolutePath(path) {
610
+ return (0, shared_1.resolveSandboxAbsolutePath)(this.state.manifest.root, path);
611
+ }
612
+ async resolveRemotePath(path, options = {}) {
613
+ return await (0, shared_1.validateRemoteSandboxPathForManifest)({
614
+ manifest: this.state.manifest,
615
+ path,
616
+ options,
617
+ runCommand: async (command) => {
618
+ const process = await this.sandbox.exec(['/bin/sh', '-c', command], {
619
+ mode: 'text',
620
+ workdir: this.state.manifest.root,
621
+ stdout: 'pipe',
622
+ stderr: 'pipe',
623
+ });
624
+ const [stdout, stderr, exitCode] = await Promise.all([
625
+ readStreamText(process.stdout),
626
+ readStreamText(process.stderr),
627
+ process.wait(),
628
+ ]);
629
+ return { status: exitCode, stdout, stderr };
630
+ },
631
+ });
632
+ }
633
+ resolveWorkdir(path) {
634
+ return (0, shared_1.resolveSandboxWorkdir)(this.state.manifest.root, path);
635
+ }
636
+ async ensureDirectory(path) {
637
+ await this.runDirectCommand(['mkdir', '-p', '--', path]);
638
+ }
639
+ async writeSandboxFile(path, content) {
640
+ await this.ensureDirectory(node_path_1.posix.dirname(path));
641
+ const handle = await this.sandbox.open(path, 'w');
642
+ try {
643
+ await handle.write(typeof content === 'string'
644
+ ? new TextEncoder().encode(content)
645
+ : content);
646
+ if (handle.flush) {
647
+ await handle.flush();
648
+ }
649
+ }
650
+ finally {
651
+ await handle.close();
652
+ }
653
+ }
654
+ async readSandboxFile(path) {
655
+ const handle = await this.sandbox.open(path, 'r');
656
+ try {
657
+ return Uint8Array.from(await handle.read());
658
+ }
659
+ finally {
660
+ await handle.close();
661
+ }
662
+ }
663
+ async removeSandboxPath(path) {
664
+ await this.runDirectCommand(['rm', '-f', '--', path]);
665
+ }
666
+ async runAsCommandRunner(command, options = {}) {
667
+ const process = await this.sandbox.exec(['/bin/sh', '-lc', (0, shared_1.sandboxUserShellCommand)(command, options.runAs)], {
668
+ mode: 'text',
669
+ workdir: this.state.manifest.root,
670
+ env: this.state.environment,
671
+ stdout: 'pipe',
672
+ stderr: 'pipe',
673
+ });
674
+ const [stdout, stderr, exitCode] = await Promise.all([
675
+ readStreamText(process.stdout),
676
+ readStreamText(process.stderr),
677
+ process.wait(),
678
+ ]);
679
+ return {
680
+ status: exitCode,
681
+ stdout,
682
+ stderr,
683
+ };
684
+ }
685
+ writer() {
686
+ return {
687
+ mkdir: async (path) => {
688
+ await this.ensureDirectory(path);
689
+ },
690
+ writeFile: async (path, content) => {
691
+ await this.writeSandboxFile(path, content);
692
+ },
693
+ };
694
+ }
695
+ archiveIo() {
696
+ return {
697
+ runCommand: async (command) => {
698
+ const process = await this.sandbox.exec(['/bin/sh', '-c', command], {
699
+ mode: 'text',
700
+ workdir: this.state.manifest.root,
701
+ env: this.state.environment,
702
+ stdout: 'pipe',
703
+ stderr: 'pipe',
704
+ });
705
+ const [stdout, stderr, exitCode] = await Promise.all([
706
+ readStreamText(process.stdout),
707
+ readStreamText(process.stderr),
708
+ process.wait(),
709
+ ]);
710
+ return { status: exitCode, stdout, stderr };
711
+ },
712
+ readFile: async (path) => await this.readSandboxFile(path),
713
+ writeFile: async (path, content) => {
714
+ await this.writeSandboxFile(path, content);
715
+ },
716
+ mkdir: async (path) => {
717
+ await this.ensureDirectory(path);
718
+ },
719
+ };
720
+ }
721
+ async runDirectCommand(command) {
722
+ const process = await this.sandbox.exec(command, {
723
+ mode: 'text',
724
+ workdir: this.state.manifest.root,
725
+ stdout: 'ignore',
726
+ stderr: 'pipe',
727
+ });
728
+ const stderr = await readStreamText(process.stderr);
729
+ const exitCode = await process.wait();
730
+ if (exitCode !== 0) {
731
+ throw new agents_core_1.UserError(`Modal sandbox command failed (${command.join(' ')}): ${stderr || `exit ${exitCode}`}`);
732
+ }
733
+ }
734
+ }
735
+ exports.ModalSandboxSession = ModalSandboxSession;
736
+ /**
737
+ * @see {@link https://modal.com/docs/guide/sandboxes | Sandboxes guide}.
738
+ * @see {@link https://modal.com/docs/guide/sdk-javascript-go | JavaScript and Go SDK guide}.
739
+ * @see {@link https://modal.com/docs/reference/modal.Sandbox | Sandbox reference}.
740
+ */
741
+ class ModalSandboxClient {
742
+ backendId = 'modal';
743
+ options;
744
+ constructor(options = {}) {
745
+ this.options = options;
746
+ }
747
+ async create(args, manifestOptions) {
748
+ const createArgs = (0, sandbox_1.normalizeSandboxClientCreateArgs)(args, manifestOptions);
749
+ (0, shared_1.assertCoreSnapshotUnsupported)('ModalSandboxClient', createArgs.snapshot);
750
+ const manifest = createArgs.manifest;
751
+ const resolvedOptions = resolveOptions(this.options, createArgs.options);
752
+ validateOptions(resolvedOptions);
753
+ const useSleepCmd = resolvedOptions.useSleepCmd ?? true;
754
+ (0, shared_1.assertSandboxManifestMetadataSupported)('ModalSandboxClient', manifest, shared_1.MOUNT_MANIFEST_METADATA_SUPPORT);
755
+ assertModalManifestMountsSupported(manifest, resolvedOptions.nativeCloudBucketSecretName);
756
+ assertModalReusableSandboxMountsSupported(manifest, resolvedOptions.sandbox);
757
+ return await (0, shared_1.withSandboxSpan)('sandbox.start', {
758
+ backend_id: this.backendId,
759
+ }, async () => {
760
+ const modalModule = await loadModalModule();
761
+ const modal = createModalClientFromModule(modalModule, resolvedOptions);
762
+ const workspacePersistence = resolvedOptions.workspacePersistence ?? 'tar';
763
+ const app = shouldResolveModalApp({
764
+ ownsSandbox: !resolvedOptions.sandbox,
765
+ workspacePersistence,
766
+ })
767
+ ? await lookupModalApp({
768
+ modal,
769
+ appName: resolvedOptions.appName,
770
+ environment: resolvedOptions.environment,
771
+ createIfMissing: !resolvedOptions.sandbox,
772
+ })
773
+ : {};
774
+ const environment = await (0, shared_1.materializeEnvironment)(manifest, resolvedOptions.env);
775
+ const imageState = modalImageStateFromOptions(resolvedOptions);
776
+ let cloudBucketMounts;
777
+ let sandbox;
778
+ if (resolvedOptions.sandbox) {
779
+ sandbox = await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'resolve sandbox selector', async () => await resolveSelectedModalSandbox(modal, resolvedOptions.sandbox));
780
+ }
781
+ else {
782
+ const { image, imageId, imageTag } = await resolveModalImage(modal, resolvedOptions);
783
+ imageState.imageId = imageId;
784
+ imageState.imageTag = imageTag;
785
+ const sandboxImage = maybeSetModalSandboxCmd(image, useSleepCmd);
786
+ cloudBucketMounts = await modalCloudBucketMountsForManifest({
787
+ modalModule,
788
+ manifest,
789
+ defaultSecretName: resolvedOptions.nativeCloudBucketSecretName,
790
+ });
791
+ const createParams = {
792
+ workdir: manifest.root,
793
+ env: environment,
794
+ ...(typeof resolvedOptions.timeoutMs === 'number'
795
+ ? { timeoutMs: resolvedOptions.timeoutMs }
796
+ : {}),
797
+ ...(typeof resolvedOptions.idleTimeoutMs === 'number'
798
+ ? { idleTimeoutMs: resolvedOptions.idleTimeoutMs }
799
+ : {}),
800
+ ...(resolvedOptions.gpu ? { gpu: resolvedOptions.gpu } : {}),
801
+ ...(resolvedOptions.exposedPorts
802
+ ? { encryptedPorts: resolvedOptions.exposedPorts }
803
+ : {}),
804
+ ...(cloudBucketMounts ? { cloudBucketMounts } : {}),
805
+ };
806
+ sandbox = await withOptionalTimeout((0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'create sandbox', async () => await modal.sandboxes.create(app, sandboxImage, createParams), { appName: resolvedOptions.appName }), resolvedOptions.sandboxCreateTimeoutS, 'Modal sandbox creation timed out.', terminateModalSandboxAfterTimeout);
807
+ }
808
+ const sessionState = {
809
+ manifest,
810
+ sandboxId: sandbox.sandboxId,
811
+ ownsSandbox: !resolvedOptions.sandbox,
812
+ appName: resolvedOptions.appName,
813
+ imageId: imageState.imageId,
814
+ imageTag: imageState.imageTag,
815
+ sandboxCreateTimeoutS: resolvedOptions.sandboxCreateTimeoutS,
816
+ workspacePersistence,
817
+ snapshotFilesystemTimeoutMs: resolvedOptions.snapshotFilesystemTimeoutMs,
818
+ snapshotFilesystemRestoreTimeoutMs: resolvedOptions.snapshotFilesystemRestoreTimeoutMs,
819
+ environment,
820
+ timeoutMs: resolvedOptions.timeoutMs,
821
+ idleTimeoutMs: resolvedOptions.idleTimeoutMs,
822
+ gpu: resolvedOptions.gpu,
823
+ configuredExposedPorts: resolvedOptions.exposedPorts,
824
+ modalEnvironment: resolvedOptions.environment,
825
+ endpoint: resolvedOptions.endpoint,
826
+ imageBuilderVersion: resolvedOptions.imageBuilderVersion,
827
+ nativeCloudBucketSecretName: resolvedOptions.nativeCloudBucketSecretName,
828
+ useSleepCmd,
829
+ };
830
+ const session = new ModalSandboxSession({
831
+ modal,
832
+ app,
833
+ sandbox,
834
+ ownsSandbox: !resolvedOptions.sandbox,
835
+ state: sessionState,
836
+ cloudBucketMounts,
837
+ concurrencyLimits: createArgs.concurrencyLimits,
838
+ cloudBucketMountsProvider: async () => await modalCloudBucketMountsForManifest({
839
+ modalModule,
840
+ manifest: sessionState.manifest,
841
+ defaultSecretName: resolvedOptions.nativeCloudBucketSecretName,
842
+ }),
843
+ });
844
+ try {
845
+ await session.applyManifest((0, shared_1.cloneManifestWithoutMountEntries)(manifest));
846
+ }
847
+ catch (error) {
848
+ if (resolvedOptions.sandbox) {
849
+ throw error;
850
+ }
851
+ await (0, shared_1.closeRemoteSessionOnManifestError)('Modal', session, error);
852
+ }
853
+ return session;
854
+ });
855
+ }
856
+ async serializeSessionState(state) {
857
+ return (0, shared_1.serializeRemoteSandboxSessionState)(state);
858
+ }
859
+ canPersistOwnedSessionState() {
860
+ return false;
861
+ }
862
+ async deserializeSessionState(state) {
863
+ const baseState = (0, shared_1.deserializeRemoteSandboxSessionStateValues)(state, this.options.env);
864
+ return {
865
+ ...state,
866
+ ...baseState,
867
+ ownsSandbox: state.ownsSandbox === false ? false : true,
868
+ appName: (0, shared_1.readString)(state, 'appName'),
869
+ imageId: (0, shared_1.readOptionalString)(state, 'imageId'),
870
+ imageTag: (0, shared_1.readString)(state, 'imageTag', DEFAULT_MODAL_IMAGE_TAG),
871
+ workspacePersistence: state.workspacePersistence ??
872
+ 'tar',
873
+ snapshotFilesystemTimeoutMs: (0, shared_1.readOptionalNumber)(state, 'snapshotFilesystemTimeoutMs'),
874
+ snapshotFilesystemRestoreTimeoutMs: (0, shared_1.readOptionalNumber)(state, 'snapshotFilesystemRestoreTimeoutMs'),
875
+ timeoutMs: (0, shared_1.readOptionalNumber)(state, 'timeoutMs'),
876
+ idleTimeoutMs: (0, shared_1.readOptionalNumber)(state, 'idleTimeoutMs'),
877
+ gpu: (0, shared_1.readOptionalString)(state, 'gpu'),
878
+ configuredExposedPorts: (0, shared_1.readOptionalNumberArray)(state.configuredExposedPorts),
879
+ modalEnvironment: (0, shared_1.readOptionalString)(state, 'modalEnvironment'),
880
+ endpoint: (0, shared_1.readOptionalString)(state, 'endpoint'),
881
+ nativeCloudBucketSecretName: (0, shared_1.readOptionalString)(state, 'nativeCloudBucketSecretName'),
882
+ imageBuilderVersion: (0, shared_1.readOptionalString)(state, 'imageBuilderVersion'),
883
+ useSleepCmd: typeof state.useSleepCmd === 'boolean' ? state.useSleepCmd : true,
884
+ };
885
+ }
886
+ async resume(state) {
887
+ if (!state.sandboxId) {
888
+ throw new agents_core_1.UserError('Modal sandbox resume requires a persisted sandboxId.');
889
+ }
890
+ const sandboxId = state.sandboxId;
891
+ const modalModule = await loadModalModule();
892
+ const modal = createModalClientFromModule(modalModule, {
893
+ ...this.options,
894
+ appName: state.appName,
895
+ image: state.imageId
896
+ ? ModalImageSelector.fromId(state.imageId)
897
+ : undefined,
898
+ imageTag: state.imageTag,
899
+ sandboxCreateTimeoutS: state.sandboxCreateTimeoutS,
900
+ workspacePersistence: state.workspacePersistence,
901
+ snapshotFilesystemTimeoutMs: state.snapshotFilesystemTimeoutMs,
902
+ snapshotFilesystemRestoreTimeoutMs: state.snapshotFilesystemRestoreTimeoutMs,
903
+ timeoutMs: state.timeoutMs,
904
+ idleTimeoutMs: state.idleTimeoutMs,
905
+ gpu: state.gpu,
906
+ exposedPorts: state.configuredExposedPorts,
907
+ environment: state.modalEnvironment ?? this.options.environment,
908
+ endpoint: state.endpoint ?? this.options.endpoint,
909
+ imageBuilderVersion: state.imageBuilderVersion ?? this.options.imageBuilderVersion,
910
+ useSleepCmd: state.useSleepCmd,
911
+ });
912
+ const sandbox = await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'resume sandbox', async () => await modal.sandboxes.fromId(sandboxId), { sandboxId });
913
+ const ownsSandbox = state.ownsSandbox !== false;
914
+ const app = shouldResolveModalApp({
915
+ ownsSandbox,
916
+ workspacePersistence: state.workspacePersistence,
917
+ })
918
+ ? await lookupModalApp({
919
+ modal,
920
+ appName: state.appName,
921
+ environment: state.modalEnvironment,
922
+ createIfMissing: ownsSandbox,
923
+ })
924
+ : {};
925
+ const exitCode = await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'poll sandbox', async () => await sandbox.poll(), { sandboxId });
926
+ if (exitCode !== null) {
927
+ throw new agents_core_1.UserError(`Modal sandbox ${state.sandboxId} is no longer running.`);
928
+ }
929
+ return new ModalSandboxSession({
930
+ state,
931
+ modal,
932
+ app,
933
+ sandbox,
934
+ ownsSandbox,
935
+ cloudBucketMountsProvider: async () => await modalCloudBucketMountsForManifest({
936
+ modalModule,
937
+ manifest: state.manifest,
938
+ defaultSecretName: state.nativeCloudBucketSecretName ??
939
+ this.options.nativeCloudBucketSecretName,
940
+ }),
941
+ });
942
+ }
943
+ }
944
+ exports.ModalSandboxClient = ModalSandboxClient;
945
+ async function loadModalModule() {
946
+ try {
947
+ return adaptModalModule(await Promise.resolve().then(() => __importStar(require('modal'))));
948
+ }
949
+ catch (error) {
950
+ throw new agents_core_1.UserError(`Modal sandbox support requires the optional \`modal\` package. Install it before using Modal-backed sandbox examples. ${error.message}`);
951
+ }
952
+ }
953
+ async function lookupModalApp(args) {
954
+ return await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'look up app', async () => await args.modal.apps.fromName(args.appName, {
955
+ createIfMissing: args.createIfMissing,
956
+ ...(args.environment ? { environment: args.environment } : {}),
957
+ }), { appName: args.appName });
958
+ }
959
+ function shouldResolveModalApp(args) {
960
+ return (args.ownsSandbox || args.workspacePersistence === 'snapshot_filesystem');
961
+ }
962
+ function adaptModalModule(modal) {
963
+ const secretApi = modal.Secret;
964
+ const usesClassSecretApi = typeof secretApi === 'function';
965
+ const cloudBucketMountClass = typeof modal.CloudBucketMount === 'function'
966
+ ? modal.CloudBucketMount
967
+ : undefined;
968
+ return {
969
+ ModalClient: modal.ModalClient,
970
+ CloudBucketMount: usesClassSecretApi && cloudBucketMountClass
971
+ ? adaptClassModalCloudBucketMount(cloudBucketMountClass)
972
+ : modal.CloudBucketMount,
973
+ Secret: {
974
+ fromName: async (name, params) => {
975
+ if (usesClassSecretApi) {
976
+ return await modal.Secret.fromName(name, {
977
+ ...(params?.environment ? { environment: params.environment } : {}),
978
+ });
979
+ }
980
+ if (isModalSecretModule(secretApi)) {
981
+ return await secretApi.fromName(name, params);
982
+ }
983
+ throw new Error('Missing Secret export from modal.');
984
+ },
985
+ fromObject: async (entries, params) => {
986
+ if (usesClassSecretApi) {
987
+ const environment = (0, shared_1.readOptionalString)(params, 'environment');
988
+ return await modal.Secret.fromObject(entries, {
989
+ ...(environment ? { environment } : {}),
990
+ });
991
+ }
992
+ if (isModalSecretModule(secretApi)) {
993
+ return await secretApi.fromObject(entries, params);
994
+ }
995
+ throw new Error('Missing Secret export from modal.');
996
+ },
997
+ },
998
+ };
999
+ }
1000
+ function adaptClassModalCloudBucketMount(CloudBucketMount) {
1001
+ return class {
1002
+ mount;
1003
+ constructor(bucketName, params) {
1004
+ const bucketEndpointUrl = (0, shared_1.readOptionalString)(params, 'bucketEndpointUrl');
1005
+ const keyPrefix = (0, shared_1.readOptionalString)(params, 'keyPrefix');
1006
+ this.mount = new CloudBucketMount(bucketName, {
1007
+ ...(bucketEndpointUrl ? { bucketEndpointUrl } : {}),
1008
+ ...(keyPrefix ? { keyPrefix } : {}),
1009
+ ...(typeof params?.readOnly === 'boolean'
1010
+ ? { readOnly: params.readOnly }
1011
+ : {}),
1012
+ ...(params?.secret ? { secret: params.secret } : {}),
1013
+ });
1014
+ }
1015
+ toProto(mountPath) {
1016
+ if (!this.mount.toProto) {
1017
+ throw new sandbox_1.SandboxProviderError('Modal CloudBucketMount is missing toProto support.', {
1018
+ provider: 'modal',
1019
+ });
1020
+ }
1021
+ return this.mount.toProto(mountPath);
1022
+ }
1023
+ };
1024
+ }
1025
+ function isModalSecretModule(value) {
1026
+ return (typeof value === 'object' &&
1027
+ value !== null &&
1028
+ 'fromName' in value &&
1029
+ 'fromObject' in value);
1030
+ }
1031
+ function createModalClientFromModule(modalModule, options) {
1032
+ const { ModalClient } = modalModule;
1033
+ const client = new ModalClient({
1034
+ ...(options.tokenId ? { tokenId: options.tokenId } : {}),
1035
+ ...(options.tokenSecret ? { tokenSecret: options.tokenSecret } : {}),
1036
+ ...(options.environment ? { environment: options.environment } : {}),
1037
+ ...(options.endpoint ? { endpoint: options.endpoint } : {}),
1038
+ });
1039
+ if (options.imageBuilderVersion !== undefined && client.imageBuilderVersion) {
1040
+ const originalImageBuilderVersion = client.imageBuilderVersion.bind(client);
1041
+ client.imageBuilderVersion = (version) => originalImageBuilderVersion(version ?? options.imageBuilderVersion);
1042
+ }
1043
+ return client;
1044
+ }
1045
+ async function resolveModalImage(modal, options) {
1046
+ const selector = modalImageSelectorFromOptions(options);
1047
+ if (selector.kind === 'image') {
1048
+ const image = selector.value;
1049
+ return {
1050
+ image,
1051
+ imageId: modalImageId(image),
1052
+ imageTag: options.imageTag ?? DEFAULT_MODAL_IMAGE_TAG,
1053
+ };
1054
+ }
1055
+ if (selector.kind === 'id') {
1056
+ const imageId = selector.value;
1057
+ const image = modal.images.fromId
1058
+ ? await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'look up image', async () => await modal.images.fromId(imageId), { imageId })
1059
+ : { imageId };
1060
+ return {
1061
+ image,
1062
+ imageId,
1063
+ imageTag: options.imageTag ?? DEFAULT_MODAL_IMAGE_TAG,
1064
+ };
1065
+ }
1066
+ const imageTag = selector.value;
1067
+ const image = await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'resolve image', async () => modal.images.fromRegistry(imageTag), { imageTag });
1068
+ return {
1069
+ image,
1070
+ imageId: modalImageId(image),
1071
+ imageTag,
1072
+ };
1073
+ }
1074
+ function modalImageStateFromOptions(options) {
1075
+ const selector = modalImageSelectorFromOptions(options);
1076
+ if (selector.kind === 'image') {
1077
+ return {
1078
+ imageId: modalImageId(selector.value),
1079
+ imageTag: options.imageTag ?? DEFAULT_MODAL_IMAGE_TAG,
1080
+ };
1081
+ }
1082
+ if (selector.kind === 'id') {
1083
+ return {
1084
+ imageId: selector.value,
1085
+ imageTag: options.imageTag ?? DEFAULT_MODAL_IMAGE_TAG,
1086
+ };
1087
+ }
1088
+ return {
1089
+ imageTag: selector.value,
1090
+ };
1091
+ }
1092
+ function modalImageSelectorFromOptions(options) {
1093
+ return (options.image ??
1094
+ ModalImageSelector.fromTag(options.imageTag ?? DEFAULT_MODAL_IMAGE_TAG));
1095
+ }
1096
+ async function resolveSelectedModalSandbox(modal, selector) {
1097
+ if (selector.kind === 'sandbox') {
1098
+ return selector.value;
1099
+ }
1100
+ return await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'resolve sandbox', async () => await modal.sandboxes.fromId(selector.value), { sandboxId: selector.value });
1101
+ }
1102
+ function modalImageId(image) {
1103
+ return image.imageId ?? image.objectId;
1104
+ }
1105
+ function maybeSetModalSandboxCmd(image, useSleepCmd) {
1106
+ if (!useSleepCmd || typeof image.cmd !== 'function') {
1107
+ return image;
1108
+ }
1109
+ return image.cmd(['sleep', 'infinity']);
1110
+ }
1111
+ function errorMessage(error) {
1112
+ return error instanceof Error ? error.message : String(error);
1113
+ }
1114
+ async function modalCloudBucketMountsForManifest(args) {
1115
+ const mountEntries = args.manifest
1116
+ .mountTargets()
1117
+ .filter(({ entry }) => (0, mounts_1.isModalCloudBucketMountEntry)(entry));
1118
+ if (mountEntries.length === 0) {
1119
+ return undefined;
1120
+ }
1121
+ if (!args.modalModule.CloudBucketMount) {
1122
+ throw new sandbox_1.SandboxProviderError('Modal cloud bucket mounts require modal.CloudBucketMount support.', {
1123
+ provider: 'modal',
1124
+ });
1125
+ }
1126
+ const cloudBucketMounts = {};
1127
+ for (const { entry, mountPath } of mountEntries) {
1128
+ const config = (0, mounts_1.buildModalCloudBucketMountConfig)(entry, {
1129
+ secretName: (0, shared_1.readOptionalString)(entry.mountStrategy, 'secretName') ??
1130
+ args.defaultSecretName,
1131
+ secretEnvironmentName: (0, shared_1.readOptionalString)(entry.mountStrategy, 'secretEnvironmentName'),
1132
+ });
1133
+ cloudBucketMounts[mountPath] = new args.modalModule.CloudBucketMount(config.bucketName, {
1134
+ ...(config.bucketEndpointUrl
1135
+ ? { bucketEndpointUrl: config.bucketEndpointUrl }
1136
+ : {}),
1137
+ ...(config.keyPrefix ? { keyPrefix: config.keyPrefix } : {}),
1138
+ ...(typeof config.readOnly === 'boolean'
1139
+ ? { readOnly: config.readOnly }
1140
+ : {}),
1141
+ ...(await modalCloudBucketMountSecret(args.modalModule, config)),
1142
+ });
1143
+ }
1144
+ return cloudBucketMounts;
1145
+ }
1146
+ async function modalCloudBucketMountSecret(modalModule, config) {
1147
+ if (!config.secretName && !config.credentials) {
1148
+ return {};
1149
+ }
1150
+ const Secret = modalModule.Secret;
1151
+ if (!Secret) {
1152
+ throw new sandbox_1.SandboxProviderError('Modal cloud bucket mounts require modal.Secret support when credentials are configured.', {
1153
+ provider: 'modal',
1154
+ bucketName: config.bucketName,
1155
+ });
1156
+ }
1157
+ const secretName = config.secretName;
1158
+ if (secretName) {
1159
+ return {
1160
+ secret: await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'resolve cloud bucket secret', async () => await Secret.fromName(secretName, {
1161
+ ...(config.secretEnvironmentName
1162
+ ? { environment: config.secretEnvironmentName }
1163
+ : {}),
1164
+ }), {
1165
+ bucketName: config.bucketName,
1166
+ secretName,
1167
+ }),
1168
+ };
1169
+ }
1170
+ return {
1171
+ secret: await (0, shared_1.withProviderError)('ModalSandboxClient', 'modal', 'create cloud bucket secret', async () => await Secret.fromObject(config.credentials ?? {}), { bucketName: config.bucketName }),
1172
+ };
1173
+ }
1174
+ function assertModalManifestMountsSupported(manifest, defaultSecretName) {
1175
+ for (const { entry, mountPath } of manifest.mountTargets()) {
1176
+ assertModalMountEntrySupported(entry, mountPath, defaultSecretName);
1177
+ }
1178
+ }
1179
+ function assertModalMountEntrySupported(entry, mountPath, defaultSecretName) {
1180
+ if (!(0, mounts_1.isModalCloudBucketMountEntry)(entry)) {
1181
+ throw new sandbox_1.SandboxUnsupportedFeatureError('ModalSandboxClient only supports ModalCloudBucketMountStrategy mount entries.', {
1182
+ provider: 'modal',
1183
+ feature: 'entry.mountStrategy',
1184
+ path: mountPath,
1185
+ mountType: entry.type,
1186
+ strategyType: entry.mountStrategy?.type,
1187
+ });
1188
+ }
1189
+ (0, mounts_1.buildModalCloudBucketMountConfig)(entry, {
1190
+ secretName: (0, shared_1.readOptionalString)(entry.mountStrategy, 'secretName') ??
1191
+ defaultSecretName,
1192
+ secretEnvironmentName: (0, shared_1.readOptionalString)(entry.mountStrategy, 'secretEnvironmentName'),
1193
+ });
1194
+ }
1195
+ function assertModalLiveManifestMountsUnsupported(manifest, currentManifest) {
1196
+ const mountPaths = uniqueStrings([
1197
+ ...manifest.mountTargets().map(({ mountPath }) => mountPath),
1198
+ ...collectExistingModalMountPathsOverlappedByPaths(currentManifest, collectModalManifestEntryPaths(manifest)),
1199
+ ]);
1200
+ if (mountPaths.length === 0) {
1201
+ return;
1202
+ }
1203
+ throwModalLiveMountsUnsupported(mountPaths);
1204
+ }
1205
+ function assertModalLiveEntryMountsUnsupported(entry, path, currentManifest) {
1206
+ const mountPaths = uniqueStrings([
1207
+ ...collectModalEntryMountPaths(entry, path),
1208
+ ...collectExistingModalMountPathsOverlappedByPaths(currentManifest, collectModalEntryPaths(entry, path, currentManifest.root)),
1209
+ ]);
1210
+ if (mountPaths.length === 0) {
1211
+ return;
1212
+ }
1213
+ throwModalLiveMountsUnsupported(mountPaths);
1214
+ }
1215
+ function collectModalManifestEntryPaths(manifest) {
1216
+ return [...manifest.iterEntries()].map(({ logicalPath }) => logicalPath);
1217
+ }
1218
+ function collectModalEntryPaths(entry, path, root) {
1219
+ const logicalPath = (0, shared_1.resolveSandboxRelativePath)(root, path);
1220
+ return collectModalEntryPathsFromRelative(entry, logicalPath);
1221
+ }
1222
+ function collectModalEntryPathsFromRelative(entry, path) {
1223
+ if (entry.type !== 'dir' || !entry.children) {
1224
+ return [normalizeManifestRelativePath(path)];
1225
+ }
1226
+ const paths = [normalizeManifestRelativePath(path)];
1227
+ for (const [childPath, childEntry] of Object.entries(entry.children)) {
1228
+ paths.push(...collectModalEntryPathsFromRelative(childEntry, node_path_1.posix.join(path, childPath)));
1229
+ }
1230
+ return paths;
1231
+ }
1232
+ function collectModalEntryMountPaths(entry, path) {
1233
+ if ((0, sandbox_1.isMount)(entry)) {
1234
+ return [entry.mountPath ?? path];
1235
+ }
1236
+ if (entry.type !== 'dir' || !entry.children) {
1237
+ return [];
1238
+ }
1239
+ const mountPaths = [];
1240
+ for (const [childPath, childEntry] of Object.entries(entry.children)) {
1241
+ mountPaths.push(...collectModalEntryMountPaths(childEntry, node_path_1.posix.join(path, childPath)));
1242
+ }
1243
+ return mountPaths;
1244
+ }
1245
+ function collectExistingModalMountPathsOverlappedByPaths(currentManifest, paths) {
1246
+ if (paths.length === 0) {
1247
+ return [];
1248
+ }
1249
+ const normalizedPaths = paths.map(normalizeManifestRelativePath);
1250
+ const mountPaths = [];
1251
+ for (const target of currentManifest.mountTargets()) {
1252
+ const mountedPaths = [
1253
+ target.logicalPath,
1254
+ existingMountPathRelativeToManifestRoot(currentManifest, target.mountPath),
1255
+ ].filter((path) => path !== null);
1256
+ if (mountedPaths.some((mountedPath) => normalizedPaths.some((path) => manifestRelativePathsOverlap(path, mountedPath)))) {
1257
+ mountPaths.push(target.mountPath);
1258
+ }
1259
+ }
1260
+ return mountPaths;
1261
+ }
1262
+ function existingMountPathRelativeToManifestRoot(manifest, mountPath) {
1263
+ return (0, internal_1.relativePosixPathWithinRoot)((0, internal_1.normalizePosixPath)(manifest.root), (0, internal_1.normalizePosixPath)(mountPath));
1264
+ }
1265
+ function manifestRelativePathsOverlap(left, right) {
1266
+ const normalizedLeft = normalizeManifestRelativePath(left);
1267
+ const normalizedRight = normalizeManifestRelativePath(right);
1268
+ if (normalizedLeft === '' || normalizedRight === '') {
1269
+ return true;
1270
+ }
1271
+ return (normalizedLeft === normalizedRight ||
1272
+ normalizedLeft.startsWith(`${normalizedRight}/`) ||
1273
+ normalizedRight.startsWith(`${normalizedLeft}/`));
1274
+ }
1275
+ function normalizeManifestRelativePath(path) {
1276
+ const normalized = (0, internal_1.normalizePosixPath)(path);
1277
+ return normalized === '.' ? '' : normalized;
1278
+ }
1279
+ function uniqueStrings(values) {
1280
+ return [...new Set(values)];
1281
+ }
1282
+ function throwModalLiveMountsUnsupported(mountPaths) {
1283
+ throw new sandbox_1.SandboxUnsupportedFeatureError('ModalSandboxClient cannot apply mount entries to a running sandbox.', {
1284
+ provider: 'modal',
1285
+ feature: 'manifest.mounts',
1286
+ mountPaths,
1287
+ });
1288
+ }
1289
+ function assertModalReusableSandboxMountsSupported(manifest, sandbox) {
1290
+ if (!sandbox) {
1291
+ return;
1292
+ }
1293
+ const mountTargets = manifest.mountTargets();
1294
+ if (mountTargets.length === 0) {
1295
+ return;
1296
+ }
1297
+ throw new sandbox_1.SandboxUnsupportedFeatureError('ModalSandboxClient cannot apply mount entries when reusing an existing sandbox.', {
1298
+ provider: 'modal',
1299
+ feature: 'manifest.mounts',
1300
+ mountPaths: mountTargets.map(({ mountPath }) => mountPath),
1301
+ });
1302
+ }
1303
+ function resolveOptions(defaults, overrides) {
1304
+ return {
1305
+ appName: overrides?.appName ?? defaults.appName ?? '',
1306
+ image: overrides?.image ?? defaults.image,
1307
+ sandbox: overrides?.sandbox ?? defaults.sandbox,
1308
+ imageTag: overrides?.imageTag ?? defaults.imageTag,
1309
+ sandboxCreateTimeoutS: overrides?.sandboxCreateTimeoutS ?? defaults.sandboxCreateTimeoutS,
1310
+ workspacePersistence: overrides?.workspacePersistence ?? defaults.workspacePersistence ?? 'tar',
1311
+ snapshotFilesystemTimeoutMs: overrides?.snapshotFilesystemTimeoutMs ??
1312
+ defaults.snapshotFilesystemTimeoutMs,
1313
+ snapshotFilesystemRestoreTimeoutMs: overrides?.snapshotFilesystemRestoreTimeoutMs ??
1314
+ defaults.snapshotFilesystemRestoreTimeoutMs,
1315
+ env: {
1316
+ ...(defaults.env ?? {}),
1317
+ ...(overrides?.env ?? {}),
1318
+ },
1319
+ timeoutMs: overrides?.timeoutMs ?? defaults.timeoutMs,
1320
+ idleTimeoutMs: overrides?.idleTimeoutMs ?? defaults.idleTimeoutMs,
1321
+ gpu: overrides?.gpu ?? defaults.gpu,
1322
+ exposedPorts: overrides?.exposedPorts ?? defaults.exposedPorts,
1323
+ environment: overrides?.environment ?? defaults.environment,
1324
+ endpoint: overrides?.endpoint ?? defaults.endpoint,
1325
+ tokenId: overrides?.tokenId ?? defaults.tokenId,
1326
+ tokenSecret: overrides?.tokenSecret ?? defaults.tokenSecret,
1327
+ imageBuilderVersion: overrides?.imageBuilderVersion ?? defaults.imageBuilderVersion,
1328
+ nativeCloudBucketSecretName: overrides?.nativeCloudBucketSecretName ??
1329
+ defaults.nativeCloudBucketSecretName,
1330
+ useSleepCmd: overrides?.useSleepCmd ?? defaults.useSleepCmd ?? true,
1331
+ };
1332
+ }
1333
+ function validateOptions(options) {
1334
+ if (!options.appName || options.appName.trim().length === 0) {
1335
+ throw new agents_core_1.UserError('ModalSandboxClient requires a non-empty appName.');
1336
+ }
1337
+ const workspacePersistence = options.workspacePersistence ?? 'tar';
1338
+ if (!['tar', 'snapshot_filesystem', 'snapshot_directory'].includes(workspacePersistence)) {
1339
+ throw new sandbox_1.SandboxUnsupportedFeatureError('ModalSandboxClient workspacePersistence must be "tar", "snapshot_filesystem", or "snapshot_directory".', {
1340
+ provider: 'modal',
1341
+ feature: 'workspacePersistence',
1342
+ });
1343
+ }
1344
+ if (options.nativeCloudBucketSecretName !== undefined &&
1345
+ options.nativeCloudBucketSecretName.trim() === '') {
1346
+ throw new sandbox_1.SandboxUnsupportedFeatureError('Modal nativeCloudBucketSecretName must be a non-empty string.', {
1347
+ provider: 'modal',
1348
+ feature: 'nativeCloudBucketSecretName',
1349
+ });
1350
+ }
1351
+ if (options.image) {
1352
+ validateModalImageSelector(options.image);
1353
+ }
1354
+ if (options.sandbox) {
1355
+ validateModalSandboxSelector(options.sandbox);
1356
+ }
1357
+ for (const [name, value] of [
1358
+ ['sandboxCreateTimeoutS', options.sandboxCreateTimeoutS],
1359
+ ['snapshotFilesystemTimeoutMs', options.snapshotFilesystemTimeoutMs],
1360
+ [
1361
+ 'snapshotFilesystemRestoreTimeoutMs',
1362
+ options.snapshotFilesystemRestoreTimeoutMs,
1363
+ ],
1364
+ ['timeoutMs', options.timeoutMs],
1365
+ ['idleTimeoutMs', options.idleTimeoutMs],
1366
+ ]) {
1367
+ if (value !== undefined &&
1368
+ (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)) {
1369
+ throw new agents_core_1.UserError(`ModalSandboxClient ${name} must be a positive number.`);
1370
+ }
1371
+ }
1372
+ }
1373
+ function validateModalImageSelector(selector) {
1374
+ if (selector.kind === 'image') {
1375
+ if (!selector.value || typeof selector.value !== 'object') {
1376
+ throw new agents_core_1.UserError('ModalSandboxClient image selector requires a Modal image object.');
1377
+ }
1378
+ return;
1379
+ }
1380
+ if ((selector.kind === 'id' || selector.kind === 'tag') &&
1381
+ (typeof selector.value !== 'string' || selector.value.trim().length === 0)) {
1382
+ throw new agents_core_1.UserError('ModalSandboxClient image selector requires a non-empty string value.');
1383
+ }
1384
+ }
1385
+ function validateModalSandboxSelector(selector) {
1386
+ if (selector.kind === 'sandbox') {
1387
+ if (!selector.value || typeof selector.value !== 'object') {
1388
+ throw new agents_core_1.UserError('ModalSandboxClient sandbox selector requires a Modal sandbox object.');
1389
+ }
1390
+ return;
1391
+ }
1392
+ if (selector.kind === 'id' &&
1393
+ (typeof selector.value !== 'string' || selector.value.trim().length === 0)) {
1394
+ throw new agents_core_1.UserError('ModalSandboxClient sandbox selector requires a non-empty sandbox id.');
1395
+ }
1396
+ }
1397
+ function snapshotIdFromModalResult(snapshot) {
1398
+ if (typeof snapshot === 'string') {
1399
+ return snapshot || undefined;
1400
+ }
1401
+ return snapshot.objectId ?? snapshot.imageId ?? snapshot.id;
1402
+ }
1403
+ function buildShellCommand(args) {
1404
+ const shellPath = args.shell ?? '/bin/sh';
1405
+ const login = args.shell ? (args.login ?? true) : false;
1406
+ const flag = login ? '-lc' : '-c';
1407
+ return [shellPath, flag, args.cmd];
1408
+ }
1409
+ function createActiveProcess(process) {
1410
+ const activeProcess = {
1411
+ process,
1412
+ stdout: '',
1413
+ stderr: '',
1414
+ droppedStdoutChars: 0,
1415
+ droppedStderrChars: 0,
1416
+ done: false,
1417
+ exitCode: null,
1418
+ donePromise: Promise.resolve(),
1419
+ };
1420
+ const stdoutPump = startTextStreamPump(process.stdout, (chunk) => {
1421
+ appendActiveProcessOutput(activeProcess, chunk, 'stdout');
1422
+ });
1423
+ const stderrPump = startTextStreamPump(process.stderr, (chunk) => {
1424
+ appendActiveProcessOutput(activeProcess, chunk, 'stderr');
1425
+ });
1426
+ activeProcess.stopOutputPumps = async () => {
1427
+ await Promise.allSettled([stdoutPump.cancel(), stderrPump.cancel()]);
1428
+ };
1429
+ activeProcess.donePromise = (async () => {
1430
+ try {
1431
+ activeProcess.exitCode = await process.wait();
1432
+ await Promise.allSettled([stdoutPump.promise, stderrPump.promise]);
1433
+ }
1434
+ catch (error) {
1435
+ activeProcess.waitError = error;
1436
+ await Promise.allSettled([stdoutPump.promise, stderrPump.promise]);
1437
+ }
1438
+ finally {
1439
+ activeProcess.done = true;
1440
+ }
1441
+ })();
1442
+ return activeProcess;
1443
+ }
1444
+ function startTextStreamPump(stream, onChunk) {
1445
+ const reader = stream.getReader();
1446
+ const promise = (async () => {
1447
+ try {
1448
+ while (true) {
1449
+ const { done, value } = await reader.read();
1450
+ if (done) {
1451
+ return;
1452
+ }
1453
+ if (typeof value === 'string') {
1454
+ onChunk(value);
1455
+ }
1456
+ }
1457
+ }
1458
+ finally {
1459
+ reader.releaseLock();
1460
+ }
1461
+ })();
1462
+ return {
1463
+ promise,
1464
+ cancel: async () => {
1465
+ await reader.cancel().catch(() => { });
1466
+ await promise.catch(() => { });
1467
+ },
1468
+ };
1469
+ }
1470
+ function clearActiveProcessCleanup(activeProcess) {
1471
+ if (activeProcess.cleanupTimer) {
1472
+ clearTimeout(activeProcess.cleanupTimer);
1473
+ activeProcess.cleanupTimer = undefined;
1474
+ }
1475
+ }
1476
+ function unrefTimer(timer) {
1477
+ const maybeTimer = timer;
1478
+ maybeTimer.unref?.();
1479
+ }
1480
+ async function pumpTextStream(stream, onChunk) {
1481
+ await startTextStreamPump(stream, onChunk).promise;
1482
+ }
1483
+ async function readStreamText(stream) {
1484
+ let text = '';
1485
+ await pumpTextStream(stream, (chunk) => {
1486
+ text += chunk;
1487
+ });
1488
+ return text;
1489
+ }
1490
+ async function waitForProcessOrTimeout(activeProcess, timeoutMs) {
1491
+ let timeout;
1492
+ try {
1493
+ await Promise.race([
1494
+ activeProcess.donePromise,
1495
+ new Promise((resolve) => {
1496
+ timeout = setTimeout(resolve, timeoutMs);
1497
+ unrefTimer(timeout);
1498
+ }),
1499
+ ]);
1500
+ }
1501
+ finally {
1502
+ if (timeout) {
1503
+ clearTimeout(timeout);
1504
+ }
1505
+ }
1506
+ if (activeProcess.waitError) {
1507
+ await activeProcess.stopOutputPumps?.();
1508
+ throw new sandbox_1.SandboxProviderError('ModalSandboxClient failed to wait for process completion.', {
1509
+ provider: 'modal',
1510
+ operation: 'wait process',
1511
+ cause: modalErrorMessage(activeProcess.waitError),
1512
+ });
1513
+ }
1514
+ }
1515
+ function modalErrorMessage(error) {
1516
+ return error instanceof Error ? error.message : String(error);
1517
+ }
1518
+ function joinCommandOutput(activeProcess) {
1519
+ return [
1520
+ withDroppedOutputPrefix(activeProcess.stdout, activeProcess.droppedStdoutChars),
1521
+ withDroppedOutputPrefix(activeProcess.stderr, activeProcess.droppedStderrChars),
1522
+ ]
1523
+ .filter((value) => value.trim().length > 0)
1524
+ .map((value) => value.trimEnd())
1525
+ .join('\n');
1526
+ }
1527
+ function consumeCommandOutput(activeProcess) {
1528
+ const output = joinCommandOutput(activeProcess);
1529
+ activeProcess.stdout = '';
1530
+ activeProcess.stderr = '';
1531
+ activeProcess.droppedStdoutChars = 0;
1532
+ activeProcess.droppedStderrChars = 0;
1533
+ return output;
1534
+ }
1535
+ function appendActiveProcessOutput(activeProcess, chunk, stream) {
1536
+ if (chunk.length === 0) {
1537
+ return;
1538
+ }
1539
+ if (stream === 'stdout') {
1540
+ const stdout = appendBoundedOutput(activeProcess.stdout, activeProcess.droppedStdoutChars, chunk);
1541
+ activeProcess.stdout = stdout.output;
1542
+ activeProcess.droppedStdoutChars = stdout.droppedChars;
1543
+ return;
1544
+ }
1545
+ const stderr = appendBoundedOutput(activeProcess.stderr, activeProcess.droppedStderrChars, chunk);
1546
+ activeProcess.stderr = stderr.output;
1547
+ activeProcess.droppedStderrChars = stderr.droppedChars;
1548
+ }
1549
+ function appendBoundedOutput(output, droppedChars, chunk) {
1550
+ const nextLength = output.length + chunk.length;
1551
+ if (nextLength <= MAX_ACTIVE_PROCESS_OUTPUT_CHARS) {
1552
+ return {
1553
+ output: output + chunk,
1554
+ droppedChars,
1555
+ };
1556
+ }
1557
+ const nextDroppedChars = droppedChars + nextLength - MAX_ACTIVE_PROCESS_OUTPUT_CHARS;
1558
+ if (chunk.length >= MAX_ACTIVE_PROCESS_OUTPUT_CHARS) {
1559
+ return {
1560
+ output: chunk.slice(-MAX_ACTIVE_PROCESS_OUTPUT_CHARS),
1561
+ droppedChars: nextDroppedChars,
1562
+ };
1563
+ }
1564
+ const existingCharsToKeep = MAX_ACTIVE_PROCESS_OUTPUT_CHARS - chunk.length;
1565
+ return {
1566
+ output: output.slice(-existingCharsToKeep) + chunk,
1567
+ droppedChars: nextDroppedChars,
1568
+ };
1569
+ }
1570
+ function withDroppedOutputPrefix(output, droppedChars) {
1571
+ if (droppedChars === 0) {
1572
+ return output;
1573
+ }
1574
+ return `[...${droppedChars} characters truncated from process output...]\n${output}`;
1575
+ }
1576
+ async function withOptionalTimeout(promise, timeoutSeconds, message, onLateResolve) {
1577
+ if (typeof timeoutSeconds !== 'number') {
1578
+ return await promise;
1579
+ }
1580
+ return await withOptionalTimeoutMs(promise, timeoutSeconds * 1000, message, onLateResolve);
1581
+ }
1582
+ async function withOptionalTimeoutMs(promise, timeoutMs, message, onLateResolve) {
1583
+ if (typeof timeoutMs !== 'number') {
1584
+ return await promise;
1585
+ }
1586
+ let settled = false;
1587
+ let timeoutId;
1588
+ try {
1589
+ return await Promise.race([
1590
+ promise.then((value) => {
1591
+ settled = true;
1592
+ return value;
1593
+ }, (error) => {
1594
+ settled = true;
1595
+ throw error;
1596
+ }),
1597
+ new Promise((_, reject) => {
1598
+ timeoutId = setTimeout(() => {
1599
+ if (settled) {
1600
+ return;
1601
+ }
1602
+ promise
1603
+ .then(async (value) => {
1604
+ await onLateResolve?.(value);
1605
+ })
1606
+ .catch(() => { });
1607
+ reject(new agents_core_1.UserError(message));
1608
+ }, timeoutMs);
1609
+ }),
1610
+ ]);
1611
+ }
1612
+ finally {
1613
+ if (timeoutId) {
1614
+ clearTimeout(timeoutId);
1615
+ }
1616
+ }
1617
+ }
1618
+ async function terminateModalSandboxAfterTimeout(sandbox) {
1619
+ await sandbox.terminate().catch(() => { });
1620
+ }
1621
+ //# sourceMappingURL=sandbox.js.map