Skip to content

Getting started

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.

  • Node.js 26 or newer
  • an ESM project ("type": "module" in package.json)
  • TypeScript if you author .tsx

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

Terminal
mkdir aml-first-workflow
cd aml-first-workflow
npm init -y
npm pkg set type=module
npm install @aml-jsx/sdk
npm install --save-dev typescript vite-node

Create tsconfig.json:

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:

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:

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:

Terminal
npm run aml
# or: pnpm aml

Expected output:

Output
Resolved: Explain what AML evaluates.
  1. JSX creates an AML node tree; it does not run <Agent /> during rendering.
  2. AmlRuntime.evaluate() validates and resolves the tree.
  3. <Agent /> builds a normalized request from its system text, prompt, capabilities, and effective scopes.
  4. The selected provider returns an AgentResponse; the runtime returns its text from the root.
  5. The runtime releases acquired resources in reverse order.

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:

src/first-workflow.tsx
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: 3

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

src/first-workflow.tsx
import { Agent, AmlRuntime, codexAgent } from "@aml-jsx/sdk"
const apiKey = process.env.OPENAI_API_KEY
if (!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:

Terminal window
npm install --global @agentclientprotocol/codex-acp@2.2.2 @openai/codex@0.162.1
codex-acp --help
codex --version
export OPENAI_API_KEY="your-development-key"
npm run aml

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

5. Add files and command execution when needed

Section titled “5. Add files and command execution when needed”

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

src/resources.tsx
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:

Terminal
npm install --save-dev @aml-jsx/cli

Create src/cli-workflow.tsx. Put the provider directly on <Agent />, export the tree, and let the CLI create and instrument the runtime:

src/cli-workflow.tsx
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:

Terminal
npx aml run ./src/cli-workflow.tsx

Expected output on standard output:

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.