feat(extension): Terminal-only sidebar — auth fix, UX polish, chat rip

The chat queue path is gone. The Chrome side panel is now just an
interactive claude PTY in xterm.js. Activity / Refs / Inspector still
exist behind the `debug` toggle in the footer.

Three threads of change, all from dogfood iteration on top of
cc-pty-import:

1. fix(server): cross-port WS auth via Sec-WebSocket-Protocol
   - Browsers can't set Authorization on a WebSocket upgrade. We had
     been minting an HttpOnly gstack_pty cookie via /pty-session, but
     SameSite=Strict cookies don't survive the cross-port jump from
     server.ts:34567 to the agent's random port from a chrome-extension
     origin. The WS opened then immediately closed → "Session ended."
   - /pty-session now also returns ptySessionToken in the JSON body.
   - Extension calls `new WebSocket(url, [`gstack-pty.<token>`])`.
     Browser sends Sec-WebSocket-Protocol on the upgrade.
   - Agent reads the protocol header, validates against validTokens,
     and MUST echo the protocol back (Chromium closes the connection
     immediately if a server doesn't pick one of the offered protocols).
   - Cookie path is kept as a fallback for non-browser callers (curl,
     integration tests).
   - New integration test exercises the full protocol-auth round-trip
     via raw fetch+Upgrade so a future regression of this exact class
     fails in CI.

2. fix(extension): UX polish on the Terminal pane
   - Eager auto-connect when the sidebar opens — no "Press any key to
     start" friction every reload.
   - Always-visible ↻ Restart button in the terminal toolbar (not
     gated on the ENDED state) so the user can force a fresh claude
     mid-session.
   - MutationObserver on #tab-terminal's class attribute drives a
     fitAddon.fit() + term.refresh() when the pane becomes visible
     again — xterm doesn't auto-redraw after display:none → display:flex.

3. feat(extension): rip the chat tab + sidebar-agent.ts
   - Sidebar is Terminal-only. No more Terminal | Chat primary nav.
   - sidebar-agent.ts deleted. /sidebar-command, /sidebar-chat,
     /sidebar-agent/event, /sidebar-tabs* and friends all deleted.
   - The pickSidebarModel router (sonnet vs opus) is gone — the live
     PTY uses whatever model the user's `claude` CLI is configured with.
   - Quick-actions (🧹 Cleanup / 📸 Screenshot / 🍪 Cookies) survive
     in the Terminal toolbar. Cleanup now injects its prompt into the
     live PTY via window.gstackInjectToTerminal — no more
     /sidebar-command POST. The Inspector "Send to Code" action uses
     the same injection path.
   - clear-chat button removed from the footer.
   - sidepanel.js shed ~900 lines of chat polling, optimistic UI,
     stop-agent, etc.

Net diff: -3.4k lines across 16 files. CLAUDE.md, TODOS.md, and
docs/designs/SIDEBAR_MESSAGE_FLOW.md rewritten to match. The sidebar
regression test (browse/test/sidebar-tabs.test.ts) is rewritten as 27
structural assertions locking the new layout — Terminal sole pane,
no chat input, quick-actions in toolbar, eager-connect, MutationObserver
repaint, restart helper.
This commit is contained in:
Garry Tan
2026-04-25 21:03:04 -07:00
parent 0361acfb6a
commit 006dbe19f1
16 changed files with 771 additions and 4229 deletions
+29 -25
View File
@@ -675,36 +675,40 @@ body::after {
}
.tab-content.active { display: flex; flex-direction: column; }
/* ─── Primary surface tabs (Terminal | Chat) ──────────────────── */
.primary-tabs {
display: flex;
border-bottom: 1px solid var(--border);
background: #0f0f0f;
padding: 0 8px;
flex-shrink: 0;
}
.primary-tab {
background: transparent;
border: none;
color: #71717a;
padding: 8px 14px;
font-size: 12px;
font-family: 'JetBrains Mono', monospace;
cursor: pointer;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
}
.primary-tab:hover { color: #e5e5e5; }
.primary-tab.active {
color: #e5e5e5;
border-bottom-color: #f59e0b;
}
/* ─── Terminal Tab ────────────────────────────────────────────── */
#tab-terminal {
background: #0a0a0a;
padding: 0;
}
.terminal-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
padding: 4px 8px;
border-bottom: 1px solid #1a1a1a;
background: #0a0a0a;
flex-shrink: 0;
}
.terminal-toolbar-actions {
display: flex;
gap: 4px;
flex-wrap: wrap;
}
.terminal-toolbar-btn {
background: transparent;
border: 1px solid #27272a;
color: #a1a1aa;
padding: 3px 10px;
font-size: 11px;
font-family: 'JetBrains Mono', monospace;
border-radius: 3px;
cursor: pointer;
}
.terminal-toolbar-btn:hover {
color: #f59e0b;
border-color: #f59e0b;
}
.terminal-bootstrap {
flex: 1;
display: flex;