@mastra/modal 0.5.1 → 0.6.0-alpha.1

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/LICENSE.md CHANGED
@@ -1,10 +1,12 @@
1
1
  Portions of this software are licensed as follows:
2
2
 
3
- - All content that resides under any directory named "ee/" within this
3
+ - All content that resides under any directory named `ee/` within this
4
4
  repository, including but not limited to:
5
- - `packages/core/src/auth/ee/`
6
- - `packages/server/src/server/auth/ee/`
7
- is licensed under the license defined in `ee/LICENSE`.
5
+ - `@mastra/core/auth/ee`
6
+ - `@mastra/core/agent-builder/ee`
7
+ - `@mastra/editor/ee`
8
+
9
+ is licensed under the license defined in [`ee/LICENSE`](https://github.com/mastra-ai/mastra/blob/main/ee/LICENSE).
8
10
 
9
11
  - All third-party components incorporated into the Mastra Software are
10
12
  licensed under the original license provided by the owner of the
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # @mastra/modal
2
+
3
+ Run Mastra workspace commands in isolated Modal cloud sandboxes with authentication, lifecycle controls, background processes, and reconnection.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @mastra/modal
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Set `MODAL_TOKEN_ID` and `MODAL_TOKEN_SECRET`, then attach the sandbox to a workspace.
14
+
15
+ ```typescript
16
+ import { Agent } from '@mastra/core/agent';
17
+ import { Workspace } from '@mastra/core/workspace';
18
+ import { ModalSandbox } from '@mastra/modal';
19
+
20
+ const workspace = new Workspace({
21
+ sandbox: new ModalSandbox({
22
+ id: 'dev-sandbox',
23
+ baseImage: 'ubuntu:22.04',
24
+ timeoutMs: 60_000,
25
+ }),
26
+ });
27
+
28
+ const agent = new Agent({
29
+ id: 'developer-agent',
30
+ name: 'Developer agent',
31
+ instructions: 'Use the workspace to inspect and modify the project.',
32
+ model: 'openai/gpt-5.6-sol',
33
+ workspace,
34
+ });
35
+ ```
36
+
37
+ ## Documentation
38
+
39
+ - [Modal](https://mastra.ai/integrations/sandboxes/modal)
40
+
41
+ ## Changelog
42
+
43
+ See the [package changelog](https://github.com/mastra-ai/mastra/blob/main/workspaces/modal/CHANGELOG.md) for version history and release notes.
44
+
45
+ ## Support
46
+
47
+ We have an [open community Discord](https://discord.gg/mastra-ai). Come and say hello and let us know if you have any questions or need any help getting things running.
package/dist/index.cjs CHANGED
@@ -137,7 +137,7 @@ var ModalProcessManager = class extends _mastra_core_workspace.SandboxProcessMan
137
137
  ];
138
138
  const proc = await sb.exec(argv, {
139
139
  env: Object.keys(env).length > 0 ? env : void 0,
140
- workdir: options.cwd,
140
+ workdir: options.cwd ?? this.sandbox.workingDirectory,
141
141
  timeoutMs: options.timeout
142
142
  });
143
143
  const pid = `modal-proc-${Date.now().toString(36)}-${++this._spawnCounter}`;
@@ -205,7 +205,6 @@ var ModalSandbox = class ModalSandbox extends _mastra_core_workspace.MastraSandb
205
205
  baseImage;
206
206
  timeoutMs;
207
207
  env;
208
- workdir;
209
208
  tokenId;
210
209
  tokenSecret;
211
210
  _instructionsOverride;
@@ -221,7 +220,7 @@ var ModalSandbox = class ModalSandbox extends _mastra_core_workspace.MastraSandb
221
220
  this.baseImage = options.baseImage ?? "ubuntu:22.04";
222
221
  this.timeoutMs = options.timeoutMs ?? 3e5;
223
222
  this.env = options.env ?? {};
224
- this.workdir = options.workdir;
223
+ if (options.workingDirectory === void 0 && options.workdir !== void 0) this.setWorkingDirectory(options.workdir);
225
224
  this.tokenId = options.tokenId;
226
225
  this.tokenSecret = options.tokenSecret;
227
226
  this._instructionsOverride = options.instructions;
@@ -278,7 +277,7 @@ var ModalSandbox = class ModalSandbox extends _mastra_core_workspace.MastraSandb
278
277
  name: this.id,
279
278
  timeoutMs: this.timeoutMs,
280
279
  env: Object.keys(this.env).length > 0 ? this.env : void 0,
281
- workdir: this.workdir
280
+ workdir: this.workingDirectory
282
281
  });
283
282
  this._createdAt = /* @__PURE__ */ new Date();
284
283
  this.logger.debug(`${LOG_PREFIX} Created new sandbox from snapshot: ${this._sb?.sandboxId}`);
@@ -290,7 +289,7 @@ var ModalSandbox = class ModalSandbox extends _mastra_core_workspace.MastraSandb
290
289
  name: this.id,
291
290
  timeoutMs: this.timeoutMs,
292
291
  env: Object.keys(this.env).length > 0 ? this.env : void 0,
293
- workdir: this.workdir
292
+ workdir: this.workingDirectory
294
293
  });
295
294
  this._createdAt = /* @__PURE__ */ new Date();
