Configuration model
agent-bundle.config.ts at the project root is the whole declarative surface. It is one small
flat object holding project identity, target selection, and the policy no route file can own.
Everything structural, which Skills exist, which MCP routes exist, which scripts ship, comes
from the src/ conventions unless you override it here.
defineConfig is an identity helper: it exists to type the object, not to transform it.
Project identity
plugin names the plugin as hosts will see it:
package.json is authoritative for release identity. A release build with no valid package
version is refused outright (AB4013) rather than packaging the development fallback. Plugin code reads its own
identity from agent-bundle/meta instead of a hand-maintained version module.
Targets
targets selects the host projections the build lays into one composite root at artifact/
(or output.distPath):
Amp's plugin is nested under .amp/plugins/; the other host manifests live in their own dotfolders,
with skills/, hooks/, mcp/, scripts/, bin/, and assets/ emitted once and shared.
Omit targets and the build emits only the portable
projection. Order is irrelevant, ['codex', 'claude'] and ['claude', 'codex'] produce the same
bytes, and any other name, plugin included, is an unknown target (AB4100).
The portable standard packages only Skills and MCP servers, so rules, commands, and hooks are honestly unavailable on that target rather than silently dropped. Claude Code consumes the standard only through CLI translation, which is why its dedicated target remains necessary.
The full surface
Host-scoped extension keys, claude, codex, cursor, portable, are contributed by the target
adapters through declaration merging, so a host-specific value stays in its own adapter instead
of leaking into compiler core. Ordinary projects need no extension key at all.
Host-scoped declarations
Every host key is optional and every field inside it is optional. claude and codex extend
the shared AgentBundleHostConfig, whose single field nativeHooks names a project-authored,
target-native hook document (hooks.json) the adapter validates and merges beside the compiled
hooks. For Claude Code the pinned schema admits the documented handler contract: every hook event
the pinned 2.1.260 host knows (the reference's full list, including PreModelSwitch and
PostModelSwitch, which require 2.1.251), the five handler types (command, http, mcp_tool, prompt,
agent), their per-type fields (args, async, shell, url, headers, server, tool,
input, prompt, model, …), and the common if, once, statusMessage, and timeout
fields; unknown keys, a field of another handler type, and a prompt/agent/http handler on an event
the reference excludes it from are rejected. The compiler itself still emits shell-form command
handlers only. The adapter-owned fields are:
Everything else about a Cursor plugin is derived from the cross-host model. The host capability tables in the host reference record which of these surfaces each pinned host version actually honours.
The five descriptive fields four of those blocks share, author, homepage, keywords,
license, and repository, do not have to be repeated. Each one defaults to the same field in
the project's package.json, plugin.metadata overrides that for every host at once, and a host
block still wins for its own artifact. Write a field as null to keep a shared value out: in
plugin.metadata it shares nothing, and in one host block it keeps the value out of that one
manifest. The resolution order and the two package.json shapes the compiler reads are in
Configuration.
Two Claude Code surfaces deserve a closer look because their contracts are narrower than their names suggest:
claude.lspServers: emitted as a root.lsp.jsonwheneverclaudeis among the selected projections. Path tokens expand only incommand,args,env, andworkspaceFolder. agent-bundle does not include the language-server binary; install it separately so the declared command is available onPATH. Codex, Cursor, and the portable format do not receive this configuration.claude.settings: emitted as a plugin-rootsettings.jsonthat Claude Code applies when the plugin is enabled. The pinned contract supports onlyagentandsubagentStatusLine, and any other key is rejected rather than shipped as a default Claude Code would silently ignore. No path tokens are expanded here, becausesettings.jsonis absent from the host's placeholder-substitution table. Declaringagentalso raises a warning while the pluginagents/component is deferred: the referenced agent has to reach the plugin root some other way, such as a prebuilt payload.
Runtime floor
Generated executables target Node.js 22.12 or newer by default. runtime.node raises that floor,
it can never be lowered, and the selected floor is recorded as runtime.node in the artifact
manifest.
JSX means rendering
Structure lives in config and conventions; JSX appears only where something is rendered. A
rendered route is one default Server Component that does the work and returns Agent.*
nodes, and it calls await agent() only when it needs host, session, actor, workspace,
capability, or state context. There is no public execute/render split, and a plain .ts
route is never wrapped in React behavior. Events use the same canonical file paths for both
forms: .ts returns a lightweight decision, .tsx renders JSX, and ctx.render() requests a separate .view.tsx module. Use defineTool for an inferred tool handler.
Route contracts (Application IR)
The compiled graph records host-independent route identity and declared metadata.
Literal config.inputJsonSchema creates a frozen RouteContract with id
contract:<route-module>#inputJsonSchema, origin: { module, binding: 'inputJsonSchema' },
input, and the route id in routes. The route references it through route.contract.
graph.contracts is absent when no route declares input metadata.
Inspection and Workbench read this data without executing schema code. Imported runtime schemas do not create shared static contracts. Generated TypeScript types and runtime validation use the actual schema exports. See input metadata.
Authorable surfaces
- Skills: Markdown Skills, their resources, and rendered Skill sources.
- Hooks: the seven canonical lifecycle events and tool selectors.
- MCP servers and Apps: generated route servers, handwritten stdio entries, and browser Apps.
- Scripts and assets: artifact scripts and static files.
- Package entries:
bin,lib, the routed CLI, and the bundler escape hatch.
How one route becomes MCP, CLI, and hooks, and what the compiler still duplicates today, is in Compiler architecture.
Handlers and components may return directly or return a Promise. The compiler checks declared exports; the generated runtime checks callability and awaits results. mapInput is also awaited before the original input schema validates once.