import type { MastraSandbox, SandboxStartHook, WorkspaceSandbox } from '@mastra/core/workspace'; import type { RepositoryAccess } from '../capabilities/version-control.js'; /** * Everything factory knows about a session's sandbox needs — the whole * contract between factory and the deployer's sandbox callback. Factory owns * intent; the provider owns resolving `sessionId` to a runnable VM. */ export interface FactorySandboxContext { /** Stable session id — the sandbox identity. */ sessionId: string; /** owner/name of the repository, when the session is repo-backed. */ repoFullName?: string; /** * Configured repo setup command, when present. Part of a repo template's * identity: a different setup command produces a different template. */ setupCommand?: string; /** * Resolves the session repository's clone URL and a fresh short-lived * credential for it. Providers use it for authenticated work that runs * outside the VM — resolving a private repo's head, or cloning it during * a template build. The credential is minted per call (installation * tokens expire in ~1h); never an org PAT. * * `undefined` when the session has no repository, which is how a provider * knows to build no repo template. The key is always present so that * passing the whole context to a provider helper keeps working when this * field changes, instead of silently resolving to "no repository". */ getRepositoryAccess: (() => Promise) | undefined; } /** * The deploy's sandbox configuration: construct a session's sandbox from * intent. The sandbox identity is the session id; the provider must honor * id-keyed getOrCreate on `start()` (reconnect/resume an existing VM for the * id, create otherwise). Construction must be cheap and side-effect-free — * VMs are provisioned on `start()` only. Local sandboxes should root their * `workingDirectory` at a per-session directory (e.g. * `join(root, ctx.sessionId)`); the repo checks out as a subdirectory of it. * * Returns a `MastraSandbox`, not the bare `WorkspaceSandbox` interface: * factory relies on the base class for the start lifecycle and the runtime * env, so providers extend it rather than reimplementing the contract. * * Factory attaches its own session setup to the returned sandbox, so the * callback never has to wire it up. A callback may still pass its own * `onStart`; it runs after factory's setup, against a prepared workspace. * * @example * ```typescript * sandbox: ({ sessionId }) => new E2BSandbox({ id: sessionId }) * ``` */ export type MastraFactorySandboxConfig = (ctx: FactorySandboxContext) => MastraSandbox; /** * What the start hook learned about the setup command before running the * session setup, and how to record its completion afterwards. */ export interface SessionSetupGate { /** True when the sandbox already carries a marker for the current setup command. */ setupDone: boolean; /** Write the marker once the setup command succeeded. Best-effort. */ markSetupDone: () => Promise; } /** * The session's setup work, run against a started sandbox on EVERY start. * Materialize and checkout are idempotent and must always run (a warm boot * still needs its pull); only the setup command consults `gate`. */ export type SessionSetupRun = (sandbox: WorkspaceSandbox, workdir: string, gate: SessionSetupGate) => Promise; /** * Per-process session-id → sandbox instance memo. * * The provider contract is id-keyed getOrCreate, but provider find-then-create * has a real double-create race across independent instances. Memoizing the * instance per session makes the base class's per-instance start coalescing * apply process-wide per session — the same single-flight scope the fleet's * per-binding coalescing provided. Cross-replica races are accepted (the * fleet was also per-replica). */ interface SessionSandboxEntry { sandbox: WorkspaceSandbox; /** * The session's repo checkout root, recorded for passive readers (fs * routes, capture, authz). Local sandboxes derive it at construction; * remote sandboxes clone into the VM's own home, so it is undefined until * `resolveSessionWorkdir` probes the first started VM — passive readers * treat an unresolved workdir as "nothing materialized". */ workdir?: string; } /** * Get the session's memoized sandbox entry, constructing (and memoizing) it on * first access. Construction is cheap and side-effect-free by contract; VMs * are provisioned on `start()` only. Local sandboxes get their workdir here; * remote workdirs are a runtime fact of the VM, resolved on first start. */ export declare function getSessionSandbox(sessionId: string, repoFullName: string, construct: () => WorkspaceSandbox): SessionSandboxEntry; /** * Resolve (and memoize on the session entry) the session's repo checkout * root. Local sandboxes answer synchronously from their configured * `workingDirectory`; remote sandboxes clone into the VM's own default cwd, * so the first resolution probes it with one `pwd` — the VM tells us where * home is, we never invent a path. Calling this against a stopped sandbox * lazily starts it (the probe is a command), so passive readers must peek * `entry.workdir` instead. */ export declare function resolveSessionWorkdir(sessionId: string, sandbox: WorkspaceSandbox, repoFullName: string): Promise; /** * The session's memoized sandbox (and its workdir) when one was already * constructed in this process, else undefined. Never constructs — passive * read paths use this so browsing files cannot provision a VM. */ export declare function peekSessionSandbox(sessionId: string): SessionSandboxEntry | undefined; /** Drop the memoized instance (on stop/destroy/retirement or construction failure). */ export declare function evictSessionSandbox(sessionId: string): void; /** Test-only: reset the process-wide memo between tests. */ export declare function __clearSessionSandboxesForTests(): void; export declare function recordFailedSetupCommand(sessionId: string, command: string): void; export declare function hasFailedSetupCommand(sessionId: string, command: string): boolean; /** * Build the session setup hook, which factory attaches to the constructed * sandbox with `setOnStart`. Runs inside the sandbox start lifecycle on * every start, fresh VM or reconnect: materialize and checkout always run, * and the setup command runs unless the sandbox already carries the marker * for it (a warm template image, or an earlier successful start). Throwing * fails `start()` loudly; core treats onStart errors as fatal. */ export declare function createSessionSetupHook(run: SessionSetupRun, sessionId: string, repoFullName: string, setupCommand: string | undefined): SandboxStartHook; export {}; //# sourceMappingURL=session-sandbox.d.ts.map