Skip to content

Run generated diagnostics in a Sandbox

Deterministic Agent + Docker Sandbox

Build a repository diagnostic with three visible boundaries:

  1. <Agent /> proposes a small Node.js program;
  2. <Script /> executes that resolved text inside an explicitly selected Docker Sandbox;
  3. 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.

  • Node.js >=26, ESM TypeScript/TSX execution, and @aml-jsx/sdk;
  • Docker running locally with node:26 available (docker pull node:26 if 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.

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())

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:

Terminal
docker image inspect node:26
npx vite-node recipe.tsx

A 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.

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.

  1. The application authors the order in TypeScript. The first <Agent /> returns source text; it does not create AML nodes or change the workflow.
  2. The enclosing <Sandbox /> makes <Script /> invoke Node inside Docker. Without that boundary the same Script would run on the AML host, and timeoutMs would still bound the execution request.
  3. The Docker adapter mounts the configured workspace at /workspace and applies the requested read-only mount. The image must already contain the interpreter; AML does not build images or install tools.
  4. 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.
  • 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.
  • Replace generator with opencodeAgent({}) or codexAgent({}) only after selecting a host or image that contains the required ACP executable and configuring its credentials. A plain node:26 image 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.