- Add per-provider preference storage: model, mode, thinkingByModel
- resolveFormState reads stored prefs as fallback between initial
values and provider defaults
- setProviderFromUser seeds model/mode/thinking from stored prefs
when switching providers
- persistFormPreferences writes full per-provider state on agent create
- Remove workingDir and serverId from stored preferences (redundant
with workspace tab context)
- Simplify settings.tsx by removing preference-based server resolution
- Fix race in agent-manager where pendingRun wasn't fully cleaned up
before the next streamAgent call, causing "already has an active run"
- Fix Claude agent query restart: null out query/input before awaiting
old iterator return so the old pump skips failActiveTurns
- Reset pendingInterruptAbort on new foreground turn
- Add system error assertion to send-during-tool-call e2e test
- Rename mode color tiers: drop "default", rename "readonly" to
"planning", update color assignments across providers and UI
Narrow zustand selectors to only subscribe to fields each component
actually uses. Split nested objects (availableModes, projectPlacement)
into separate selectors with structural equality checks. Remove the
updatedAt processing-spinner hack in agent-input-area — the server
already guarantees agent status is "running" before ACKing the send
request, so isProcessing can clear on submit success directly.
Replace three competing event paths (foreground stream, live event pump,
JSONL history poller) with a single push-based subscribe() + startTurn()
contract. This fixes duplicate user messages and stuck running state caused
by timing-based routing between concurrent event sources.
Key changes:
- AgentSession interface: remove stream(), add subscribe() and startTurn()
- All providers (Claude, Codex, OpenCode): single subscribers Set with
notifySubscribers() for push-based event delivery and turnId stamping
- Agent manager: identity-based turn ownership via activeForegroundTurnId
replacing pendingRun async generator
- Delete: dual queues, routeSdkMessageFromPump, startLiveHistoryPolling,
snapHistoryOffsetToEnd, liveEventBacklog, Pushable
- Fix Codex provider not clearing activeForegroundTurnId on turn completion
- Add real-provider integration tests for event stream invariants
* fix(app): move keyboard focus to target pane on keyboard navigation (#137)
Add a pane focus registry so each panel type can expose its own focus
callback. When keyboard shortcuts navigate between panes, the workspace
screen calls the target panel's registered focus function, moving DOM
focus to the correct input element.
- Agent panels register a callback that focuses the message input
- Terminal panels register a callback that triggers terminal focus
- Panel-internal focus logic stays inside each panel type
- No DOM selectors or panel knowledge in shared infrastructure
* fix(app): simplify workspace pane focus handoff
- Add detail card above command list showing full description and argument hint
- Compact command rows to single line (label + truncated description)
- Match dropdown component styling (surface1 bg, borderAccent border, shadow)
Multiple clients viewing the same terminal would fight over PTY size
because every client sent resizes independently. Gate resize sends on
three focus levels: pane focus (split panes), screen focus (navigation),
and app visibility (browser window / mobile foreground). When a client
regains focus, clear the last reported size and trigger a reflow to
reclaim the PTY dimensions.
Hides ScreenHeader, LeftSidebar, and ExplorerSidebar on desktop,
replacing the split layout with the single focused pane. Respects
macOS traffic light padding in the tab bar when active. Shortcut
is restricted to workspace screens. Also nudges traffic light
buttons up 3px globally.
Client-side xterm.js was generating Device Attributes responses via
onData, which fed back to the PTY as visible text. Register CSI handlers
to consume query responses on the client and respond to DA1 on the server.
Reassign Cmd+. (Mac) and Ctrl+. (Win/Linux) from toggling the left
sidebar to toggling both sidebars at once. If either is open, both
close; if both are closed, both open. Left sidebar toggle on non-Mac
now uses Ctrl+B to match the Mac Cmd+B binding.
- Reduce diff stat number brightness with muted green/red
- PR badge uses foregroundMuted by default, foreground on hover
- Show PR state label (Open/Merged/Closed) next to number
- Show external link icon on hover
Sidebar workspace and project rows now keep their icon (Monitor/FolderGit2
for workspaces, project icon/initial for projects) and overlay a small
colored status dot on the bottom-right, matching the tab icon pattern.
Loading and syncing states still replace the icon entirely.
- Replace REST API PR lookup with `gh pr view` which handles fork PRs
- Fix state comparison to use lowercase (matching server output)
- Remove pill/badge styling, show plain icon + #number
- Add brighter hover colors and underline for clickability hint
Replace the flat "global" section with navigation, tabs-panes, projects,
and panels groups. Rename "Keyboard Shortcuts" to "Shortcuts" in settings
nav, section titles, and dialog.