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/configuration.md.
Skip to main content
  • English
  • Configuration

    The conceptual model, what config owns versus what the src/ conventions own, is in Configuration model. This page is the field contract.

    import { 
    const defineConfig: (config: AgentBundleConfig | ConfigFactory) => AgentBundleConfig | ConfigFactory
    defineConfig
    } from 'agent-bundle/config';
    export default
    function defineConfig(config: AgentBundleConfig | ConfigFactory): AgentBundleConfig | ConfigFactory
    defineConfig
    ({
    AgentBundleConfig.plugin: AgentBundlePluginConfig
    plugin
    : {
    AgentBundlePluginConfig.description?: string | undefined
    description
    : 'Evidence-backed project tools.',
    AgentBundlePluginConfig.name: string
    name
    : 'my-plugin' },
    AgentBundleConfig.targets?: string[] | undefined
    targets
    : ['portable', 'codex', 'claude'],
    });

    Top-level fields

    FieldTypeDefault
    plugin{ name, description?, logo?, ... }Required.
    targetsstring[]['portable'], the only adapter registered as a default; a --target flag overrides both the default and a declared list.
    skillsstring[]The src/skills/* convention.
    hooksPartial<Record<CanonicalHookEvent, ...>>The src/hooks/* convention.
    mcp{ servers: Record<string, ...> }The src/mcp/* convention.
    scriptsRecord<string, string | { entry, targets? }>The src/scripts/* convention.
    assetsstring[]The root assets/ convention.
    binfalse | Record<string, string | { entry }>The src/cli.ts convention.
    libfalse | string | { entry, dts? }The src/index.ts convention.
    routesRoute-graph policy (cli, mcpCommands, servers)Convention-derived. A tool with a colocated <tool>.cli.ts is excluded from routes.mcpCommands.
    output{ distPath?, sourceMap?, repositoryMarketplace? }artifact from the CLI; dist from build() without packageOutputs. Both boolean options default to false.
    runtime{ node }Node 22.12.
    payloadRecord<string, string | { source, targets? }>None.
    statefalseThe src/state.ts convention.
    notices{ retention?: { terminalTtl?, maxTerminal?, maxJournalBytes? } }Runtime defaults (7d, 500, 16777216).
    marketplacebooleanfalse. Only marketplace: true is carried into the normalized model; it is what makes the Cursor marketplace plan emit .cursor-plugin/marketplace.json.
    dev{ agentApi?, contracts?, runtime? }None.
    evals{ include?, runsDir?, semanticGrader? }See below.
    tools{ rsbuild?, rspack? }None.
    web{ apps, open? }None, omit it and nothing is emitted.

    Host-scoped extension keys (claude, codex, cursor, portable) are contributed by target adapters through declaration merging on AgentBundleConfigExtensions. Extension values must be strict finite JSON (AB4500), and host-specific values stay in their adapter rather than in compiler core. The field-by-field list of every adapter-owned key is in Host-scoped declarations.

    Generated type definitions

    The config is a TypeScript contract, not a runtime schema: defineConfig accepts an AgentBundleConfig (or a factory returning one), and validateSource enforces the rules on this page with structured diagnostics. The exact shapes below are generated from the package source by TypeDoc on every documentation build, so they cannot drift from the published types. Two keys in the table above are not members of AgentBundleConfig: evals and routes are read through its [key: string]: unknown index signature, so tsc does not check their shape. The route compiler still parses routes during discovery, so agent-bundle validate reports a malformed override through the route graph's diagnostics; validateSource never reads evals, the evals rules below fire when agent-bundle eval or the Workbench loads the config, as EVAL_CONFIG_INVALID, EVAL_INCLUDE_INVALID, or EVAL_RUNS_DIR_INVALID errors rather than AB diagnostics. A tool that already has a colocated <tool>.cli.ts projection module is excluded from routes.mcpCommands so the bulk <bin> <server> <tool> --input command is not also compiled for that operation. See Project one tool as an idiomatic command.

    plugin

    FieldRule
    nameRequired. The host-native plugin slug, never the npm package name.
    descriptionOne sentence written into generated manifests.
    logoProject-relative image path. Missing, non-file, or outside the project reports AB4012; missing from the built artifact or escaping the deploy tree reports AB6025.
    metadataThe shared descriptive layer: author, homepage, keywords, license, repository. Each field defaults to the same field in package.json; a value here overrides it, and null shares nothing. A malformed value reports AB4014.

    Shared descriptive metadata

    author, homepage, keywords, license, and repository are declared once, not once per host block. Each field resolves in one order:

    1. the host block for that artifact, portable.license, cursor.license, codex.license, or claude.marketplace.plugin.license;
    2. plugin.metadata.license;
    3. the license field of the project's package.json.

    null is a deliberate absence rather than a value: plugin.metadata.homepage: null keeps a package.json homepage out of every artifact, and cursor: { homepage: null } keeps a shared homepage out of the Cursor manifest alone while the other hosts still carry it. The one exception is codex: { author: null }: the Codex manifest always names an author, so it falls back to the generated plugin name rather than omitting the field.

    Each host emits only what its pinned schema admits, and the value still passes that host's own validation. Cursor's author object is closed to name and email, so a shared author.url is dropped for Cursor and kept for the others; Cursor, Codex, and Claude Code all require author.name, so a package.json author that is only an email address shares no author with them, while the portable manifest still carries it.

    Two package.json shapes are read: npm's person string ("Ada <ada@example.com> (https://ada.example.com)") and its object form, and for repository, an absolute http(s) URL or the git+https://github.com/owner/repo.git URL npm writes for one, whose git+ prefix and trailing .git are removed. Shorthands like owner/repo, SSH URLs, and git+http:// URLs are not converted to a provider's web URL: the field is withheld with the AB4015 warning, and plugin.metadata.repository declares it explicitly. A URL or email address the pinned host schemas would reject is withheld the same way rather than emitted for a host to refuse.

    An empty package.json field is an absent one: "keywords": [] and "license": "" share nothing and report nothing. Under plugin.metadata the same value is the config author's, so plugin.metadata.license: '' reports AB4014; null is how a field is opted out.

    Claude Code's .claude-plugin/plugin.json carries no descriptive fields, its pinned schema admits none, and its author.name stays the generated plugin name. The shared values reach Claude Code through the marketplace entry.

    package.json is authoritative for release identity. Declare the release version there. A release build with no valid package.json version is refused with AB4013 rather than shipping the 0.0.0-dev.<short-revision> development fallback. The retired plugin.version key is rejected with AB4001 so an un-typechecked config cannot silently ignore it.

    targets

    The host projections build lays into the composite plugin root: any combination of amp, claude, codex, cursor, and portable. Order is irrelevant, the selection is sorted by name, so ['codex', 'claude'] and ['claude', 'codex'] build the same bytes. Omit the key and only the portable projection is emitted. --target on the command line replaces the configured list for one run. Any other name, plugin included, is an unknown target (AB4100). What each projection places in the root is in Targets and artifacts.

    hooks

    Keys are the seven canonical events: sessionStart, beforeTool, afterTool, stop, agentStart, agentStop, workspaceOpen. A value is one entry, or an array of entries, each either a module path or { handler, tools?, targets?, timeout?, args? }. How each canonical event and tool selector lowers to a host-native event and matcher is the generated Event and hook matrix.

    The hook result contract is documented in full in Hooks. One field is worth repeating because it is easy to get wrong: reason is a non-empty string that is valid only when denying a beforeTool, stop, or agentStop hook, and denying one of those without a reason fails.

    output and runtime

    output.repositoryMarketplace: true makes an explicit build also generate the selected hosts' marketplace documents at the project root, with local plugin sources pointing to the artifact output directory. Cursor additionally requires marketplace: true. Commit these files and the artifact directory for installation directly from GitHub. The compiler replaces the selected marketplace files; do not hand-edit them. It refuses symlinked destinations, paths that overlap the artifact, custom adapters, and authored marketplace source or metadata.pluginRoot overrides. These generated files are excluded from source snapshots. Development and evaluation staging do not publish repository marketplaces. Temporary builds used by MCP commands, hook inspection/simulation, and serve-app also leave these files unchanged, including when the operation fails.

    output.distPath is the artifact output directory relative to the project root, the composite plugin root every selected host reads, with no per-host subdirectory beneath it. The CLI (build, prepack, dev) defaults it to artifact, because the CLI also runs the package build and that owns dist/; the programmatic build() defaults to dist unless packageOutputs: true. The per-invocation --output flag wins, but is subject to the same project-root containment check; an output path that is absolute or external, has an invalid shape, or lands in a reserved compiler namespace is unsupported (AB4707–AB4709).

    output.sourceMap is an explicit opt-in for source maps on generated executables. It defaults to false. When true, the compiler profile sets the bundler's output.sourceMap to an inline JS map so the map stays inside the executable, sibling .map files are not part of the planned artifact file set. A non-boolean value is AB4707. MCP App views keep their own default (off) and still opt in through tools.rsbuild.output.sourceMap.

    Generated executables keep readable code. The compiler profile runs the SWC minimizer only to remove comments, with compression, mangling, and whitespace minification off, so bundled dependency documentation does not ship. Legal comments, such as /*! … */ license headers, stay inline. Set tools.rsbuild.output.minify to false to keep every comment.

    runtime.node is a minimum Node.js version in major.minor[.patch] form. It can only raise the default floor for generated executables, never lower it, and the selected floor is recorded as runtime.node in the artifact manifest. The floor itself is described in Configuration model.

    notices

    notices.retention sets the retention policy of the notice ledger a stateful project co-mounts beside src/state.ts. Terminal notices, expired, unavailable, withdrawn, acknowledged, and attempted with an exhausted retry budget, are pruned from the ledger once they have been settled for terminalTtl, or earliest-settled first once more than maxTerminal remain, and the store's journal is compacted onto its head once it exceeds maxJournalBytes. Pruning runs only on admitted events and explicit retain() calls; no timer is implied.

    FieldDefaultRule
    terminalTtl'7d'Positive integer of milliseconds, or a duration <integer><ms|s|m|h|d> such as '7d', '12h', '30m'.
    maxTerminal500Positive integer.
    maxJournalBytes16777216Positive integer of bytes.

    Anything else, an unknown key, a non-positive or fractional value, a duration the grammar does not spell, or a policy declared by a project without a state module, is AB4833. inspect --state and the Workbench State panel show the resolved policy and whether it was declared or defaulted; live counts and the last compaction belong to each installed store (AgentNoticeLedger.inspect()).

    payload

    Keys are artifact-root destination directories, one safe path segment outside the compiler-owned namespaces, and values are already-built source directories. Payload trees are copied byte-for-byte and are opaque to the compiler, which cannot rewrite their internal sibling references, so stable names are the correctness contract. Integrity stays content-addressed through the artifact manifest.

    evals

    Accepts exactly three keys; anything else is rejected.

    FieldDefaultRule
    include['evals/**/*.eval.ts']A non-empty array of relative patterns that never escape the project root.
    runsDir.agent-bundle/runsA relative child directory of the project root.
    semanticGradernoneMust contain exactly harness and model. harness must be 'claude'; model must be a safe model identifier, not a path.

    Provider credential material in the evals block is rejected outright (EVAL_CREDENTIAL_REJECTED), Agent Bundle reuses the host CLI's existing signed-in session. See Security.

    import { 
    const defineConfig: (config: AgentBundleConfig | ConfigFactory) => AgentBundleConfig | ConfigFactory
    defineConfig
    } from 'agent-bundle/config';
    export default
    function defineConfig(config: AgentBundleConfig | ConfigFactory): AgentBundleConfig | ConfigFactory
    defineConfig
    ({
    evals: {
        include: string[];
        runsDir: string;
        semanticGrader: {
            harness: string;
            model: string;
        };
    }
    evals
    : {
    include: string[]
    include
    : ['evals/**/*.eval.ts'],
    runsDir: string
    runsDir
    : '.agent-bundle/runs',
    semanticGrader: {
        harness: string;
        model: string;
    }
    semanticGrader
    : {
    harness: string
    harness
    : 'claude',
    model: string
    model
    : 'claude-sonnet-4-5' },
    },
    AgentBundleConfig.plugin: AgentBundlePluginConfig
    plugin
    : {
    AgentBundlePluginConfig.description?: string | undefined
    description
    : 'Evidence-backed project tools.',
    AgentBundlePluginConfig.name: string
    name
    : 'my-plugin' },
    AgentBundleConfig.targets?: string[] | undefined
    targets
    : ['portable', 'claude'],
    });

    dev

    Development-only settings that never become part of a built artifact.

    FieldMeaning
    dev.agentApiExposes the authenticated, loopback-only Agent API from agent-bundle dev. The --agent-api / --no-agent-api flags override it.
    dev.contracts.fixturesRequired when dev.contracts is set. Project-relative module whose default export maps route ids to contract fixtures. Declaring dev.contracts switches agent-bundle dev from adopting every epoch directly to gating host-facing adoption on the development contract matrix: an epoch whose checks fail still publishes to the Workbench route workspace, but live host connections and development installs keep the last passing epoch (AB7211). A malformed block, a fixtures module that escapes the project root, cannot load, or exports the wrong shape is AB7210.
    dev.contracts.serverThe MCP server the matrix checks. Optional only when the project compiles exactly one server.
    dev.runtime.providerThe development runtime provider module: it exports createDevRuntimeProvider (CreateDevRuntimeProvider), returning a DevRuntimeProvider whose start takes a DevRuntimeStartContext and returns a DevRuntimeSession. The whole protocol a provider implements ships from agent-bundle/api, the inspection envelope (DevRuntimeInspectionEnvelope), the errors it throws (DevRuntimeUnavailableError, DevRuntimeGenerationConflictError), and the generation store a session drives, as a contract (DevRuntimeGenerationStore) with its constructor (createRuntimeGenerationStore).

    web

    Exposure and policy for the artifact-resident browser host. web.apps selects among MCP Apps already declared under mcp.servers.<id>.apps; it is not a second route graph and does not discover Apps from src/web/**. Omit the key and the build writes no web section, no framework-owned web command, and no extra bin.

    web: {
      apps: [
        'status/status',
        {
          app: 'status/status',
          tool: 'show-status',
          input: {},
          allow: ['call-tool'],
        },
      ],
      open: 'never',
    }
    FieldTypeDefaultMeaning
    apps(string | { app, tool?, input?, allow? })[]Required when web is set.Each entry is <server>/<app>, or an object with that app plus optional opening tool, opening input (default {}), and allow. A string is shorthand for { app }.
    apps[].toolstringthe App's only toolThe opening tool: one whose _meta.ui.resourceUri is the App. Required when several tools advertise it.
    apps[].inputRecord<string, unknown>{}Arguments for that opening tools/call.
    apps[].allow('call-tool' | 'download-file' | 'open-external-link' | 'request-display-mode')[]nonePre-approve App-initiated consent capabilities. Browser hardware and clipboard permissions always ask in the host page.
    open'browser' | 'never''never'Whether <plugin> web opens the default browser. --open / --no-open override.

    Invalid web is AB4341 (error): an app no mcp.servers.<id>.apps declares; an unknown allow capability; open outside browser | never; a duplicate <server>/<app>; a tool the route graph does not declare for that generated server; a generated-mode src/cli/** command (or top-level alias) spelled web while web is configured; or a web surface with no executable to carry it. A selected target whose adapter publishes no supported web capability row is an AB4341 warning: its artifact does not host the surface. Message shapes and recoveries are in the diagnostics reference.

    The web command lives only in the framework-generated bin/<plugin>.mjs. web never displaces authored code: a hand-written src/cli.ts, a bin entry that claims the plugin name, or bin: false keeps the executable it describes, and AB4341 reports that the web surface has nowhere to live, move the commands under src/cli/** (generated mode) or drop web.

    When web is valid, the composite artifact's agent-bundle.manifest.json gains a web section and bin/<plugin>.mjs exists even without src/cli/**, see Targets and artifacts and Exposing an App in the browser.

    tools

    The single bundler escape hatch. Both fragments merge last-but-bounded into every bundler config the framework synthesizes, and the artifact invariant assertions still run after the merge, so a hatch value that breaks an artifact contract is a hard diagnostic rather than a silent override (AB472x). AB4720–AB4723 check the shape of tools, tools.rsbuild, and tools.rspack; AB4724 rejects a tools.rsbuild.plugins entry that re-adds a framework-owned plugin (@rsbuild/plugin-react, rsbuild:react), because plugins arrays concatenate and Rsbuild never dedupes plugins by name, the framework registers it for you. AB4725 rejects tools.rsbuild.output.autoExternal that is not false, and a string or object externals entry that names a package (neither a Node built-in, pnpapi, nor a relative path), in tools.rsbuild.output or an object-form tools.rspack. AB4726 rejects deprecated or removed Rsbuild v2 paths in tools.rsbuild: source.alias, source.aliasStrategy, performance.bundleAnalyze, performance.removeMomentLocale, performance.profile, performance.chunkSplit, output.sourceMap.extract.js, provider, tools.webpack, tools.webpackChain, dev.setupMiddlewares, and legacy proxy context / on* event keys. Each diagnostic names the current replacement. This check is path-specific to tools.rsbuild; authored html.templateParameters variables and valid Rspack configuration under tools.rspack are not subject to it.

    The hatch executes on one bundler engine, the @rspack/core behind agent-bundle's pinned @rsbuild/core, shared by the Rslib-compiled outputs (artifact scripts, MCP entries, hooks, the package build) and the MCP App views, but that engine is agent-bundle's dependency, not your project's. Never construct plugins or perform instanceof checks against an imported @rspack/core, use the utils argument passed to tools.rspack mutator functions ((config, { rspack }) => ...), which always hands you the executing engine's own rspack object.

    MCP App builds keep Rsbuild's default resolve.aliasStrategy (prefer-tsconfig), so the project's tsconfig.json paths resolve inside a compiled view as they do anywhere else. The reserved agent-bundle/meta specifier is the exception: the compiler rewrites that exact import to the generated identity module before resolution starts, ahead of both paths and any resolve.alias, so no paths mapping can shadow it. The resolved-config assertion refuses a hatch value that removes that rewrite, as it refuses one that breaks the self-contained output.

    The synthesized view profile targets Chrome >= 144, the oldest Chromium generation in the currently shipped desktop MCP App hosts when this floor was set: Cursor 3.18 reports Chromium 144, Claude Desktop's official Linux package reports Chromium 148, and the Codex Windows package reports Chromium 151. It deduplicates exactly react, react-dom, and scheduler so an App resolves one React runtime. Author HTML templates receive only Rsbuild's safe mountId, entryName, and assetPrefix defaults; compilation and plugin internals remain withheld. Framework script injection and the default root mount, language, and App title remain intact.

    Plugins

    The framework registers exactly one Rsbuild plugin in the configs it synthesizes: @rsbuild/plugin-react (plugin name rsbuild:react), on every artifact entry, routes are authored as TSX and the plugin selects the automatic JSX runtime, and on every MCP App view, whatever its entry's extension (fast refresh off), so a .ts entry importing .tsx components compiles to the same automatic runtime. That set is frameworkOwnedRsbuildPlugins in packages/agent-bundle/src/build/framework-plugins.ts; a unit test derives it from the synthesized configs, so it cannot drift, and validateTools reads the same map to report AB4724 when a tools.rsbuild.plugins entry re-adds one of its names.

    Every other official Rsbuild plugin registers under its own name and may be added through tools.rsbuild.plugins. The fragment reaches every synthesized config; the table says which surface each plugin is relevant to:

    PluginRelevant toNotes
    @rsbuild/plugin-babelartifact entries and MCP App viewsAdds a Babel pass beside SWC.
    @rsbuild/plugin-svgr, @rsbuild/plugin-styled-componentsMCP App viewsReact-only concerns; the framework's @rsbuild/plugin-react stays in charge of JSX.
    @rsbuild/plugin-sass, @rsbuild/plugin-less, @rsbuild/plugin-stylus, @rsbuild/plugin-tailwindcss, @rsbuild/plugin-typed-css-modules, @rsbuild/plugin-css-minimizerMCP App viewsCSS exists only in the browser bundle; views inline it into the self-contained HTML.
    @rsbuild/plugin-yaml, @rsbuild/plugin-toml, @rsbuild/plugin-mdxartifact entries and MCP App viewsExtra module types.
    @rsbuild/plugin-type-check, @rsbuild/plugin-eslintartifact entries and MCP App viewsSeparate-process checks; a failure fails the build in production mode.
    @rsbuild/plugin-check-syntaxMCP App views onlyThe plugin skips non-web environments, so artifact entries (target: 'node') are never checked by it.
    @rsbuild/plugin-node-polyfill, @rsbuild/plugin-assets-retry, @rsbuild/plugin-image-compressMCP App viewsBrowser-only concerns.

    Not applicable: the Vue, Vue JSX, Preact, Solid, and Svelte plugins (routes and views are React; those plugins reconfigure the JSX transform the React plugin owns), @rsbuild/plugin-umd (the framework emits ESM), and the package-publishing checks rsbuild-plugin-publint and rsbuild-plugin-arethetypeswrong (they lint the package.json of the build root after the build, not the artifact).