Structured View (Web Dashboard)

The structured view is the default rendering for AI coding agents in the web dashboard and the native TUI. Instead of a terminal pane showing the agent’s rendered screen, it renders the agent’s state directly: plan, tool-call cards, diffs, and approvals. It is mobile-first and scales into a multi-pane desktop layout.

It speaks the Agent Client Protocol (ACP), a JSON-RPC standard for editor-agent communication, with aoe as the client and the agent as the server. Any ACP-capable agent uses it by default and can opt into the terminal view per session; agents with no ACP adapter always run in the terminal view.

The structured view rendering an agent’s plan, tool-call cards, and a pending approval

Supported agents

aoe ships an ACP registry entry for each tool whose ACP server we have verified. For those the web wizard shows a per-session Structured switch (on by default).

Agent ACP adapter Install Auth
claude claude-agent-acp (Zed) npm install -g @agentclientprotocol/claude-agent-acp@latest claude login, or ANTHROPIC_API_KEY
codex codex-acp npm install -g @agentclientprotocol/codex-acp@latest OPENAI_API_KEY, or ChatGPT login (local only)
opencode opencode acp (native, ≥1.16.0 recommended) curl -fsSL https://opencode.ai/install | bash opencode auth / provider env
gemini (deprecated) gemini --acp (native) npm install -g @google/gemini-cli GEMINI_API_KEY, OAuth, or Vertex
vibe vibe-acp (native) see mistral-vibe Mistral API key
pi pi-acp npm install -g pi-acp (plus @earendil-works/pi-coding-agent) pi-acp --terminal-login, or provider env
omp omp acp (native) curl -fsSL https://omp.sh/install | sh OMP login or provider env
kimi kimi acp (native) curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash kimi login, or provider env
prime-agent prime-agent --mode acp (native 0.10.x) curl -fsSL https://app.primeintellect.ai/prime-agent/install.sh | sh /login once, or provider env
aoe-agent bundled (Vercel AI SDK 7) aoe acp doctor --fix --adapter aoe-agent provider env vars

Gemini CLI is deprecated upstream for individual accounts; enterprise and API-key auth remain valid, and Antigravity CLI is the consumer replacement.

The npm install -g lines are optional: aoe acp doctor --fix installs the claude, codex, and pi adapters and aoe-agent into the data dir instead (see Requirements).

Tools not in the registry (aider, cursor, copilot, droid, hermes, kiro) always run in the terminal view. A custom agent opts in either with an explicit agent_acp_cmd, or by mapping onto a supported base with agent_detect_as, which inherits that base’s adapter; see Configuration.

Provider credentials

Each built-in adapter receives only the provider variables it is known to read, from the environment that runs aoe serve:

Adapter Forwarded
claude ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CONFIG_DIR
codex CODEX_API_KEY, OPENAI_API_KEY, OPENAI_BASE_URL, CODEX_HOME
opencode ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, GOOGLE_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY, OPENCODE_API_KEY
gemini GEMINI_API_KEY, GOOGLE_API_KEY, GOOGLE_GENAI_USE_VERTEXAI, GOOGLE_APPLICATION_CREDENTIALS, GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_LOCATION
aoe-agent ANTHROPIC_API_KEY, OPENAI_API_KEY, OPENAI_BASE_URL, GOOGLE_GENERATIVE_AI_API_KEY
prime-agent PRIME_API_KEY, PRIME_TEAM_ID, every provider key its model registry reads, and the Vertex, Bedrock, and AWS credential-chain variables (see src/acp/agent_registry.rs)

vibe, pi, omp, kimi, and custom adapters have no ambient allowlist yet: give them auth through the session’s extra_env, the environment list, or session.inherit_host_environment for host sessions. In a sandboxed session the per-adapter keys above still cross the container boundary, but inherit_host_environment does not, so use sandbox.environment there. Allowlist entries naming a file or directory (CLAUDE_CONFIG_DIR, CODEX_HOME, GOOGLE_APPLICATION_CREDENTIALS, the AWS file variables) are host-only and never cross, since each agent’s config dir is already bind-mounted at its canonical container location; each drop is logged under the acp target with the key and the reason.

Prime runtime ownership

