* feat: add branch switching and stash management to workspace header
Adds the ability to switch git branches from the workspace header by
clicking the branch name. Includes full stash support: when switching
away from a dirty branch, users are prompted to stash changes; when
switching to a branch with a Paseo stash, they're prompted to restore.
An amber stash indicator in the header provides anytime restore/discard.
New WebSocket messages: checkout_switch_branch, stash_save, stash_pop,
stash_drop, stash_list. Stashes are tracked via git stash messages
with a "paseo-auto-stash:" prefix convention.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: await query invalidation after branch switch and stash
The branch switcher became non-clickable after switching because
checkout query invalidation was fire-and-forget. Now we await
invalidation so the UI has fresh checkout status (including the new
branch name) before rendering. Also invalidate after stash-save
so the checkout query sees clean state before the switch.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: emit workspace update immediately after branch switch
The sidebar and header were slow to reflect the new branch name because
they relied on the daemon's background git watcher to detect changes.
Now the switch handler calls emitWorkspaceUpdateForCwd immediately after
checkout, pushing the updated workspace descriptor to connected clients.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: stash detection and dirty-tree branch switch handling
- Fix stash list parser: git prepends "On <branch>: " to stash
messages, so use indexOf instead of startsWith to find the
paseo-auto-stash prefix.
- Remove switchBranchMutation in favor of inline async flow in
handleBranchSelect. Now tries the switch first; if the server
returns an uncommitted-changes error, offers the stash dialog
automatically — no more red error toast on first attempt.
- Move post-switch stash restore prompt into handleBranchSelect
so the full flow (switch → check stashes → prompt) is sequential.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: toast.success → toast.show and await stash invalidation
- Replace toast.success() with toast.show() — the ToastApi only
exposes show/copied/error, not success.
- Await invalidateStashAndCheckout in stashPop and stashDrop mutation
onSuccess handlers so the stash indicator disappears immediately
after restore/discard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add stash diff preview and multi-stash management
- Add stash_show message pair: server runs `git stash show -p` and
returns ParsedDiffFile[] through the existing diff parser.
- Stash indicator dropdown now lists ALL Paseo stashes (not just
current branch) with per-stash Preview, Restore, and Discard actions.
- Preview opens a full diff modal showing file-by-file changes with
syntax-colored add/remove lines, file stats, and Restore/Discard
action buttons.
- Clean up: remove unused handleRestoreStash/handleDropStash callbacks,
add Eye/Trash2 icons, import Modal/ScrollView.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use Fonts.mono instead of theme.fontFamily.mono
The theme object doesn't have a fontFamily property. The codebase uses
the Fonts constant from @/constants/theme for font family values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: filter stashes to current branch and handle restore conflicts
- Stash indicator only shows when the current branch has stashes,
not all branches. Switching branches hides/shows it correctly.
- When restoring a stash conflicts with uncommitted changes, offers
to stash current changes first then restore the target stash
(auto-adjusts stash index after the new save).
- Multiple stashes on the same branch show as "Stash 1", "Stash 2"
instead of repeating the branch name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: improve branch switcher with recency sort, sticky search, and branch icons
- Sort branches by committer date (most recent first) instead of alphabetical
- Use full refname to fix normalization of branches with slashes
- Filter out bare "origin" symref leaking as a branch name
- Remove server-side search query; fetch all branches once and filter client-side
- Port combobox sticky search design from dev (borderless bar above scroll area)
- Add GitBranch icons to branch switcher items
- Prioritize prefix matches in combobox search results
* fix: move useIsCompactFormFactor hook above early return in ToastViewport
The hook was called after `if (!toast) return null`, violating React's
rules of hooks and causing "Rendered more hooks than during the previous
render" crash.
* fix: include untracked files in stash-and-switch flow
git stash push without --include-untracked left untracked files behind,
causing the clean-tree check to fail on the subsequent checkout.
* refactor: strip stash management UI, keep stash-and-switch flow
Remove the stash indicator icon, dropdown menu, preview modal,
and stash_drop/stash_show RPCs. Keep the auto-stash on branch switch
and the restore prompt on arrival — that's the useful surface.
* refactor: extract filterAndRankComboboxOptions with tests
* refactor: extract BranchSwitcher component and useBranchSwitcher hook
---------
Co-authored-by: heyimsteve <8645831+heyimsteve@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add CombinedModelSelector with two-level drill-down (providers → models),
favorites section, search, and single-provider auto-drill mode
- Add provider snapshot system (ProviderSnapshotManager) for cached provider/model state
- Add provider diagnostics with resolved binary paths and versions across all providers
- Add StatusBadge shared component for consistent status visual language
- Add ProviderDiagnosticSheet for detailed provider health inspection
- Add desktop auto-focus on input after any status bar interaction (model, mode,
thinking, features) using focusWithRetries
- Fix Combobox flicker on height change by switching to bottom-based CSS positioning
once initial floating-ui position resolves
- Add dynamic dropdown height: Level 1 fits content, Level 2 calculates from model count
- Remove "Other providers" section from model selector (only show available providers)
AgentProviderSchema was z.enum() which caused old clients to reject
session messages containing unknown providers (pi, copilot), breaking
the entire session. Changed to z.string() for future clients.
For currently deployed clients (<0.1.45), the daemon now filters out
unknown providers based on the appVersion sent in the WebSocket hello
message. Clients that don't send appVersion only see claude/codex/opencode.
Introduce a generic feature system where providers declare dynamic
features (toggles/selects) and the app renders controls automatically.
One message pair (set_agent_feature_request/response) handles all
feature mutations. Feature values persist and restore on agent resume.
First consumer: Codex fast mode (service_tier) — gated to supported
model families, with proper cleanup on model switch.
Remove the hardcoded mode whitelist so user-defined agents (from
opencode.json or global config) appear in the mode picker. Add a
listProviderModes API (client → session → provider) so the draft
agent form fetches dynamic modes instead of relying on the static
manifest. Fix slash command dispatch to accept optional arguments.
Closes#185
* style: add subtle teal tint to dark mode surfaces
Replace neutral gray surfaces with a teal-tinted palette across
the app and website, giving Paseo a warmer, more recognizable feel.
App uses a restrained tint (G-R ~3), website is slightly stronger
(G-R ~6) as a brand showcase.
* fix(opencode): handle message.part.delta events for assistant text streaming
OpenCode v2 streams assistant text via `message.part.delta` events (with
field "text" or "reasoning"), but the translator only handled
`message.part.updated`. This caused assistant messages to be silently
dropped during live streaming.
* feat: show agent short ID in tab context menu and tooltip (#161)
Add agent short ID to the "Copy agent id" menu item as trailing hint
text and to the tab tooltip next to the title. Add leading icons to
all workspace tab context menu items.
* add batch close rpc for workspace tabs
New CLI commands: ls, create, kill, capture (tmux capture-pane style with
line range slicing and ANSI stripping), and send-keys (with special token
interpretation). Server-side adds capture_terminal_request RPC and
list-all-terminals support (optional CWD). Includes e2e tests and skill docs.
Speech runtime was leaking 8+ individual resolvers into bootstrap and
blocking server startup for ~3s with synchronous Sherpa native model
loading. Refactor into a self-contained SpeechService that owns its
full lifecycle (init, download, monitor, cleanup) and defer start()
until after httpServer.listen() so native loading doesn't block
accepting connections. Server startup drops from ~3.2s to ~0.5s.
Also removes unused client-triggered speech model download/list path
(CLI commands, session handlers, message types) — the service manages
its own models.
* Add metrics collection and terminal performance tests
* feat: add loop, schedule, and chat commands with slash-namespaced RPC
Introduce three new server-side features with CLI command groups:
- `paseo loop` — iterative agent execution with verify-check/verify-prompt
- `paseo schedule` — recurring tasks on interval or cron cadence
- `paseo chat` — chat rooms for agent-to-agent coordination
All features use new slash-namespaced RPC methods (e.g. `loop/run`,
`schedule/create`, `chat/post`) instead of flat message types, backed
by file-based persistence and wired through the existing WebSocket
session dispatch.
* test: stabilize loop schedule chat rollout
The catch-all handler was replacing real error messages with "Request
failed", and DaemonRpcError only showed the error string without
requestType or code context.
Replace the multi-step new-agent route flow with a single
create_paseo_worktree endpoint that registers the workspace immediately
and creates the git worktree in the background. The sidebar now calls
this endpoint directly and shows a creating spinner inline.
Also auto-resolves the base branch from origin/HEAD when not explicitly
provided, removes the unused Tauri WebSocket transport layer, and
normalizes loopback endpoints to localhost.
Closes#125
Unify the initial connection handshake and capability update paths.
The server now sends a server_info status message on connect and
whenever capabilities change, enabling the onboarding flow where
voice becomes available after models are configured.
Replace inline data-URL image handling with a persistent attachment storage service, add draft lifecycle states (active/abandoned/sent) with generation tracking, and update fetchAgent to return project placement alongside agent snapshots.
What changed:
- Added private helper in to centralize request-id resolution, schema parsing, and correlated RPC dispatch defaults.
- Migrated checkout/worktree/branch/file-explorer/provider/speech/command request methods to use the helper while preserving existing message types and per-method timeouts.
- Added a deterministic unit test covering request/response wiring.
Reasoning:
- The prior implementation duplicated the same correlated-RPC boilerplate across many methods, increasing maintenance cost and risk of drift.
- Centralizing this path reduces logic density and keeps transport behavior consistent.
- Additional test coverage protects one of the checkout RPC flows touched by the refactor.
Accomplishments for next agent:
- Reduced repetitive correlated RPC scaffolding in without changing external behavior.
- Verified with workspace typecheck and daemon-client test suite.
Challenges / follow-ups for next agent:
- remains very large; this commit intentionally scoped to request plumbing only.
- Next high-value split is extracting cohesive domains (for example checkout/worktree flows) behind focused modules while keeping wire contracts unchanged.
Move terminal stream buffering, handler fanout, and ack offset state out of DaemonClient into a focused TerminalStreamManager.\n\nReasoning:\n- daemon-client.ts mixed RPC transport concerns with terminal stream state machine details.\n- terminal buffering/ack logic is cohesive and testable in isolation, so splitting it lowers logic density and makes future stream changes safer.\n\nWhat changed:\n- Added daemon-client-terminal-stream-manager.ts to own stream handlers, buffered chunks, and ack monotonicity.\n- Updated DaemonClient to delegate terminal stream state transitions (subscribe, receive chunk, clear stream/all, note ack).\n- Preserved public API by re-exporting TerminalStreamChunk from daemon-client.ts.\n- Hardened daemon-client tests to assert behavior (no delivery/ack after detach or stream_exit) rather than private internal maps.\n- Added dedicated manager unit tests for buffering, eviction, handler-failure behavior, and stale ack suppression.\n\nVerification:\n- npm run test --workspace=@getpaseo/server -- src/client/daemon-client.test.ts src/client/daemon-client-terminal-stream-manager.test.ts\n- npm run typecheck\n\nNext-agent notes:\n- Accomplishment: terminal stream state is now isolated; daemon-client.ts is slimmer and easier to extend.\n- Challenge: daemon-client.ts is still large (~3k lines) with other cohesive seams remaining (dictation flow and checkout RPC groups).
Reasoning:\n- daemon-client.ts had transport construction, ws adapter, relay e2ee wrapper, encoding helpers, and core client behavior mixed in one class file.\n- extracted transport/encoding concerns to a dedicated module to reduce logic density and tighten boundaries while preserving runtime behavior.\n\nAccomplishments:\n- added packages/server/src/client/daemon-client-transport.ts with websocket transport factory, relay e2ee transport wrapper, and transport utility helpers.\n- kept daemon-client public transport type exports stable by re-exporting types from the new module.\n- removed duplicated helper implementations from daemon-client.ts and replaced them with imports.\n- added focused tests in packages/server/src/client/daemon-client-transport.test.ts for websocket adapter behavior and helper normalization/encoding paths.\n- validated with @getpaseo/server typecheck and targeted daemon-client test suite.\n\nChallenges / handoff notes for next agent:\n- daemon-client.ts is still large and retains agent config resolution + high-level RPC orchestration concerns; next safe split is request/response waiter coordination or terminal stream buffering into dedicated modules.\n- a pre-existing unrelated workspace modification remains in scripts/codex-refactor-loop.sh and was intentionally left untouched.
What changed:
- Added a private sendCorrelatedRequest helper in daemon-client to centralize requestId-correlated response selection.
- Migrated a large contiguous set of RPC methods to the helper: checkout/worktree/branch operations, file explorer/token/icon, provider+speech+commands RPCs, wait_for_finish, and terminal list/create/subscribe/kill/attach/detach.
- Preserved existing behavior including special-case filtering for checkout subscriptionId.
Reasoning:
- daemon-client had repeated type+requestId matching blocks across many methods, increasing maintenance cost and decision-point sprawl.
- This refactor keeps transport behavior unchanged while reducing duplicated control flow and making future RPC additions less error-prone.
Verification:
- npm run -w packages/server test -- src/client/daemon-client.test.ts
- npm run -w packages/server typecheck
- npm run typecheck
Notes for next agent:
- There are still many non-migrated sendRequest callsites in daemon-client (notably agent lifecycle/interaction and some status-typed selectors) that can be moved to the same helper in follow-up refactors.
- I hit TS generic narrowing limits while extracting the helper and solved it via a constrained correlated message type + explicit payload narrowing cast; behavior remains covered by existing daemon-client tests.