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/guide/development/workbench.md.
Skip to main content
  • English
  • Developer Workbench

    agent-bundle dev serves a prebuilt developer Workbench over loopback. It is the place to look at what the compiler actually emitted, per host, per epoch, and to run the emitted wrappers instead of trusting that a green build implies a working plugin.

    npx agent-bundle dev --root .
    npx agent-bundle dev --root . --port 3100 --no-open

    Boundaries

    These are contracts, not defaults:

    • Loopback only. The server binds to loopback and is never exposed beyond the local machine.
    • A foreground development session, not a hosted service. Closing the process ends the session.
    • Epoch-pinned reads. Nothing follows a new build implicitly; a surface that reads an artifact names the epoch it read.
    • Trusted-local operations only. The browser can select only a compiled command projection; it never supplies an arbitrary executable, working directory, native model, or credential. See Security.

    Workbench navigation

    The primary navigation has five destinations:

    • Application: the compiled application tree and the workspace for its selected leaf.
    • Trace: the correlated timeline of application activity observed in this foreground development session.
    • Host sessions: explicit Claude Code and Codex terminals attached to the current dev plugin.
    • Problems: current compiler, runtime, and contract diagnostics.
    • Advanced: Evals, Artifact, Protocol, Host diagnostics, and Raw logs.

    The header stays visible across them. It shows the project name, build state and epoch, contract-gate and diagnostic failure count (linked to Problems), and foreground connection state.

    Application tree

    Application is one tree derived from the compiled application graph:

    MCP
      <server>
        Tools
        Resources
        Prompts
        Apps
    Events / Hooks
    CLI
    Scripts
    Skills
    Rules / Commands

    Each leaf is an authored or configured plugin surface. Generated MCP servers group tools, resources, prompts, and Apps beneath their server. Event routes and configured hooks appear under Events / Hooks. Routed commands form the CLI branch. Conventional and explicitly configured scripts share Scripts. Skills, host rules, and host commands remain visible even when they do not have an executable route id.

    Config-only hooks and scripts are not hidden merely because there is no matching file-convention route. The tree combines the route manifest with the compiler inspection that owns those declarations. A stale or unavailable compiler catalog is called out in the tree instead of being presented as an empty application.

    Route workspace

    Selecting a leaf opens one workspace rather than sending you to a surface-specific page. For an executable route, the input editor uses declared config.inputJsonSchema metadata (or JSON input without it) and offers applicable fixtures and the last input kept for that leaf. Run executes the production route path on the foreground server.

    Results open on Rendered, the browser rendering of the production Agent Document. While a route runs, streamed progress and Suspense replacements update this view in place and Cancel stops the child; cancelled invocations have no outcome. Secondary result tabs are Structured result, Raw AgentDocument, MCP projection, CLI projection when available, and Trace. The inspector opens only when requested and contains Source, Schema, Context, Providers, Execution timings, Projection, and Raw protocol. A provider the run did not measure shows unobserved with no duration, and timings list only measured phases; 0 ms is a measured zero, not a placeholder.

    The browser does not import or execute arbitrary route modules. The server runs the same RSC route path used by generated executables and sends its semantic render-event stream and final Agent Document to the Workbench.

    A tool with a colocated CLI projection has one tree leaf and one canonical tool:<server>/<tool> operation. Its surface selector offers MCP, CLI <command>, and Unit render. The CLI choice shows the projected argv and runs the generated CLI bin's confirmation, defaults, mapInput, and canonical validation; it does not create a second cli: operation.

    Event routes

    An event leaf has fixture choices Canonical, Claude, Codex, and Cursor. Running a host fixture decodes the native envelope exactly as that host wrapper would, then shows canonical input, request context, providers, the returned decision or document, and projected host results. Canonical-to-host mapping and native input/output are secondary projection details. For a .ts event handler, Canonical is disabled and the selector defaults to the project's first generated host target; submitting that route without a concrete host is AB8255.

    Observed event receipts appear in the leaf's Replay tab and in Trace. Select a receipt there to rerun its epoch-bound input; there is no separate lifecycle destination.

    App and Skill leaves

    An App leaf places the sandboxed MCP App preview in the center of the workspace, bound to its server. Its Launch selector lists the launches the dev server judges eligible for that server on the published build, the declared targets grouped by launch identity, with installed hosts consulted, and the session opens with the chosen launch's representative target, so a target the build declares but no host can launch is never offered. Calling a tool mounts the preview at once: the App receives ui/notifications/tool-input while the call is in flight, then exactly one terminal outcome, the tool result (including an isError result) as ui/notifications/tool-result, or ui/notifications/tool-cancelled when Cancel call aborts it. A late outcome for a call the workspace no longer shows is dropped rather than delivered to a newer preview. A bound App can also be reached from its tool's projection details. The same host stack serves one App standalone in a plain browser tab through agent-bundle serve-app; when the project configures web, agent-bundle dev also serves GET /web/<server>/<app> on the foreground server, the same page, relay, routes, sandbox proxy, and consent behavior, bound to the current epoch's server session. Apps not listed in web.apps are 404. The launch resolves from the artifact's declared projections (?target= is validated, and materially different launches answer 409 naming the choices), web sessions are keyed by epoch, server, and launch identity and retire only when a rebuild publishes a new epoch, and a non-read-only opening tool runs once per session, tool, App, and input. Its bounded execution record remains authoritative if the retained result is evicted or the call fails, so refresh never silently repeats a possible mutation; the page instead directs the user to run the tool explicitly from the App. See Exposing an App in the browser.

    A Skill leaf renders the emitted Skill document by default. Source/generated differences, frontmatter, resources, and eval coverage live in the inspector. Raw HTML, JSX/MDX, and Mermaid inside Skill Markdown remain inert.

    Host sessions

    Start agent-bundle dev with the hosts whose development installs should follow the adopted epoch:

    pnpm add -D @lydell/node-pty
    npx agent-bundle dev --install-host claude --install-host codex

    Host sessions can then launch claude [<prompt>] or codex [<prompt>] in a PTY. The command and arguments are fixed by the server, the working directory is always the project root, and no session starts until you select Launch Claude, Launch Codex, Open in Claude, or Open in Codex. The route actions seed a prompt for tool, event, and CLI leaves and open the new terminal at /sessions?session=<id>.

    The authority strip under the terminal shows the project root, adopted epoch, host install destination, process id while running, and current state. A restarted session also names the session it replaced. At most four sessions can be live at once, and each session retains at most 256 KiB of PTY output for reconnecting terminals. Install @lydell/node-pty in the project; when it cannot be resolved or loaded, launch is disabled with AB8260.

    The foreground server remains loopback-only and applies the same session cookie or bearer guard to every /api/sessions route. Requests cannot choose an executable, arguments, working directory, or environment; the optional prompt is passed as the host's positional argument and is refused (AB8261) when blank or starting with -. The child never receives the foreground session token, and Agent Bundle does not pass a permission or approval bypass to either host. The terminal inherits the dev server environment plus the hook receipt variables, AGENT_BUNDLE_DEV_SESSION=<hs_…>, TERM=xterm-256color, and COLORTERM=truecolor.

    AGENT_BUNDLE_DEV_SESSION is the Workbench resource id, not a credential. Every generated hook receipt carries it as devSession alongside the host's own session_id. Once the first receipt arrives, Trace aliases the resource to the host session id and groups later lifecycle, hook, and MCP entries by that id; a session/start receipt with a new host id (Claude Code issues one on /clear) moves the alias and publishes another session.attached entry. Entries that arrive before any receipt stay grouped under the hs_… id. Claude inherits the variable into its stdio MCP server, so the proxy supplies the alias header. Codex filters the environment of stdio MCP servers, so the proxy cannot inherit that variable; it always sends its process id instead, and the foreground server walks the proxy's parent process chain to the PTY child that owns the Workbench session. Codex frames can also carry the host session id in x-codex-turn-metadata.session_id. Its hook still inherits AGENT_BUNDLE_DEV_SESSION and sends the devSession receipt field. The Trace link always uses the learned host id when available.

    Trace

    Trace is the live, ordered timeline of what the foreground server observes the application doing. For example, running the sessionStart event route in Hooks and Scripts can produce a sequence like:

    22:41:04.101 invocation.started       event:session/start
    22:41:04.118 kernel.handler.start   session/start · claude
    22:41:04.121 kernel.execute.start     event:session/start
    22:41:04.126 kernel.providers.start
    22:41:04.129 kernel.providers.finish
    22:41:04.132 kernel.render.start
    22:41:04.146 kernel.render.finish
    22:41:04.149 invocation.completed     sessionStart succeeded

    An invocation through the Protocol inspector adds mcp.request, progress or logging notifications, and mcp.response to the same timeline. A hook delivered by an attached host adds hook.received and hook.completed or hook.failed; its payload-free kernel phases are retained in the terminal row's details.

    Entries, correlation, and grouping

    The timeline is a lowering of records that already exist. It does not make a second copy of an invocation, protocol frame, hook receipt, log record, or diagnostic. Each row carries its occurrence time, source, publisher-owned dotted kind, one-line summary, known correlation keys, optional status and duration, and, when a full record is available, an href to it:

    SourceKindsCorrelation keysDestination
    invocationinvocation.started, invocation.completed, invocation.failed, invocation.cancelledcorrelationId, invocationId, routeId, epochId, plus session/conversation when availableThe Application route with ?invocation=<id>
    kernelkernel.handler.start, kernel.handler.outcome, kernel.execute.start, provider and render start/finish, kernel.failureexecutionId, route, host, session and conversation identity when availableThe corresponding route invocation
    mcprequest, response, notification, progress, logging, session, and stderr kindsmcpSessionId, JSON-RPC mcpRequestId, requestId, route and host metadata when knownThe route workspace with ?session=<id>, or the bound Protocol session
    hookreceipt/completion/failure and host session start/endrequestId, executionId, sessionId, conversationId, routeId, hostThe event route and captured receipt
    sessionsession.started, session.attached, session.exited, session.terminatedHost session, host, and epoch identity/sessions?session=<hs id>
    loglog.<producer>.<kind> for records with a shared keyAny correlation key retained by the safe log projectionAdvanced → Raw logs
    diagnosticbuild, contract, and host-sync failuresBuild, epoch, route, and shared request identity when knownProblems or the affected route

    Trace groups entries transitively on shared identity. The group label uses the strongest available key in this order: conversation, session, invocation, kernel execution, MCP request, then the browser-minted correlation id. An MCP request id joins only within its MCP session. The grouping is evidence-based: unrelated activity is not joined merely because it happened nearby. /trace?correlation=<value> selects the group containing an entry with that exact correlation value.

    Inspecting and deep-linking

    Select a row to open its detail at /trace/<entry-id>. Open route follows the row's href and loads the immutable invocation snapshot in the Application workspace rather than rerunning the route. The route workspace's Open in Trace action returns to the matching correlated group. Advanced → Raw logs offers the same action when a record carries correlationId, invocationId, or mcpSessionId.

    Trace replay loads from GET /api/trace?after=<sequence> and the live view continues from GET /api/trace/stream?after=<sequence>. A dropped replay window is represented explicitly as a gap; the browser does not silently imply that the remaining rows are complete. Trace belongs to the current foreground development session and is not durable across server restarts.

    Host hook receipts

    Attached generated hook wrappers can post a receipt of at most 16 KiB to POST /api/trace/receipts. The wrapper discovers the active loopback endpoint and random per-dev-server bearer token from the development install marker and the project's owner-only receipt file; simulations receive the pair directly from the foreground server, and shutdown removes the file. The write-only route rejects a non-loopback peer and any request with an Origin header; neither the Workbench cookie nor its session header authorizes it. A receipt contains execution identity, payload-free kernel events, host/session/request ids, and resolved lineage, never the native hook payload. Posting has a 750 ms budget and failure is ignored, so Workbench observation cannot change the hook result. Because the wrapper awaits that post after writing stdout, the same 750 ms timeout bounds added hook exit latency. This is narrow authenticated telemetry, not a remote-control or general-purpose ingestion endpoint.

    Deliberately absent data

    Trace never includes request or response payload bodies, rendered Agent Documents, native event envelopes, environment variables, credentials, absolute paths, or error stacks. Open the linked invocation, route, Protocol session, Raw log record, or Problem when its bounded full record is available and you need more detail.

    Problems and stale-catalog repair

    Problems collects the current diagnostics. A failed build publishes no new epoch, so the header shows the failed state while Application continues to expose the last good catalog and artifact. For example, an MCP App compile failure contributes one AB4770 row per Rspack error, including the failing source and location; AB4771 warnings and the AB4772 size advisory belong to a successful epoch.

    Repair a stale catalog in this order:

    1. Open Problems from the header failure count and fix the reported source.
    2. Rebuild, then wait for the completed Idle state; Building is not repair evidence.
    3. Return to Application. The new published epoch replaces the stale tree and clears repaired diagnostics.

    Advanced

    • Evals has Runs and Compare views for admitting, cancelling, inspecting, and comparing eval runs.
    • Artifact defaults to a simple emitted-file tree. Select a file and enable its details to see hashes, modes, and provenance. Provenance is the file's own compiler.provenance row from agent-bundle.manifest.json: the project source inputs it derives from. The compiler records at least one input for every file it emits; a row with none reads No source inputs recorded. Nothing is inferred from paths or file contents.
    • Protocol is the low-level MCP session inspector. It retains protocol traces, task controls, consent, restart/cancel actions, and the standalone MCP Inspector launcher.
    • Host diagnostics is limited to installed state, version, path, whether the current plugin is attached, actionable errors, and one MCP handshake indicator.
    • Raw logs remains the framework-level producer stream for details that do not belong on the typed timeline. Trace is the normal observability view; a log record carrying correlationId, invocationId, or mcpSessionId offers Open in Trace.

    An MCP protocol session remains pinned to { epochId, target, serverName }. Restarting it respawns that generated server on the selected epoch; open a new session to use a newly published epoch. Compatible Apps preview through the same bound session. See Development vs production builds.

    The standalone MCP Inspector still launches as a separate localhost app. Streamable HTTP sessions can deep-link into Inspector 2.x; stdio sessions use the downloadable resolved command, arguments, and non-secret environment. AB8112 reports launch failure or timeout and AB8113 reports unavailable launcher routes.

    URL model

    Workbench uses paths and browser history, not #page hashes:

    /routes/mcp/<server>/tool/<name>
    /routes/mcp/<server>/resource/<name>
    /routes/mcp/<server>/prompt/<name>
    /routes/mcp/<server>/app/<name>
    /routes/events/<event...>
    /routes/cli/<command...>
    /routes/scripts/<name>
    /routes/skills/<id>
    /routes/commands/<id>
    /routes/rules/<id>
    /sessions
    /sessions?session=<hs-id>
    /trace/<entry-id>
    /trace?correlation=<correlation-value>
    /problems
    /advanced/<section>

    The Advanced section names are evals, artifact, protocol, hosts, and logs. Append ?invocation=<id> to an Application route to load a recorded invocation. The dev server serves the Workbench shell for these paths, so route, trace, Problems, and Advanced deep links survive refresh.

    Route invocation API

    The route workspace uses one authenticated, origin-guarded foreground API:

    • POST /api/routes/invocations accepts a route invocation request and returns its completed invocation envelope. With stream: true, it returns 202 immediately with the running id.
    • GET /api/routes/invocations/<id>/stream streams retained and live render, trace, truncated, and terminal final messages as server-sent events. Retained render events are the render-history window described below, and a replay that starts with truncated has lost older events to it.
    • POST /api/routes/invocations/<id>/cancel stops a running or queued invocation and returns its cancelled final envelope; cancelling an already-final invocation reports AB8256 (409).
    • GET /api/routes/invocations?limit=50 returns newest-first summaries for Trace.
    • GET /api/routes/invocations/<id> returns one invocation.
    • /api/project/events publishes the running record and final summary as route.invocation events.
    • GET /api/trace?after=<sequence> returns a correlated trace replay.
    • GET /api/trace/stream?after=<sequence> streams trace entries and replay gaps as NDJSON.
    • POST /api/trace/receipts accepts one bounded, bearer-authenticated hook receipt from a development wrapper; it is not a browser route.

    Requests identify one canonical operation with routeId and may select one shaped surface: { kind: "mcp" }, { kind: "cli", command, args }, { kind: "event", host?, fixtureId? }, { kind: "script" }, or { kind: "unit-render" }. When omitted, the surface follows the route kind: tools/resources/prompts use MCP, events use event, scripts use script, and standalone CLI routes use their compiled command with empty argv. unit-render is never selected by default.

    Production surfaces execute the selected route from the last published compiler epoch, so compiler aliases and defines, generated-entry request scope, providers, persistent state, CLI mapInput and confirmation, event gates, and render semantics match the installed artifact. The explicit unit-render surface is a component-preview fallback: it loads live source with the route-unit harness and mounts disposable state. It is not an artifact-parity receipt. A projected CLI command keeps the canonical tool: route id; a mismatched command is AB8253, while using its duplicate-looking cli: id is AB8254 with the canonical id and surface to use.

    A production surface selects its executable from the published epoch's agent-bundle.manifest.json before the child runs. Rendered CLI routes, projected tool commands, and rendered scripts use their manifest worker; MCP routes use their owning compiled server's launch.worker; and event routes use the host wrapper plus the worker selected by routes.events[].execution: the standalone hooks worker, or the first compiled server that owns the shared runtime for that host. The binding fails closed: an unavailable published artifact is AB8250; an ineligible host or surface, missing executable, or missing preparation row is AB8251; and a preparation module that cannot be imported or does not export its contract is AB8252. A gated or lightweight event route submitted without a host is refused as AB8255. These failures never reach the handler, and an import, handler, or handler failure never causes another executable to run. Production state lives at <project>/.agent-bundle/state, outside the published epochs that retirement removes. It is shared with dev MCP sessions through AGENT_BUNDLE_STATE_ROOT, so it survives a successful republish; unit-render still uses a fresh temporary state root per run.

    The envelope records the resolved surface beside routeId and carries canonical input, request context, providers, ordered render events, the final Agent Document, structured result, projections, diagnostics, and execution timings. Render events are retained under one render-history window, applied identically to the live stream replay, the completed and cancelled envelopes, GET /api/routes/invocations/<id>, the final stream message, and the Workbench's live view: at most 256 events and 2 MiB serialized, with the newest event and the newest document-bearing event (shell, replace, or complete) always kept. Those two alone may carry the window past 2 MiB, so events folds to a coherent latest document even after eviction. Older intermediate events are dropped; the child never returns the stream a second time. When anything was evicted the envelope carries retention, producedEvents, evictedEvents, evictedBytes, and retainedBytes, and the stream signals truncated once. A cancelled or failed run keeps the same retained window and the latest document it reached. The final document, result, outcome, and correlationId are never truncated by this window; the runtime's own maxDocumentBytes bounds the document before it reaches the service. When the worker reports observations, providers contains only providers it selected and reports each as mounted or failed; unselected catalog providers are absent. A durationMs appears only when measured. When the child reports no observations, a plain script, a handler short-circuit, or a failure before observations began, every catalog provider is unobserved and durationMs is omitted; 0 is a measured zero, not "unknown". timings lists only measured phases: render (the child's render or script-run duration), projection (host-projection time in the service), and, when observed, handler, providers, and provider:<name>. A failed invocation records an elapsed timing for the wall time until failure and does not invent failed provider rows. A represented Agent.Error remains a rendered result; unknown routes or invocation ids (AB8231), unavailable epochs (AB8232), render timeouts or crashes (AB8236), malformed requests (AB8237), unknown fixture ids (AB8238), a queued invocation whose published revision moved before it acquired a slot (AB8239), unavailable compiled artifacts (AB8250), missing compiled route executables (AB8251), compiled projection or handler failures (AB8252), CLI command/operation mismatches (AB8253), projected duplicate CLI ids (AB8254), handler routes submitted without a concrete host (AB8255), and cancellation of an already-final invocation (AB8256) are reported as diagnostics. See the diagnostics reference for the individual triggers and recovery guidance.

    The envelope, the route.invocation event payload, and the route workspace report two separate facts. status says whether the execution boundary completed; outcome says what a completed run meant, judged by the surface the route runs through. A failed boundary carries no outcome, and a completed run always carries one, so a represented error or a non-zero exit is never reported as plain success. route.invocation also fires when a run starts with a status: "running" record that has no completedAt or outcome; narrow on status before reading completion fields.

    FieldValueMeaning
    statussucceededThe child completed a final document (or a plain script exited).
    statusfailedThe boundary never completed: crash, timeout, or an AB825x diagnostic.
    statuscancelledThe operator cancelled a running or queued invocation; no outcome is present.
    outcome{ kind: "success" }The projected result is a success by the route's surface.
    outcome{ kind: "represented-error", summary }The MCP projection carries isError: true (a non-success Agent Document, such as Agent.Error), or an event route's decision is deny. summary lists the Agent.Error codes and messages, or the deny reason.
    outcome{ kind: "process-exit", exitCode }A CLI or script route completed and its generated executable set a non-zero exit code, the bin's own exitCode policy applied to the validated result, or 1 for a non-success document. A zero exit reports success.

    The Workbench status line shows the execution status (Running / Completed / Failed / Cancelled) beside an outcome badge, and Trace lists both for every entry.

    The complete browser-facing HTTP shapes, including the trace entry and replay contracts, are in the development-server HTTP reference.

    The same session programmatically

    The public startDevServer export accepts the options the CLI flags map to (root, port, open, agentApi, installHosts, workbenchDevOrigins) and resolves to a DevServerSession exposing the loopback url, a status() snapshot, and close():

    import { 
    const startDevServer: (options: StartDevServerOptions) => Promise<DevServerSession>

    Starts one loopback foreground session over the current project services.

    startDevServer
    } from 'agent-bundle';
    export const
    const serve: () => Promise<void>
    serve
    = async ():
    interface Promise<T>

    Represents the completion of an asynchronous operation

    Promise
    <void> => {
    const
    const session: DevServerSession
    session
    = await
    function startDevServer(options: StartDevServerOptions): Promise<DevServerSession>

    Starts one loopback foreground session over the current project services.

    startDevServer
    ({
    StartDevServerOptions.port?: number | undefined
    port
    : 3100,
    StartDevServerOptions.root: string
    root
    :
    var process: NodeJS.Process
    process
    .
    NodeJS.Process.cwd(): string

    The process.cwd() method returns the current working directory of the Node.js process.

    import { cwd } from 'node:process';
    
    console.log(`Current directory: ${cwd()}`);
    @sincev0 .1.8
    cwd
    () });
    var console: Console
    console
    .
    Console.log(...data: any[]): void

    The console.log() static method outputs a message to the console.

    MDN Reference

    log
    (
    const session: DevServerSession
    session
    .
    DevServerSession.url: string
    url
    );
    await
    const session: DevServerSession
    session
    .
    DevServerSession.close(): Promise<void>
    close
    ();
    };

    Development host installs

    Pass --install-host <claude|codex|cursor> more than once to install development variants into the selected hosts:

    npx agent-bundle dev --install-host cursor --install-host claude

    The first successful epoch uses the ordinary host installer. Claude and Codex register from the stable, receipt-owned <project>/.agent-bundle/dev/<host> source and read the installed files from their host-owned plugins/cache/<marketplace>/<plugin>/<version> directory; Cursor reads ~/.cursor/plugins/local/<plugin>. The installed root carries an .agent-bundle-dev.json with schema version 1, the project root, the host, and the installed epoch.

    Its MCP document always launches the framework CLI through the running dev server's Node executable:

    agent-bundle dev proxy --root <projectRoot> --server <serverName> --target <host>

    Rebuilds never replace that stable command with an epoch path, and the contents of the host process PATH do not affect whether the project-local framework can be spawned.

    Before a Codex epoch is reported synchronized, Agent Bundle sends Codex's idempotent plugin/install request over an existing local app-server control socket. This refreshes the app-server's MCP and hook component snapshot by atomically adopting the stable receipt-owned source, without restarting the server. When no app-server is running, later Codex sessions load the installed registration normally.

    Every selected host installs from the same composite epoch root. Outside the running Codex app-server path, each later artifact.available event copies that root into an immutable installed generation for each host. Top-level directories switch by atomic symlink (or Windows junction) rename and top-level files by atomic sibling-file rename, so a host sees an old or a new complete entry. After the first direct publication, no synchronized directory disappears between generations. A failed direct publication rolls the pointers back when a prior generation remains and emits an AB7202 diagnostic on dev.host.sync; a failed build emits no artifact.available at all, so the last-good install is untouched. Re-sync records top-level ownership under .agent-bundle-dev/ and removes only entries recorded as manager-published; neighboring host or user entries are never inferred to be owned. Re-sync writes the host cache directly and does not invoke the Claude or Codex CLI again.

    Stopping the dev server unregisters and removes its Claude and Codex development installs. A later run registers the same stable source path again, so restart does not leave duplicate marketplaces. If a receipt-owned marketplace source is removed without teardown, Doctor reports AB7333; uninstall <host> --from <bundle-dir> --force removes the dangling registration.

    Live host MCP proxy

    A host can keep one stdio MCP process connected while dev rebuilds the generated server behind it. The command, the /mcp/host/<serverName> endpoint, and its discovery through the project's development lock are documented in MCP servers and MCP Apps.

    One rebuild rule matters here: a generated server that crashes is not silently respawned within the same epoch. Calls remain failed until a successful rebuild swaps in a newly primed epoch session.

    Optional Agent API

    The Agent API is a separate, authenticated Streamable HTTP MCP endpoint for a Codex client. It is off by default and is mounted only at /mcp on the existing loopback foreground server:

    AGENT_BUNDLE_AGENT_API_TOKEN='replace-with-a-secret' \
      npx agent-bundle dev --agent-api --no-open --port 3100

    dev: { agentApi: true } enables it from configuration, and --no-agent-api overrides that setting. Startup fails before serving if the endpoint is enabled without AGENT_BUNDLE_AGENT_API_TOKEN. The fixed token is read once, never logged, persisted, or returned, and is required as standard Authorization: Bearer authentication. Clients may omit Origin; a supplied origin must exactly match the foreground URL. When the endpoint is disabled it is absent, not merely unauthorized.

    It has exactly thirteen fixed, ordered tools:

    #Tool#Tool
    1project_status8hooks_list
    2skills_list9hook_simulate
    3skill_inspect10evals_list
    4artifacts_list11eval_run
    5artifact_inspect12eval_get
    6mcp_servers_list13diagnostics_list
    7mcp_invoke

    eval_run is limited to deterministic harnesses; it cannot select native hosts. Tool schemas reject undeclared root, path, command, cwd, environment, harness, evidence, and outcome fields. Artifact-backed calls may name an epoch id; otherwise they atomically lease the active epoch, so a hot rebuild sends later calls to the new epoch while an admitted call stays pinned to its original epoch. The transport is stateless, so an initialized client can issue later requests at the same fixed URL once the foreground server returns.

    Contributor UI HMR

    Working on the Workbench UI itself is a different loop from consuming a published one: an Rsbuild development server serves the UI with hot module replacement and proxies /api to a separately started foreground server. Published agent-bundle dev serves prebuilt assets and project events; it does not run an Rsbuild development server. The loop takes two terminals.

    Terminal A starts the foreground server and names the Rsbuild origin it should accept:

    npx agent-bundle dev --root . --port 3100 --no-open \
      --workbench-dev-origin http://localhost:3000

    Terminal B starts the Rsbuild development server against that foreground URL:

    AGENT_BUNDLE_WORKBENCH_API_PROXY=http://127.0.0.1:3100 \
      pnpm --filter agent-bundle-workbench dev

    Open http://localhost:3000. packages/workbench/scripts/dev.mjs requires that proxy URL and passes any further arguments through to rsbuild dev. Rsbuild's defaults are host localhost and port 3000; pnpm --filter agent-bundle-workbench dev --port 3001 moves the UI, in which case allowlist http://localhost:3001 in Terminal A instead. Whenever AGENT_BUNDLE_WORKBENCH_API_PROXY is set, packages/workbench/rsbuild.config.ts also sets server.strictPort: true, so a busy port 3000 fails loudly instead of Rsbuild silently moving the UI to a port that is not allowlisted.

    What is proxied. Only /api, every typed Workbench client route plus the /api/project/events SSE stream, is forwarded to the foreground server. The HTML document, the JavaScript and CSS, and the HMR websocket come from the Rsbuild server. MCP App preview iframes are bound to the foreground origin: the App sandbox exchanges postMessage frames only with the foreground URL. They are not covered by this loop, open the foreground URL itself (http://127.0.0.1:3100 above) to work on those pages.

    The origin rule. The proxy never rewrites Origin (Rsbuild's changeOrigin rewrites only Host), so whenever the browser sends Origin, on every mutation, the foreground server sees Origin: http://localhost:3000, not its own. --workbench-dev-origin <origin> (repeatable; startDevServer({ workbenchDevOrigins }) programmatically) makes it accept session bootstrap (GET /api/project/session), mutation (POST/DELETE with the x-agent-bundle-session token), and project-event stream (GET /api/project/events, session cookie) requests whose Origin is one of the listed origins, in addition to its own origin. Every other origin is still AB8003, and the Agent API origin rule on /mcp is unchanged. Each value must be a bare loopback http: or https: origin, localhost, 127.0.0.1, or [::1], such as http://localhost:3000, with no path, otherwise the foreground server refuses to start (agent-bundle dev exits before serving anything; startDevServer rejects with code AB8000). GET /api/project/session additionally returns devOrigins: [...] when the list is non-empty (origin in that body remains the foreground URL), and the Workbench UI accepts the session when the page origin equals the foreground origin or is listed in devOrigins. Without the flag the guard is byte-for-byte unchanged: the allowlist is never on by default.

    The Origin guard defends the loopback foreground server against request forgery from any other origin, including other loopback origins (MCP App sandbox iframes run third-party plugin code on separate loopback ports, and Rsbuild's development server admits every loopback origin through its default CORS policy), so the allowlist names exactly your own Rsbuild origin, and the foreground still sees the browser's real Origin.

    Without --workbench-dev-origin, the UI at http://localhost:3000 refuses its own bootstrap as AB8003 (the session body names the foreground origin, not the page's) and stays on the "Foreground connection unavailable" gate, reporting AB8003 — Origin http://localhost:3000 is not allowed by the foreground server at http://127.0.0.1:3100. Open http://127.0.0.1:3100 instead, or start agent-bundle dev with --workbench-dev-origin http://localhost:3000 to allow this origin. The gate appends (HTTP <status>) only when the foreground response itself failed, a 403 AB8003 from the foreground renders as AB8003 — Request origin is not this foreground server. (HTTP 403), so the no-flag line carries no status: the foreground answered 200 and the UI refused the body. Restarting the foreground without the flag while the page is open ends on the same status-less AB8003 — Origin … line: the gate first reports Foreground project event stream disconnected., then the refusal once the reconnect's own 200 bootstrap is turned away by the UI. The foreground still refuses mutations carrying that Origin with AB8003. The foreground URL keeps working regardless.

    What changed

    • Overview status moved into the persistent header and Problems.
    • Routes, Hooks, Skills, and MCP previews moved into Application leaves.
    • Event replay moved into event routes and Trace; script execution moved into Script leaves.
    • Evals and comparisons became Advanced → Evals → Runs / Compare; artifact, protocol, host diagnostics, and raw logs also moved under Advanced.

    Next

    • Testing: the proof levels behind the surfaces the Workbench exercises.
    • Evaluations: eval runs and comparisons under Advanced.