Native Prime Agent 0.10.x uses a daemon owned by each structured execution. Ownership follows the resolved ACP backend, including inherited custom agents, rather than the session tool label. The daemon and frontend receive the same effective environment and use the original agent directory for credentials, settings, skills, and history. Sandboxed preparation resolves Prime with the frontend’s executable, environment, working directory, and pinned container ID. AoE does not reuse a daemon launched with another session’s credentials.

Stopping or restarting a session requires process settlement before replacement. Runner retirement is bound to its registry generation, kernel birth identity, and boot. Linux uses pidfds; macOS uses audit-token PID versions. Linux 6.9 and newer can signal the original process group through a retained pidfd after the leader exits. A cold collector without an attested group anchor retains ownership. Foreign-user permission errors do not establish ownership; an uninspectable owned incarnation blocks retirement. Positively scoped native auxiliaries are fenced even if they detached with setsid. Detached runner expiry removes its record and sockets only after cleanup succeeds.

Successful cleanup removes the private runtime directory and retains a compact termination receipt in the AoE worker directory. The receipt binds launch evidence to its original host boot or guest PID namespace. A changed epoch invalidates old descriptors and launch PID files across retries. A missing runtime directory can settle only after the current census proves no owned processes remain. Definitive absence of the pinned container on its recorded runtime endpoint settles that guest’s obligations without signalling host PIDs; stopped, paused, and unreachable containers are not treated as absent. The receipt’s absolute host path is pinned before applying the agent environment, and preparation commits it before launching native processes. Failure rolls back only the directory created by that attempt.

Container images must provide Bash and GNU grep for scoped process inspection, plus Python 3.9 or newer with os.pidfd_open and signal.pidfd_send_signal on the guest kernel. AoE probes pidfd signalling during preparation and binds guest signals to the recorded incarnation, without a numeric PID fallback. Missing capabilities refuse launch. Stopping aoe serve only detaches; an admitted turn continues and the next daemon reattaches to the same worker.

On Apple Container, worker descriptor snapshots are streamed as a tar archive by the guest Python helper. Symlinks and non-regular descriptors are refused; no host copy destination is used.

CLI stop and kill persist a stop intent before signalling. Reattachment, automatic respawn, queued turns, and runner publication respect that fence. Only an explicit resume after settled retirement can clear the stop intent it observed; a newer stop remains authoritative. For a tracked runtime that cannot prove settlement, externally stop only that execution on its original host or pinned container and retry aoe acp stop <id>.

A legacy Prime record without an attested native cleanup route cannot be retired automatically. Upgrades pin an existing control receipt only when it matches the recorded execution route; otherwise the route and ownership record remain intact with an unknown control path. First identify its original host or container and externally stop every native worker and service belonging to that execution. If that scope cannot be identified or its shutdown cannot be confirmed, retain the record. Do not use a global Prime shutdown that would affect unrelated executions.

After confirming native shutdown and verifying the original runner has exited, run aoe acp recover <id> --confirm-prime-stopped on the original worker host. The flag is an operator attestation, not proof obtained by AoE. Recovery refuses live original runners, attested native routes, non-Prime records, and changed execution identities. It retires only the selected record and runner sockets, not conversation history.

Malformed registry bytes remain an admission fence. After externally stopping the original runner and all its host or container native processes, use aoe acp recover <id> --confirm-worker-stopped. This covers unattested legacy runners and orphaned runner groups after external settlement. Recovery observes rather than signals: a known runner or group still executing, a live control-socket peer, changed evidence, or failed cleanup of an attested native route prevents retirement. Unidentified native receipts remain intact. If the original scope cannot be identified and stopped, retain the record rather than discarding it.

Prime 0.10.0 does not advertise ACP session/load: reattachment preserves a live session, but a cold ACP start creates a new native context and reports a context reset.

Switching provider

A Claude session can be moved between the direct API, Bedrock, and Vertex without losing its transcript, from the provider picker next to the model picker or with aoe acp switch-provider <session> <api|bedrock|vertex>.

The pick only sets CLAUDE_CODE_USE_BEDROCK and CLAUDE_CODE_USE_VERTEX, and it outranks both the host environment and the environment list. Credentials are not provisioned by it: the target provider’s own variables (ANTHROPIC_VERTEX_PROJECT_ID and CLOUD_ML_REGION for Vertex, the AWS credential chain for Bedrock) must already be present where aoe serve runs, or the next turn fails on authentication.

