Gate the GitBranch icon behind isGitCheckout, and keep the header
skeleton visible until the checkout status query resolves so the icon
doesn't pop in and cause layout shift.
* feat: add server-side TTL for provider snapshots and model list UI
Provider snapshots cached forever, causing newly available models
(e.g. OpenCode Go minimax, glm) to never appear in the picker.
Server: ProviderSnapshotManager now returns stale data immediately
and kicks off a background re-warm when the snapshot is older than
5 minutes. Injectable TTL/clock for testing.
App: Provider settings rows show model count and are tappable to
open a diagnostic sheet with a read-only model list (label + ID).
* feat: add per-provider refresh for models and diagnostic
* refactor: await refresh completion and clean up diagnostic sheet state
- ProviderSnapshotManager.refresh is now async; session.ts awaits it so
the RPC ACK reflects real completion instead of just queuing work.
- Preserve models/modes/fetchedAt on entries during targeted refresh so
the list no longer flashes empty mid-refresh.
- Show "Updated Xs ago" next to the Models count, plus loading and
error states for the list body.
- Split resetSnapshotToLoading into full vs targeted branches.
- Flatten nested ternary in model list rendering into renderModelsBody.
- Drop redundant local refreshing state and cargo useMemo wrappers.
* fix(app): widen isImeComposingKeyboardEvent type to accept optional fields
TextInputKeyPressEventData has isComposing and keyCode as optional, so
Pick<KeyboardEvent, ...> was too narrow and broke typecheck on main.
Add `selectable` prop to the code line Text component so users can
long-press to select and copy text on iOS. On web/desktop this is
already the default behavior via CSS user-select.
Markdown file preview has a similar issue but requires a different
fix (custom rules for react-native-markdown-display), left for a
follow-up.
Refs #238
Related #21
Co-authored-by: muzhi1991 <muzhi1991@limuzhideMac-mini-2.local>
* style(app): theme native scrollbars in tool call detail views
Apply CSS scrollbar-color to all ScrollViews in ToolCallDetailsContent
and DiffViewer so the browser scrollbar matches the app's dark theme
instead of showing the default white native scrollbar.
* refactor: split useWebScrollbarStyle into .web.ts/.native.ts platform files
- Native shim returns undefined without calling useUnistyles
- Web variant uses properly typed WebScrollbarStyle interface instead of `as any`
- Add .d.ts for TypeScript module resolution
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
Detect .md and .markdown files in the shared file pane and render them as markdown instead of syntax highlighted code. This makes README-style files easier to read without changing the existing preview flow for other text files or .mdx.
Keep the change focused to the app renderer by reusing the existing markdown stack and adding a narrow extension check with targeted coverage.
hasInitializedRef was set to true before confirming the directory
listing request actually succeeded. On page refresh the WebSocket
client is still reconnecting, so requestDirectoryListing returned
early with "Host is not connected" and the ref stayed true —
preventing any retry once the client became available.
Fix: make requestDirectoryListing return Promise<boolean> and reset
hasInitializedRef on failure so the init effect re-runs automatically
when requestDirectoryListing is recreated after the client reconnects.
Fixes#441
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: file preview shows stale content when re-opening a file
The file content query inherits `refetchOnMount: false` and
`gcTime: Infinity` from the global QueryClient defaults, so
re-opening a previously viewed file never re-fetches its content.
Set `staleTime: 0` and `refetchOnMount: true` on the file preview
query so it always fetches the latest content when the component
mounts.
Fixes#351
* fix: keep original staleTime, only add refetchOnMount
staleTime: 5_000 is reasonable — avoids redundant fetches when
quickly toggling the same file. The actual fix only needs
refetchOnMount: true to override the global default of false.
---------
Co-authored-by: muzhi1991 <muzhi1991@limuzhideMac-mini-2.local>
The prop represents whether the pane/panel is focused, not whether the
text input is focused. Rename across all 5 files to match the caller
names and reduce confusion.
Show a muted "⌘L to focus" hint in the top-right of the message input
when unfocused and empty on web. Registers Cmd+L (Mac) / Ctrl+L
(non-Mac) bindings for the existing message-input.focus action.
On mobile, the server drops live stream events for backgrounded or
unfocused agents. When the user returns, a seq gap triggers a
catch-up fetch whose canonical entries are appended to the tail —
but the head was never flushed. Stale live items from before the
gap stayed in the head and rendered after the newer catch-up entries,
breaking chronological order. Worse, subsequent live events of the
same kind (e.g. assistant_message) would append to the stale head
item, garbling message content.
Now the incremental path in processTimelineResponse flushes head →
tail before reducing canonical entries, keeping the timeline ordered
and the head clean for new live events.
Always render the GitBranch icon regardless of branch availability so
the layout doesn't shift when the branch name loads. Only the chevron
remains dynamic. Also removes vertical padding on mobile to tighten
the gap between the branch row and project subtitle.
The new-worktree button spinner was hidden when the cursor left the
project row. Force visibility when the mutation is pending so users
see the loading state throughout creation.
Route all git checkout queries through WorkspaceGitService instead of
shelling out to git on every call. This makes fetch_agents_request
instant when warm (cached in-memory snapshots) and deduplicates
concurrent cold-start refreshes via ensureWorkspaceTarget.
- buildProjectPlacementForCwd now uses workspaceGitService.getSnapshot()
- worktree-session call sites use WorkspaceGitService
- Deleted getCheckoutStatusLite and its 4 return types from checkout-git
- Fixed cold-start thundering herd: getSnapshot deduplicates via
ensureWorkspaceTarget instead of spawning parallel git processes
- Extracted checkout-diff logic into WorkspaceGitService
- Unify resolveProviderAndModel across CLI and server, supporting
provider/model syntax (e.g. codex/gpt-5.4) for agent runs and schedules
- Add --provider flag to schedule create CLI and both MCP schedule tools
- Add structured debug logging to workspace fetch hydrate, sidebar
refresh, and real-time update paths
- Update orchestrate skill references
* feat: add provider profiles for custom provider definitions
Users can define custom providers in config.json that appear as first-class
entries alongside built-ins. A provider can override a built-in (custom binary,
env, models) or create a new one by extending a base via `extends`. Generic
ACP transport supported via `extends: "acp"`. Providers can be hidden with
`enabled: false`. Hardcoded models merge with runtime-fetched ones.
- Config schema with Zod validation, auto-migration from old format
- Dynamic provider registry replaces static provider lists
- GenericACPAgentClient for user-defined ACP providers
- Snapshot entries carry label/description/defaultModeId over the wire
- MCP tools accept dynamic provider IDs
- App derives provider definitions from snapshot with static fallback
- CLI `provider ls` calls daemon with label column
- Schedule/session rehydration validates providers against registry
* fix: accept any provider status in CLI provider ls test
The test now connects to a real daemon where providers may be loading
or unavailable, not just the static "available" fallback.
* ci: re-trigger CI checks
* style: fix checkout-git.ts formatting to match CI Biome version
* fix: relax provider ls test assertions for daemon-backed responses
The daemon snapshot may not include all 5 built-in providers in CI
(some require external binaries). Assert at least the core 3
(claude, codex, opencode) instead of all 5.
* fix app combobox dropdown positioning flash
* refactor: stop merging models in provider registry, use override models directly
Override models now replace instead of merge with base provider models.
Also add icon/colorTier fallback from definition modes in fetchModes.
* refactor: make provider definitions fully dynamic from server snapshots
Remove static AGENT_PROVIDER_DEFINITIONS fallbacks from the client — providers,
modes, icons, and color tiers now flow entirely from runtime snapshots. Add icon
and colorTier to AgentMode schema so the server can advertise mode visuals
directly. Fix setAgentMode to persist modeId in agent config so the selected
mode survives session reload. Simplify model merging so profile models replace
runtime models instead of prepending.
* docs: add ad-hoc daemon testing guide
The workspace Stack.Screen was missing a getId callback, causing
expo-router to reuse the same screen instance across workspace
navigations. This left stale params — sidebar switching and new
workspace creation both appeared to navigate but the header stayed
on the old workspace (skeleton for new workspaces).
Adds e2e tests for sidebar workspace switching and new workspace
creation flow (URL, sidebar row, header, single draft tab).
* fix: skip Enter key handling during IME composition
During CJK input (Chinese, Japanese, Korean), pressing Enter while
composing characters should confirm character selection, not send the
message. Check isComposing and keyCode 229 on key events before
handling Enter, matching react-native-web's own IME detection logic.
Fixes#241, #264
* fix: scope IME composition guard to web-only handler
Move isComposing check to top of handleDesktopKeyPress so it never
leaks into cross-platform onKeyPress interfaces. This keeps CJK IME
handling explicit and web-gated.
* refactor: remove IS_WEB alias, use isWeb from platform.ts directly
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
Add settings.toggle action with Cmd+, (Mac) / Ctrl+, (non-Mac) bindings.
When on a settings route, pressing the shortcut navigates back; otherwise
it pushes to the host settings route. Also adds comma to the shortcut
key map so the combo string parses correctly.
The wrapper View with onPointerEnter/onPointerLeave is needed to
keep hover state stable when the mouse moves from the row to the
kebab dropdown (which opens in an overlay outside the Pressable).
Without it, the Pressable fires onHoverOut and the kebab disappears.
On native, isNative makes controls always-visible so this is
web-only behavior which is correct (onPointerEnter works on web).
Add `packages/app/src/constants/platform.ts` with canonical gates
(isWeb, isNative, getIsElectron) and refactor all ~100 ad-hoc
Platform.OS checks across 69 files to use shared imports.
Fix sidebar hover crash on native (onPointerEnter → onHoverIn),
fix tooltip to use breakpoint instead of platform detection, make
sidebar action buttons always visible on native where hover doesn't
work, and document the platform gating decision matrix in CLAUDE.md.
* Fix uncaught timeout rejection when archiving agents
* fix(server): centralize git subprocesses under runGitCommand with p-limit throttling
Supersedes #299. Same problem (unbounded git subprocess concurrency causing
zombie accumulation), different approach: a single `runGitCommand(args, opts)`
function backed by `p-limit` instead of a custom semaphore pool.
- Always uses `spawn` (one code path, no exec/execFile split)
- Concurrency configurable via PASEO_GIT_CONCURRENCY env var (default 8)
- 30s default timeout for read-only ops, 120s for mutations
- Centralized error formatting, stdout truncation, process reaping
- Runtime tests verifying throttling, timeout kills, deadlock prevention
* fix: update lockfile signatures and Nix hash
* fix(server): add Windows compatibility to runGitCommand
- shell: true on Windows (resolves .cmd/.bat git shims)
- windowsHide: true always (prevents console window flashes)
- quoteForCmd for args with spaces when routing through cmd.exe
- Tests verify shell/windowsHide behavior per platform
* refactor: use spawnProcess instead of duplicating Windows handling
spawnProcess from spawn.ts already handles shell: true on Windows,
arg quoting for cmd.exe, and windowsHide: true. No need to reimplement.
* fix: non-null assertions for stdio streams in strict build
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
xterm.js sends plain \r for Enter regardless of modifiers. Intercept
modified Enter in the custom key handler so it routes through CSI u
encoding (Kitty keyboard protocol), which Claude Code uses for
Shift+Enter newlines.
Move useBranchSwitcher into BranchSwitcher component so query state
changes only re-render the small header widget instead of the entire
workspace screen. Also memo-wrap AgentStatusBar to skip re-renders
on keystroke.
Server: remove activityAt computation from workspace descriptors (always
sends null for backwards compat) and deduplicate emissions in
emitWorkspaceUpdatesForWorkspaceIds using fast-deep-equal — workspace
updates are now only sent when the descriptor actually changed.
Client: remove activityAt from WorkspaceDescriptor, sidebar entry types,
and baseline sort comparators. Workspace/project ordering is fully
handled by the persisted sidebar order store.
When bulk-closing tabs (e.g. "Close to the left"), the reconciliation
useEffect would re-add tabs for still-active agents before the server
archived them, causing tabs to flicker back then disappear one by one.
Adds hiddenAgentIdsByWorkspace as the inverse of pinnedAgentIds: a
local, in-memory-only override that prevents reconciliation from
re-opening tabs for agents the user just closed. Hidden intent is
automatically cleared when explicitly opening, retargeting, or pinning
an agent tab.