# Runtime lifecycle

Configure AML evaluation domains, limits, cancellation, events, and cleanup.
Canonical: https://agent-markup-language.com/docs/runtime/
Documentation index: https://agent-markup-language.com/docs/
Complete documentation: https://agent-markup-language.com/docs/llms.txt

[`AmlRuntime`](https://agent-markup-language.com/docs/reference/runtime/) 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](https://agent-markup-language.com/docs/reference/runtime/).

Application entry points construct `AmlRuntime` directly. If a trusted workflow should run as an exported TSX file instead, the [experimental CLI](https://agent-markup-language.com/docs/cli/) creates the runtime and expects providers on the relevant AML components.

```tsx
import { AmlRuntime } from "@aml-jsx/sdk"

const runtime = new AmlRuntime({
  agentProvider,
  maxAgentCalls: 24,
  maxConcurrentAgents: 4,
})

const result = await runtime.evaluate(<Workflow />)
```

Nested [`evaluate()`](https://agent-markup-language.com/docs/reference/runtime/#component-local-evaluation) calls stay in the root domain. Concurrent calls to `runtime.evaluate()` get independent domains even when they share one runtime instance.

## Runtime options

| Option              |         Default | Scope      | What it bounds or configures                                                                                                                                                                                                                                                                                 |
| ------------------- | --------------: | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `agentProvider`     |            none | runtime    | Default Agent provider for [`<Agent />`](https://agent-markup-language.com/docs/reference/primitives/agent/) components without `provider`                                                                                                                                                                                                    |
| `sandboxProvider`   |            none | runtime    | Default provider for outer [`<Sandbox />`](https://agent-markup-language.com/docs/reference/primitives/sandbox/) components                                                                                                                                                                                                                   |
| `workspaceProvider` |            none | evaluation | Default provider for the one top-level [`<Workspace />`](https://agent-markup-language.com/docs/reference/primitives/workspace/)                                                                                                                                                                                                              |
| `system`            |            none | runtime    | First system fragment supplied to every `<Agent />`                                                                                                                                                                                                                                                          |
| `cwd`               | `process.cwd()` | runtime    | Base directory for local [`<Include src>`](https://agent-markup-language.com/docs/reference/primitives/include/), [`<File src>`](https://agent-markup-language.com/docs/reference/primitives/file/), and [`<Skill src>`](https://agent-markup-language.com/docs/reference/primitives/skill/) reads plus unsandboxed [`<Script />`](https://agent-markup-language.com/docs/reference/primitives/script/) execution and its relative cwd overrides |
| `allowedTools`      |    unrestricted | runtime    | Exact names of JavaScript [`<Tool />`](https://agent-markup-language.com/docs/reference/primitives/tool/) capabilities that may be granted                                                                                                                                                                                                    |
| `allowedMcpServers` |    unrestricted | runtime    | Exact names of [`<Mcp />`](https://agent-markup-language.com/docs/reference/primitives/mcp/) servers that may be granted                                                                                                                                                                                                                      |
| `trace`             |            none | runtime    | Trace sink captured when the runtime is constructed                                                                                                                                                                                                                                                          |
| `onTraceError`      |            none | runtime    | Receives 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.

## Finite safety limits

The runtime defaults are finite and apply per evaluation domain:

| Limit                 | Default | Counts                                                                                             |
| --------------------- | ------: | -------------------------------------------------------------------------------------------------- |
| `maxAgentCalls`       |    `32` | Provider-backed Agent sessions                                                                     |
| `maxConcurrentAgents` |     `4` | Active Agent provider calls                                                                        |
| `maxDepth`            |    `16` | Nested component/primitive depth; arrays and promises do not add semantic depth                    |
| `maxTurnsPerAgent`    |    `16` | Authored initial and [`<FollowUp />`](https://agent-markup-language.com/docs/reference/primitives/follow-up/) 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.

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

## Cancellation

Pass a caller-owned `AbortSignal` per evaluation:

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

### Process signals

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:

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

**Caution — Use one owner at the process boundary**

Do not construct one helper per request. A service should create one at its composition root, stop accepting new work,
combine its signal with request deadlines using `AbortSignal.any()`, await every active evaluation, and then dispose
the helper. Frameworks or supervisors that already own shutdown should pass their existing signal instead of
installing a competing process listener.

## Lifecycle events

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

```tsx
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](https://agent-markup-language.com/docs/observability/) for the discriminated shapes and content policy.

## Cleanup is part of the result

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 />`](https://agent-markup-language.com/docs/reference/primitives/workspace/).
2. Acquire each [`<Sandbox />`](https://agent-markup-language.com/docs/reference/primitives/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.

**Caution — Do not reuse a live evaluation domain**

A runtime instance may be reused, but each `evaluate()` call owns fresh mutable state. Do not retain component-local
`evaluate()` calls after the component that created them has returned; AML rejects detached calls.

## A production baseline

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