Run generated diagnostics in a Sandbox
Build a repository diagnostic with three visible boundaries:
<Agent />proposes a small Node.js program;<Script />executes that resolved text inside an explicitly selected Docker Sandbox;- a later
<Agent />receives standard output as evidence.
The workflow is deterministic at the Agent layer, so the example needs no model credentials. It still needs Docker and a local node:26 image: that image supplies the Node interpreter used by <Script />, not an Agent executable. A live Agent provider can be substituted later, but the selected image must then also contain the compatible ACP executable and its dependencies.
Prerequisites and status
Section titled “Prerequisites and status”- Node.js
>=26, ESM TypeScript/TSX execution, and@aml-jsx/sdk; - Docker running locally with
node:26available (docker pull node:26if needed); - the current directory is mounted read-only into the container by the configured Docker Sandbox;
- no model credentials are required because both Agent responses are deterministic fixtures.
This is a runnable boundary demonstration, not a claim that arbitrary generated code is safe. The source returned by an Agent remains untrusted input.
Complete source
Section titled “Complete source”import { Agent, AmlRuntime, Sandbox, Script, dockerSandbox } from "@aml-jsx/sdk"import { DeterministicAgentProvider } from "@aml-jsx/sdk/testing"
const generator = new DeterministicAgentProvider({ name: "diagnostic-generator", respond() { return { text: `const { readdir } = await import("node:fs/promises")const entries = (await readdir(process.cwd(), { withFileTypes: true })) .filter(entry => entry.isFile()) .map(entry => entry.name) .sort()console.log(JSON.stringify({ files: entries }))`, } },})
const reviewer = new DeterministicAgentProvider({ name: "diagnostic-reviewer", respond(request) { return { text: `Evidence received: ${request.prompt}` } },})
const DiagnosticsSandbox = dockerSandbox({ image: "node:26", workspace: process.cwd(),})
async function TriageRepository() { const source = await new AmlRuntime({ agentProvider: generator }).evaluate( <Agent>Return only a short Node.js diagnostic that lists files in the current working directory as JSON.</Agent> )
const evidence = await new AmlRuntime().evaluate( <Sandbox provider={DiagnosticsSandbox} access="read-only"> <Script shell="node" timeoutMs={10_000}> {source} </Script> </Sandbox> )
return await new AmlRuntime({ agentProvider: reviewer }).evaluate( <Agent> Review only this diagnostic output. Do not claim to have inspected files that are not listed: {evidence} </Agent> )}
console.log(await TriageRepository())Run it
Section titled “Run it”Save the complete source as recipe.tsx in a project configured as shown in Getting started. Verify the required image, then run that exact workflow:
docker image inspect node:26npx vite-node recipe.tsxA successful run prints JSON evidence from the container and then a reviewer message containing that evidence. File names vary with the directory from which the program is launched.
Observable result
Section titled “Observable result”The diagnostic emits a line shaped like:
{"files":["README.md","package.json"]}The final <Agent /> receives that line as ordinary prompt data. Docker Sandbox is acquired and released around the <Script /> evaluation; the workflow does not expose the container as durable execution.
How the boundaries work
Section titled “How the boundaries work”- The application authors the order in TypeScript. The first
<Agent />returns source text; it does not create AML nodes or change the workflow. - The enclosing
<Sandbox />makes<Script />invoke Node inside Docker. Without that boundary the same Script would run on the AML host, andtimeoutMswould still bound the execution request. - The Docker adapter mounts the configured workspace at
/workspaceand applies the requested read-only mount. The image must already contain the interpreter; AML does not build images or install tools. - The second
<Agent />gets the script’s stdout as data. It is not given an implicit tool, filesystem access, or permission to execute the generated source again.
Failure, cancellation, and security notes
Section titled “Failure, cancellation, and security notes”- If Docker cannot start
node:26, the Sandbox acquisition fails before the script runs. - If the generated source is empty,
<Script shell="node" />rejects it. A non-zero exit code becomes an AML evaluation error and includes the captured stderr detail. - Cancellation is propagated to the active Sandbox command and provider work. It cannot undo a side effect that already completed inside a command.
access="read-only"protects the mounted workspace from ordinary writes through that mount; it does not make the image, kernel, network, host, or inherited credentials a complete isolation boundary.- Do not run arbitrary model-authored code with
localSandbox(): local execution is trusted host-process execution, not isolation. - Do not put secrets in generated source, prompts, Workspace files, or trace content.
Variations
Section titled “Variations”- Replace
generatorwithopencodeAgent({})orcodexAgent({})only after selecting a host or image that contains the required ACP executable and configuring its credentials. A plainnode:26image is not sufficient for that live-provider variant. - Replace the Docker provider with Daytona or Modal when the deployment needs remote disposable compute; read their transfer and cancellation semantics before relying on Workspace state.
- Add a schema or ordinary TypeScript validator for the generated source and output when the diagnostic has a stricter contract. Shape validation still does not establish authorization or factual correctness.
- Wrap the Sandbox in a Workspace when the workflow must materialize or reconcile files. A Sandbox lease alone is not durable persistence.