296
295
  this.logger.debug(`${LOG_PREFIX} Created sandbox: ${this._sb.sandboxId}`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["ProcessHandle","UnsupportedStdinCloseError","SandboxProcessManager","MastraSandbox","SandboxNotReadyError","NotFoundError","ClientClosedError","ModalClient"],"sources":["../src/sandbox/process-manager.ts","../src/sandbox/index.ts"],"sourcesContent":["/**\n * Modal process manager. Each spawn() creates a ContainerProcess via exec().\n * kill() has no SDK equivalent — it cancels stream readers locally; the remote\n * process runs until the sandbox timeout.\n */\n\nimport { ProcessHandle, UnsupportedStdinCloseError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { CommandResult, ProcessInfo, SpawnProcessOptions } from '@mastra/core/workspace';\nimport type { ContainerProcess } from 'modal';\nimport type { ModalSandbox } from './index';\n\n// =============================================================================\n// Modal Process Handle\n// =============================================================================\n\n/**\n * Wraps a Modal ContainerProcess to conform to Mastra's ProcessHandle.\n */\nclass ModalProcessHandle extends ProcessHandle {\n readonly pid: string;\n\n private readonly _proc: ContainerProcess<string>;\n private readonly _startTime: number;\n private readonly _timeout?: number;\n\n private _exitCode: number | undefined;\n private _waitPromise: Promise<CommandResult> | null = null;\n private _streamingPromise: Promise<void> | null = null;\n private _killed = false;\n private _stdoutReader: ReadableStreamDefaultReader<string> | null = null;\n private _stderrReader: ReadableStreamDefaultReader<string> | null = null;\n\n constructor(pid: string, proc: ContainerProcess<string>, startTime: number, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this._proc = proc;\n this._startTime = startTime;\n this._timeout = options?.timeout;\n }\n\n get exitCode(): number | undefined {\n return this._exitCode;\n }\n\n /** @internal Set by the process manager after streaming starts. */\n set streamingPromise(p: Promise<void>) {\n this._streamingPromise = p;\n\n // Resolve exit code when streaming ends so exitCode is available without calling wait()\n p.then(() => this._resolveExitCode()).catch(() => this._resolveExitCode());\n }\n\n /** @internal Set by the process manager so kill() can cancel the readers. */\n setReaders(\n stdoutReader: ReadableStreamDefaultReader<string>,\n stderrReader: ReadableStreamDefaultReader<string>,\n ): void {\n this._stdoutReader = stdoutReader;\n this._stderrReader = stderrReader;\n }\n\n /** Fetch the exit code from the Modal process. No-op if already set. */\n private async _resolveExitCode(): Promise<void> {\n if (this._exitCode !== undefined) return;\n try {\n this._exitCode = await this._proc.wait();\n } catch {\n if (this._exitCode === undefined) {\n this._exitCode = 1;\n }\n }\n }\n\n async wait(): Promise<CommandResult> {\n if (!this._waitPromise) {\n this._waitPromise = this._doWait();\n }\n return this._waitPromise;\n }\n\n private async _doWait(): Promise<CommandResult> {\n const streamDone = this._streamingPromise ?? Promise.resolve();\n\n if (this._timeout) {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(`Command timed out after ${this._timeout}ms`)), this._timeout);\n });\n\n try {\n await Promise.race([streamDone, timeoutPromise]);\n } catch (error) {\n if (error instanceof Error && error.message.includes('timed out')) {\n await this.kill();\n this._exitCode = 124; // conventional timeout exit code\n return {\n success: false,\n exitCode: 124,\n stdout: this.stdout,\n stderr: this.stderr || error.message,\n executionTimeMs: Date.now() - this._startTime,\n killed: true,\n timedOut: true,\n };\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n } else {\n await streamDone.catch(() => {});\n }\n\n if (this._killed) {\n return {\n success: false,\n exitCode: this._exitCode ?? 137,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n killed: true,\n timedOut: false,\n };\n }\n\n await this._resolveExitCode();\n\n return {\n success: this._exitCode === 0,\n exitCode: this._exitCode ?? 1,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n };\n }\n\n async kill(): Promise<boolean> {\n if (this._exitCode !== undefined) return false;\n this._killed = true;\n this._exitCode = 137; // SIGKILL\n // The remote process may continue running; Modal JS SDK has no per-exec kill.\n try {\n await this._stdoutReader?.cancel();\n } catch {\n // Ignore cancellation errors\n }\n try {\n await this._stderrReader?.cancel();\n } catch {\n // Ignore cancellation errors\n }\n return true;\n }\n\n async sendStdin(_data: string): Promise<void> {\n throw new Error('Modal JS SDK does not expose stdin on exec() — sendStdin() is not supported');\n }\n\n async closeStdin(): Promise<void> {\n throw new UnsupportedStdinCloseError(\n 'Modal JS SDK does not expose stdin on exec() — closeStdin() is not supported',\n );\n }\n}\n\n// =============================================================================\n// Modal Process Manager\n// =============================================================================\n\n/**\n * Modal implementation of SandboxProcessManager.\n * Uses the Modal SDK's exec() API with one ContainerProcess per spawn.\n */\nexport class ModalProcessManager extends SandboxProcessManager<ModalSandbox> {\n private _spawnCounter = 0;\n\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n return this.sandbox.retryOnDead(async () => {\n const sb = this.sandbox.modal;\n\n // The base spawn wrapper already merged the sandbox env into options.env\n const mergedEnv = { ...options.env };\n const env = Object.fromEntries(\n Object.entries(mergedEnv).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n\n // exec() takes string[] — wrap in sh -c to support pipes, redirects, etc.\n const argv = ['sh', '-c', command];\n\n const proc = await sb.exec(argv, {\n env: Object.keys(env).length > 0 ? env : undefined,\n workdir: options.cwd,\n timeoutMs: options.timeout,\n });\n\n const pid = `modal-proc-${Date.now().toString(36)}-${++this._spawnCounter}`;\n const handle = new ModalProcessHandle(pid, proc, Date.now(), options);\n\n const stdoutReader = proc.stdout.getReader();\n const stderrReader = proc.stderr.getReader();\n handle.setReaders(stdoutReader, stderrReader);\n\n const streamingPromise = Promise.all([\n drainReader(stdoutReader, chunk => handle.emitStdout(chunk)),\n drainReader(stderrReader, chunk => handle.emitStderr(chunk)),\n ]).then(() => {});\n\n handle.streamingPromise = streamingPromise;\n\n this._tracked.set(pid, handle);\n return handle;\n });\n }\n\n async list(): Promise<ProcessInfo[]> {\n const result: ProcessInfo[] = [];\n for (const [pid, handle] of this._tracked) {\n result.push({\n pid,\n command: handle.command,\n running: handle.exitCode === undefined,\n exitCode: handle.exitCode,\n });\n }\n return result;\n }\n}\n\n// =============================================================================\n// Stream Helpers\n// =============================================================================\n\n/** Reads chunks from a stream until done or cancelled. */\nasync function drainReader(reader: ReadableStreamDefaultReader<string>, emit: (chunk: string) => void): Promise<void> {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n emit(value);\n }\n } catch {\n // cancelled or network error\n }\n}\n","/**\n * @see https://modal.com/docs/reference/modal.Sandbox\n */\n\nimport type { MastraSandboxOptions, ProviderStatus, SandboxCloneOptions, SandboxInfo } from '@mastra/core/workspace';\nimport { MastraSandbox, SandboxNotReadyError } from '@mastra/core/workspace';\nimport { ClientClosedError, ModalClient, NotFoundError } from 'modal';\nimport type { App, Image, Sandbox } from 'modal';\nimport { ModalProcessManager } from './process-manager';\n\nconst LOG_PREFIX = '[ModalSandbox]';\n\n// =============================================================================\n// Options\n// =============================================================================\n\ntype InstructionsOption = string | ((opts: { defaultInstructions: string }) => string);\n\n/**\n * Modal sandbox provider configuration.\n */\nexport interface ModalSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** Stable name for this sandbox. Reusing the same id reconnects to a running sandbox. */\n id?: string;\n /**\n * Modal App name to associate sandboxes with.\n *\n * @default 'mastra'\n */\n appName?: string;\n /**\n * Docker image to use for the sandbox.\n *\n * @default 'ubuntu:22.04'\n */\n baseImage?: string;\n /**\n * Wall-clock max lifetime in milliseconds. The sandbox is terminated when this expires,\n * regardless of activity. Modal's maximum is 24 hours (86_400_000).\n *\n * @default 300_000 // 5 minutes\n */\n timeoutMs?: number;\n /** Environment variables baked into the sandbox at create time. */\n env?: Record<string, string>;\n /** Default working directory inside the sandbox. */\n workdir?: string;\n /** Modal token ID. Falls back to MODAL_TOKEN_ID env var. */\n tokenId?: string;\n /** Modal token secret. Falls back to MODAL_TOKEN_SECRET env var. */\n tokenSecret?: string;\n /** Custom instructions for getInstructions(). String replaces the default; function receives it. */\n instructions?: InstructionsOption;\n}\n\n// =============================================================================\n// ModalSandbox\n// =============================================================================\n\n/**\n * Modal cloud sandbox provider for Mastra workspaces.\n *\n * @example\n * ```typescript\n * import { Workspace } from '@mastra/core/workspace';\n * import { ModalSandbox } from '@mastra/modal';\n *\n * const sandbox = new ModalSandbox({\n * baseImage: 'ubuntu:22.04',\n * timeoutMs: 60_000,\n * });\n *\n * const workspace = new Workspace({ sandbox });\n * const result = await workspace.executeCommand('echo hello');\n * ```\n */\nexport class ModalSandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'ModalSandbox';\n readonly provider = 'modal';\n status: ProviderStatus = 'pending';\n\n declare readonly processes: ModalProcessManager;\n\n private _sb: Sandbox | null = null;\n private _imageSnapshot: Image | null = null; // for stop-and-resume\n private _client: ModalClient | null = null;\n private _createdAt: Date | null = null;\n private _isRetrying = false;\n\n private readonly appName: string;\n private readonly baseImage: string;\n private readonly timeoutMs: number;\n private readonly env: Record<string, string>;\n private readonly workdir?: string;\n private readonly tokenId?: string;\n private readonly tokenSecret?: string;\n private readonly _instructionsOverride?: InstructionsOption;\n private readonly _constructorOptions: ModalSandboxOptions;\n\n constructor(options: ModalSandboxOptions = {}) {\n super({\n ...options,\n name: 'ModalSandbox',\n processes: new ModalProcessManager(),\n });\n\n this.id = options.id ?? this._generateId();\n this.appName = options.appName ?? 'mastra';\n this.baseImage = options.baseImage ?? 'ubuntu:22.04';\n this.timeoutMs = options.timeoutMs ?? 300_000;\n this.env = options.env ?? {};\n this.workdir = options.workdir;\n this.tokenId = options.tokenId;\n this.tokenSecret = options.tokenSecret;\n this._instructionsOverride = options.instructions;\n this._constructorOptions = { ...options };\n }\n\n /**\n * Construct a sibling `ModalSandbox` that inherits this sandbox's\n * configuration (credentials, app, base image, workdir, instructions) with\n * per-instance overrides.\n *\n * Performs no I/O — the sandbox clone provisions (or reconnects to a\n * running Modal sandbox with the same logical `id`) on its own `start()`.\n * Use it when one configured sandbox acts as the template for a fleet of\n * independent sandboxes (e.g. one per project).\n *\n * `options.idleTimeoutMinutes` maps to Modal's `timeoutMs` (wall-clock\n * lifetime); `options.sandboxId` is ignored because Modal reconnects by\n * logical `id`.\n */\n clone(options: SandboxCloneOptions = {}): ModalSandbox {\n const { id: _id, ...base } = this._constructorOptions;\n return new ModalSandbox({\n ...base,\n ...(options.id !== undefined && { id: options.id }),\n ...(options.env !== undefined && { env: options.env }),\n ...(options.idleTimeoutMinutes !== undefined && { timeoutMs: options.idleTimeoutMinutes * 60_000 }),\n });\n }\n\n /**\n * Get the underlying Modal Sandbox instance for direct SDK access.\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started.\n */\n get modal(): Sandbox {\n if (!this._sb) {\n throw new SandboxNotReadyError(this.id);\n }\n return this._sb;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /** Reconnects to a running sandbox with this id if one exists, otherwise creates a new one. */\n async start(): Promise<void> {\n if (this._sb) {\n return;\n }\n\n const client = this._getClient();\n\n try {\n this._sb = await client.sandboxes.fromName(this.appName, this.id);\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Reconnected to running sandbox: ${this.id}`);\n return;\n } catch (error) {\n if (!(error instanceof NotFoundError)) {\n throw error;\n }\n }\n\n const app: App = await client.apps.fromName(this.appName, { createIfMissing: true });\n\n if (this._imageSnapshot) {\n this.logger.debug(`${LOG_PREFIX} Rebooting from snapshot: ${this.id}`);\n this._sb = await client.sandboxes.create(app, this._imageSnapshot, {\n name: this.id,\n timeoutMs: this.timeoutMs,\n env: Object.keys(this.env).length > 0 ? this.env : undefined,\n workdir: this.workdir,\n });\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Created new sandbox from snapshot: ${this._sb?.sandboxId}`);\n return;\n }\n\n const image: Image = client.images.fromRegistry(this.baseImage);\n this.logger.debug(`${LOG_PREFIX} Creating sandbox: ${this.id} (baseImage: ${this.baseImage})`);\n this._sb = await client.sandboxes.create(app, image, {\n name: this.id,\n timeoutMs: this.timeoutMs,\n env: Object.keys(this.env).length > 0 ? this.env : undefined,\n workdir: this.workdir,\n });\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Created sandbox: ${this._sb.sandboxId}`);\n }\n\n /**\n * Snapshot the sandbox filesystem before terminating it.\n * Future starts will create net-new sandboxes from the snapshot.\n */\n async stop(): Promise<void> {\n if (!this._sb) return;\n\n try {\n const procs = await this.processes.list();\n await Promise.all(procs.filter(p => p.running).map(p => this.processes.kill(p.pid)));\n } catch {\n // Best-effort: sandbox may already be dead\n }\n\n try {\n this._imageSnapshot = await this._sb.snapshotFilesystem();\n this.logger.debug(`${LOG_PREFIX} Snapshot created: ${this._imageSnapshot.imageId}`);\n } catch (error) {\n this.logger.debug(`${LOG_PREFIX} Snapshot failed, terminating without snapshot:`, error);\n }\n\n try {\n await this._sb.terminate({ wait: true });\n this.logger.debug(`${LOG_PREFIX} Sandbox terminated: ${this._sb.sandboxId}`);\n this._sb = null;\n } catch (error) {\n // Best-effort: sandbox may already be dead\n if (this.isSandboxDeadError(error)) {\n this._sb = null;\n } else {\n throw error;\n }\n }\n }\n\n /** Terminates the sandbox, ending its lifetime. Unlike stop(), no snapshot is preserved. */\n async destroy(): Promise<void> {\n if (this._sb) {\n try {\n const procs = await this.processes.list();\n await Promise.all(procs.filter(p => p.running).map(p => this.processes.kill(p.pid)));\n } catch {\n // Best-effort: sandbox may already be dead\n }\n\n try {\n await this._sb.terminate();\n this.logger.debug(`${LOG_PREFIX} Sandbox terminated: ${this._sb.sandboxId}`);\n } catch {\n // Ignore errors during destroy\n }\n\n this._sb = null;\n }\n\n this._imageSnapshot = null;\n }\n\n async getInfo(): Promise<SandboxInfo> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n appName: this.appName,\n image: this._imageSnapshot?.imageId ?? this.baseImage,\n timeoutMs: this.timeoutMs,\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions = this._getDefaultInstructions();\n if (this._instructionsOverride === undefined) return defaultInstructions;\n if (typeof this._instructionsOverride === 'string') return this._instructionsOverride;\n return this._instructionsOverride({ defaultInstructions });\n }\n\n private _getDefaultInstructions(): string {\n return `Modal cloud sandbox running ${this.baseImage}. Use executeCommand() to run shell commands.`;\n }\n\n // ---------------------------------------------------------------------------\n // Dead-sandbox Retry\n // ---------------------------------------------------------------------------\n\n private isSandboxDeadError(error: unknown): boolean {\n if (!error) return false;\n if (error instanceof ClientClosedError) return true;\n if (error instanceof NotFoundError) return true;\n const errorStr = String(error);\n return (\n errorStr.includes('sandbox not found') ||\n errorStr.includes('has been terminated') ||\n errorStr.includes('already completed') ||\n errorStr.includes('was cancelled') ||\n // gRPC NOT_FOUND (code 5)\n /status[:\\s]+5\\b/.test(errorStr) ||\n errorStr.includes('NOT_FOUND')\n );\n }\n\n private handleSandboxDead(): void {\n this._sb = null;\n this.status = 'stopped';\n }\n\n /** @internal Retries fn() once after restarting if the sandbox is dead. */\n async retryOnDead<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n if (this.isSandboxDeadError(error) && !this._isRetrying) {\n this.handleSandboxDead();\n this._isRetrying = true;\n try {\n await this.ensureRunning();\n return await fn();\n } finally {\n this._isRetrying = false;\n }\n }\n throw error;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal Helpers\n // ---------------------------------------------------------------------------\n\n private _generateId(): string {\n return `modal-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n private _getClient(): ModalClient {\n if (!this._client) {\n this._client = new ModalClient({\n ...(this.tokenId && { tokenId: this.tokenId }),\n ...(this.tokenSecret && { tokenSecret: this.tokenSecret }),\n });\n }\n return this._client;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["ProcessHandle","UnsupportedStdinCloseError","SandboxProcessManager","MastraSandbox","SandboxNotReadyError","NotFoundError","ClientClosedError","ModalClient"],"sources":["../src/sandbox/process-manager.ts","../src/sandbox/index.ts"],"sourcesContent":["/**\n * Modal process manager. Each spawn() creates a ContainerProcess via exec().\n * kill() has no SDK equivalent — it cancels stream readers locally; the remote\n * process runs until the sandbox timeout.\n */\n\nimport { ProcessHandle, UnsupportedStdinCloseError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { CommandResult, ProcessInfo, SpawnProcessOptions } from '@mastra/core/workspace';\nimport type { ContainerProcess } from 'modal';\nimport type { ModalSandbox } from './index';\n\n// =============================================================================\n// Modal Process Handle\n// =============================================================================\n\n/**\n * Wraps a Modal ContainerProcess to conform to Mastra's ProcessHandle.\n */\nclass ModalProcessHandle extends ProcessHandle {\n readonly pid: string;\n\n private readonly _proc: ContainerProcess<string>;\n private readonly _startTime: number;\n private readonly _timeout?: number;\n\n private _exitCode: number | undefined;\n private _waitPromise: Promise<CommandResult> | null = null;\n private _streamingPromise: Promise<void> | null = null;\n private _killed = false;\n private _stdoutReader: ReadableStreamDefaultReader<string> | null = null;\n private _stderrReader: ReadableStreamDefaultReader<string> | null = null;\n\n constructor(pid: string, proc: ContainerProcess<string>, startTime: number, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this._proc = proc;\n this._startTime = startTime;\n this._timeout = options?.timeout;\n }\n\n get exitCode(): number | undefined {\n return this._exitCode;\n }\n\n /** @internal Set by the process manager after streaming starts. */\n set streamingPromise(p: Promise<void>) {\n this._streamingPromise = p;\n\n // Resolve exit code when streaming ends so exitCode is available without calling wait()\n p.then(() => this._resolveExitCode()).catch(() => this._resolveExitCode());\n }\n\n /** @internal Set by the process manager so kill() can cancel the readers. */\n setReaders(\n stdoutReader: ReadableStreamDefaultReader<string>,\n stderrReader: ReadableStreamDefaultReader<string>,\n ): void {\n this._stdoutReader = stdoutReader;\n this._stderrReader = stderrReader;\n }\n\n /** Fetch the exit code from the Modal process. No-op if already set. */\n private async _resolveExitCode(): Promise<void> {\n if (this._exitCode !== undefined) return;\n try {\n this._exitCode = await this._proc.wait();\n } catch {\n if (this._exitCode === undefined) {\n this._exitCode = 1;\n }\n }\n }\n\n async wait(): Promise<CommandResult> {\n if (!this._waitPromise) {\n this._waitPromise = this._doWait();\n }\n return this._waitPromise;\n }\n\n private async _doWait(): Promise<CommandResult> {\n const streamDone = this._streamingPromise ?? Promise.resolve();\n\n if (this._timeout) {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(`Command timed out after ${this._timeout}ms`)), this._timeout);\n });\n\n try {\n await Promise.race([streamDone, timeoutPromise]);\n } catch (error) {\n if (error instanceof Error && error.message.includes('timed out')) {\n await this.kill();\n this._exitCode = 124; // conventional timeout exit code\n return {\n success: false,\n exitCode: 124,\n stdout: this.stdout,\n stderr: this.stderr || error.message,\n executionTimeMs: Date.now() - this._startTime,\n killed: true,\n timedOut: true,\n };\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n } else {\n await streamDone.catch(() => {});\n }\n\n if (this._killed) {\n return {\n success: false,\n exitCode: this._exitCode ?? 137,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n killed: true,\n timedOut: false,\n };\n }\n\n await this._resolveExitCode();\n\n return {\n success: this._exitCode === 0,\n exitCode: this._exitCode ?? 1,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n };\n }\n\n async kill(): Promise<boolean> {\n if (this._exitCode !== undefined) return false;\n this._killed = true;\n this._exitCode = 137; // SIGKILL\n // The remote process may continue running; Modal JS SDK has no per-exec kill.\n try {\n await this._stdoutReader?.cancel();\n } catch {\n // Ignore cancellation errors\n }\n try {\n await this._stderrReader?.cancel();\n } catch {\n // Ignore cancellation errors\n }\n return true;\n }\n\n async sendStdin(_data: string): Promise<void> {\n throw new Error('Modal JS SDK does not expose stdin on exec() — sendStdin() is not supported');\n }\n\n async closeStdin(): Promise<void> {\n throw new UnsupportedStdinCloseError(\n 'Modal JS SDK does not expose stdin on exec() — closeStdin() is not supported',\n );\n }\n}\n\n// =============================================================================\n// Modal Process Manager\n// =============================================================================\n\n/**\n * Modal implementation of SandboxProcessManager.\n * Uses the Modal SDK's exec() API with one ContainerProcess per spawn.\n */\nexport class ModalProcessManager extends SandboxProcessManager<ModalSandbox> {\n private _spawnCounter = 0;\n\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n return this.sandbox.retryOnDead(async () => {\n const sb = this.sandbox.modal;\n\n // The base spawn wrapper already merged the sandbox env into options.env\n const mergedEnv = { ...options.env };\n const env = Object.fromEntries(\n Object.entries(mergedEnv).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n\n // exec() takes string[] — wrap in sh -c to support pipes, redirects, etc.\n const argv = ['sh', '-c', command];\n\n const proc = await sb.exec(argv, {\n env: Object.keys(env).length > 0 ? env : undefined,\n workdir: options.cwd ?? this.sandbox.workingDirectory,\n timeoutMs: options.timeout,\n });\n\n const pid = `modal-proc-${Date.now().toString(36)}-${++this._spawnCounter}`;\n const handle = new ModalProcessHandle(pid, proc, Date.now(), options);\n\n const stdoutReader = proc.stdout.getReader();\n const stderrReader = proc.stderr.getReader();\n handle.setReaders(stdoutReader, stderrReader);\n\n const streamingPromise = Promise.all([\n drainReader(stdoutReader, chunk => handle.emitStdout(chunk)),\n drainReader(stderrReader, chunk => handle.emitStderr(chunk)),\n ]).then(() => {});\n\n handle.streamingPromise = streamingPromise;\n\n this._tracked.set(pid, handle);\n return handle;\n });\n }\n\n async list(): Promise<ProcessInfo[]> {\n const result: ProcessInfo[] = [];\n for (const [pid, handle] of this._tracked) {\n result.push({\n pid,\n command: handle.command,\n running: handle.exitCode === undefined,\n exitCode: handle.exitCode,\n });\n }\n return result;\n }\n}\n\n// =============================================================================\n// Stream Helpers\n// =============================================================================\n\n/** Reads chunks from a stream until done or cancelled. */\nasync function drainReader(reader: ReadableStreamDefaultReader<string>, emit: (chunk: string) => void): Promise<void> {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n emit(value);\n }\n } catch {\n // cancelled or network error\n }\n}\n","/**\n * @see https://modal.com/docs/reference/modal.Sandbox\n */\n\nimport type { MastraSandboxOptions, ProviderStatus, SandboxCloneOptions, SandboxInfo } from '@mastra/core/workspace';\nimport { MastraSandbox, SandboxNotReadyError } from '@mastra/core/workspace';\nimport { ClientClosedError, ModalClient, NotFoundError } from 'modal';\nimport type { App, Image, Sandbox } from 'modal';\nimport { ModalProcessManager } from './process-manager';\n\nconst LOG_PREFIX = '[ModalSandbox]';\n\n// =============================================================================\n// Options\n// =============================================================================\n\ntype InstructionsOption = string | ((opts: { defaultInstructions: string }) => string);\n\n/**\n * Modal sandbox provider configuration.\n */\nexport interface ModalSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** Stable name for this sandbox. Reusing the same id reconnects to a running sandbox. */\n id?: string;\n /**\n * Modal App name to associate sandboxes with.\n *\n * @default 'mastra'\n */\n appName?: string;\n /**\n * Docker image to use for the sandbox.\n *\n * @default 'ubuntu:22.04'\n */\n baseImage?: string;\n /**\n * Wall-clock max lifetime in milliseconds. The sandbox is terminated when this expires,\n * regardless of activity. Modal's maximum is 24 hours (86_400_000).\n *\n * @default 300_000 // 5 minutes\n */\n timeoutMs?: number;\n /** Environment variables baked into the sandbox at create time. */\n env?: Record<string, string>;\n /** Default working directory inside the sandbox.\n * @deprecated Use `workingDirectory` (the base sandbox option) instead.\n * When both are set, `workingDirectory` wins.\n */\n workdir?: string;\n /** Modal token ID. Falls back to MODAL_TOKEN_ID env var. */\n tokenId?: string;\n /** Modal token secret. Falls back to MODAL_TOKEN_SECRET env var. */\n tokenSecret?: string;\n /** Custom instructions for getInstructions(). String replaces the default; function receives it. */\n instructions?: InstructionsOption;\n}\n\n// =============================================================================\n// ModalSandbox\n// =============================================================================\n\n/**\n * Modal cloud sandbox provider for Mastra workspaces.\n *\n * @example\n * ```typescript\n * import { Workspace } from '@mastra/core/workspace';\n * import { ModalSandbox } from '@mastra/modal';\n *\n * const sandbox = new ModalSandbox({\n * baseImage: 'ubuntu:22.04',\n * timeoutMs: 60_000,\n * });\n *\n * const workspace = new Workspace({ sandbox });\n * const result = await workspace.executeCommand('echo hello');\n * ```\n */\nexport class ModalSandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'ModalSandbox';\n readonly provider = 'modal';\n status: ProviderStatus = 'pending';\n\n declare readonly processes: ModalProcessManager;\n\n private _sb: Sandbox | null = null;\n private _imageSnapshot: Image | null = null; // for stop-and-resume\n private _client: ModalClient | null = null;\n private _createdAt: Date | null = null;\n private _isRetrying = false;\n\n private readonly appName: string;\n private readonly baseImage: string;\n private readonly timeoutMs: number;\n private readonly env: Record<string, string>;\n private readonly tokenId?: string;\n private readonly tokenSecret?: string;\n private readonly _instructionsOverride?: InstructionsOption;\n private readonly _constructorOptions: ModalSandboxOptions;\n\n constructor(options: ModalSandboxOptions = {}) {\n super({\n ...options,\n name: 'ModalSandbox',\n processes: new ModalProcessManager(),\n });\n\n this.id = options.id ?? this._generateId();\n this.appName = options.appName ?? 'mastra';\n this.baseImage = options.baseImage ?? 'ubuntu:22.04';\n this.timeoutMs = options.timeoutMs ?? 300_000;\n this.env = options.env ?? {};\n if (options.workingDirectory === undefined && options.workdir !== undefined) {\n this.setWorkingDirectory(options.workdir);\n }\n this.tokenId = options.tokenId;\n this.tokenSecret = options.tokenSecret;\n this._instructionsOverride = options.instructions;\n this._constructorOptions = { ...options };\n }\n\n /**\n * Construct a sibling `ModalSandbox` that inherits this sandbox's\n * configuration (credentials, app, base image, workdir, instructions) with\n * per-instance overrides.\n *\n * Performs no I/O — the sandbox clone provisions (or reconnects to a\n * running Modal sandbox with the same logical `id`) on its own `start()`.\n * Use it when one configured sandbox acts as the template for a fleet of\n * independent sandboxes (e.g. one per project).\n *\n * `options.idleTimeoutMinutes` maps to Modal's `timeoutMs` (wall-clock\n * lifetime); `options.sandboxId` is ignored because Modal reconnects by\n * logical `id`.\n */\n clone(options: SandboxCloneOptions = {}): ModalSandbox {\n const { id: _id, ...base } = this._constructorOptions;\n return new ModalSandbox({\n ...base,\n ...(options.id !== undefined && { id: options.id }),\n ...(options.env !== undefined && { env: options.env }),\n ...(options.idleTimeoutMinutes !== undefined && { timeoutMs: options.idleTimeoutMinutes * 60_000 }),\n });\n }\n\n /**\n * Get the underlying Modal Sandbox instance for direct SDK access.\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started.\n */\n get modal(): Sandbox {\n if (!this._sb) {\n throw new SandboxNotReadyError(this.id);\n }\n return this._sb;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /** Reconnects to a running sandbox with this id if one exists, otherwise creates a new one. */\n async start(): Promise<void> {\n if (this._sb) {\n return;\n }\n\n const client = this._getClient();\n\n try {\n this._sb = await client.sandboxes.fromName(this.appName, this.id);\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Reconnected to running sandbox: ${this.id}`);\n return;\n } catch (error) {\n if (!(error instanceof NotFoundError)) {\n throw error;\n }\n }\n\n const app: App = await client.apps.fromName(this.appName, { createIfMissing: true });\n\n if (this._imageSnapshot) {\n this.logger.debug(`${LOG_PREFIX} Rebooting from snapshot: ${this.id}`);\n this._sb = await client.sandboxes.create(app, this._imageSnapshot, {\n name: this.id,\n timeoutMs: this.timeoutMs,\n env: Object.keys(this.env).length > 0 ? this.env : undefined,\n workdir: this.workingDirectory,\n });\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Created new sandbox from snapshot: ${this._sb?.sandboxId}`);\n return;\n }\n\n const image: Image = client.images.fromRegistry(this.baseImage);\n this.logger.debug(`${LOG_PREFIX} Creating sandbox: ${this.id} (baseImage: ${this.baseImage})`);\n this._sb = await client.sandboxes.create(app, image, {\n name: this.id,\n timeoutMs: this.timeoutMs,\n env: Object.keys(this.env).length > 0 ? this.env : undefined,\n workdir: this.workingDirectory,\n });\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Created sandbox: ${this._sb.sandboxId}`);\n }\n\n /**\n * Snapshot the sandbox filesystem before terminating it.\n * Future starts will create net-new sandboxes from the snapshot.\n */\n async stop(): Promise<void> {\n if (!this._sb) return;\n\n try {\n const procs = await this.processes.list();\n await Promise.all(procs.filter(p => p.running).map(p => this.processes.kill(p.pid)));\n } catch {\n // Best-effort: sandbox may already be dead\n }\n\n try {\n this._imageSnapshot = await this._sb.snapshotFilesystem();\n this.logger.debug(`${LOG_PREFIX} Snapshot created: ${this._imageSnapshot.imageId}`);\n } catch (error) {\n this.logger.debug(`${LOG_PREFIX} Snapshot failed, terminating without snapshot:`, error);\n }\n\n try {\n await this._sb.terminate({ wait: true });\n this.logger.debug(`${LOG_PREFIX} Sandbox terminated: ${this._sb.sandboxId}`);\n this._sb = null;\n } catch (error) {\n // Best-effort: sandbox may already be dead\n if (this.isSandboxDeadError(error)) {\n this._sb = null;\n } else {\n throw error;\n }\n }\n }\n\n /** Terminates the sandbox, ending its lifetime. Unlike stop(), no snapshot is preserved. */\n async destroy(): Promise<void> {\n if (this._sb) {\n try {\n const procs = await this.processes.list();\n await Promise.all(procs.filter(p => p.running).map(p => this.processes.kill(p.pid)));\n } catch {\n // Best-effort: sandbox may already be dead\n }\n\n try {\n await this._sb.terminate();\n this.logger.debug(`${LOG_PREFIX} Sandbox terminated: ${this._sb.sandboxId}`);\n } catch {\n // Ignore errors during destroy\n }\n\n this._sb = null;\n }\n\n this._imageSnapshot = null;\n }\n\n async getInfo(): Promise<SandboxInfo> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n appName: this.appName,\n image: this._imageSnapshot?.imageId ?? this.baseImage,\n timeoutMs: this.timeoutMs,\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions = this._getDefaultInstructions();\n if (this._instructionsOverride === undefined) return defaultInstructions;\n if (typeof this._instructionsOverride === 'string') return this._instructionsOverride;\n return this._instructionsOverride({ defaultInstructions });\n }\n\n private _getDefaultInstructions(): string {\n return `Modal cloud sandbox running ${this.baseImage}. Use executeCommand() to run shell commands.`;\n }\n\n // ---------------------------------------------------------------------------\n // Dead-sandbox Retry\n // ---------------------------------------------------------------------------\n\n private isSandboxDeadError(error: unknown): boolean {\n if (!error) return false;\n if (error instanceof ClientClosedError) return true;\n if (error instanceof NotFoundError) return true;\n const errorStr = String(error);\n return (\n errorStr.includes('sandbox not found') ||\n errorStr.includes('has been terminated') ||\n errorStr.includes('already completed') ||\n errorStr.includes('was cancelled') ||\n // gRPC NOT_FOUND (code 5)\n /status[:\\s]+5\\b/.test(errorStr) ||\n errorStr.includes('NOT_FOUND')\n );\n }\n\n private handleSandboxDead(): void {\n this._sb = null;\n this.status = 'stopped';\n }\n\n /** @internal Retries fn() once after restarting if the sandbox is dead. */\n async retryOnDead<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n if (this.isSandboxDeadError(error) && !this._isRetrying) {\n this.handleSandboxDead();\n this._isRetrying = true;\n try {\n await this.ensureRunning();\n return await fn();\n } finally {\n this._isRetrying = false;\n }\n }\n throw error;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal Helpers\n // ---------------------------------------------------------------------------\n\n private _generateId(): string {\n return `modal-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n private _getClient(): ModalClient {\n if (!this._client) {\n this._client = new ModalClient({\n ...(this.tokenId && { tokenId: this.tokenId }),\n ...(this.tokenSecret && { tokenSecret: this.tokenSecret }),\n });\n }\n return this._client;\n }\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -136,7 +136,7 @@ var ModalProcessManager = class extends SandboxProcessManager {
136
136
  ];
137
137
  const proc = await sb.exec(argv, {
138
138
  env: Object.keys(env).length > 0 ? env : void 0,
139
- workdir: options.cwd,
139
+ workdir: options.cwd ?? this.sandbox.workingDirectory,
140
140
  timeoutMs: options.timeout
141
141
  });
142
142
  const pid = `modal-proc-${Date.now().toString(36)}-${++this._spawnCounter}`;
@@ -204,7 +204,6 @@ var ModalSandbox = class ModalSandbox extends MastraSandbox {
204
204
  baseImage;
205
205
  timeoutMs;
206
206
  env;
207
- workdir;
208
207
  tokenId;
209
208
  tokenSecret;
210
209
  _instructionsOverride;
@@ -220,7 +219,7 @@ var ModalSandbox = class ModalSandbox extends MastraSandbox {
220
219
  this.baseImage = options.baseImage ?? "ubuntu:22.04";
221
220
  this.timeoutMs = options.timeoutMs ?? 3e5;
222
221
  this.env = options.env ?? {};
223
- this.workdir = options.workdir;
222
+ if (options.workingDirectory === void 0 && options.workdir !== void 0) this.setWorkingDirectory(options.workdir);
224
223
  this.tokenId = options.tokenId;
225
224
  this.tokenSecret = options.tokenSecret;
226
225
  this._instructionsOverride = options.instructions;
@@ -277,7 +276,7 @@ var ModalSandbox = class ModalSandbox extends MastraSandbox {
277
276
  name: this.id,
278
277
  timeoutMs: this.timeoutMs,
279
278
  env: Object.keys(this.env).length > 0 ? this.env : void 0,
280
- workdir: this.workdir
279
+ workdir: this.workingDirectory
281
280
  });
282
281
  this._createdAt = /* @__PURE__ */ new Date();
283
282
  this.logger.debug(`${LOG_PREFIX} Created new sandbox from snapshot: ${this._sb?.sandboxId}`);
@@ -289,7 +288,7 @@ var ModalSandbox = class ModalSandbox extends MastraSandbox {
289
288
  name: this.id,
290
289
  timeoutMs: this.timeoutMs,
291
290
  env: Object.keys(this.env).length > 0 ? this.env : void 0,
292
- workdir: this.workdir
291
+ workdir: this.workingDirectory
293
292
  });
294
293
  this._createdAt = /* @__PURE__ */ new Date();
295
294
  this.logger.debug(`${LOG_PREFIX} Created sandbox: ${this._sb.sandboxId}`);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/sandbox/process-manager.ts","../src/sandbox/index.ts"],"sourcesContent":["/**\n * Modal process manager. Each spawn() creates a ContainerProcess via exec().\n * kill() has no SDK equivalent — it cancels stream readers locally; the remote\n * process runs until the sandbox timeout.\n */\n\nimport { ProcessHandle, UnsupportedStdinCloseError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { CommandResult, ProcessInfo, SpawnProcessOptions } from '@mastra/core/workspace';\nimport type { ContainerProcess } from 'modal';\nimport type { ModalSandbox } from './index';\n\n// =============================================================================\n// Modal Process Handle\n// =============================================================================\n\n/**\n * Wraps a Modal ContainerProcess to conform to Mastra's ProcessHandle.\n */\nclass ModalProcessHandle extends ProcessHandle {\n readonly pid: string;\n\n private readonly _proc: ContainerProcess<string>;\n private readonly _startTime: number;\n private readonly _timeout?: number;\n\n private _exitCode: number | undefined;\n private _waitPromise: Promise<CommandResult> | null = null;\n private _streamingPromise: Promise<void> | null = null;\n private _killed = false;\n private _stdoutReader: ReadableStreamDefaultReader<string> | null = null;\n private _stderrReader: ReadableStreamDefaultReader<string> | null = null;\n\n constructor(pid: string, proc: ContainerProcess<string>, startTime: number, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this._proc = proc;\n this._startTime = startTime;\n this._timeout = options?.timeout;\n }\n\n get exitCode(): number | undefined {\n return this._exitCode;\n }\n\n /** @internal Set by the process manager after streaming starts. */\n set streamingPromise(p: Promise<void>) {\n this._streamingPromise = p;\n\n // Resolve exit code when streaming ends so exitCode is available without calling wait()\n p.then(() => this._resolveExitCode()).catch(() => this._resolveExitCode());\n }\n\n /** @internal Set by the process manager so kill() can cancel the readers. */\n setReaders(\n stdoutReader: ReadableStreamDefaultReader<string>,\n stderrReader: ReadableStreamDefaultReader<string>,\n ): void {\n this._stdoutReader = stdoutReader;\n this._stderrReader = stderrReader;\n }\n\n /** Fetch the exit code from the Modal process. No-op if already set. */\n private async _resolveExitCode(): Promise<void> {\n if (this._exitCode !== undefined) return;\n try {\n this._exitCode = await this._proc.wait();\n } catch {\n if (this._exitCode === undefined) {\n this._exitCode = 1;\n }\n }\n }\n\n async wait(): Promise<CommandResult> {\n if (!this._waitPromise) {\n this._waitPromise = this._doWait();\n }\n return this._waitPromise;\n }\n\n private async _doWait(): Promise<CommandResult> {\n const streamDone = this._streamingPromise ?? Promise.resolve();\n\n if (this._timeout) {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(`Command timed out after ${this._timeout}ms`)), this._timeout);\n });\n\n try {\n await Promise.race([streamDone, timeoutPromise]);\n } catch (error) {\n if (error instanceof Error && error.message.includes('timed out')) {\n await this.kill();\n this._exitCode = 124; // conventional timeout exit code\n return {\n success: false,\n exitCode: 124,\n stdout: this.stdout,\n stderr: this.stderr || error.message,\n executionTimeMs: Date.now() - this._startTime,\n killed: true,\n timedOut: true,\n };\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n } else {\n await streamDone.catch(() => {});\n }\n\n if (this._killed) {\n return {\n success: false,\n exitCode: this._exitCode ?? 137,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n killed: true,\n timedOut: false,\n };\n }\n\n await this._resolveExitCode();\n\n return {\n success: this._exitCode === 0,\n exitCode: this._exitCode ?? 1,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n };\n }\n\n async kill(): Promise<boolean> {\n if (this._exitCode !== undefined) return false;\n this._killed = true;\n this._exitCode = 137; // SIGKILL\n // The remote process may continue running; Modal JS SDK has no per-exec kill.\n try {\n await this._stdoutReader?.cancel();\n } catch {\n // Ignore cancellation errors\n }\n try {\n await this._stderrReader?.cancel();\n } catch {\n // Ignore cancellation errors\n }\n return true;\n }\n\n async sendStdin(_data: string): Promise<void> {\n throw new Error('Modal JS SDK does not expose stdin on exec() — sendStdin() is not supported');\n }\n\n async closeStdin(): Promise<void> {\n throw new UnsupportedStdinCloseError(\n 'Modal JS SDK does not expose stdin on exec() — closeStdin() is not supported',\n );\n }\n}\n\n// =============================================================================\n// Modal Process Manager\n// =============================================================================\n\n/**\n * Modal implementation of SandboxProcessManager.\n * Uses the Modal SDK's exec() API with one ContainerProcess per spawn.\n */\nexport class ModalProcessManager extends SandboxProcessManager<ModalSandbox> {\n private _spawnCounter = 0;\n\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n return this.sandbox.retryOnDead(async () => {\n const sb = this.sandbox.modal;\n\n // The base spawn wrapper already merged the sandbox env into options.env\n const mergedEnv = { ...options.env };\n const env = Object.fromEntries(\n Object.entries(mergedEnv).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n\n // exec() takes string[] — wrap in sh -c to support pipes, redirects, etc.\n const argv = ['sh', '-c', command];\n\n const proc = await sb.exec(argv, {\n env: Object.keys(env).length > 0 ? env : undefined,\n workdir: options.cwd,\n timeoutMs: options.timeout,\n });\n\n const pid = `modal-proc-${Date.now().toString(36)}-${++this._spawnCounter}`;\n const handle = new ModalProcessHandle(pid, proc, Date.now(), options);\n\n const stdoutReader = proc.stdout.getReader();\n const stderrReader = proc.stderr.getReader();\n handle.setReaders(stdoutReader, stderrReader);\n\n const streamingPromise = Promise.all([\n drainReader(stdoutReader, chunk => handle.emitStdout(chunk)),\n drainReader(stderrReader, chunk => handle.emitStderr(chunk)),\n ]).then(() => {});\n\n handle.streamingPromise = streamingPromise;\n\n this._tracked.set(pid, handle);\n return handle;\n });\n }\n\n async list(): Promise<ProcessInfo[]> {\n const result: ProcessInfo[] = [];\n for (const [pid, handle] of this._tracked) {\n result.push({\n pid,\n command: handle.command,\n running: handle.exitCode === undefined,\n exitCode: handle.exitCode,\n });\n }\n return result;\n }\n}\n\n// =============================================================================\n// Stream Helpers\n// =============================================================================\n\n/** Reads chunks from a stream until done or cancelled. */\nasync function drainReader(reader: ReadableStreamDefaultReader<string>, emit: (chunk: string) => void): Promise<void> {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n emit(value);\n }\n } catch {\n // cancelled or network error\n }\n}\n","/**\n * @see https://modal.com/docs/reference/modal.Sandbox\n */\n\nimport type { MastraSandboxOptions, ProviderStatus, SandboxCloneOptions, SandboxInfo } from '@mastra/core/workspace';\nimport { MastraSandbox, SandboxNotReadyError } from '@mastra/core/workspace';\nimport { ClientClosedError, ModalClient, NotFoundError } from 'modal';\nimport type { App, Image, Sandbox } from 'modal';\nimport { ModalProcessManager } from './process-manager';\n\nconst LOG_PREFIX = '[ModalSandbox]';\n\n// =============================================================================\n// Options\n// =============================================================================\n\ntype InstructionsOption = string | ((opts: { defaultInstructions: string }) => string);\n\n/**\n * Modal sandbox provider configuration.\n */\nexport interface ModalSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** Stable name for this sandbox. Reusing the same id reconnects to a running sandbox. */\n id?: string;\n /**\n * Modal App name to associate sandboxes with.\n *\n * @default 'mastra'\n */\n appName?: string;\n /**\n * Docker image to use for the sandbox.\n *\n * @default 'ubuntu:22.04'\n */\n baseImage?: string;\n /**\n * Wall-clock max lifetime in milliseconds. The sandbox is terminated when this expires,\n * regardless of activity. Modal's maximum is 24 hours (86_400_000).\n *\n * @default 300_000 // 5 minutes\n */\n timeoutMs?: number;\n /** Environment variables baked into the sandbox at create time. */\n env?: Record<string, string>;\n /** Default working directory inside the sandbox. */\n workdir?: string;\n /** Modal token ID. Falls back to MODAL_TOKEN_ID env var. */\n tokenId?: string;\n /** Modal token secret. Falls back to MODAL_TOKEN_SECRET env var. */\n tokenSecret?: string;\n /** Custom instructions for getInstructions(). String replaces the default; function receives it. */\n instructions?: InstructionsOption;\n}\n\n// =============================================================================\n// ModalSandbox\n// =============================================================================\n\n/**\n * Modal cloud sandbox provider for Mastra workspaces.\n *\n * @example\n * ```typescript\n * import { Workspace } from '@mastra/core/workspace';\n * import { ModalSandbox } from '@mastra/modal';\n *\n * const sandbox = new ModalSandbox({\n * baseImage: 'ubuntu:22.04',\n * timeoutMs: 60_000,\n * });\n *\n * const workspace = new Workspace({ sandbox });\n * const result = await workspace.executeCommand('echo hello');\n * ```\n */\nexport class ModalSandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'ModalSandbox';\n readonly provider = 'modal';\n status: ProviderStatus = 'pending';\n\n declare readonly processes: ModalProcessManager;\n\n private _sb: Sandbox | null = null;\n private _imageSnapshot: Image | null = null; // for stop-and-resume\n private _client: ModalClient | null = null;\n private _createdAt: Date | null = null;\n private _isRetrying = false;\n\n private readonly appName: string;\n private readonly baseImage: string;\n private readonly timeoutMs: number;\n private readonly env: Record<string, string>;\n private readonly workdir?: string;\n private readonly tokenId?: string;\n private readonly tokenSecret?: string;\n private readonly _instructionsOverride?: InstructionsOption;\n private readonly _constructorOptions: ModalSandboxOptions;\n\n constructor(options: ModalSandboxOptions = {}) {\n super({\n ...options,\n name: 'ModalSandbox',\n processes: new ModalProcessManager(),\n });\n\n this.id = options.id ?? this._generateId();\n this.appName = options.appName ?? 'mastra';\n this.baseImage = options.baseImage ?? 'ubuntu:22.04';\n this.timeoutMs = options.timeoutMs ?? 300_000;\n this.env = options.env ?? {};\n this.workdir = options.workdir;\n this.tokenId = options.tokenId;\n this.tokenSecret = options.tokenSecret;\n this._instructionsOverride = options.instructions;\n this._constructorOptions = { ...options };\n }\n\n /**\n * Construct a sibling `ModalSandbox` that inherits this sandbox's\n * configuration (credentials, app, base image, workdir, instructions) with\n * per-instance overrides.\n *\n * Performs no I/O — the sandbox clone provisions (or reconnects to a\n * running Modal sandbox with the same logical `id`) on its own `start()`.\n * Use it when one configured sandbox acts as the template for a fleet of\n * independent sandboxes (e.g. one per project).\n *\n * `options.idleTimeoutMinutes` maps to Modal's `timeoutMs` (wall-clock\n * lifetime); `options.sandboxId` is ignored because Modal reconnects by\n * logical `id`.\n */\n clone(options: SandboxCloneOptions = {}): ModalSandbox {\n const { id: _id, ...base } = this._constructorOptions;\n return new ModalSandbox({\n ...base,\n ...(options.id !== undefined && { id: options.id }),\n ...(options.env !== undefined && { env: options.env }),\n ...(options.idleTimeoutMinutes !== undefined && { timeoutMs: options.idleTimeoutMinutes * 60_000 }),\n });\n }\n\n /**\n * Get the underlying Modal Sandbox instance for direct SDK access.\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started.\n */\n get modal(): Sandbox {\n if (!this._sb) {\n throw new SandboxNotReadyError(this.id);\n }\n return this._sb;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /** Reconnects to a running sandbox with this id if one exists, otherwise creates a new one. */\n async start(): Promise<void> {\n if (this._sb) {\n return;\n }\n\n const client = this._getClient();\n\n try {\n this._sb = await client.sandboxes.fromName(this.appName, this.id);\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Reconnected to running sandbox: ${this.id}`);\n return;\n } catch (error) {\n if (!(error instanceof NotFoundError)) {\n throw error;\n }\n }\n\n const app: App = await client.apps.fromName(this.appName, { createIfMissing: true });\n\n if (this._imageSnapshot) {\n this.logger.debug(`${LOG_PREFIX} Rebooting from snapshot: ${this.id}`);\n this._sb = await client.sandboxes.create(app, this._imageSnapshot, {\n name: this.id,\n timeoutMs: this.timeoutMs,\n env: Object.keys(this.env).length > 0 ? this.env : undefined,\n workdir: this.workdir,\n });\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Created new sandbox from snapshot: ${this._sb?.sandboxId}`);\n return;\n }\n\n const image: Image = client.images.fromRegistry(this.baseImage);\n this.logger.debug(`${LOG_PREFIX} Creating sandbox: ${this.id} (baseImage: ${this.baseImage})`);\n this._sb = await client.sandboxes.create(app, image, {\n name: this.id,\n timeoutMs: this.timeoutMs,\n env: Object.keys(this.env).length > 0 ? this.env : undefined,\n workdir: this.workdir,\n });\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Created sandbox: ${this._sb.sandboxId}`);\n }\n\n /**\n * Snapshot the sandbox filesystem before terminating it.\n * Future starts will create net-new sandboxes from the snapshot.\n */\n async stop(): Promise<void> {\n if (!this._sb) return;\n\n try {\n const procs = await this.processes.list();\n await Promise.all(procs.filter(p => p.running).map(p => this.processes.kill(p.pid)));\n } catch {\n // Best-effort: sandbox may already be dead\n }\n\n try {\n this._imageSnapshot = await this._sb.snapshotFilesystem();\n this.logger.debug(`${LOG_PREFIX} Snapshot created: ${this._imageSnapshot.imageId}`);\n } catch (error) {\n this.logger.debug(`${LOG_PREFIX} Snapshot failed, terminating without snapshot:`, error);\n }\n\n try {\n await this._sb.terminate({ wait: true });\n this.logger.debug(`${LOG_PREFIX} Sandbox terminated: ${this._sb.sandboxId}`);\n this._sb = null;\n } catch (error) {\n // Best-effort: sandbox may already be dead\n if (this.isSandboxDeadError(error)) {\n this._sb = null;\n } else {\n throw error;\n }\n }\n }\n\n /** Terminates the sandbox, ending its lifetime. Unlike stop(), no snapshot is preserved. */\n async destroy(): Promise<void> {\n if (this._sb) {\n try {\n const procs = await this.processes.list();\n await Promise.all(procs.filter(p => p.running).map(p => this.processes.kill(p.pid)));\n } catch {\n // Best-effort: sandbox may already be dead\n }\n\n try {\n await this._sb.terminate();\n this.logger.debug(`${LOG_PREFIX} Sandbox terminated: ${this._sb.sandboxId}`);\n } catch {\n // Ignore errors during destroy\n }\n\n this._sb = null;\n }\n\n this._imageSnapshot = null;\n }\n\n async getInfo(): Promise<SandboxInfo> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n appName: this.appName,\n image: this._imageSnapshot?.imageId ?? this.baseImage,\n timeoutMs: this.timeoutMs,\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions = this._getDefaultInstructions();\n if (this._instructionsOverride === undefined) return defaultInstructions;\n if (typeof this._instructionsOverride === 'string') return this._instructionsOverride;\n return this._instructionsOverride({ defaultInstructions });\n }\n\n private _getDefaultInstructions(): string {\n return `Modal cloud sandbox running ${this.baseImage}. Use executeCommand() to run shell commands.`;\n }\n\n // ---------------------------------------------------------------------------\n // Dead-sandbox Retry\n // ---------------------------------------------------------------------------\n\n private isSandboxDeadError(error: unknown): boolean {\n if (!error) return false;\n if (error instanceof ClientClosedError) return true;\n if (error instanceof NotFoundError) return true;\n const errorStr = String(error);\n return (\n errorStr.includes('sandbox not found') ||\n errorStr.includes('has been terminated') ||\n errorStr.includes('already completed') ||\n errorStr.includes('was cancelled') ||\n // gRPC NOT_FOUND (code 5)\n /status[:\\s]+5\\b/.test(errorStr) ||\n errorStr.includes('NOT_FOUND')\n );\n }\n\n private handleSandboxDead(): void {\n this._sb = null;\n this.status = 'stopped';\n }\n\n /** @internal Retries fn() once after restarting if the sandbox is dead. */\n async retryOnDead<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n if (this.isSandboxDeadError(error) && !this._isRetrying) {\n this.handleSandboxDead();\n this._isRetrying = true;\n try {\n await this.ensureRunning();\n return await fn();\n } finally {\n this._isRetrying = false;\n }\n }\n throw error;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal Helpers\n // ---------------------------------------------------------------------------\n\n private _generateId(): string {\n return `modal-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n private _getClient(): ModalClient {\n if (!this._client) {\n this._client = new ModalClient({\n ...(this.tokenId && { tokenId: this.tokenId }),\n ...(this.tokenSecret && { tokenSecret: this.tokenSecret }),\n });\n }\n return this._client;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/sandbox/process-manager.ts","../src/sandbox/index.ts"],"sourcesContent":["/**\n * Modal process manager. Each spawn() creates a ContainerProcess via exec().\n * kill() has no SDK equivalent — it cancels stream readers locally; the remote\n * process runs until the sandbox timeout.\n */\n\nimport { ProcessHandle, UnsupportedStdinCloseError, SandboxProcessManager } from '@mastra/core/workspace';\nimport type { CommandResult, ProcessInfo, SpawnProcessOptions } from '@mastra/core/workspace';\nimport type { ContainerProcess } from 'modal';\nimport type { ModalSandbox } from './index';\n\n// =============================================================================\n// Modal Process Handle\n// =============================================================================\n\n/**\n * Wraps a Modal ContainerProcess to conform to Mastra's ProcessHandle.\n */\nclass ModalProcessHandle extends ProcessHandle {\n readonly pid: string;\n\n private readonly _proc: ContainerProcess<string>;\n private readonly _startTime: number;\n private readonly _timeout?: number;\n\n private _exitCode: number | undefined;\n private _waitPromise: Promise<CommandResult> | null = null;\n private _streamingPromise: Promise<void> | null = null;\n private _killed = false;\n private _stdoutReader: ReadableStreamDefaultReader<string> | null = null;\n private _stderrReader: ReadableStreamDefaultReader<string> | null = null;\n\n constructor(pid: string, proc: ContainerProcess<string>, startTime: number, options?: SpawnProcessOptions) {\n super(options);\n this.pid = pid;\n this._proc = proc;\n this._startTime = startTime;\n this._timeout = options?.timeout;\n }\n\n get exitCode(): number | undefined {\n return this._exitCode;\n }\n\n /** @internal Set by the process manager after streaming starts. */\n set streamingPromise(p: Promise<void>) {\n this._streamingPromise = p;\n\n // Resolve exit code when streaming ends so exitCode is available without calling wait()\n p.then(() => this._resolveExitCode()).catch(() => this._resolveExitCode());\n }\n\n /** @internal Set by the process manager so kill() can cancel the readers. */\n setReaders(\n stdoutReader: ReadableStreamDefaultReader<string>,\n stderrReader: ReadableStreamDefaultReader<string>,\n ): void {\n this._stdoutReader = stdoutReader;\n this._stderrReader = stderrReader;\n }\n\n /** Fetch the exit code from the Modal process. No-op if already set. */\n private async _resolveExitCode(): Promise<void> {\n if (this._exitCode !== undefined) return;\n try {\n this._exitCode = await this._proc.wait();\n } catch {\n if (this._exitCode === undefined) {\n this._exitCode = 1;\n }\n }\n }\n\n async wait(): Promise<CommandResult> {\n if (!this._waitPromise) {\n this._waitPromise = this._doWait();\n }\n return this._waitPromise;\n }\n\n private async _doWait(): Promise<CommandResult> {\n const streamDone = this._streamingPromise ?? Promise.resolve();\n\n if (this._timeout) {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(`Command timed out after ${this._timeout}ms`)), this._timeout);\n });\n\n try {\n await Promise.race([streamDone, timeoutPromise]);\n } catch (error) {\n if (error instanceof Error && error.message.includes('timed out')) {\n await this.kill();\n this._exitCode = 124; // conventional timeout exit code\n return {\n success: false,\n exitCode: 124,\n stdout: this.stdout,\n stderr: this.stderr || error.message,\n executionTimeMs: Date.now() - this._startTime,\n killed: true,\n timedOut: true,\n };\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n } else {\n await streamDone.catch(() => {});\n }\n\n if (this._killed) {\n return {\n success: false,\n exitCode: this._exitCode ?? 137,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n killed: true,\n timedOut: false,\n };\n }\n\n await this._resolveExitCode();\n\n return {\n success: this._exitCode === 0,\n exitCode: this._exitCode ?? 1,\n stdout: this.stdout,\n stderr: this.stderr,\n executionTimeMs: Date.now() - this._startTime,\n };\n }\n\n async kill(): Promise<boolean> {\n if (this._exitCode !== undefined) return false;\n this._killed = true;\n this._exitCode = 137; // SIGKILL\n // The remote process may continue running; Modal JS SDK has no per-exec kill.\n try {\n await this._stdoutReader?.cancel();\n } catch {\n // Ignore cancellation errors\n }\n try {\n await this._stderrReader?.cancel();\n } catch {\n // Ignore cancellation errors\n }\n return true;\n }\n\n async sendStdin(_data: string): Promise<void> {\n throw new Error('Modal JS SDK does not expose stdin on exec() — sendStdin() is not supported');\n }\n\n async closeStdin(): Promise<void> {\n throw new UnsupportedStdinCloseError(\n 'Modal JS SDK does not expose stdin on exec() — closeStdin() is not supported',\n );\n }\n}\n\n// =============================================================================\n// Modal Process Manager\n// =============================================================================\n\n/**\n * Modal implementation of SandboxProcessManager.\n * Uses the Modal SDK's exec() API with one ContainerProcess per spawn.\n */\nexport class ModalProcessManager extends SandboxProcessManager<ModalSandbox> {\n private _spawnCounter = 0;\n\n async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {\n return this.sandbox.retryOnDead(async () => {\n const sb = this.sandbox.modal;\n\n // The base spawn wrapper already merged the sandbox env into options.env\n const mergedEnv = { ...options.env };\n const env = Object.fromEntries(\n Object.entries(mergedEnv).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n\n // exec() takes string[] — wrap in sh -c to support pipes, redirects, etc.\n const argv = ['sh', '-c', command];\n\n const proc = await sb.exec(argv, {\n env: Object.keys(env).length > 0 ? env : undefined,\n workdir: options.cwd ?? this.sandbox.workingDirectory,\n timeoutMs: options.timeout,\n });\n\n const pid = `modal-proc-${Date.now().toString(36)}-${++this._spawnCounter}`;\n const handle = new ModalProcessHandle(pid, proc, Date.now(), options);\n\n const stdoutReader = proc.stdout.getReader();\n const stderrReader = proc.stderr.getReader();\n handle.setReaders(stdoutReader, stderrReader);\n\n const streamingPromise = Promise.all([\n drainReader(stdoutReader, chunk => handle.emitStdout(chunk)),\n drainReader(stderrReader, chunk => handle.emitStderr(chunk)),\n ]).then(() => {});\n\n handle.streamingPromise = streamingPromise;\n\n this._tracked.set(pid, handle);\n return handle;\n });\n }\n\n async list(): Promise<ProcessInfo[]> {\n const result: ProcessInfo[] = [];\n for (const [pid, handle] of this._tracked) {\n result.push({\n pid,\n command: handle.command,\n running: handle.exitCode === undefined,\n exitCode: handle.exitCode,\n });\n }\n return result;\n }\n}\n\n// =============================================================================\n// Stream Helpers\n// =============================================================================\n\n/** Reads chunks from a stream until done or cancelled. */\nasync function drainReader(reader: ReadableStreamDefaultReader<string>, emit: (chunk: string) => void): Promise<void> {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n emit(value);\n }\n } catch {\n // cancelled or network error\n }\n}\n","/**\n * @see https://modal.com/docs/reference/modal.Sandbox\n */\n\nimport type { MastraSandboxOptions, ProviderStatus, SandboxCloneOptions, SandboxInfo } from '@mastra/core/workspace';\nimport { MastraSandbox, SandboxNotReadyError } from '@mastra/core/workspace';\nimport { ClientClosedError, ModalClient, NotFoundError } from 'modal';\nimport type { App, Image, Sandbox } from 'modal';\nimport { ModalProcessManager } from './process-manager';\n\nconst LOG_PREFIX = '[ModalSandbox]';\n\n// =============================================================================\n// Options\n// =============================================================================\n\ntype InstructionsOption = string | ((opts: { defaultInstructions: string }) => string);\n\n/**\n * Modal sandbox provider configuration.\n */\nexport interface ModalSandboxOptions extends Omit<MastraSandboxOptions, 'processes'> {\n /** Stable name for this sandbox. Reusing the same id reconnects to a running sandbox. */\n id?: string;\n /**\n * Modal App name to associate sandboxes with.\n *\n * @default 'mastra'\n */\n appName?: string;\n /**\n * Docker image to use for the sandbox.\n *\n * @default 'ubuntu:22.04'\n */\n baseImage?: string;\n /**\n * Wall-clock max lifetime in milliseconds. The sandbox is terminated when this expires,\n * regardless of activity. Modal's maximum is 24 hours (86_400_000).\n *\n * @default 300_000 // 5 minutes\n */\n timeoutMs?: number;\n /** Environment variables baked into the sandbox at create time. */\n env?: Record<string, string>;\n /** Default working directory inside the sandbox.\n * @deprecated Use `workingDirectory` (the base sandbox option) instead.\n * When both are set, `workingDirectory` wins.\n */\n workdir?: string;\n /** Modal token ID. Falls back to MODAL_TOKEN_ID env var. */\n tokenId?: string;\n /** Modal token secret. Falls back to MODAL_TOKEN_SECRET env var. */\n tokenSecret?: string;\n /** Custom instructions for getInstructions(). String replaces the default; function receives it. */\n instructions?: InstructionsOption;\n}\n\n// =============================================================================\n// ModalSandbox\n// =============================================================================\n\n/**\n * Modal cloud sandbox provider for Mastra workspaces.\n *\n * @example\n * ```typescript\n * import { Workspace } from '@mastra/core/workspace';\n * import { ModalSandbox } from '@mastra/modal';\n *\n * const sandbox = new ModalSandbox({\n * baseImage: 'ubuntu:22.04',\n * timeoutMs: 60_000,\n * });\n *\n * const workspace = new Workspace({ sandbox });\n * const result = await workspace.executeCommand('echo hello');\n * ```\n */\nexport class ModalSandbox extends MastraSandbox {\n readonly id: string;\n readonly name = 'ModalSandbox';\n readonly provider = 'modal';\n status: ProviderStatus = 'pending';\n\n declare readonly processes: ModalProcessManager;\n\n private _sb: Sandbox | null = null;\n private _imageSnapshot: Image | null = null; // for stop-and-resume\n private _client: ModalClient | null = null;\n private _createdAt: Date | null = null;\n private _isRetrying = false;\n\n private readonly appName: string;\n private readonly baseImage: string;\n private readonly timeoutMs: number;\n private readonly env: Record<string, string>;\n private readonly tokenId?: string;\n private readonly tokenSecret?: string;\n private readonly _instructionsOverride?: InstructionsOption;\n private readonly _constructorOptions: ModalSandboxOptions;\n\n constructor(options: ModalSandboxOptions = {}) {\n super({\n ...options,\n name: 'ModalSandbox',\n processes: new ModalProcessManager(),\n });\n\n this.id = options.id ?? this._generateId();\n this.appName = options.appName ?? 'mastra';\n this.baseImage = options.baseImage ?? 'ubuntu:22.04';\n this.timeoutMs = options.timeoutMs ?? 300_000;\n this.env = options.env ?? {};\n if (options.workingDirectory === undefined && options.workdir !== undefined) {\n this.setWorkingDirectory(options.workdir);\n }\n this.tokenId = options.tokenId;\n this.tokenSecret = options.tokenSecret;\n this._instructionsOverride = options.instructions;\n this._constructorOptions = { ...options };\n }\n\n /**\n * Construct a sibling `ModalSandbox` that inherits this sandbox's\n * configuration (credentials, app, base image, workdir, instructions) with\n * per-instance overrides.\n *\n * Performs no I/O — the sandbox clone provisions (or reconnects to a\n * running Modal sandbox with the same logical `id`) on its own `start()`.\n * Use it when one configured sandbox acts as the template for a fleet of\n * independent sandboxes (e.g. one per project).\n *\n * `options.idleTimeoutMinutes` maps to Modal's `timeoutMs` (wall-clock\n * lifetime); `options.sandboxId` is ignored because Modal reconnects by\n * logical `id`.\n */\n clone(options: SandboxCloneOptions = {}): ModalSandbox {\n const { id: _id, ...base } = this._constructorOptions;\n return new ModalSandbox({\n ...base,\n ...(options.id !== undefined && { id: options.id }),\n ...(options.env !== undefined && { env: options.env }),\n ...(options.idleTimeoutMinutes !== undefined && { timeoutMs: options.idleTimeoutMinutes * 60_000 }),\n });\n }\n\n /**\n * Get the underlying Modal Sandbox instance for direct SDK access.\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started.\n */\n get modal(): Sandbox {\n if (!this._sb) {\n throw new SandboxNotReadyError(this.id);\n }\n return this._sb;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /** Reconnects to a running sandbox with this id if one exists, otherwise creates a new one. */\n async start(): Promise<void> {\n if (this._sb) {\n return;\n }\n\n const client = this._getClient();\n\n try {\n this._sb = await client.sandboxes.fromName(this.appName, this.id);\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Reconnected to running sandbox: ${this.id}`);\n return;\n } catch (error) {\n if (!(error instanceof NotFoundError)) {\n throw error;\n }\n }\n\n const app: App = await client.apps.fromName(this.appName, { createIfMissing: true });\n\n if (this._imageSnapshot) {\n this.logger.debug(`${LOG_PREFIX} Rebooting from snapshot: ${this.id}`);\n this._sb = await client.sandboxes.create(app, this._imageSnapshot, {\n name: this.id,\n timeoutMs: this.timeoutMs,\n env: Object.keys(this.env).length > 0 ? this.env : undefined,\n workdir: this.workingDirectory,\n });\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Created new sandbox from snapshot: ${this._sb?.sandboxId}`);\n return;\n }\n\n const image: Image = client.images.fromRegistry(this.baseImage);\n this.logger.debug(`${LOG_PREFIX} Creating sandbox: ${this.id} (baseImage: ${this.baseImage})`);\n this._sb = await client.sandboxes.create(app, image, {\n name: this.id,\n timeoutMs: this.timeoutMs,\n env: Object.keys(this.env).length > 0 ? this.env : undefined,\n workdir: this.workingDirectory,\n });\n this._createdAt = new Date();\n this.logger.debug(`${LOG_PREFIX} Created sandbox: ${this._sb.sandboxId}`);\n }\n\n /**\n * Snapshot the sandbox filesystem before terminating it.\n * Future starts will create net-new sandboxes from the snapshot.\n */\n async stop(): Promise<void> {\n if (!this._sb) return;\n\n try {\n const procs = await this.processes.list();\n await Promise.all(procs.filter(p => p.running).map(p => this.processes.kill(p.pid)));\n } catch {\n // Best-effort: sandbox may already be dead\n }\n\n try {\n this._imageSnapshot = await this._sb.snapshotFilesystem();\n this.logger.debug(`${LOG_PREFIX} Snapshot created: ${this._imageSnapshot.imageId}`);\n } catch (error) {\n this.logger.debug(`${LOG_PREFIX} Snapshot failed, terminating without snapshot:`, error);\n }\n\n try {\n await this._sb.terminate({ wait: true });\n this.logger.debug(`${LOG_PREFIX} Sandbox terminated: ${this._sb.sandboxId}`);\n this._sb = null;\n } catch (error) {\n // Best-effort: sandbox may already be dead\n if (this.isSandboxDeadError(error)) {\n this._sb = null;\n } else {\n throw error;\n }\n }\n }\n\n /** Terminates the sandbox, ending its lifetime. Unlike stop(), no snapshot is preserved. */\n async destroy(): Promise<void> {\n if (this._sb) {\n try {\n const procs = await this.processes.list();\n await Promise.all(procs.filter(p => p.running).map(p => this.processes.kill(p.pid)));\n } catch {\n // Best-effort: sandbox may already be dead\n }\n\n try {\n await this._sb.terminate();\n this.logger.debug(`${LOG_PREFIX} Sandbox terminated: ${this._sb.sandboxId}`);\n } catch {\n // Ignore errors during destroy\n }\n\n this._sb = null;\n }\n\n this._imageSnapshot = null;\n }\n\n async getInfo(): Promise<SandboxInfo> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n createdAt: this._createdAt ?? new Date(),\n metadata: {\n appName: this.appName,\n image: this._imageSnapshot?.imageId ?? this.baseImage,\n timeoutMs: this.timeoutMs,\n },\n };\n }\n\n getInstructions(): string {\n const defaultInstructions = this._getDefaultInstructions();\n if (this._instructionsOverride === undefined) return defaultInstructions;\n if (typeof this._instructionsOverride === 'string') return this._instructionsOverride;\n return this._instructionsOverride({ defaultInstructions });\n }\n\n private _getDefaultInstructions(): string {\n return `Modal cloud sandbox running ${this.baseImage}. Use executeCommand() to run shell commands.`;\n }\n\n // ---------------------------------------------------------------------------\n // Dead-sandbox Retry\n // ---------------------------------------------------------------------------\n\n private isSandboxDeadError(error: unknown): boolean {\n if (!error) return false;\n if (error instanceof ClientClosedError) return true;\n if (error instanceof NotFoundError) return true;\n const errorStr = String(error);\n return (\n errorStr.includes('sandbox not found') ||\n errorStr.includes('has been terminated') ||\n errorStr.includes('already completed') ||\n errorStr.includes('was cancelled') ||\n // gRPC NOT_FOUND (code 5)\n /status[:\\s]+5\\b/.test(errorStr) ||\n errorStr.includes('NOT_FOUND')\n );\n }\n\n private handleSandboxDead(): void {\n this._sb = null;\n this.status = 'stopped';\n }\n\n /** @internal Retries fn() once after restarting if the sandbox is dead. */\n async retryOnDead<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n if (this.isSandboxDeadError(error) && !this._isRetrying) {\n this.handleSandboxDead();\n this._isRetrying = true;\n try {\n await this.ensureRunning();\n return await fn();\n } finally {\n this._isRetrying = false;\n }\n }\n throw error;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal Helpers\n // ---------------------------------------------------------------------------\n\n private _generateId(): string {\n return `modal-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n private _getClient(): ModalClient {\n if (!this._client) {\n this._client = new ModalClient({\n ...(this.tokenId && { tokenId: this.tokenId }),\n ...(this.tokenSecret && { tokenSecret: this.tokenSecret }),\n });\n }\n return this._client;\n }\n}\n"],"mappings":"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"}
@@ -35,7 +35,10 @@ export interface ModalSandboxOptions extends Omit<MastraSandboxOptions, 'process
35
35
  timeoutMs?: number;
36
36
  /** Environment variables baked into the sandbox at create time. */
37
37
  env?: Record<string, string>;
38
- /** Default working directory inside the sandbox. */
38
+ /** Default working directory inside the sandbox.
39
+ * @deprecated Use `workingDirectory` (the base sandbox option) instead.
40
+ * When both are set, `workingDirectory` wins.
41
+ */
39
42
  workdir?: string;
40
43
  /** Modal token ID. Falls back to MODAL_TOKEN_ID env var. */
41
44
  tokenId?: string;
@@ -76,7 +79,6 @@ export declare class ModalSandbox extends MastraSandbox {
76
79
  private readonly baseImage;
77
80
  private readonly timeoutMs;
78
81
  private readonly env;
79
- private readonly workdir?;
80
82
  private readonly tokenId?;
81
83
  private readonly tokenSecret?;
82
84
  private readonly _instructionsOverride?;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/sandbox/process-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAA8B,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC1G,OAAO,KAAK,EAAiB,WAAW,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE9F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAgK5C;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,qBAAqB,CAAC,YAAY,CAAC;IAC1E,OAAO,CAAC,aAAa,CAAK;IAEpB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,aAAa,CAAC;IAsCjF,IAAI,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;CAYrC"}
1
+ {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/sandbox/process-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAA8B,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC1G,OAAO,KAAK,EAAiB,WAAW,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE9F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAgK5C;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,qBAAqB,CAAC,YAAY,CAAC;IAC1E,OAAO,CAAC,aAAa,CAAK;IAEpB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,aAAa,CAAC,CAoCtF;IAEK,IAAI,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAWnC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/modal",
3
- "version": "0.5.1",
3
+ "version": "0.6.0-alpha.1",
4
4
  "description": "Modal cloud sandbox provider for Mastra workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,19 +29,18 @@
29
29
  "dotenv": "^17.4.2",
30
30
  "eslint": "^10.7.0",
31
31
  "tsdown": "0.22.9",
32
- "typescript": "^6.0.3",
32
+ "typescript": "^7.0.2",
33
33
  "vitest": "4.1.10",
34
- "@internal/lint": "0.0.126",
35
- "@internal/types-builder": "0.0.101",
36
- "@internal/workspace-test-utils": "0.0.70",
37
- "@mastra/core": "1.62.0"
34
+ "@internal/lint": "0.0.129",
35
+ "@internal/workspace-test-utils": "0.0.73",
36
+ "@mastra/core": "1.64.0-alpha.7",
37
+ "@internal/types-builder": "0.0.104"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@mastra/core": ">=1.12.0-0 <2.0.0-0"
41
41
  },
42
42
  "files": [
43
- "dist",
44
- "CHANGELOG.md"
43
+ "dist"
45
44
  ],
46
45
  "homepage": "https://mastra.ai",
47
46
  "repository": {
package/CHANGELOG.md DELETED
@@ -1,211 +0,0 @@
1
- # @mastra/modal
2
-
3
- ## 0.5.1
4
-
5
- ### Patch Changes
6
-
7
- - Honor the sandbox runtime environment (`setEnv()`/`getEnv()` from `@mastra/core`) in every workspace sandbox provider. Environment variables set after construction now reach subsequent commands on all providers: ([#22250](https://github.com/mastra-ai/mastra/pull/22250))
8
-
9
- - Process-manager-routed providers (E2B, Blaxel, Cloudflare, Daytona, Docker, Modal, Vercel microVM spawns) inherit the merge from the core spawn wrapper; their duplicated per-manager env plumbing is removed.
10
- - Providers with their own exec transports (AgentCore, Apple Container, Railway, Vercel microVM and serverless `executeCommand`, Platform's private-network, WebSocket lease, and E2B lease paths) now merge `getEnv()` under per-call env.
11
-
12
- Constructor `env` continues to behave as before: it seeds the sandbox runtime environment, and providers that bake env into the VM or container at creation time (Docker, Modal, Railway, Apple Container, Vercel, Platform) still do so. Per-call `env` on `executeCommand` still takes precedence for that command only.
13
-
14
- Removed exported types (minor bump for these two packages): `BlaxelProcessManagerOptions` from `@mastra/blaxel` and `RailwayProcessManagerOptions` from `@mastra/railway`. Both existed only to pass `env` into a process manager constructed by hand; the core spawn wrapper now owns that merge, so the option and its type are gone.
15
-
16
- - Updated dependencies [[`79f04a7`](https://github.com/mastra-ai/mastra/commit/79f04a7f6c6829da541139f638f2f1d267916e08), [`65edab1`](https://github.com/mastra-ai/mastra/commit/65edab1c233d17b8f163bad12fca410d0e6f16b1), [`1e47b75`](https://github.com/mastra-ai/mastra/commit/1e47b7520cab4cfaa8daed52f17e2e6d14ff7539), [`ab20a38`](https://github.com/mastra-ai/mastra/commit/ab20a38d0275f8d85e0f3833bd87ef487bcc609f), [`fd4d5fe`](https://github.com/mastra-ai/mastra/commit/fd4d5fe4f943699b85db5e74404f190d5a6b8c2a), [`ae8790c`](https://github.com/mastra-ai/mastra/commit/ae8790c4bfaa088d2ab279d1dcc06f326b9fd109), [`2c85f42`](https://github.com/mastra-ai/mastra/commit/2c85f428e04ccd63ea31a7ec80b5b327afdad555), [`11bbeb9`](https://github.com/mastra-ai/mastra/commit/11bbeb9b108ef2264e05acefc6dafb9cbb342921), [`48ef1f1`](https://github.com/mastra-ai/mastra/commit/48ef1f1d24eedafbb07f64e659a81b52b67b8bf6), [`aa3a85d`](https://github.com/mastra-ai/mastra/commit/aa3a85daf094c683bb97efdf4b6a696d2e474af5), [`d29d06f`](https://github.com/mastra-ai/mastra/commit/d29d06fe00bbd35b4571150ea04c59d2ed783c71), [`e6516df`](https://github.com/mastra-ai/mastra/commit/e6516dfcdae4f4ac0e7971d84359a81385ee602f), [`1a485f3`](https://github.com/mastra-ai/mastra/commit/1a485f3538f5ec64d58bd8b5e1e99de0c695c87b), [`0d37487`](https://github.com/mastra-ai/mastra/commit/0d37487d9f349388a3f1cef6a536cf9dcc4b6273), [`8661d7d`](https://github.com/mastra-ai/mastra/commit/8661d7d7179f0a024456aabdd8679bcecd09ac28), [`dbbfeb8`](https://github.com/mastra-ai/mastra/commit/dbbfeb85ec949dc9ebc0755e1ad262e4f5eba8db), [`575e343`](https://github.com/mastra-ai/mastra/commit/575e343900451021d96110916497d334af7bc252), [`0b2a3d1`](https://github.com/mastra-ai/mastra/commit/0b2a3d1783875c5b97b7b36ab3d03d7360e0dde7), [`6bb5d71`](https://github.com/mastra-ai/mastra/commit/6bb5d7193fe9166b219f0fccae17db7a5ae86e65), [`3cc9d00`](https://github.com/mastra-ai/mastra/commit/3cc9d00b2b4333e0377a5e9df5eff92c17ce7630), [`cacb839`](https://github.com/mastra-ai/mastra/commit/cacb8392d9e74189b56d857290b0615f98a2683d), [`57de7d6`](https://github.com/mastra-ai/mastra/commit/57de7d644ba7146edb4e9e6111ec4fa98c3a59e9), [`c8e4cea`](https://github.com/mastra-ai/mastra/commit/c8e4ceac9a390d78c8327dff3cdb2861dd71957f), [`ed01e9a`](https://github.com/mastra-ai/mastra/commit/ed01e9a807514a904374bf687a7b8f18750f6f78), [`b47b26e`](https://github.com/mastra-ai/mastra/commit/b47b26e6fe95cb8a3482be2c5e52de157fe59d0b), [`0d37487`](https://github.com/mastra-ai/mastra/commit/0d37487d9f349388a3f1cef6a536cf9dcc4b6273), [`733a537`](https://github.com/mastra-ai/mastra/commit/733a537489a858b5880b2e98809334fba895a221), [`e8e299c`](https://github.com/mastra-ai/mastra/commit/e8e299cc6abdfc39947e2fec25803493015d3882), [`edfc548`](https://github.com/mastra-ai/mastra/commit/edfc548886bc7bae17b681f8b6b41a47eb32bcd2), [`b05f486`](https://github.com/mastra-ai/mastra/commit/b05f48612984d5fe2447ea2d6cdd5c604d285b97), [`a8a4871`](https://github.com/mastra-ai/mastra/commit/a8a4871215f51da95c47129602157ce5372f634a), [`eb9ecaa`](https://github.com/mastra-ai/mastra/commit/eb9ecaa89c36e889749e3b825cfc507ce7f7980b), [`4ff3ee2`](https://github.com/mastra-ai/mastra/commit/4ff3ee2bff7ed07528b4817f8f49639031c72a4d), [`9207dfa`](https://github.com/mastra-ai/mastra/commit/9207dfab8062e5fc68b751684797ff86fe0b4e70), [`5165cdc`](https://github.com/mastra-ai/mastra/commit/5165cdcdcf50e144bb8113278535196cc9b07065), [`e737014`](https://github.com/mastra-ai/mastra/commit/e737014e0fc7035759762bb5b48baef1d6c0f6a7), [`6bb5d71`](https://github.com/mastra-ai/mastra/commit/6bb5d7193fe9166b219f0fccae17db7a5ae86e65), [`f591643`](https://github.com/mastra-ai/mastra/commit/f591643becdf0be9bddce6ba1748e64bc30d77f1), [`63796ba`](https://github.com/mastra-ai/mastra/commit/63796ba0fda60253be17535e68f6bbbf1e6ffa09), [`b1ad324`](https://github.com/mastra-ai/mastra/commit/b1ad324d657f3544b0701332aef7eb10e9a36258), [`61c566d`](https://github.com/mastra-ai/mastra/commit/61c566dd2f2cde2b23ed8f139924e530d4202214), [`c24754c`](https://github.com/mastra-ai/mastra/commit/c24754c1fb6fe144e5051e536e98c8a18b0214ac), [`12c61d2`](https://github.com/mastra-ai/mastra/commit/12c61d280c8cb208bc3c8dbcbe5dcc60cf9d1cd0), [`c46eb09`](https://github.com/mastra-ai/mastra/commit/c46eb09ce4987509af57a0ac582c61241a6dd2f1), [`9ee8120`](https://github.com/mastra-ai/mastra/commit/9ee8120ce17f76b9f617489e05a283353742690a), [`d975e92`](https://github.com/mastra-ai/mastra/commit/d975e924d4936f46c386bd3dee39c671720289f6), [`45dd6ee`](https://github.com/mastra-ai/mastra/commit/45dd6ee089bd7df0d0c98a10098e483fd388e04a), [`4e9a228`](https://github.com/mastra-ai/mastra/commit/4e9a2283d5fd6ed1b70a2751eb3dc2cbf82ada20), [`d6ce34a`](https://github.com/mastra-ai/mastra/commit/d6ce34aeceb06ddf3d595a1eed5cc74f481a46a1), [`f95f468`](https://github.com/mastra-ai/mastra/commit/f95f468cf1e7c2b924a13826494f98b8f2ccd581), [`30ed33e`](https://github.com/mastra-ai/mastra/commit/30ed33ee14084a26019aba15fceadda6d6ddefaf), [`04a815f`](https://github.com/mastra-ai/mastra/commit/04a815fc8971d29e97fcdcc5008a1eb472fc00ff), [`1cfa878`](https://github.com/mastra-ai/mastra/commit/1cfa8784d8da0dfaa0317e5048bc48b6084a5ea5), [`9a12ef3`](https://github.com/mastra-ai/mastra/commit/9a12ef3fccf3f4186db0f294f4ee1f02cf4d8db2), [`32d3583`](https://github.com/mastra-ai/mastra/commit/32d358332cb8ac2306b83b73cf3536e74dbd435e), [`7960688`](https://github.com/mastra-ai/mastra/commit/7960688828e04eaf3106e34f7758fa580257eef6), [`91ad69d`](https://github.com/mastra-ai/mastra/commit/91ad69d64994c89199b0c55399e64ed91c61df2f), [`8dc408d`](https://github.com/mastra-ai/mastra/commit/8dc408d34438f9e13297f792c11a5cfd6cf952e1), [`c92def1`](https://github.com/mastra-ai/mastra/commit/c92def10a13c822972c96f0a4ca6ffc1f4258aed), [`63041eb`](https://github.com/mastra-ai/mastra/commit/63041eb4c50b520a0a80e03d4cd6ea99f67715a0), [`c118318`](https://github.com/mastra-ai/mastra/commit/c1183181c9804303db4b511c2e2648f8b714712b), [`c5eaec5`](https://github.com/mastra-ai/mastra/commit/c5eaec5a860d80d0e3805e67db0414b87ac8cbed), [`fc07c64`](https://github.com/mastra-ai/mastra/commit/fc07c6465043e08e99193a6751a01c56ffc2e7a1), [`cced745`](https://github.com/mastra-ai/mastra/commit/cced745a056ec2225c5bc702e32d848847aa8b65), [`542dee2`](https://github.com/mastra-ai/mastra/commit/542dee254167f974ff8cbbbfc0ce10f9a2616a7b), [`3c19dce`](https://github.com/mastra-ai/mastra/commit/3c19dcef8e73062a80627a4927eae3ec11145afd), [`aca2869`](https://github.com/mastra-ai/mastra/commit/aca2869b2031982f3c4a2f52525c9be7cf123ef8), [`a58483c`](https://github.com/mastra-ai/mastra/commit/a58483cff1a9d41fce7c931843f48cb0ac450f64), [`a58483c`](https://github.com/mastra-ai/mastra/commit/a58483cff1a9d41fce7c931843f48cb0ac450f64), [`e6f8450`](https://github.com/mastra-ai/mastra/commit/e6f845074d478527026b18d85031b23353e1d0a4), [`895e9df`](https://github.com/mastra-ai/mastra/commit/895e9dfc17d6f34299eca64e317ded9e5f5e5ef8), [`e66b2ba`](https://github.com/mastra-ai/mastra/commit/e66b2ba100db63eaeab6e21e1ea34b113f2ec781), [`3e8727e`](https://github.com/mastra-ai/mastra/commit/3e8727e11ec1a5d733acedb5c872896394be18c1)]:
17
- - @mastra/core@1.62.0
18
-
19
- ## 0.5.1-alpha.0
20
-
21
- ### Patch Changes
22
-
23
- - Honor the sandbox runtime environment (`setEnv()`/`getEnv()` from `@mastra/core`) in every workspace sandbox provider. Environment variables set after construction now reach subsequent commands on all providers: ([#22250](https://github.com/mastra-ai/mastra/pull/22250))
24
-
25
- - Process-manager-routed providers (E2B, Blaxel, Cloudflare, Daytona, Docker, Modal, Vercel microVM spawns) inherit the merge from the core spawn wrapper; their duplicated per-manager env plumbing is removed.
26
- - Providers with their own exec transports (AgentCore, Apple Container, Railway, Vercel microVM and serverless `executeCommand`, Platform's private-network, WebSocket lease, and E2B lease paths) now merge `getEnv()` under per-call env.
27
-
28
- Constructor `env` continues to behave as before: it seeds the sandbox runtime environment, and providers that bake env into the VM or container at creation time (Docker, Modal, Railway, Apple Container, Vercel, Platform) still do so. Per-call `env` on `executeCommand` still takes precedence for that command only.
29
-
30
- Removed exported types (minor bump for these two packages): `BlaxelProcessManagerOptions` from `@mastra/blaxel` and `RailwayProcessManagerOptions` from `@mastra/railway`. Both existed only to pass `env` into a process manager constructed by hand; the core spawn wrapper now owns that merge, so the option and its type are gone.
31
-
32
- - Updated dependencies [[`aa3a85d`](https://github.com/mastra-ai/mastra/commit/aa3a85daf094c683bb97efdf4b6a696d2e474af5), [`d29d06f`](https://github.com/mastra-ai/mastra/commit/d29d06fe00bbd35b4571150ea04c59d2ed783c71), [`e6516df`](https://github.com/mastra-ai/mastra/commit/e6516dfcdae4f4ac0e7971d84359a81385ee602f), [`0b2a3d1`](https://github.com/mastra-ai/mastra/commit/0b2a3d1783875c5b97b7b36ab3d03d7360e0dde7), [`6bb5d71`](https://github.com/mastra-ai/mastra/commit/6bb5d7193fe9166b219f0fccae17db7a5ae86e65), [`57de7d6`](https://github.com/mastra-ai/mastra/commit/57de7d644ba7146edb4e9e6111ec4fa98c3a59e9), [`e8e299c`](https://github.com/mastra-ai/mastra/commit/e8e299cc6abdfc39947e2fec25803493015d3882), [`edfc548`](https://github.com/mastra-ai/mastra/commit/edfc548886bc7bae17b681f8b6b41a47eb32bcd2), [`a8a4871`](https://github.com/mastra-ai/mastra/commit/a8a4871215f51da95c47129602157ce5372f634a), [`5165cdc`](https://github.com/mastra-ai/mastra/commit/5165cdcdcf50e144bb8113278535196cc9b07065), [`6bb5d71`](https://github.com/mastra-ai/mastra/commit/6bb5d7193fe9166b219f0fccae17db7a5ae86e65), [`9ee8120`](https://github.com/mastra-ai/mastra/commit/9ee8120ce17f76b9f617489e05a283353742690a), [`d975e92`](https://github.com/mastra-ai/mastra/commit/d975e924d4936f46c386bd3dee39c671720289f6), [`1cfa878`](https://github.com/mastra-ai/mastra/commit/1cfa8784d8da0dfaa0317e5048bc48b6084a5ea5), [`c118318`](https://github.com/mastra-ai/mastra/commit/c1183181c9804303db4b511c2e2648f8b714712b), [`fc07c64`](https://github.com/mastra-ai/mastra/commit/fc07c6465043e08e99193a6751a01c56ffc2e7a1), [`542dee2`](https://github.com/mastra-ai/mastra/commit/542dee254167f974ff8cbbbfc0ce10f9a2616a7b), [`a58483c`](https://github.com/mastra-ai/mastra/commit/a58483cff1a9d41fce7c931843f48cb0ac450f64), [`a58483c`](https://github.com/mastra-ai/mastra/commit/a58483cff1a9d41fce7c931843f48cb0ac450f64), [`895e9df`](https://github.com/mastra-ai/mastra/commit/895e9dfc17d6f34299eca64e317ded9e5f5e5ef8)]:
33
- - @mastra/core@1.62.0-alpha.8
34
-
35
- ## 0.5.0
36
-
37
- ### Minor Changes
38
-
39
- - Added `ProcessHandle.closeStdin()` to signal end-of-file to background processes. Local and Docker sandboxes support closing stdin, while providers without an available stdin-close API return a provider-specific unsupported-operation error. Providers signal the unsupported case with the new `UnsupportedStdinCloseError`, and the base class supplies that behavior by default so existing `ProcessHandle` subclasses keep compiling. Calling `handle.writer.end()` also closes stdin, and finishes without an error when the provider cannot close stdin. ([#21606](https://github.com/mastra-ai/mastra/pull/21606))
40
-
41
- ### Patch Changes
42
-
43
- - Updated dependencies [[`587f6ef`](https://github.com/mastra-ai/mastra/commit/587f6efcfc25880b93760a8607d1cd381ec612fe), [`7e096f0`](https://github.com/mastra-ai/mastra/commit/7e096f02f0dddbf09b85d306458351245ed2f886), [`d7e6745`](https://github.com/mastra-ai/mastra/commit/d7e67456954863c55440ea9c49bc6ceb9949972d), [`6223446`](https://github.com/mastra-ai/mastra/commit/6223446ddce6166e96e0ba5e00d628b615dee8ca), [`15101bb`](https://github.com/mastra-ai/mastra/commit/15101bb53c0d934f31af6b8813b88191e382a5e5), [`4e7a421`](https://github.com/mastra-ai/mastra/commit/4e7a421dce8a48742f785d1e93ad2f43a572b282), [`c2c3deb`](https://github.com/mastra-ai/mastra/commit/c2c3debcf670c7082d0a5e553aa99818a864698c), [`d8308a2`](https://github.com/mastra-ai/mastra/commit/d8308a2be3c07e777393d1017a381dcae3890d30), [`b0a2a07`](https://github.com/mastra-ai/mastra/commit/b0a2a07800d42bd9823292e7db832374ed084c9c), [`74e5bd3`](https://github.com/mastra-ai/mastra/commit/74e5bd315b8b3a1e04cb6cf480bb0f5fc4951dc8), [`242e324`](https://github.com/mastra-ai/mastra/commit/242e3241e73cbd5c9bb86a31ebb49ca0256488d4), [`217e967`](https://github.com/mastra-ai/mastra/commit/217e9672d8b3160eb729d8e9f0044949e88da239), [`d774e89`](https://github.com/mastra-ai/mastra/commit/d774e8930c781df8c9effe3763e6b501c099b6cc), [`9c27a53`](https://github.com/mastra-ai/mastra/commit/9c27a53cd9d3de4f3f025bc387d94ce371c33f95), [`8f0a332`](https://github.com/mastra-ai/mastra/commit/8f0a3321bf180368d76fe7b36aa1a8f60f00b6de), [`0b4f108`](https://github.com/mastra-ai/mastra/commit/0b4f1089aa8d92e67c2a8e99726822c5ee410784), [`9acb50f`](https://github.com/mastra-ai/mastra/commit/9acb50f71cec9c362f06820033f90ae6b1f8282f), [`46e9e3f`](https://github.com/mastra-ai/mastra/commit/46e9e3f73babe1bc70080a596cf2ac0b9da48519), [`3f9a190`](https://github.com/mastra-ai/mastra/commit/3f9a19057c027155867b9317294ee4ca7bd0581a), [`dff25a1`](https://github.com/mastra-ai/mastra/commit/dff25a1103fa72ee082a9b6f805ebeb5ce400753), [`6db7a5d`](https://github.com/mastra-ai/mastra/commit/6db7a5dd3dd2b6f7ef75dcd804fcffef5fa83963), [`217e967`](https://github.com/mastra-ai/mastra/commit/217e9672d8b3160eb729d8e9f0044949e88da239), [`583e235`](https://github.com/mastra-ai/mastra/commit/583e23519c13af16c1746f9c49722d011216611b), [`b098de9`](https://github.com/mastra-ai/mastra/commit/b098de9d7cb9f672e0883a5c716465a3a689693d), [`e8808e3`](https://github.com/mastra-ai/mastra/commit/e8808e3d8eb585a2565be53e56a7e0e1477352a4), [`a77f8d4`](https://github.com/mastra-ai/mastra/commit/a77f8d4740d2178a74c41e4bf678b4fcd8fa0bb2), [`7f78585`](https://github.com/mastra-ai/mastra/commit/7f785857e401570e2ffb316911f126ed363aa537), [`33374ba`](https://github.com/mastra-ai/mastra/commit/33374ba359e4fb13eaa918ae925fe167a3c55414), [`940bf5c`](https://github.com/mastra-ai/mastra/commit/940bf5ccf04f2c9ebd8a1390431733222a03b1cd), [`c549e2f`](https://github.com/mastra-ai/mastra/commit/c549e2f40edc1cac5d9e74e82f90da22b48df084), [`58c43d3`](https://github.com/mastra-ai/mastra/commit/58c43d3f7cb2eeaeb8ac733ae71dde822348e588), [`ef6e295`](https://github.com/mastra-ai/mastra/commit/ef6e295b59bc25a5b61b633a89c97bcfce9fb465), [`208e1b3`](https://github.com/mastra-ai/mastra/commit/208e1b39f30f4b386e494394e9d71d96f0f90241), [`c938d34`](https://github.com/mastra-ai/mastra/commit/c938d34739936c8ecbabd67ad6a4a4396f41c4c6), [`88ddc7c`](https://github.com/mastra-ai/mastra/commit/88ddc7ce01d40175f13a3228b789a906779680bd), [`f2a4afd`](https://github.com/mastra-ai/mastra/commit/f2a4afd7e37e809669001ed17724b341a5c1f45e), [`d438148`](https://github.com/mastra-ai/mastra/commit/d438148e222c1e2fb3c652725ce75680962ebec4), [`ba05fe0`](https://github.com/mastra-ai/mastra/commit/ba05fe0738f70cb686777546e968237d09269142), [`40d358e`](https://github.com/mastra-ai/mastra/commit/40d358e29d55543803e64b49241122f598ffabc7), [`d26a8d4`](https://github.com/mastra-ai/mastra/commit/d26a8d4281f28414715b333c85bedaf70d0b2890), [`e80cd7e`](https://github.com/mastra-ai/mastra/commit/e80cd7e7683e7d732e1cc6784bcac1d2640d2ce3), [`ccbbcd9`](https://github.com/mastra-ai/mastra/commit/ccbbcd974eedff4367a54ed0e24c9ee742ab2f61), [`1d9a0ea`](https://github.com/mastra-ai/mastra/commit/1d9a0ea4a9901baee6cd56737243bd6d1f631ac0), [`677cdc6`](https://github.com/mastra-ai/mastra/commit/677cdc6af564dec29a13464d12b7ab2a4efc22e9), [`c549e2f`](https://github.com/mastra-ai/mastra/commit/c549e2f40edc1cac5d9e74e82f90da22b48df084), [`a7dd322`](https://github.com/mastra-ai/mastra/commit/a7dd32247d95afc539f483ca37f4594af0387f59), [`3f5c6f7`](https://github.com/mastra-ai/mastra/commit/3f5c6f728ea35da344248de9aa070f12849f3aa0), [`a318490`](https://github.com/mastra-ai/mastra/commit/a318490e17da32f338d50929c770d901a9b3dd72), [`b860493`](https://github.com/mastra-ai/mastra/commit/b86049391100e665d579f700c8a2034c036defc3), [`d4be8c1`](https://github.com/mastra-ai/mastra/commit/d4be8c1739d22d621e3f78790e1dd5eb5ecc3589), [`a5d2eb1`](https://github.com/mastra-ai/mastra/commit/a5d2eb10347eade1ae2816d88f466c25186c54a5), [`3667679`](https://github.com/mastra-ai/mastra/commit/3667679db057edfb086846d13369fdda4902ad65), [`49696e8`](https://github.com/mastra-ai/mastra/commit/49696e8e42f870674a0a58f5abcd22cc54dd2864), [`2ef2f23`](https://github.com/mastra-ai/mastra/commit/2ef2f230a7aed342e7dc3b2000cd42e4c43e08a7), [`763e0c6`](https://github.com/mastra-ai/mastra/commit/763e0c61e04d76ad9a9efd301aa57525ca0cbea9), [`20504b2`](https://github.com/mastra-ai/mastra/commit/20504b2ecebd0e077acda3d457ab57480a98ed3e), [`77e6b1b`](https://github.com/mastra-ai/mastra/commit/77e6b1bc4c46ce94fe501023fb4393c812ec6be3), [`c5f964d`](https://github.com/mastra-ai/mastra/commit/c5f964d3f77064e978f8066ec506eed77ba5c63c), [`23e0be2`](https://github.com/mastra-ai/mastra/commit/23e0be261381e49534b4ff3101c60ee64a946cbf), [`7fc8806`](https://github.com/mastra-ai/mastra/commit/7fc880627d3cbf995d31ea0e8b807bf15417e651), [`0e02eac`](https://github.com/mastra-ai/mastra/commit/0e02eacdb2e30e1697a41910b41163742a181dc1), [`4df174c`](https://github.com/mastra-ai/mastra/commit/4df174c32bddf093a82f273070b8380aef7c9e90), [`f7c25b5`](https://github.com/mastra-ai/mastra/commit/f7c25b5106ddfb48e591f98df7a51e0f2dd01dba), [`7aad631`](https://github.com/mastra-ai/mastra/commit/7aad631b43bc10db77d5b8c66b200d7a49d18bf2), [`512100a`](https://github.com/mastra-ai/mastra/commit/512100a7d8b7e9c920f2590c6b3612f5de0d3cff), [`e81744c`](https://github.com/mastra-ai/mastra/commit/e81744cd13c46619c142dc521dc0baac47607a84), [`f8f653f`](https://github.com/mastra-ai/mastra/commit/f8f653f10980d01a73706cc3c8689ca5e40ce808), [`dc09cc1`](https://github.com/mastra-ai/mastra/commit/dc09cc1083d861cde192c1cd235324dc75b8c731), [`9ef432b`](https://github.com/mastra-ai/mastra/commit/9ef432b6faa534b57b0d182a610e13dd9a7123ff), [`36b4649`](https://github.com/mastra-ai/mastra/commit/36b4649045a3a380cbab8ceca866db4086223aff), [`b9cf308`](https://github.com/mastra-ai/mastra/commit/b9cf30846f97f99ac1906ee8a68f4f2d117b0378), [`2e1d098`](https://github.com/mastra-ai/mastra/commit/2e1d0984e325fd319d32ea182f596b3170be3847), [`377eb81`](https://github.com/mastra-ai/mastra/commit/377eb81ce43b964e3a6b541df172da74a8ff3716), [`1794a79`](https://github.com/mastra-ai/mastra/commit/1794a79178c418004a7261b1ad9114066f7ef01d), [`0cdc5dc`](https://github.com/mastra-ai/mastra/commit/0cdc5dc69024957815da4f51acc4119eb4f447d7), [`5740ec6`](https://github.com/mastra-ai/mastra/commit/5740ec60c760ffdfbfaa59d603d03b847c864e05)]:
44
- - @mastra/core@1.60.0
45
-
46
- ## 0.5.0-alpha.0
47
-
48
- ### Minor Changes
49
-
50
- - Added `ProcessHandle.closeStdin()` to signal end-of-file to background processes. Local and Docker sandboxes support closing stdin, while providers without an available stdin-close API return a provider-specific unsupported-operation error. Providers signal the unsupported case with the new `UnsupportedStdinCloseError`, and the base class supplies that behavior by default so existing `ProcessHandle` subclasses keep compiling. Calling `handle.writer.end()` also closes stdin, and finishes without an error when the provider cannot close stdin. ([#21606](https://github.com/mastra-ai/mastra/pull/21606))
51
-
52
- ### Patch Changes
53
-
54
- - Updated dependencies [[`4e7a421`](https://github.com/mastra-ai/mastra/commit/4e7a421dce8a48742f785d1e93ad2f43a572b282), [`242e324`](https://github.com/mastra-ai/mastra/commit/242e3241e73cbd5c9bb86a31ebb49ca0256488d4), [`217e967`](https://github.com/mastra-ai/mastra/commit/217e9672d8b3160eb729d8e9f0044949e88da239), [`d774e89`](https://github.com/mastra-ai/mastra/commit/d774e8930c781df8c9effe3763e6b501c099b6cc), [`9c27a53`](https://github.com/mastra-ai/mastra/commit/9c27a53cd9d3de4f3f025bc387d94ce371c33f95), [`dff25a1`](https://github.com/mastra-ai/mastra/commit/dff25a1103fa72ee082a9b6f805ebeb5ce400753), [`217e967`](https://github.com/mastra-ai/mastra/commit/217e9672d8b3160eb729d8e9f0044949e88da239), [`7f78585`](https://github.com/mastra-ai/mastra/commit/7f785857e401570e2ffb316911f126ed363aa537), [`f2a4afd`](https://github.com/mastra-ai/mastra/commit/f2a4afd7e37e809669001ed17724b341a5c1f45e), [`d438148`](https://github.com/mastra-ai/mastra/commit/d438148e222c1e2fb3c652725ce75680962ebec4), [`ba05fe0`](https://github.com/mastra-ai/mastra/commit/ba05fe0738f70cb686777546e968237d09269142), [`d26a8d4`](https://github.com/mastra-ai/mastra/commit/d26a8d4281f28414715b333c85bedaf70d0b2890), [`677cdc6`](https://github.com/mastra-ai/mastra/commit/677cdc6af564dec29a13464d12b7ab2a4efc22e9), [`a318490`](https://github.com/mastra-ai/mastra/commit/a318490e17da32f338d50929c770d901a9b3dd72), [`763e0c6`](https://github.com/mastra-ai/mastra/commit/763e0c61e04d76ad9a9efd301aa57525ca0cbea9), [`23e0be2`](https://github.com/mastra-ai/mastra/commit/23e0be261381e49534b4ff3101c60ee64a946cbf), [`7fc8806`](https://github.com/mastra-ai/mastra/commit/7fc880627d3cbf995d31ea0e8b807bf15417e651), [`0e02eac`](https://github.com/mastra-ai/mastra/commit/0e02eacdb2e30e1697a41910b41163742a181dc1), [`4df174c`](https://github.com/mastra-ai/mastra/commit/4df174c32bddf093a82f273070b8380aef7c9e90), [`f7c25b5`](https://github.com/mastra-ai/mastra/commit/f7c25b5106ddfb48e591f98df7a51e0f2dd01dba), [`dc09cc1`](https://github.com/mastra-ai/mastra/commit/dc09cc1083d861cde192c1cd235324dc75b8c731), [`36b4649`](https://github.com/mastra-ai/mastra/commit/36b4649045a3a380cbab8ceca866db4086223aff), [`377eb81`](https://github.com/mastra-ai/mastra/commit/377eb81ce43b964e3a6b541df172da74a8ff3716)]:
55
- - @mastra/core@1.60.0-alpha.8
56
-
57
- ## 0.4.0
58
-
59
- ### Minor Changes
60
-
61
- - Added `clone()` support to the sandbox providers. `clone()` constructs an unstarted sibling sandbox that inherits the template's configuration (credentials, image, resources) with per-instance overrides for `id` and `env`, so one configured sandbox can act as a template for a fleet of sandbox clones (for example, one per project). ([#19616](https://github.com/mastra-ai/mastra/pull/19616))
62
-
63
- ```ts
64
- const template = new E2BSandbox({ apiKey, template: 'base' });
65
-
66
- const projectSandbox = template.clone({
67
- id: 'mc-project-42',
68
- env: { GITHUB_TOKEN: token },
69
- idleTimeoutMinutes: 30,
70
- });
71
- await projectSandbox.start();
72
- ```
73
-
74
- `idleTimeoutMinutes` is a best-effort hint that maps to each provider's native lifetime knob (Railway `idleTimeoutMinutes`, E2B/Modal/Vercel timeout in milliseconds, Daytona `autoStopInterval`, Blaxel TTL duration). Docker and Apple Container ignore it since they have no provider-side idle teardown.
75
-
76
- ### Patch Changes
77
-
78
- - Updated dependencies [[`ec857fc`](https://github.com/mastra-ai/mastra/commit/ec857fc79c264b53b38e16478c789b7177f2ad59), [`d7385ad`](https://github.com/mastra-ai/mastra/commit/d7385ad9e88f9e4f33d15c0ec0bfebedde0cbc2e), [`41a5392`](https://github.com/mastra-ai/mastra/commit/41a5392d9f6c5e18d6b227f0fc0ddf49c50774e9), [`3d6e539`](https://github.com/mastra-ai/mastra/commit/3d6e539272eb2ea0407034605ee1906b3be06b39), [`1426af2`](https://github.com/mastra-ai/mastra/commit/1426af24975879c000d13ac75673f630fcc970c1), [`a40adeb`](https://github.com/mastra-ai/mastra/commit/a40adeb222b961a56a58af56a106106525721b74), [`8a0d145`](https://github.com/mastra-ai/mastra/commit/8a0d145aadbdf7278665aceaaec364b35dd9bd94), [`bd2f1d2`](https://github.com/mastra-ai/mastra/commit/bd2f1d274d05e60e2366f005ea0d94d5cea0d5ff), [`e1f2fae`](https://github.com/mastra-ai/mastra/commit/e1f2faebaf048c3d4c2e2c01d293767c195d5794), [`63aa799`](https://github.com/mastra-ai/mastra/commit/63aa799c6b44eacc7806cda6846b7c5bbee06b37), [`b7e79c3`](https://github.com/mastra-ai/mastra/commit/b7e79c3c02ac5cd415db34ba0975ceafc1464333), [`675fbff`](https://github.com/mastra-ai/mastra/commit/675fbff84d3274391b33e852f76083c38a5514e5), [`da009e1`](https://github.com/mastra-ai/mastra/commit/da009e1aacd89ed94b8d1b2af09c9d4fe7c4db49), [`3b77e77`](https://github.com/mastra-ai/mastra/commit/3b77e7704936522e4769d29de1b5ea6901f302bd), [`c7d30cd`](https://github.com/mastra-ai/mastra/commit/c7d30cd86009c407df91105591f03cd6e3d2854d), [`21a0eb8`](https://github.com/mastra-ai/mastra/commit/21a0eb86746ba0b703acea360d4f84c6a5a493f2), [`8b20926`](https://github.com/mastra-ai/mastra/commit/8b20926cd59e2ba3d66458e062fa0e6e2ada3e68), [`975295d`](https://github.com/mastra-ai/mastra/commit/975295d418552f0d46a59edfef4c3ee555f9930a), [`73db8db`](https://github.com/mastra-ai/mastra/commit/73db8db90d69ab6153c7942749f624db0d96952d), [`6b1bf3b`](https://github.com/mastra-ai/mastra/commit/6b1bf3b9494bd51aa8f654c68c9355d6046fa2a1), [`35c2181`](https://github.com/mastra-ai/mastra/commit/35c2181e6a50e47c90ba36260db7c9723d54696f), [`0a2c22c`](https://github.com/mastra-ai/mastra/commit/0a2c22c902604439ec490319e14c17f331e0c84c), [`4cfdd64`](https://github.com/mastra-ai/mastra/commit/4cfdd645794feaea0c4ea711e70ecdfbef0c5b8e), [`b75d749`](https://github.com/mastra-ai/mastra/commit/b75d749621ff5d17e86bcb4ee809d301fb4f7cf3), [`821648b`](https://github.com/mastra-ai/mastra/commit/821648bf2871ef840100c7bacbecf676010bd12a), [`de86fd7`](https://github.com/mastra-ai/mastra/commit/de86fd7119f0438381d1a642e3d258143c0b9c29), [`2745031`](https://github.com/mastra-ai/mastra/commit/2745031d1d4a4978f037092da371428c32e2842a), [`b4b7ea8`](https://github.com/mastra-ai/mastra/commit/b4b7ea8733f033fc441ea47ed03f6afb17ec2248), [`3a8024c`](https://github.com/mastra-ai/mastra/commit/3a8024ce615f8aa89479c0d71fe61d10bb0040be), [`35865a5`](https://github.com/mastra-ai/mastra/commit/35865a53e194aa9634d6a70a97010e7a6b9d58b1), [`74faf8b`](https://github.com/mastra-ai/mastra/commit/74faf8bd9c1018f2492653c06b1e25fc8300e9e6), [`ef03fbc`](https://github.com/mastra-ai/mastra/commit/ef03fbcc556bcbc04c9b3d06fab88771ecaa043c), [`675fbff`](https://github.com/mastra-ai/mastra/commit/675fbff84d3274391b33e852f76083c38a5514e5), [`70687f7`](https://github.com/mastra-ai/mastra/commit/70687f7e495a322a02070b4a67cb0c77a5ca91ec), [`1fadac4`](https://github.com/mastra-ai/mastra/commit/1fadac44537caeefe81f9f775ae2f2f3d94e9069), [`73db8db`](https://github.com/mastra-ai/mastra/commit/73db8db90d69ab6153c7942749f624db0d96952d), [`76b7181`](https://github.com/mastra-ai/mastra/commit/76b71810366e6d90b9d3973149d1c7ba3659ffb9), [`792ec9a`](https://github.com/mastra-ai/mastra/commit/792ec9a0869bab8274cf5e0ed2840738737a1607), [`712b864`](https://github.com/mastra-ai/mastra/commit/712b864aa1ed12b14c54390ec17b69de163c37f7), [`85e4fb5`](https://github.com/mastra-ai/mastra/commit/85e4fb50087a81c74df3a762f53b56373db0b912), [`0c0e8d7`](https://github.com/mastra-ai/mastra/commit/0c0e8d7becd4d1445c656b78d5d845f606c1ff9d), [`a7bbe77`](https://github.com/mastra-ai/mastra/commit/a7bbe773577f60bc4761b534ef7ec6b476332dad), [`72e437c`](https://github.com/mastra-ai/mastra/commit/72e437c515942c80b9def5b026e0bdee61b469d9), [`8f7a5de`](https://github.com/mastra-ai/mastra/commit/8f7a5dedc246cdc938bb65516703cf9b27b03756), [`a7bbe77`](https://github.com/mastra-ai/mastra/commit/a7bbe773577f60bc4761b534ef7ec6b476332dad), [`11f6cd9`](https://github.com/mastra-ai/mastra/commit/11f6cd96fe42582403416608beb212cc1a2cc79e), [`ef03c0c`](https://github.com/mastra-ai/mastra/commit/ef03c0cfc62367a458e4cc56462e2148b35681c5), [`4fb4d88`](https://github.com/mastra-ai/mastra/commit/4fb4d881bc107acee13890ad4d78661016c510ed), [`4e68363`](https://github.com/mastra-ai/mastra/commit/4e683634f94ebd062d26a3bb6093a8dfc7263d37), [`c328769`](https://github.com/mastra-ai/mastra/commit/c3287698ff8ef98dba86d415faa566fa3e5f4d56), [`9f7c67a`](https://github.com/mastra-ai/mastra/commit/9f7c67abeeb52c41c51a9b5edee60b62afe7cd8d), [`3b65e68`](https://github.com/mastra-ai/mastra/commit/3b65e68d7f1c771c7a70eea42d83fefdd28cad88), [`4eba27a`](https://github.com/mastra-ai/mastra/commit/4eba27adcf60f991df0e62f94b3e75b4e67f3b4b), [`c701be3`](https://github.com/mastra-ai/mastra/commit/c701be32d7d9aa94a66da8c6cc38dcac6856f464), [`db650ce`](https://github.com/mastra-ai/mastra/commit/db650ce490348914e85b93651d83acdf8f2a4c31), [`232fcbc`](https://github.com/mastra-ai/mastra/commit/232fcbc14fce625dd672ba043329c0b732c62be2), [`6354eeb`](https://github.com/mastra-ai/mastra/commit/6354eeb32efa9f5f68f51dda394e90e2ee76f1fb), [`a8799bb`](https://github.com/mastra-ai/mastra/commit/a8799bb8e44f4a60d01e4e2acd3448ff80bf14f8), [`3d6e539`](https://github.com/mastra-ai/mastra/commit/3d6e539272eb2ea0407034605ee1906b3be06b39), [`e3868e2`](https://github.com/mastra-ai/mastra/commit/e3868e22babfffd0133771669ca724501c2dd58e), [`9251370`](https://github.com/mastra-ai/mastra/commit/9251370ad413af464aa22d7566338bec5613e8de), [`3491666`](https://github.com/mastra-ai/mastra/commit/34916663c4fdd43b48c21f4ab2d5fb6dcccc94f9), [`c0bec73`](https://github.com/mastra-ai/mastra/commit/c0bec732c93d1a22ae5e51ed66cf8cacca8bd6a6)]:
79
- - @mastra/core@1.52.0
80
-
81
- ## 0.4.0-alpha.0
82
-
83
- ### Minor Changes
84
-
85
- - Added `clone()` support to the sandbox providers. `clone()` constructs an unstarted sibling sandbox that inherits the template's configuration (credentials, image, resources) with per-instance overrides for `id` and `env`, so one configured sandbox can act as a template for a fleet of sandbox clones (for example, one per project). ([#19616](https://github.com/mastra-ai/mastra/pull/19616))
86
-
87
- ```ts
88
- const template = new E2BSandbox({ apiKey, template: 'base' });
89
-
90
- const projectSandbox = template.clone({
91
- id: 'mc-project-42',
92
- env: { GITHUB_TOKEN: token },
93
- idleTimeoutMinutes: 30,
94
- });
95
- await projectSandbox.start();
96
- ```
97
-
98
- `idleTimeoutMinutes` is a best-effort hint that maps to each provider's native lifetime knob (Railway `idleTimeoutMinutes`, E2B/Modal/Vercel timeout in milliseconds, Daytona `autoStopInterval`, Blaxel TTL duration). Docker and Apple Container ignore it since they have no provider-side idle teardown.
99
-
100
- ### Patch Changes
101
-
102
- - Updated dependencies [[`ec857fc`](https://github.com/mastra-ai/mastra/commit/ec857fc79c264b53b38e16478c789b7177f2ad59), [`e1f2fae`](https://github.com/mastra-ai/mastra/commit/e1f2faebaf048c3d4c2e2c01d293767c195d5794), [`63aa799`](https://github.com/mastra-ai/mastra/commit/63aa799c6b44eacc7806cda6846b7c5bbee06b37), [`73db8db`](https://github.com/mastra-ai/mastra/commit/73db8db90d69ab6153c7942749f624db0d96952d), [`73db8db`](https://github.com/mastra-ai/mastra/commit/73db8db90d69ab6153c7942749f624db0d96952d), [`76b7181`](https://github.com/mastra-ai/mastra/commit/76b71810366e6d90b9d3973149d1c7ba3659ffb9), [`0c0e8d7`](https://github.com/mastra-ai/mastra/commit/0c0e8d7becd4d1445c656b78d5d845f606c1ff9d), [`9f7c67a`](https://github.com/mastra-ai/mastra/commit/9f7c67abeeb52c41c51a9b5edee60b62afe7cd8d), [`3b65e68`](https://github.com/mastra-ai/mastra/commit/3b65e68d7f1c771c7a70eea42d83fefdd28cad88), [`e3868e2`](https://github.com/mastra-ai/mastra/commit/e3868e22babfffd0133771669ca724501c2dd58e)]:
103
- - @mastra/core@1.52.0-alpha.5
104
-
105
- ## 0.3.0
106
-
107
- ### Minor Changes
108
-
109
- - Random bump ([#18178](https://github.com/mastra-ai/mastra/pull/18178))
110
-
111
- ### Patch Changes
112
-
113
- - Updated dependencies [[`7c0d868`](https://github.com/mastra-ai/mastra/commit/7c0d868d97d0fdbc04c14d0166dbf44d4c5a4a62), [`d9d2273`](https://github.com/mastra-ai/mastra/commit/d9d2273c702690c9a26eab2aebea879701d4355a), [`b04369d`](https://github.com/mastra-ai/mastra/commit/b04369d6b167c698ef103981171a8bf92808e756), [`8f3c262`](https://github.com/mastra-ai/mastra/commit/8f3c262587b335588a02d96b17fd6aca34c885b3)]:
114
- - @mastra/core@1.45.0
115
-
116
- ## 0.3.0-alpha.0
117
-
118
- ### Minor Changes
119
-
120
- - Random bump ([#18178](https://github.com/mastra-ai/mastra/pull/18178))
121
-
122
- ### Patch Changes
123
-
124
- - Updated dependencies [[`7c0d868`](https://github.com/mastra-ai/mastra/commit/7c0d868d97d0fdbc04c14d0166dbf44d4c5a4a62), [`d9d2273`](https://github.com/mastra-ai/mastra/commit/d9d2273c702690c9a26eab2aebea879701d4355a), [`b04369d`](https://github.com/mastra-ai/mastra/commit/b04369d6b167c698ef103981171a8bf92808e756), [`8f3c262`](https://github.com/mastra-ai/mastra/commit/8f3c262587b335588a02d96b17fd6aca34c885b3)]:
125
- - @mastra/core@1.45.0-alpha.0
126
-
127
- ## 0.2.4
128
-
129
- ### Patch Changes
130
-
131
- - Security remediation for the 2026-06-17 "easy-day-js" supply-chain incident. Patch bump to publish clean versions and move the `latest` dist-tag forward, superseding the compromised versions that declared the malicious `easy-day-js` dependency. ([#18056](https://github.com/mastra-ai/mastra/pull/18056))
132
-
133
- - Updated dependencies [[`339c57c`](https://github.com/mastra-ai/mastra/commit/339c57c5b2c6dbe75a125e138228e0556528976f), [`1dd4117`](https://github.com/mastra-ai/mastra/commit/1dd4117dcbd8e031ede9f0489436bfbc6f0315b8), [`2b11d1f`](https://github.com/mastra-ai/mastra/commit/2b11d1f6ac7024c5dd2b2dd12a48a956ac9d63bd), [`77a2351`](https://github.com/mastra-ai/mastra/commit/77a2351ee79296e360bce822cb3391f7cfd6489d), [`b7dff0a`](https://github.com/mastra-ai/mastra/commit/b7dff0a3d1022eb6868f48dc40a2b1febd5c277f), [`02087e1`](https://github.com/mastra-ai/mastra/commit/02087e1fbc54aa07f3071f7a200df1bf5be601a8), [`49af8df`](https://github.com/mastra-ai/mastra/commit/49af8df589c4ff71a5015a4553b377b32704b691), [`30ce559`](https://github.com/mastra-ai/mastra/commit/30ce55902ecf819b8ab8697398dd68b108228063), [`c241b92`](https://github.com/mastra-ai/mastra/commit/c241b929dc8c8d6a7b7219c99ed13ac1f3124a77), [`7d6ff70`](https://github.com/mastra-ai/mastra/commit/7d6ff708727297a0526ca0e26e93eeb5bbaaa187), [`ab975d4`](https://github.com/mastra-ai/mastra/commit/ab975d4dd9488752f05bda7afa03166d207e3e2a), [`9d6aa1b`](https://github.com/mastra-ai/mastra/commit/9d6aa1bae407e2afa6a089abc2a6accbbcb287b8)]:
134
- - @mastra/core@1.44.0
135
-
136
- ## 0.2.4-alpha.0
137
-
138
- ### Patch Changes
139
-
140
- - Security remediation for the 2026-06-17 "easy-day-js" supply-chain incident. Patch bump to publish clean versions and move the `latest` dist-tag forward, superseding the compromised versions that declared the malicious `easy-day-js` dependency. ([#18056](https://github.com/mastra-ai/mastra/pull/18056))
141
-
142
- - Updated dependencies [[`77a2351`](https://github.com/mastra-ai/mastra/commit/77a2351ee79296e360bce822cb3391f7cfd6489d)]:
143
- - @mastra/core@1.43.1-alpha.0
144
-
145
- ## 0.2.1
146
-
147
- ### Patch Changes
148
-
149
- - Fixed sandbox execution results to report killed and timed out commands. ([#17281](https://github.com/mastra-ai/mastra/pull/17281))
150
-
151
- - Updated dependencies [[`fa63872`](https://github.com/mastra-ai/mastra/commit/fa6387280954e6b667bec5714b55ba082bc627ff), [`d779de3`](https://github.com/mastra-ai/mastra/commit/d779de3cd9d2e7ed8110547190e2f15e786a0e41), [`1750c97`](https://github.com/mastra-ai/mastra/commit/1750c975d6179fbf6db2813b15229d4f8f23fc55), [`9283971`](https://github.com/mastra-ai/mastra/commit/928397157009b4aef4d5fdf3a0a273cb371beb55), [`f07b646`](https://github.com/mastra-ai/mastra/commit/f07b64604ab7d25391179790b7fd4823df9e2dff), [`d8838ae`](https://github.com/mastra-ai/mastra/commit/d8838ae80b69780361693d27098f7f6684af12fe), [`40f9297`](https://github.com/mastra-ai/mastra/commit/40f9297003b921c62373d3e8d3a4bda76c9f6de3), [`19a8658`](https://github.com/mastra-ai/mastra/commit/19a86589c788ef48bb6c1b0612cc82a201857379), [`850af77`](https://github.com/mastra-ai/mastra/commit/850af7779cb87c350804488734544a5b1843de25), [`0f0d1ba`](https://github.com/mastra-ai/mastra/commit/0f0d1ba67bfcb2204e571401662f1eceefc03357), [`a18775a`](https://github.com/mastra-ai/mastra/commit/a18775a693172546ee2378d39b67d4e32895b251), [`1baf2d1`](https://github.com/mastra-ai/mastra/commit/1baf2d152c6881338ff8f114633d5316fe13dd15), [`8c31bcd`](https://github.com/mastra-ai/mastra/commit/8c31bcdb00e597880d5939b1b7d7566fbe5dacae), [`0e32507`](https://github.com/mastra-ai/mastra/commit/0e32507962cdfa5569b7bda5bc6fb3dd34e40b03), [`95b14cd`](https://github.com/mastra-ai/mastra/commit/95b14cdd820e86d97ac05fe568424c513a252e31), [`07c3de7`](https://github.com/mastra-ai/mastra/commit/07c3de7f7bc418beccaea3b5e6b7f7cdda79d492), [`0bf2d93`](https://github.com/mastra-ai/mastra/commit/0bf2d932d20e2936f2d9abb8c0a86e24fbc97ec6), [`7b0d34c`](https://github.com/mastra-ai/mastra/commit/7b0d34cfe4a2fce22ac86ae17404685ff67a2ddb), [`a659a77`](https://github.com/mastra-ai/mastra/commit/a659a779bdebe3a52a518c56d2260592d0240fe0), [`aa36be2`](https://github.com/mastra-ai/mastra/commit/aa36be23aa513b7dc53cb8ca16b7fab8f20e43ad), [`3332be9`](https://github.com/mastra-ai/mastra/commit/3332be9701ecd77aba840959d9a1d1ce7aef02d3), [`212c635`](https://github.com/mastra-ai/mastra/commit/212c635203e61d036ab41db8ff86c3893dc795b3), [`d8838ae`](https://github.com/mastra-ai/mastra/commit/d8838ae80b69780361693d27098f7f6684af12fe), [`9aa5a73`](https://github.com/mastra-ai/mastra/commit/9aa5a73e7e110f6e9365eec69364a33d5f03bb56), [`f73c789`](https://github.com/mastra-ai/mastra/commit/f73c789e8ef21561580395d2c410119cab5848c8), [`8bd16da`](https://github.com/mastra-ai/mastra/commit/8bd16da73a4cb874d739373643dbd6a6e7f88684), [`c8630f8`](https://github.com/mastra-ai/mastra/commit/c8630f80d4f40cb5d22e60ab162b618b1907167a), [`94dfef6`](https://github.com/mastra-ai/mastra/commit/94dfef6e2bf19a88467ea3940afcbce88a433f0f), [`47f71dc`](https://github.com/mastra-ai/mastra/commit/47f71dc6fbcbd12d71e21a979e676e20a02bd77d), [`50ceae2`](https://github.com/mastra-ai/mastra/commit/50ceae270878e2f8fb2b2c6c2faab09df0007c8a), [`a122f79`](https://github.com/mastra-ai/mastra/commit/a122f79427ae225ec79c7b2ed46278da48d04b17), [`8cdde58`](https://github.com/mastra-ai/mastra/commit/8cdde5875bbba6702d9df226f2b20232b8d75d6c), [`3a081c1`](https://github.com/mastra-ai/mastra/commit/3a081c1255c5ae8c99f6dad91cc612934ef6f2bd), [`49f8abc`](https://github.com/mastra-ai/mastra/commit/49f8abce8258e4f2f87bd326acfbdb641264a47c), [`847ff1e`](https://github.com/mastra-ai/mastra/commit/847ff1e0d94368d94b2e173e4e0908e115568ef3), [`0c1ed1d`](https://github.com/mastra-ai/mastra/commit/0c1ed1d00c7d87b5ac99ca95896211a2fa9189fa), [`259d409`](https://github.com/mastra-ai/mastra/commit/259d409a514174299dbde1ff5e1121209b3ba850), [`9e16c68`](https://github.com/mastra-ai/mastra/commit/9e16c6818b6485ccb43df28aba6f3a2219d28662), [`cefca33`](https://github.com/mastra-ai/mastra/commit/cefca33ae666e69810c935fedf95a929c173d1d7), [`d00e8c5`](https://github.com/mastra-ai/mastra/commit/d00e8c50daebe5bce5bf2f48bde39c86fc3d2fe4), [`36fa7e2`](https://github.com/mastra-ai/mastra/commit/36fa7e24d14e58a1eb46147097b32f583e5b8775), [`87e9774`](https://github.com/mastra-ai/mastra/commit/87e97741c1e493cd6d62f478eb810b49bda4d57c), [`65a72e7`](https://github.com/mastra-ai/mastra/commit/65a72e70c25eedea8ff985a6624b96be2850236b), [`fe9eacd`](https://github.com/mastra-ai/mastra/commit/fe9eacd9545a0a9d64aad31c9fa90294a425289e), [`4c02027`](https://github.com/mastra-ai/mastra/commit/4c020277235eaa6b1dc957c90ad0639eef213992), [`0f77241`](https://github.com/mastra-ai/mastra/commit/0f7724108806703799a8ba80ad0f09414afd5066), [`849efb9`](https://github.com/mastra-ai/mastra/commit/849efb9fca6dc976589c1f90a303fea618769109), [`92ff509`](https://github.com/mastra-ai/mastra/commit/92ff5098ef8a990438ca038077021a5f7541ec1d), [`3fce5e7`](https://github.com/mastra-ai/mastra/commit/3fce5e70d011d289043e75003ef3336ed4aa43c3), [`a763592`](https://github.com/mastra-ai/mastra/commit/a763592c3db46963ef1011cfe16fe372816e775e), [`db79c86`](https://github.com/mastra-ai/mastra/commit/db79c86c60723d57e02f9636ca2611bd4515f194), [`6855012`](https://github.com/mastra-ai/mastra/commit/685501247cc4717506f3e89beed03509d63a5370), [`80c7737`](https://github.com/mastra-ai/mastra/commit/80c7737e32d7917b5f356957d67c169d01744fd3), [`7fef31c`](https://github.com/mastra-ai/mastra/commit/7fef31c0d2a6d362a43a647a8a4f6ab893758a23), [`7fef31c`](https://github.com/mastra-ai/mastra/commit/7fef31c0d2a6d362a43a647a8a4f6ab893758a23), [`3f1cf47`](https://github.com/mastra-ai/mastra/commit/3f1cf476f74c1e4cc2df908837e05853a5347e31)]:
152
- - @mastra/core@1.38.0
153
-
154
- ## 0.2.1-alpha.0
155
-
156
- ### Patch Changes
157
-
158
- - Fixed sandbox execution results to report killed and timed out commands. ([#17281](https://github.com/mastra-ai/mastra/pull/17281))
159
-
160
- - Updated dependencies [[`8ace89d`](https://github.com/mastra-ai/mastra/commit/8ace89df77f762e622d3b9f7f65ad7524350d050), [`fa63872`](https://github.com/mastra-ai/mastra/commit/fa6387280954e6b667bec5714b55ba082bc627ff), [`f07b646`](https://github.com/mastra-ai/mastra/commit/f07b64604ab7d25391179790b7fd4823df9e2dff), [`d8838ae`](https://github.com/mastra-ai/mastra/commit/d8838ae80b69780361693d27098f7f6684af12fe), [`40f9297`](https://github.com/mastra-ai/mastra/commit/40f9297003b921c62373d3e8d3a4bda76c9f6de3), [`0f0d1ba`](https://github.com/mastra-ai/mastra/commit/0f0d1ba67bfcb2204e571401662f1eceefc03357), [`8c31bcd`](https://github.com/mastra-ai/mastra/commit/8c31bcdb00e597880d5939b1b7d7566fbe5dacae), [`95b14cd`](https://github.com/mastra-ai/mastra/commit/95b14cdd820e86d97ac05fe568424c513a252e31), [`aa36be2`](https://github.com/mastra-ai/mastra/commit/aa36be23aa513b7dc53cb8ca16b7fab8f20e43ad), [`212c635`](https://github.com/mastra-ai/mastra/commit/212c635203e61d036ab41db8ff86c3893dc795b3), [`d8838ae`](https://github.com/mastra-ai/mastra/commit/d8838ae80b69780361693d27098f7f6684af12fe), [`9aa5a73`](https://github.com/mastra-ai/mastra/commit/9aa5a73e7e110f6e9365eec69364a33d5f03bb56), [`f73c789`](https://github.com/mastra-ai/mastra/commit/f73c789e8ef21561580395d2c410119cab5848c8), [`8bd16da`](https://github.com/mastra-ai/mastra/commit/8bd16da73a4cb874d739373643dbd6a6e7f88684), [`c8630f8`](https://github.com/mastra-ai/mastra/commit/c8630f80d4f40cb5d22e60ab162b618b1907167a), [`47f71dc`](https://github.com/mastra-ai/mastra/commit/47f71dc6fbcbd12d71e21a979e676e20a02bd77d), [`50ceae2`](https://github.com/mastra-ai/mastra/commit/50ceae270878e2f8fb2b2c6c2faab09df0007c8a), [`8cdde58`](https://github.com/mastra-ai/mastra/commit/8cdde5875bbba6702d9df226f2b20232b8d75d6c), [`847ff1e`](https://github.com/mastra-ai/mastra/commit/847ff1e0d94368d94b2e173e4e0908e115568ef3), [`259d409`](https://github.com/mastra-ai/mastra/commit/259d409a514174299dbde1ff5e1121209b3ba850), [`9e16c68`](https://github.com/mastra-ai/mastra/commit/9e16c6818b6485ccb43df28aba6f3a2219d28662), [`cefca33`](https://github.com/mastra-ai/mastra/commit/cefca33ae666e69810c935fedf95a929c173d1d7), [`d00e8c5`](https://github.com/mastra-ai/mastra/commit/d00e8c50daebe5bce5bf2f48bde39c86fc3d2fe4), [`36fa7e2`](https://github.com/mastra-ai/mastra/commit/36fa7e24d14e58a1eb46147097b32f583e5b8775), [`87e9774`](https://github.com/mastra-ai/mastra/commit/87e97741c1e493cd6d62f478eb810b49bda4d57c), [`65a72e7`](https://github.com/mastra-ai/mastra/commit/65a72e70c25eedea8ff985a6624b96be2850236b), [`0f77241`](https://github.com/mastra-ai/mastra/commit/0f7724108806703799a8ba80ad0f09414afd5066), [`92ff509`](https://github.com/mastra-ai/mastra/commit/92ff5098ef8a990438ca038077021a5f7541ec1d), [`3fce5e7`](https://github.com/mastra-ai/mastra/commit/3fce5e70d011d289043e75003ef3336ed4aa43c3), [`a763592`](https://github.com/mastra-ai/mastra/commit/a763592c3db46963ef1011cfe16fe372816e775e), [`80c7737`](https://github.com/mastra-ai/mastra/commit/80c7737e32d7917b5f356957d67c169d01744fd3), [`3f1cf47`](https://github.com/mastra-ai/mastra/commit/3f1cf476f74c1e4cc2df908837e05853a5347e31)]:
161
- - @mastra/core@1.38.0-alpha.3
162
-
163
- ## 0.2.0
164
-
165
- ### Minor Changes
166
-
167
- - Added `@mastra/modal` — Modal cloud sandbox provider for Mastra workspaces. ([#14486](https://github.com/mastra-ai/mastra/pull/14486))
168
-
169
- Use `ModalSandbox` to run commands in an isolated Modal environment with pause/resume support:
170
-
171
- ```ts
172
- import { Workspace } from '@mastra/core/workspace';
173
- import { ModalSandbox } from '@mastra/modal';
174
-
175
- const workspace = new Workspace({
176
- sandbox: new ModalSandbox({
177
- tokenId: process.env.MODAL_TOKEN_ID!,
178
- tokenSecret: process.env.MODAL_TOKEN_SECRET!,
179
- }),
180
- });
181
- ```
182
-
183
- ### Patch Changes
184
-
185
- - Updated dependencies [[`733bf53`](https://github.com/mastra-ai/mastra/commit/733bf53d9352aedd3ef38c3d501edb275b65b43c), [`5405b3b`](https://github.com/mastra-ai/mastra/commit/5405b3b35325c5b8fb34fc7ac109bd2feb7bb6fe), [`45e29cb`](https://github.com/mastra-ai/mastra/commit/45e29cb5b5737f3083eb3852db02b944b9cf37ed), [`750b4d3`](https://github.com/mastra-ai/mastra/commit/750b4d3d8231f92e769b2c485921ac5a8ca639b9), [`c321127`](https://github.com/mastra-ai/mastra/commit/c3211275fc195de9ad1ead2746b354beb8eae6e8), [`a07bcef`](https://github.com/mastra-ai/mastra/commit/a07bcefea77c03d6d322caad973dca49b4b15fa1), [`696694e`](https://github.com/mastra-ai/mastra/commit/696694e00f29241a25dd1a1b749afa06c3a626b4), [`b084a80`](https://github.com/mastra-ai/mastra/commit/b084a800db0f82d62e1fc3d6e3e3480da1ba5a53), [`82b7a96`](https://github.com/mastra-ai/mastra/commit/82b7a964169636c1d1e0c694fc892a213b0179d5), [`df97812`](https://github.com/mastra-ai/mastra/commit/df97812bd949dcafeb074b80ecab501724b49c3b), [`8bbe360`](https://github.com/mastra-ai/mastra/commit/8bbe36042af7fc4be0244dffd8913f6795179421), [`f6b8ba8`](https://github.com/mastra-ai/mastra/commit/f6b8ba8dbf533b7a8db90c72b6805ddc804a3a72), [`a07bcef`](https://github.com/mastra-ai/mastra/commit/a07bcefea77c03d6d322caad973dca49b4b15fa1)]:
186
- - @mastra/core@1.28.0
187
-
188
- ## 0.2.0-alpha.0
189
-
190
- ### Minor Changes
191
-
192
- - Added `@mastra/modal` — Modal cloud sandbox provider for Mastra workspaces. ([#14486](https://github.com/mastra-ai/mastra/pull/14486))
193
-
194
- Use `ModalSandbox` to run commands in an isolated Modal environment with pause/resume support:
195
-
196
- ```ts
197
- import { Workspace } from '@mastra/core/workspace';
198
- import { ModalSandbox } from '@mastra/modal';
199
-
200
- const workspace = new Workspace({
201
- sandbox: new ModalSandbox({
202
- tokenId: process.env.MODAL_TOKEN_ID!,
203
- tokenSecret: process.env.MODAL_TOKEN_SECRET!,
204
- }),
205
- });
206
- ```
207
-
208
- ### Patch Changes
209
-
210
- - Updated dependencies [[`733bf53`](https://github.com/mastra-ai/mastra/commit/733bf53d9352aedd3ef38c3d501edb275b65b43c), [`5405b3b`](https://github.com/mastra-ai/mastra/commit/5405b3b35325c5b8fb34fc7ac109bd2feb7bb6fe), [`c321127`](https://github.com/mastra-ai/mastra/commit/c3211275fc195de9ad1ead2746b354beb8eae6e8), [`a07bcef`](https://github.com/mastra-ai/mastra/commit/a07bcefea77c03d6d322caad973dca49b4b15fa1), [`b084a80`](https://github.com/mastra-ai/mastra/commit/b084a800db0f82d62e1fc3d6e3e3480da1ba5a53), [`82b7a96`](https://github.com/mastra-ai/mastra/commit/82b7a964169636c1d1e0c694fc892a213b0179d5), [`df97812`](https://github.com/mastra-ai/mastra/commit/df97812bd949dcafeb074b80ecab501724b49c3b), [`8bbe360`](https://github.com/mastra-ai/mastra/commit/8bbe36042af7fc4be0244dffd8913f6795179421), [`f6b8ba8`](https://github.com/mastra-ai/mastra/commit/f6b8ba8dbf533b7a8db90c72b6805ddc804a3a72), [`a07bcef`](https://github.com/mastra-ai/mastra/commit/a07bcefea77c03d6d322caad973dca49b4b15fa1)]:
211
- - @mastra/core@1.28.0-alpha.0