Combine Sandboxes and Workspaces
Use <Workspace /> for files that should survive resource scopes, then give <Sandbox /> a narrower execution boundary for <Agent />. This recipe is intentionally deterministic and uses a temporary directory; it does not require Docker, credentials, or a model.
AML separates the two responsibilities:
Workspace = materialize, lock, load, save, and release file stateSandbox = lease an execution environment with root/cwd/access policyPrerequisites
Section titled “Prerequisites”- Node.js
>=26; @aml-jsx/sdkand@aml-jsx/sdk/testing;- a writable OS temporary directory;
- no credentials or network access.
Complete source
Section titled “Complete source”import { mkdtemp } from "node:fs/promises"import { tmpdir } from "node:os"import { join } from "node:path"
import { Agent, AmlRuntime, File, localSandbox, localWorkspace, Sandbox, supportsSandboxRuntime, Workspace,} from "@aml-jsx/sdk"import { DeterministicAgentProvider } from "@aml-jsx/sdk/testing"
const directory = await mkdtemp(join(tmpdir(), "aml-cookbook-"))const workspace = localWorkspace({ directory })const sandbox = localSandbox()
const provider = new DeterministicAgentProvider({ supportsSandbox: supportsSandboxRuntime, respond(_request, context) { if (context.sandbox === undefined) { throw new Error("The Agent must receive the active Sandbox") }
return { text: `inspected:${context.sandbox.cwd}:${context.sandbox.lease.id}`, } },})
const workflow = ( <Workspace id="cookbook-review" provider={workspace} save={false}> <File path="reports/summary.md">Generated before the Sandbox runs.</File> <Sandbox provider={sandbox} access="read-only" root="."> <Agent provider={provider} cwd="."> Inspect the report without modifying files. </Agent> </Sandbox> </Workspace>)
console.log(await new AmlRuntime().evaluate(workflow))console.log(`workspace=${directory}`)Run it
Section titled “Run it”Save the complete combined source as recipe.tsx in a project configured as shown in Getting started, then run it directly:
npx vite-node recipe.tsxFrom an AML repository checkout, the smaller maintained resource fixtures are:
npm run example -- workspacenpm run example -- sandboxThe repository’s workspace.tsx and sandbox.tsx isolate the same contracts in smaller fixtures.
Expected output
Section titled “Expected output”The exact lease ID is provider-generated, so the stable shape is:
inspected:.:<sandbox-lease-id>workspace=/tmp/aml-cookbook-<random-suffix>The temporary directory contains reports/summary.md until the operating system or your workflow cleanup removes it.
How it works
Section titled “How it works”<Workspace />acquires one materialization. By default it loads and locks;save={false}means this recipe does not publish a revision.<File />writes into the active materialized Workspace because it appears before the Sandbox scope. If placed inside a read-write Sandbox it would instead write the live guest; this recipe’s read-only Sandbox would reject that write.<Sandbox access="read-only" />reuses the Workspace and narrows the active root/cwd. Nested<Sandbox />components narrow one existing lease; they do not acquire another provider resource.localSandbox()executes trusted host processes. The deterministic<Agent />receives the Sandbox session and reports its narrowed cwd.
Provider choices
Section titled “Provider choices”localWorkspace()directly materializes an existing directory; writes are immediately visible and there are no archived revisions.filesystemWorkspace()adds local revision storage, retention, and conditional publication.s3Workspace()stores revisions remotely and depends on conditional object writes and ETags.dockerSandbox()bind-mounts the Workspace into a disposable container, but its security posture depends on the Docker daemon and image.- Daytona and Modal transfer a Workspace and reconcile changes on writable release; unsynchronized remote changes can be lost if the Sandbox fails before reconciliation.
Failure and security notes
Section titled “Failure and security notes”- A read-only Sandbox is not universally launchable by every Agent provider. ACP providers need to create state and spawn their executable; check the compatibility guide.
rootandcwdare logical paths within the materialized Workspace. Validate model-selected paths before turning them into a Workspace or provider option.save={false}is deliberate here. For durable revisions usesave={{ on: "success", retention: 3 }}with a revision-backed provider and handle conflicts as a concurrency decision.- Cleanup can fail independently of the Agent result. Preserve both the primary failure and cleanup cause in operational logs.