Three fixes from acceptance review:
- ConnectionPool.mount cached a connection's kind (terminal/structured)
permanently from whatever `info` it saw first. A deep link or restored
layout mounts before the catalog snapshot names a session's view, so
`info` is undefined and a structured session got an xterm connection
forever. The pool now tracks the view each cached connection was built
for, subscribes to the catalog itself, and replaces a mistyped
connection in place the moment the real view resolves — without
downgrading a resolved connection just because a later caller couldn't
resolve the session, and without treating a session merely absent from
a snapshot (mid-removal) as "Terminal".
- The approval card offered all three decisions unconditionally. Per
the daemon's decision mapping (session.rs's permission_outcome), a
decision with no matching PermissionOptionKind in the request's own
options resolves Cancelled silently — so "Allow for session" could
look like it worked while the daemon withdrew it. Buttons are now
gated on the options actually offered.
- Clicking a decision disabled the buttons and never re-enabled them if
the POST failed. The decision path is async now: a rejection restores
the buttons and surfaces the error only if the approval is still the
pending one — a superseded/resolved approval's stale rejection is a
no-op, not a wrongly revived control.
Regression tests: 12 new (connections.test.ts, structured.test.ts,
structuredRender.test.ts). 488 tests total.
Adds a transcript surface for a Structured-view session alongside the
existing xterm attach view: a WS /api/structured/:id connection
(structured.ts) mounted by ConnectionPool whenever a session's `view`
is `"Structured"`, and pure per-row/rail renderers (structuredRender.ts,
structuredDiff.ts) covering all eight transcript row kinds.
Hierarchy follows the design reference (artifact 88ecd690): the
approval is the only lifted, card-like element; a user turn is a
tinted block with an accent rail; agent prose is plain text; tool
calls are compact bordered rows expanding to output or a coloured,
horizontally-scrolling diff computed from ACP's full old/new text.
The client renders `reduced_state` and rows exactly as received, with
no raw-event folding or turn tracking of its own.
Extracted attach.ts's pane-header title building into paneTitle.ts and
its connection interface into pane.ts (PaneConnection) so both
connection kinds share one contract and one header.