For AI agents: the complete documentation index is available at https://scriptedalchemy.github.io/agent-bundle/llms.txt, the full documentation bundle is available at https://scriptedalchemy.github.io/agent-bundle/llms-full.txt, and this page is available as Markdown at https://scriptedalchemy.github.io/agent-bundle/reference/runtime-environment.md.
Skip to main content
  • English
  • Runtime environment

    Node.js

    The compiler and the Workbench need Node.js 22.19 or later; generated executables target Node.js 22.12 or newer unless runtime.node raises that floor. Both are stated in Installation, and the selected executable floor is recorded as runtime.node in the artifact manifest.

    Host path tokens

    Generated documents reference the plugin root, the composite root every selected host loads, through the token each host substitutes, and the compiler writes the right spelling into each host's own documents rather than assuming one:

    HostPlugin rootPlugin data
    Claude Code${CLAUDE_PLUGIN_ROOT}${CLAUDE_PLUGIN_DATA}
    Cursor${CURSOR_PLUGIN_ROOT}none (rejected: no documented host equivalent)
    Codex${PLUGIN_ROOT} (hook commands only)none (rejected: no documented host equivalent)
    portable${PLUGIN_ROOT}${PLUGIN_DATA}

    Substitution is positional, not global. Codex's ${PLUGIN_ROOT} is the command root of its generated hook commands and nothing else: the Codex MCP runtime interpolates no path tokens, so a plugin-root token in an MCP command, args, or env value is accepted only when it leads the value and the server's cwd is the plugin root, in which case the compiler rewrites it to a ./-relative path under cwd: "./"; an embedded token, a token without that cwd, or any plugin-data or workspace-root token is a build error. Claude Code substitutes ${CLAUDE_PLUGIN_ROOT} and its siblings in Skill and agent content, hook and monitor commands, MCP servers, and LSP servers only, never in settings.json, which is why claude.settings rejects path tokens outright. Cursor's pinned loader has its own substituted-field table, and a token outside it reports AB6028 at build time and AB7320 from Doctor.

    Environment variables

    VariableRead byMeaning
    AGENT_BUNDLE_PLUGIN_ROOTGenerated executablesThe plugin code root, the composite plugin root, the directory holding the host manifests, mcp/, hooks/, and bin/. Overrides the built-in fallback; surfaced to routes and providers as (await agent()).plugin.value.root (source: 'native'). An unexpanded ${…} token is treated as unset.
    AGENT_BUNDLE_STATE_ROOTGenerated executablesThe framework state root: the exact directory where the SQLite state kernel, notice ledger, and lineage journal live. Overrides the derived location. An unexpanded ${…} token is treated as unset.
    XDG_STATE_HOMEGenerated executablesWhen set to an absolute path (a relative value is ignored, per the base-directory spec), the derived state root is $XDG_STATE_HOME/agent-bundle/<plugin>-<digest> instead of ~/.agent-bundle/state/<plugin>-<digest>.
    AGENT_BUNDLE_ENV_FILEGenerated executablesThe operator env file(s) an installed pack reads at launch instead of <plugin root>/.env and .env.local: one path, or several joined by the platform path delimiter, later files winning; none disables the layer. mcp run sets it for its child from --env-file / --no-env.
    AGENT_BUNDLE_AGENT_API_TOKENagent-bundle devThe bearer token the Agent API requires before it can be enabled.
    AGENT_BUNDLE_HOOK_SIMULATIONGenerated hook wrappers1 marks a simulated invocation; the Workbench event route workspace sets it.
    AGENT_BUNDLE_DEV_TRACE_URLGenerated hook wrappers in developmentInternal loopback origin for posting a payload-free hook trace receipt. The foreground server sets it for simulations; host-invoked development wrappers normally discover the same endpoint from the development install marker.
    AGENT_BUNDLE_DEV_TRACE_TOKENGenerated hook wrappers in developmentInternal bearer token paired with AGENT_BUNDLE_DEV_TRACE_URL. It authenticates POST /api/trace/receipts and must not be logged or persisted outside the private development endpoint record.
    AGENT_BUNDLE_DEV_SESSIONGenerated hook wrappers and agent-bundle dev proxy in developmentHost-session id (hs_ + 16 lowercase characters) the Workbench sets on a PTY-backed host. When set, the hook wrapper posts it as the receipt devSession field; when valid, the proxy sends it as x-agent-bundle-dev-session. Must not be logged.
    AGENT_BUNDLE_NATIVE_HOST_CONTRACTSContributor test suites1 compares the installed host CLI contract.
    AGENT_BUNDLE_NATIVE_CLAUDE_SMOKEContributor test suites1 runs the signed-in Claude native smoke.
    AGENT_BUNDLE_NATIVE_CODEX_SMOKEContributor test suites1 runs the signed-in Codex native smoke.
    AGENT_BUNDLE_WORKBENCH_API_PROXYContributor HMR (packages/workbench/scripts/dev.mjs)The running Agent Bundle foreground server URL required before Workbench UI HMR starts; the Rsbuild dev server proxies /api there and, while it is set, refuses to move off its port (server.strictPort). Pair it with agent-bundle dev --workbench-dev-origin <origin> on the foreground server. See Contributor UI HMR.

    The three native-smoke opt-ins exist because those runs need a real signed-in CLI; they are never part of an ordinary test run.

    .env precedence under mcp run

    agent-bundle mcp run composes the launch environment in three layers, lowest priority first:

    1. The generated server's own env, with path tokens already resolved.
    2. The .env file layer.
    3. The operator's real process.env, so an exported variable always wins.

    The .env layer is the conventional Rsbuild set (.env, .env.local, .env.<mode>, .env.<mode>.local) at the project root, which the runtime calls the workspace root, unless --env-file <path> replaces it with exactly the files you name, or --no-env removes it. The two flags are mutually exclusive. Files are read into a scratch object, so the real process.env is never mutated.

    Operator .env files in installed packs

    A host launches an installed pack directly, so the pack's own shells apply the same layer themselves: the stdio MCP entries, the hook wrappers that run plugin code, and the artifact CLI bin/<name>.mjs read <plugin root>/.env then .env.local at launch (or the files AGENT_BUNDLE_ENV_FILE names), filling only variables the host did not set, an exported variable wins, a manifest env default the host passed through yields to the file (the stdio entry carries its server's declared block to tell the two apart), .env.local beats .env, values are never logged, a missing file costs nothing, an unreadable one is skipped. The layer is applied before the server, handler, route, provider, and state modules evaluate, so a module-level process.env read sees it. The plugin root is AGENT_BUNDLE_PLUGIN_ROOT when the host set it, otherwise the directory above the shell's own. No ${VAR} interpolation is performed. Under mcp run the plugin root is the project root, so the shell re-reads the files mcp run already composed and changes nothing; --env-file and --no-env travel down as AGENT_BUNDLE_ENV_FILE. The npm package bin reads no pack file. Doctor reports the presence and variable count of each file (AB7331). Details and the precedence table: Installation.

    Durable state

    Installed artifacts, the MCP entry, Flight worker, artifact CLI bin, render worker, and standalone hook wrappers, derive ~/.agent-bundle/state/<plugin>-<digest> as their framework state root. <plugin> is the code root's basename, or plugin when that basename is not a safe path segment; <digest> is the first 16 hexadecimal characters of the SHA-256 digest of the code root's real path. Two installs therefore never share state, and a read-only install launches. XDG_STATE_HOME moves the derived root as described above, and AGENT_BUNDLE_STATE_ROOT overrides it. The npm package bin keeps ./.agent-bundle/state.

    Only a workspace-durable state definition uses the SQLite driver; other lifetimes use the in-memory driver and keep nothing on disk. Every generated process resolves the code root and state root exactly once (resolvePluginRoot from @agent-bundle/runtime) and publishes them as (await agent()).plugin, { root, stateRoot }, so a route or provider that keeps its own files beside the framework's reads plugin.value.stateRoot instead of re-deriving the path.

    Under mcp run, plugin-root anchors in env values expand to the project root by default, not the artifact: the artifact is an ephemeral build product there, and anchoring durable state on it would fragment that state on every rebuild. Pass --plugin-root <path> pointing at the artifact root itself, the composite plugin root, not a host subdirectory, for a byte-faithful rehearsal of a copied-artifact launch. The state root then derives from that expanded code root (~/.agent-bundle/state/<project>-<digest>) unless AGENT_BUNDLE_STATE_ROOT names one. Workbench MCP sessions and Workbench route invocations launched by agent-bundle dev pin AGENT_BUNDLE_STATE_ROOT to <project>/.agent-bundle/state instead, one state root per project, beside the build epochs rather than inside them, so it survives republishes and epoch retirement without accumulating one user-data directory per rebuild. The warm dev runtime itself does not set that variable.

    A per-server state directory uses the server name when it is a single safe segment; any other name becomes a content-addressed server-<digest> segment, so a name like ../shared can never traverse out of the state root.

    Default state projection budgets are 5,000 ms per commit, 262,144 bytes per event, 1,048,576 bytes of state, and 100,000 revisions. agent-bundle inspect --state reports the resolved driver, lifetime, durable location, and budget source for each definition.

    Stores written before the current SQLite layout

    The driver reads only the layout it writes today. It neither adopts nor migrates a store written before that layout, in either of two ways.

    A database under the pre-#201 file name, whose suffix was the state id's own leading bytes in hex rather than a SHA-256 digest, is never opened. When a root-mode store finds that file and no database under the current name, the open fails with a corrupt AgentStateError that names the old file, instead of creating an empty store beside it. The old file is left untouched. Once a database under the current name exists, the old file is not consulted.

    A database under the current file name whose agent_state_journal table differs from the one the driver creates fails at open with a corrupt AgentStateError naming the table. Column names, their order, and their NOT NULL flags all count, so a journal whose result_state column is nullable, as an older runtime's ALTER TABLE left it, is rejected before any row is read.

    Recover from either by moving the stale database files out of the state root to keep a copy, or deleting them: each *.sqlite plus its -wal and -shm sidecars, or the whole state root. The next launch creates a fresh store at the definition's initial state. Nothing reconstructs the old history.

    Next

    • Security: the credential and network boundaries around these processes.
    • CLI: the flags referenced above.