ADR-021 — Reposition Agent Chat as the Vault Console
- Status: Accepted — superseded by ADR-024 (2026-06-14, the
builtin-persona job) and ADR-025 (2026-06-18, the approval-seat job); narrowed by ADR-031 (2026-06-20 — observe → observe + interrupt + inject; theorigin/peer_display_namefields are collapsed to an optionalchannel_binding) - Spec: 008-agent-chat (repositioning — no new spec number; Spec 008
spec.mdis updated before implementation) - Related: 009-channels (ADR-014, shared turn seams), 007-memory (the vault the console talks to)
Note (2026-06-14): ADR-024 supersedes the "converse with the vault through the
builtinagent" job below — the built-in agent is retired as a chat persona and recast as internal Coffer capabilities (coffer__search_toolssemantic ranking,coffer__ask); the chat surface reverts from Vault Console to Chat.Note (2026-06-18): ADR-025 supersedes the "approval seat" job (job 2) below — the entire interactive tool-approval system is removed. Agents run with full permissions; owner-pairing is the trust boundary. The console's remaining role is to observe channel-driven conversations, not to approve their tool calls.
Context
Spec 008 Agent Chat shipped as a full multi-agent chat client: three agents (builtin Coffer Assistant, claude_code via the Python SDK, codex via the codex app-server subprocess), an agent picker, a working-directory picker, a model picker, conversation history, and per-tool approval cards.
But the realistic surfaces for talking to a coding agent are (a) the agent's own UI — the focused place you write code — and (b) IM — the async, mobile, away-from-keyboard place. A browser chat client that competes with both has no durable usage and is off-mission: Coffer's value is the vault (memory, skills, knowledge, aggregated MCP, sync), not being yet another chat front-end. "A chat page with no real usage" also fails the project's deliverable bar (every shipped surface must have real usage).
Two roles, however, are unique to Coffer and served nowhere else:
- Talking to the vault itself. The
builtinagent is already an in-process MCP client of Coffer's own gateway (memory / KB / skills / aggregated MCP tools, session idcoffer-builtin-agent). That is a console / playground for the vault, not a coding chat. - A human-in-the-loop seat for channel/IM-driven conversations. Channels (Spec 009) already create conversations and route tool approvals through the same
ConversationPort/TurnPortseams the web UI uses; an agent cannot tell a channel turn from a UI turn. The web UI is the natural place to observe those conversations and take the approval seat for them.
Decision
Reposition the Agent Chat surface as the Vault Console. Its job is:
- Converse with the vault through the
builtinagent, and - Observe and approve channel/IM-driven conversations.
Explicitly de-scope "use Coffer as your day-to-day in-browser coding chat." Three concrete moves:
- Builtin agent → vault console / playground. Reframe naming, empty state, and CTA away from "general assistant" toward "ask your memory / skills / KB." Surface, per turn, which memory facts / KB docs / skills / aggregated MCP tools the turn actually touched, so the console doubles as a way to inspect what an agent would get from the vault.
- Origin-aware conversation list + approval seat. The conversation list distinguishes origin (web draft vs. channel peer), shows the peer identity for channel-originated conversations, and surfaces pending tool approvals prominently. Approving from the web
ApprovalCardis equivalent to tapping the IM approval button — both resolve the samesubmit_approval. - CLI agents reframed, not removed.
claude_code/codexstay available but are repositioned: not "your daily coding chat," but (a) test-drive targets and (b) the conversations IM drives that you observe/approve here.
Per Constitution Principle II, Spec 008 spec.md and its acceptance scenarios are updated to this positioning before implementation; this ADR records the direction.
Invariants
- Same seams, no parallel path. The Vault Console drives turns and approvals only through
ConversationPort/TurnPort, exactly as channels do. It introduces no privileged turn or approval path; an agent cannot distinguish a console turn from a channel turn. - Approval parity. A pending tool approval is resolvable from either the web
ApprovalCardor the IM button; both callsubmit_approval. The console gets no special approval authority. - No daily-driver creep. Affordances that only make sense for "Coffer as your primary coding chat" (rich coding UX competing with native clients) stay out of scope unless a future spec deliberately re-opens that positioning.
Alternatives considered
A — Keep positioning it as a full multi-agent chat client
Rejected. No durable usage against the agents' own UIs and IM; off-mission (Coffer is a vault, not a chat client); fails the real-usage deliverable bar.
B — Cut the chat surface entirely
Rejected. That discards two genuinely unique roles (vault console + approval seat) and the already-built shared turn/approval seams. Channels still need a human approval seat somewhere; the console is the cheapest home for it.
C — Split into two separate pages (a console page + a channels-approval page)
Rejected for now. Both rely on the same conversation / turn / approval machinery and the same thread UI. One origin-aware surface is simpler and avoids duplicating the thread component; a split can be revisited if the two roles diverge.
Consequences
- Spec 008
spec.md, UI copy, and the builtin-agent detail-page CTA are updated to the Vault Console positioning. - Channel-originated conversations become first-class in the conversation list (origin badge, peer identity, pending-approval affordance).
continue/resumeof foreign agent sessions stays out of scope.- This is positioning + frontend surfacing; the backend turn / approval / channel machinery is reused unchanged.