Two further effects are worth knowing before you switch:

  • The model is reset to the new provider’s default, because model ids differ between providers and a resumed conversation would otherwise stay on the model it last ran. Pick a model again afterwards if you had one pinned.
  • A sandboxed session’s container is recreated, since the GCP credential mount is decided when the container is built. Anything written inside the container but outside the workspace volume is lost.

The switch is refused while a turn or a background agent is running, since it restarts the worker. Once idle, the worker restarts and resumes the same conversation.

Feature matrix

Feature Claude Codex OpenCode Gemini Other ACP
Streaming text, tool-call cards, approvals ✓ ✓ ✓ ✓ ✓
Mode picker ✓ depends ✓ depends depends
Slash-command palette ✓ depends ✓ ✓ depends
Usage / context-window display ✓ depends ✓ ✓ depends
/clear boundary divider /clear /new /new none none
TodoWrite / Skill / ExitPlanMode / ScheduleWakeup cards ✓ — — — —
Subagent indentation ✓ — unverified — —
Session resume across aoe serve restart ✓ depends ✓ depends depends

“Depends” means the adapter has to advertise the matching channel; when it does not, the UI stays empty rather than showing stale state. Codex does not advertise a Plan-mode channel, so no Codex Plan switch is rendered. Older opencode falls back to generic tool cards, verbose read text, and blind permission prompts. If a tool renders as a generic card, file an issue with the observed tool.kind and tool.name; how profiles gate these is covered in Structured View Internals.

Quickstart

  1. Run aoe serve and open the dashboard.
  2. Click New session, pick your project and agent, and launch. Structured view is on by default.
  3. Open the session: you get the plan and tool-call cards instead of a terminal.

The CLI is the optional path for scripting. Unlike the wizard, aoe add defaults to the terminal view:

aoe acp doctor                              # confirm prerequisites
aoe add . --cmd claude --structured-view    # structured view for an ACP tool
aoe add . --agent codex --model gpt-5       # pick an ACP agent + model (implies structured view)
aoe add . --agent codex --effort high       # pick the reasoning effort

--agent for an uninstalled adapter errors with an install hint; --structured-view without --agent falls back to the terminal view with a warning, so the command still succeeds.

Requirements

  • Node.js 22+ on PATH (the structured view spawns the adapter as a subprocess); aoe-agent needs 22.6+.
  • For Claude Code, a claude login session.

If Node is missing or too old, the session falls back to the terminal view with an actionable warning. Verify with aoe acp doctor:

aoe acp doctor                            # Node + each configured agent's reachability
aoe acp doctor --fix                      # bundled Node if missing, then claude-agent-acp
aoe acp doctor --fix --adapter codex-acp  # a specific adapter
aoe acp doctor --fix --all-adapters       # every bundled adapter

--fix installs a pinned npm adapter under $AOE_DATA_DIR/acp-worker/adapters/<adapter>/ with the bundled Node’s own npm: no npm install -g, no sudo. Each adapter is a separate several-hundred-MB tree (pi-acp is the exception at ~7 MB), so --fix installs only claude-agent-acp unless you ask for more. An adapter already on your PATH normally wins, unless it is below the version floor aoe requires, in which case aoe uses the pinned copy and logs the substitution. doctor exits 1 if Node is missing, 2 if some agents are unreachable, else 0; --json for machine-readable output. Install the native CLIs through their own channels.

Choosing the view per session

  • Web wizard: structured view by default (set acp.default_new_session_view to change it); turn off Use structured view for the terminal.
  • CLI / TUI: terminal view by default; opt in with --structured-view or --agent, or the Structured field in the TUI new-session dialog.
  • An existing session can switch either way from the web sidebar’s right-click menu or the TUI context menu (which needs a running aoe serve). Both confirm first, and the worktree, files, and commits are always preserved. For a claude session, the conversation is kept in both directions only when AoE can resolve a shared native store. A terminal switch whose store cannot be resolved, or which the structured worker does not share, is refused with HTTP 409 and set-session-id --store recovery guidance. Every other agent starts fresh on the target surface.

Launch command and session naming

--cmd <tool> resolves through session.agent_command_override like a terminal session, so an override makes --cmd opencode launch opencode-plannotator acp with the required ACP args preserved. Adapter-backed agents such as Claude use session.agent_acp_cmd for a full command swap. The wizard shows the resolved command read-only.

