Skip to content

Runtime lifecycle

AmlRuntime is the owner of one evaluation domain. A domain contains the run identity, cancellation signal, Agent scheduler, context registry, resource scopes, limits, lifecycle events, and trace correlation.

This page explains lifecycle choices and production behavior. For the concise constructor options, exact defaults, and per-evaluation decisions, use the runtime reference.

Application entry points construct AmlRuntime directly. If a trusted workflow should run as an exported TSX file instead, the experimental CLI creates the runtime and expects providers on the relevant AML components.

import { AmlRuntime } from "@aml-jsx/sdk"
const runtime = new AmlRuntime({
agentProvider,
maxAgentCalls: 24,
maxConcurrentAgents: 4,
})
const result = await runtime.evaluate(<Workflow />)

Nested evaluate() calls stay in the root domain. Concurrent calls to runtime.evaluate() get independent domains even when they share one runtime instance.

OptionDefaultScopeWhat it bounds or configures
agentProvidernoneruntimeDefault Agent provider for <Agent /> components without provider
sandboxProvidernoneruntimeDefault provider for outer <Sandbox /> components
workspaceProvidernoneevaluationDefault provider for the one top-level <Workspace />
systemnoneruntimeFirst system fragment supplied to every <Agent />
cwdprocess.cwd()runtimeBase directory for local <Include src>, <File src>, and <Skill src> reads plus unsandboxed <Script /> execution and its relative cwd overrides
allowedToolsunrestrictedruntimeExact names of JavaScript <Tool /> capabilities that may be granted
allowedMcpServersunrestrictedruntimeExact names of <Mcp /> servers that may be granted
tracenoneruntimeTrace sink captured when the runtime is constructed
onTraceErrornoneruntimeReceives trace-consumer failures without joining them to workflow failure

Provider options remain on provider factories. Runtime defaults do not install executables, credentials, Docker images, or remote resources.

The runtime defaults are finite and apply per evaluation domain:

LimitDefaultCounts
maxAgentCalls32Provider-backed Agent sessions
maxConcurrentAgents4Active Agent provider calls
maxDepth16Nested component/primitive depth; arrays and promises do not add semantic depth
maxTurnsPerAgent16Authored initial and <FollowUp /> inputs in one session

Every limit must be a non-negative safe integer. 0 disables that limit, which is an explicit choice rather than an unlimited default. Prefer finite values when workflows can be user-authored or provider-controlled.

const runtime = new AmlRuntime({
maxAgentCalls: 12,
maxConcurrentAgents: 3,
maxDepth: 12,
maxTurnsPerAgent: 6,
})

maxConcurrentAgents is a scheduler bound, not a promise cancellation mechanism. If one concurrent branch fails, application code should decide whether to abort sibling work.

Pass a caller-owned AbortSignal per evaluation:

const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), 60_000)
try {
return await runtime.evaluate(<Workflow />, { signal: controller.signal })
} finally {
clearTimeout(timer)
}

An already-aborted signal rejects before the tree starts. During execution AML propagates the signal to active provider calls and Sandbox operations and stops advancing to later frames. Cancellation does not roll back a Tool, Script, provider, or network side effect that already completed.

An application-owned Node entry point can opt into graceful SIGINT and SIGTERM handling with ProcessSignalCancellation. Constructing the helper installs process listeners; importing the SDK does not. Pass its signal to every active evaluation and dispose it only after those evaluations settle:

import { AmlRuntime, ProcessSignalCancellation } from "@aml-jsx/sdk"
const cancellation = new ProcessSignalCancellation({ cleanupDeadlineMs: 15_000 })
try {
await runtime.evaluate(<Workflow />, { signal: cancellation.signal })
} catch (error) {
if (cancellation.exitCode === undefined) throw error
} finally {
cancellation.dispose()
}
if (cancellation.exitCode !== undefined) {
process.exitCode = cancellation.exitCode
}

The first signal aborts the evaluation and lets AML release Agent sessions, Sandbox leases, and Workspace scopes. A second signal exits immediately. Cleanup is bounded to 10 seconds by default; set cleanupDeadlineMs for the deployment’s shutdown budget. When cleanup completes first, use exitCode to preserve status 130 for SIGINT or 143 for SIGTERM without calling process.exit() early.

start and finish are runtime lifecycle events. They are dispatched to lifecycle listeners as part of evaluation control flow:

const runtime = new AmlRuntime()
runtime.on("start", ({ runId }) => {
logger.info({ runId }, "AML evaluation started")
})
runtime.on("finish", ({ runId, status, error }) => {
logger.info({ runId, status, error }, "AML evaluation finished")
})

finish is emitted after cleanup. Its status is "ok" or "error"; on error, inspect error and correlate it with the run ID. Unsubscribe with the function returned by on() when a listener is temporary.

Trace events are a separate stream and are intentionally dispatched without blocking workflow completion. Agent sessions add actual turn and cleanup spans; the shared ACP path adds ordered session updates and process lifecycle facts without claiming visibility into internal model calls. See Observability for the discriminated shapes and content policy.

AML releases lexical scopes in reverse nesting order. Workspace save and lock release belong to the Workspace contract; Agent sessions and Sandbox leases belong to their providers. If cleanup fails, the run remains observable as failed rather than being presented as successful with hidden resource leaks.

  1. Resolve the root and enter any top-level <Workspace />.
  2. Acquire each <Sandbox /> before descendants use it.
  3. Resolve capabilities and execute Agent sessions in dependency order.
  4. Save Workspace state according to its policy.
  5. Release Sandbox and Workspace resources, preserving cleanup errors.
const runtime = new AmlRuntime({
agentProvider,
sandboxProvider,
workspaceProvider,
allowedTools: ["read_review_fixture"],
allowedMcpServers: ["project"],
maxAgentCalls: 20,
maxConcurrentAgents: 4,
maxDepth: 16,
maxTurnsPerAgent: 8,
})

Pair finite limits with provider-specific timeouts, cancellation, structured logging, and a Sandbox appropriate to the threat model. The runtime limits protect orchestration; they do not make a host-process Sandbox secure.