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.
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:
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:
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:
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:
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:
- Open Problems from the header failure count and fix the reported source.
- Rebuild, then wait for the completed Idle state; Building is not repair evidence.
- 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.provenancerow fromagent-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, ormcpSessionIdoffers 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:
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/invocationsaccepts a route invocation request and returns its completed invocation envelope. Withstream: true, it returns202immediately with the running id.GET /api/routes/invocations/<id>/streamstreams retained and liverender,trace,truncated, and terminalfinalmessages as server-sent events. Retained render events are the render-history window described below, and a replay that starts withtruncatedhas lost older events to it.POST /api/routes/invocations/<id>/cancelstops a running or queued invocation and returns its cancelled final envelope; cancelling an already-final invocation reportsAB8256(409).GET /api/routes/invocations?limit=50returns newest-first summaries for Trace.GET /api/routes/invocations/<id>returns one invocation./api/project/eventspublishes the running record and final summary asroute.invocationevents.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/receiptsaccepts 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.
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():
Development host installs
Pass --install-host <claude|codex|cursor> more than once to install development variants into
the selected hosts:
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:
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:
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:
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:
Terminal B starts the Rsbuild development server against that foreground URL:
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.