aoe add names a session from --title, else the worktree branch, else a generated name (-i prompts like the TUI and wizard do). A structured session still carrying a generated name is auto-renamed once its first turn finishes, by a one-shot call to the session’s own agent, so the title reflects both your prompt and the response. See session.smart_rename in the configuration reference.

If the rename never lands, right-click the session and pick Auto-name now (TUI: v or the command palette), which works even with smart_rename off and regenerates the title even over one you picked yourself (the automatic rename never does). If the agent’s one-shot fails (for example an expired login or an unsupported model), its error is shown instead of a new title. The sidebar shows an Auto-name chip while a session is still default-named and a Naming… chip while the call is in flight. Two more chips flag a session that parked itself but is still alive: a ⏰ countdown to a scheduled wakeup, and a 👁 monitoring badge while an armed Monitor keeps re-invoking the agent, which clears when you send a new prompt.

Per-agent startup defaults live under [acp.acp_defaults.<agent>] (model, effort, mode, effort_by_model, pin_model) and are editable from the web settings; see Configuration. The rest of [acp] is in Structured View Internals.

Agent artifacts

Each session gets a managed artifact directory, exposed as AOE_ARTIFACT_DIR (bind-mounted at /aoe/artifacts in a sandbox). Files the agent writes there are served over an authenticated, session-scoped route: transcript links open them in a new tab and markdown images render inline. Files written anywhere else render as plain text, so write anything you want viewable under $AOE_ARTIFACT_DIR.

Cross-machine attach

Point AOE_DAEMON_URL (and optionally AOE_DAEMON_TOKEN) at a remote aoe serve:

AOE_DAEMON_URL=https://aoe.example.com AOE_DAEMON_TOKEN=… aoe   # remote session picker
aoe acp attach <session_id> --daemon-url https://aoe.example.com

With AOE_DAEMON_URL set, the TUI opens a remote session picker; aoe serve --status, daemon-backed aoe acp verbs and the read-only CLI commands select that daemon. Local operations such as tmux attach, aoe stop, file editing and worker recovery still require the local machine or SSH. The TUI picker sends bearer tokens only over HTTPS or loopback. Runtime CLI reads permit non-loopback HTTP without credentials; remote authentication requires HTTPS, and localhost is pinned to loopback without DNS. See runtime CLI reads for endpoint selection and fallback rules.

For runtime reads and ACP requests to a daemon running --auth=passphrase, use AOE_DAEMON_PASSPHRASE instead of a bearer token. These clients share the /api/login handshake, device binding, and persisted session cache. Login secrets never follow redirects; loopback login bypasses environment proxies. ACP local discovery can also read the saved serve.passphrase file. A revoked cached session triggers a fresh login; a failed authentication never substitutes local rows. TUI session-list authentication supports bearer tokens only; an unauthenticated daemon needs no credential. Ancillary plugin UI requests can use passphrase authentication through the ACP client.

Headless CLI verbs

Every structured-view operation has a matching verb against the same daemon:

Verb What it does
aoe acp history <id> Dump the persisted transcript
aoe acp status <id> Print highest/lowest seq and the daemon source
aoe acp prompt <id> <text> Send a prompt (- reads stdin)
aoe acp approve <id> <nonce> [--always|--deny|--option <id>] Resolve a pending approval
aoe acp cancel <id> Cancel the in-flight prompt
aoe acp set-option <id> <config_id> <value> Set a model, mode or effort option on a running session
aoe acp tail <id> Stream broadcast frames as JSON lines
aoe acp attach <id> Open the TUI structured view
aoe acp stop / kill / restart / logs / switch-agent Worker management
aoe acp recover <id> --confirm-prime-stopped Retire an identifiable missing-route Prime record after operator-confirmed native shutdown
aoe acp recover <id> --confirm-worker-stopped Retire malformed or unattested legacy ownership after external shutdown confirmation
aoe ps --acp List workers with their ACP columns; --dead includes dead and orphaned ones

Daemon-backed verbs need a running aoe serve and exit with a hint if none is found. The CLI never spawns a daemon on your behalf. Recovery operates on the local worker registry, so run it on the original worker host, using SSH for a remote host.