Skip to content

feat(console): agent console shell + per-endpoint chat (agent-consoles slice 2) - #67

Merged
brettchien merged 3 commits into
mainfrom
feat/adr49-slice2-agent-console
Aug 17, 2026
Merged

feat(console): agent console shell + per-endpoint chat (agent-consoles slice 2)#67
brettchien merged 3 commits into
mainfrom
feat/adr49-slice2-agent-console

Conversation

@brettchien

Copy link
Copy Markdown
Contributor

What

Slice 2 of 4 for the agent-consoles ADR (#49), stacked on the merged slice 1 (#66). Slice 1 generalized the backend into a per-agent endpoint registry + RemoteState keyed by agent. This slice builds the frontend: the chat panel becomes a reusable per-endpoint primitive, and an agent-console shell (selector + read-only config + per-agent chat) sits on top of it — the ADR's Part A/C surface.

Frontend-only; the Rust side is untouched (slice 1's remote_agents / per-agent remote_connect / agent_prompt commands are what this consumes).

Changes

  • console/src/chatPanel.ts (new) — extract the management chat wiring into createChatPanel({agent, source, mock}): transcript, one-turn-at-a-time queue, streaming spinner, stop/copy. Each panel owns its own state + DOM listeners (AbortController-scoped) and is keyed by endpoint name; onChunk/onTurnEnd/setConnected feed it events.
  • console/src/agentConsole.ts (new) — the shell: a selector over remote_agents + a per-agent console that dials on open (remote_connect(agent)), tears down on close (remote_disconnect(agent)), shows read-only config (name/url/cwd/status — never the token), and mounts a chat panel bound to that agent. One console open at a time (ADR §7 bounded fan-out).
  • console/src/main.ts — the management console now mounts the same primitive. A single agent-update / remote-status subscription routes each event to the owning panel by endpoint name (chatPanels map); the management endpoint's name is learned from the registry.
  • console/src/render.ts — pure agentListHtml + agentConsoleHeaderHtml.
  • console/src/{types,source,fixtures}.tsAgentEndpointView; remoteAgents() + optional agent param threaded through connect/disconnect/prompt/cancel; FIXTURE_AGENTS for the browser build.
  • index.html / styles.css — the agents section (selector + console with its own chat region), reusing chat-wrap and the identity id-field primitives.

Scope / ADR fidelity

  • Read-only per ADR §6 — the remote file editor's write path waits on the openab fs/* wire (slices 3–4). The console ships chat + read-only config today, exactly as the ADR sequences it.
  • Least privilege preserved — the management endpoint keeps its dedicated top-level console (chat + fleet control); the agent-console selector shows it but does not open a duplicate console for it. Reverse-MCP oab publication stays gated to the management binding (backend, slice 1).
  • Secrets — the bearer token is never in AgentEndpointView and never rendered.

Testing

  • npm run build green (tsc --noEmit + vite build, 49 modules).
  • vitest run75 tests green, incl. 13 new render cases (selector rows: openable / management / unconfigured / open-marking / escaping; console header: identity/target/status, no-token-leak, read-only note).
  • The chat primitive is a faithful extraction of the already-shipping management chat; the new HTML is unit-tested pure-render.

Stacked slices to follow: 3 (remote file-editor read path), 4 (write/apply behind the upstream fs/* wire).

🤖 Generated with Claude Code

brettchien and others added 3 commits August 17, 2026 08:54
…nsoles slice 1) (#66)

ADR agent-consoles Part B: generalize the single `remote.toml` connection into a
registry of named `/acp` endpoints and rekey the live-connection state per agent,
so Studio can reach N agents instead of one.

- crates/acp-tunnel/config.rs: add `AgentEndpoint` (name + url/token/cwd +
  `management` flag) and `AgentRegistry` (`[[agent]]` in agents.toml). Explicit
  fields + a `conn()` accessor reuse `RemoteConfig` validation as one source of
  truth (no serde-flatten, which is order-fragile under toml). `from_legacy`
  adopts a `remote.toml` as one `management = true` entry. Structural `validate`
  (unique non-empty names, ≤1 management). Unit-tested.
- src-tauri/remote.rs: `Remote` is now `HashMap<name, RemoteState>` — each agent
  console has its own task/status/reconnect. `oab` reverse-MCP tools are declared
  only when the endpoint is `management` (least privilege, Part A); an ordinary
  agent console runs chat-only. `remote-status` / `agent-update` events carry an
  `agent` field so the UI can route per console. `agents.toml` preferred, legacy
  `remote.toml` adopted when absent. `disconnect_all` on app teardown.
- src-tauri/lib.rs: remote_connect/disconnect + agent_prompt/cancel take an
  optional `agent` (None ⇒ management, so the current single-console UI is
  unchanged); new `remote_agents` command lists the registry + live status for
  the slice-2 selector.

Backend-only; the frontend still drives the management endpoint. Desktop crate
is built by the bundle-macos CI job (GTK libs absent locally).

Co-authored-by: Orca <orca@openab.dev>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…les slice 2 · pt2)

Turns the chat panel into the per-endpoint primitive the ADR calls for and adds
the agent-console shell on top of it (ADR agent-consoles Parts A/C):

- chatPanel.ts: extract the management chat wiring (transcript, one-turn queue,
  streaming spinner, stop/copy) into `createChatPanel({agent, source, mock})`.
  Each panel owns its own state + DOM listeners (AbortController-scoped) and is
  keyed by endpoint name; `onChunk`/`onTurnEnd`/`setConnected` feed it events.
- main.ts: the management console now mounts this primitive. One `agent-update`
  /`remote-status` subscription routes each event to the owning panel by name
  (`chatPanels` map); the management endpoint's name is learned from the registry.
- agentConsole.ts: selector over `remote_agents` + a per-agent console that dials
  on open (`remote_connect(agent)`), tears down on close, shows read-only config,
  and mounts a chat panel bound to that agent. One console open at a time.
- render.ts: pure `agentListHtml` + `agentConsoleHeaderHtml`.
- index.html: the agents section (selector + console with its own chat region).

Read-only per ADR §6 — the file editor's write path waits on the openab `fs/*`
wire (slices 3–4). Reverse-MCP `oab` stays gated to the management binding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- render.test.ts: cover `agentListHtml` (openable vs management vs unconfigured
  rows, open-marking, escaping) and `agentConsoleHeaderHtml` (identity/target/
  status, no token leak, read-only note). 75 tests green.
- styles.css: the agents selector + open-console layout, reusing the chat-wrap
  and identity `id-field` primitives so the surfaces read as one system.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@brettchien
brettchien merged commit 1101e77 into main Aug 17, 2026
2 checks passed
@brettchien
brettchien deleted the feat/adr49-slice2-agent-console branch August 17, 2026 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant