Understand evaluation
Learn post-order resolution, lexical scopes, concurrency, and cleanup in Core concepts.
A deterministic Agent provider runs AML without model credentials, an agent executable, Docker, or a remote account. Agent providers control how sessions run; the same components can use a live provider once its executable and credentials are configured.
"type": "module" in package.json).tsxIf a coding agent will author or review the workflow, optionally install the AML authoring skill. The skill teaches the agent the current public API and safety boundaries; it does not replace the SDK installation below.
mkdir aml-first-workflowcd aml-first-workflownpm init -ynpm pkg set type=modulenpm install @aml-jsx/sdknpm install --save-dev typescript vite-nodemkdir aml-first-workflowcd aml-first-workflowpnpm initpnpm add @aml-jsx/sdkpnpm add --save-dev typescript vite-nodeAdd "type": "module" to package.json.
Create tsconfig.json:
{ "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "target": "ES2024", "strict": true, "skipLibCheck": true, "jsx": "react-jsx", "jsxImportSource": "@aml-jsx/sdk" }}The automatic JSX runtime turns AML elements into AML nodes. React is not installed or involved. skipLibCheck skips
checking upstream declaration files loaded by the bundled provider adapters while preserving strict checks for your
project source.
Add one repeatable command to the generated package.json:
{ "type": "module", "scripts": { "aml": "vite-node src/first-workflow.tsx" }}Keep the dependencies added by your package manager when you merge this snippet into the generated file.
Create src/first-workflow.tsx:
import { Agent, AmlRuntime } from "@aml-jsx/sdk"import { DeterministicAgentProvider } from "@aml-jsx/sdk/testing"
const agentProvider = new DeterministicAgentProvider({ name: "first-workflow", respond: request => ({ text: `Resolved: ${request.prompt}` }),})
const runtime = new AmlRuntime({ agentProvider })
const result = await runtime.evaluate(<Agent system="Answer in one sentence.">Explain what AML evaluates.</Agent>)
console.log(result)Run it with the script you just added:
npm run aml# or: pnpm amlExpected output:
Resolved: Explain what AML evaluates.<Agent /> during rendering.AmlRuntime.evaluate() validates and resolves the tree.<Agent /> builds a normalized request from its system text, prompt, capabilities, and effective scopes.AgentResponse; the runtime returns its text from the root.The DeterministicAgentProvider deliberately echoes a known result and records calls. It is part of the public testing entrypoint, making it useful for tutorials, examples, and workflow tests—not just AML’s own test suite.
An AML component can combine several agents into a reusable unit. Here two specialists receive the same change description in parallel. Their results become input to a release editor, which starts after both specialists finish.
AML.Component<{ change: string }> declares the component’s input and lets it return any AML value. Import AML as a type. When a component accepts nested content, declare it with AML.PropsWithChildren or AML.PropsWithRequiredChildren.
Replace src/first-workflow.tsx with this credential-free example:
import { type AML, Agent, AmlRuntime, Block, Parallel } from "@aml-jsx/sdk"import { DeterministicAgentProvider } from "@aml-jsx/sdk/testing"
const ReleaseBrief: AML.Component<{ change: string }> = ({ change }) => { return ( <Agent name="release-editor" system="Write a release brief from the supplied findings."> <Parallel> <Block tag="user-impact"> <Agent name="impact-specialist" system="Explain the user impact."> {change} </Agent> </Block> <Block tag="rollout-checks"> <Agent name="rollout-specialist" system="List checks before rollout."> {change} </Agent> </Block> </Parallel> </Agent> )}
const provider = new DeterministicAgentProvider({ respond(request) { if (request.system === "Explain the user impact.") { return { text: "Users can export their invoices as CSV." } } if (request.system === "List checks before rollout.") { return { text: "Check account permissions and CSV escaping." } } return { text: request.prompt } },})
const runtime = new AmlRuntime({ agentProvider: provider })console.log(await runtime.evaluate(<ReleaseBrief change="Add CSV export to the invoice list." />))console.log(`Agent sessions: ${provider.calls.length}`)Run npm run aml. The editor fixture echoes the resolved specialist sections, followed by Agent sessions: 3. The output contains:
<user-impact>Users can export their invoices as CSV.</user-impact>
<rollout-checks>Check account permissions and CSV escaping.</rollout-checks>Agent sessions: 3Each <Block> adds blank lines around its section, so adjacent Blocks produce four newline characters between tags. JSX children otherwise concatenate without automatic separators. The fixture supplies known answers; it demonstrates composition, not live reasoning.
<Parallel> starts independent branches and preserves their authored output order. Each <Agent> is a separate session. Its result flows upward; its tools and system instructions do not transfer to its parent. The runtime default supplies a provider to every Agent that omits a provider prop.
To run this team live, keep ReleaseBrief, replace the fixture provider using the next section, and remove the provider.calls diagnostic. The same pattern is available from a repository checkout as npm run example -- agent-team.
The authored node remains the same. Replace the contents of src/first-workflow.tsx with one complete tab below, then run the same aml script.
import { Agent, AmlRuntime, codexAgent } from "@aml-jsx/sdk"
const apiKey = process.env.OPENAI_API_KEYif (!apiKey) throw new Error("OPENAI_API_KEY is required")
const runtime = new AmlRuntime({ agentProvider: codexAgent({ apiKey, model: "gpt-5.6-luna", reasoningEffort: "low", }),})const result = await runtime.evaluate(<Agent>Describe this project in three bullets.</Agent>)
console.log(result)Install and verify the ACP adapter version exercised by this repository, then supply a credential through your shell or secret manager:
npm install --global @agentclientprotocol/codex-acp@2.2.2 @openai/codex@0.162.1codex-acp --helpcodex --versionexport OPENAI_API_KEY="your-development-key"npm run amlDo not commit the key or place it in a Workspace. See the Codex guide for executable overrides, CODEX_API_KEY, model precedence, permissions, and Sandbox compatibility.
import { Agent, AmlRuntime, opencodeAgent } from "@aml-jsx/sdk"
const apiKey = process.env.OPENCODE_API_KEYif (!apiKey) throw new Error("OPENCODE_API_KEY is required")
const runtime = new AmlRuntime({ agentProvider: opencodeAgent({ env: { OPENCODE_API_KEY: apiKey }, model: "opencode-go/deepseek-v4.1-flash", }),})const result = await runtime.evaluate(<Agent>Describe this project in three bullets.</Agent>)
console.log(result)Install and verify the OpenCode executable, then configure a model provider:
npm install --global opencode-ai@1.19.0opencode --versionexport OPENCODE_API_KEY="your-development-key"npm run amlThe OpenCode configuration passes the model credential through env. An interactive opencode auth login alone is insufficient for AML’s default isolated session: AML gives each invocation a private XDG_DATA_HOME. To use file-based login, stage the required credentials in a dedicated data directory and pass its path through env.XDG_DATA_HOME. Select a model available to that account.
import { Agent, AmlRuntime, piAgent } from "@aml-jsx/sdk"
const apiKey = process.env.OPENCODE_API_KEYif (!apiKey) throw new Error("OPENCODE_API_KEY is required")
const runtime = new AmlRuntime({ agentProvider: piAgent({ command: "pi-acp", env: { OPENCODE_API_KEY: apiKey }, model: "opencode-go/deepseek-v4.1-flash", piCommand: "pi", }),})const result = await runtime.evaluate(<Agent>Describe this project in three bullets.</Agent>)
console.log(result)Install and verify the ACP bridge and Pi CLI:
npm install --global pi-acp@0.0.34 @earendil-works/pi-coding-agent@1.2.0pi --versioncommand -v pi-acpexport OPENCODE_API_KEY="your-development-key"npm run amlThis follows the repository smoke path with DeepSeek V4.1 Flash through OpenCode Go. If you select another Pi model provider, change the model identifier and forward that provider’s credential instead. The Pi guide explains when AML generates a wrapper and how to install pi-mcp-adapter for Tools, authored MCP servers, or structured output.
<File /> writes through the nearest active <Workspace /> or <Sandbox /> filesystem. <Script /> can run directly on the trusted host, but this example deliberately uses a fresh Workspace directory and scopes command execution to that materialization.
import { mkdtemp } from "node:fs/promises"import { tmpdir } from "node:os"import { join } from "node:path"import { AmlRuntime, File, Sandbox, Script, Workspace, localSandbox, localWorkspace } from "@aml-jsx/sdk"
const directory = await mkdtemp(join(tmpdir(), "aml-guide-"))const runtime = new AmlRuntime({ sandboxProvider: localSandbox(), workspaceProvider: localWorkspace({ directory }),})
const output = await runtime.evaluate( <Workspace id="guide" load={false} save={false}> <File path="brief.txt">Inspect this isolated guide directory.</File> <Sandbox access="read-write"> <Script command="node" args={["-e", "console.log('workspace ready')"]} /> </Sandbox> </Workspace>)
console.log(output)Run npx vite-node src/resources.tsx. Expected output is workspace ready. This example creates a temporary directory; remove directory with rm(directory, { recursive: true, force: true }) from node:fs/promises after inspecting its contents. A real local Workspace writes directly to its directory even with save={false}; that flag controls revision publication, not rollback.
The application above constructs AmlRuntime because it owns the process. For a standalone workflow file, install the experimental CLI beside the SDK:
npm install --save-dev @aml-jsx/cliCreate src/cli-workflow.tsx. Put the provider directly on <Agent />, export the tree, and let the CLI create and instrument the runtime:
import { Agent } from "@aml-jsx/sdk"import { DeterministicAgentProvider } from "@aml-jsx/sdk/testing"
const provider = new DeterministicAgentProvider({ name: "cli-workflow", respond: request => ({ text: `Resolved: ${request.prompt}` }),})
export default <Agent provider={provider}>Explain what AML evaluates.</Agent>Run the exported workflow:
npx aml run ./src/cli-workflow.tsxExpected output on standard output:
Resolved: Explain what AML evaluates.Understand evaluation
Learn post-order resolution, lexical scopes, concurrency, and cleanup in Core concepts.
Build from a recipe
Copy a complete workflow from the Cookbook, with expected output and provider requirements.
Choose infrastructure
Compare the complete provider catalog across Agent, Sandbox, and Workspace responsibilities.
Prepare production
Review isolation, secrets, persistence, cancellation, and telemetry in Production.
Run workflow files
Use the experimental CLI when an exported AML tree should run without application entry-point glue.
Work with a coding agent
Install and verify the AML authoring skill before asking an agent to generate or review AML code.