* fix(claude): stop probing context usage after turns
Use Claude stream/result usage for parent context tracking, and update the meter from compact boundary post tokens. Subagent usage reports stay out of the parent context meter.
* fix(claude): clear compact context fallback between turns
* Fix OMP slash command discovery
Keep Pi on get_commands while allowing the OMP-backed provider to use get_available_commands. Both providers reject the other command without echoing an id, so the adapter needs an explicit command-list RPC setting instead of a timeout-based fallback.
* Address OMP command test review
* Fix OpenCode helper server ownership
Own OpenCode helper generations as soon as spawn returns so startup timeout and shutdown clean up through the manager/reaper path. Collapse the OpenCode runtime wrapper so the provider talks to the server manager directly.
* Protect dedicated OpenCode server startup
Native state restore can resume at the host route instead of the root index, so host home now resolves the remembered workspace itself. Keep shared route policy outside src/app so Expo Router does not register it as a page.
* fix(server): align provider catalog diagnostics
* fix(server): preserve catalog profile models
* refactor: unify provider catalog discovery under AgentClient.fetchCatalog
Remove listModels/listModes from AgentClient and fetchModels/fetchModes
from ProviderDefinition. All provider runtime discovery now flows through
a single fetchCatalog(options) => ProviderCatalog API.
ProviderSnapshotManager.listModels/listModes remain as cached snapshot
conveniences only. Provider implementations (acp, codex, opencode, pi,
claude, mock) updated accordingly; agent-manager default model resolution
now calls fetchCatalog.
Reshape step toward issue pi-model-list-empty.
* refactor: remove remaining provider listModels/listModes runtime API residue
Migrate remaining AgentClient/provider-client implementations and tests to
fetchCatalog. Remove obsolete ListModelsOptions/ListModesOptions interfaces.
Update ProviderSnapshotManager.getProviderDiagnostic to materialize clients
via ensureClient(provider, definition) so diagnostics self-heal the settings
sheet instead of failing when providerClients[provider] is absent.
Allowed to remain: ProviderSnapshotManager.listModels/listModes as cached
snapshot readers; protocol/client legacy list_provider_models names; unrelated
local helper in create-agent-mode.
* test(server): repair test clients after fetchCatalog refactor
- Restore TestAgentClient.fetchCatalog with proper model list and resumeSession.
- Restore NativeArchiveRecordingClient and EnvProbeAgentClient removed during refactor.
- Fix ResumeCaptureClient.fetchCatalog and resumeSession.
- Fix stream-coalescing TestAgentClient.fetchCatalog shape and isAvailable.
- Mock accessible OpenCode provider in full-access mode tests so fetchCatalog does not throw.
* refactor(app): update stable discovered models ref directly during render
* feat(app): add toolbar and loading states to the GitHub PR panel
Adds a toolbar under the PR title with a refresh button (refreshes git
and GitHub status + timeline in one tap), an always-visible View Pull
Request button, and a Merge control with a method dropdown that is
disabled-with-reason when the PR can't be merged yet.
The merge control reuses the existing git-actions policy verbatim via a
new buildPrPanelMergeActions selector fed into GitActionsSplitButton, so
no merge readiness/squash/auto-merge logic is duplicated.
Also replaces the empty-state flash with skeletons: a full-panel
skeleton while PR status loads and an activity skeleton while the
timeline loads, with empty states shown only when loaded-and-empty and
an error state with retry.
* refactor(app): tidy PR panel skeleton (i18n, shared pulse, index)
Addresses review feedback on the PR panel work:
- Localize the skeleton's "Checks" label via t() instead of hardcoding.
- Hoist the shared SkeletonPulse + useSkeletonPulse into activity-skeleton
so pane-skeleton reuses them instead of duplicating the pulse driver.
- Export PullRequestPaneError/Skeleton from the module index and import
them through it, matching the module's existing public surface.
* test(app): remove mock-based PR pane test
* fix(app): simplify PR panel toolbar
* fix(app): report PR panel retry failures
* fix(app): preserve composer mode preferences
Treat undefined provider preference updates as no-ops, and keep saved mode selections instead of silently defaulting them when provider metadata is incomplete or does not list the saved mode.
* fix(app): document mode preference preservation
* fix(app): keep compact explorer mounted while open
* fix(app): share compact explorer host state
* fix(app): refresh diff row metrics after font changes
* refactor(server): move checkout write handlers into CheckoutSession
The checkout read side moved into session/checkout/checkout-session.ts in
#1644; the 17 inline write/PR/stash handlers stayed on the Session god-object.
This carves them into the same subsystem behind an expanded CheckoutSessionHost,
so dispatchCheckoutMessage becomes pure delegation and session.ts drops ~800
lines.
Moved into CheckoutSession (verbatim, only this.X -> this.host.X rewiring):
switch/rename branch, commit, merge, merge-from-base, pull, push, PR
create/merge, github auto-merge/check-details, PR status/timeline, github
search, stash save/pop/list, plus resolveCurrentPullRequest and the
PR-timeline helpers.
CheckoutSessionHost gains the Session-owned, non-checkout collaborators these
handlers orchestrate (notifyGitMutation, emitWorkspaceUpdateForCwd,
handleWorkspaceGitBranchSnapshot, renameCurrentBranch, checkoutExistingBranch,
and the LLM commit/PR-text generators); paseoHome/worktreesRoot join the
options bag. GitMutationRefreshReason moves to utils/checkout-git.ts so both
the shell and the subsystem share one canonical type.
session.test.ts checkout tests now drive the handlers through the public
handleMessage boundary instead of reaching into private methods; new mock-free
fake-host tests cover the moved handlers directly in checkout-session.test.ts.
Behavior-preserving: full session.test.ts + checkout-session.test.ts green,
typecheck/lint/format clean.
* refactor(server): extract chat/schedule/loop handlers into ChatScheduleLoopSession
Move the 7 chat/*, 9 schedule/*, and 5 loop/* request handlers, the three
rpc-error emitters (kept separate), and toScheduleSummary out of the Session
god-object into session/chat/chat-schedule-loop-session.ts, mirroring the shipped
CheckoutSession/VoiceSession deep-module-with-narrow-Host pattern. The least-coupled
remaining domain: stateless request/response over chatService/scheduleService/
loopService, reaching the shell only through a narrow ChatScheduleLoopSessionHost
(emit + agent-roster reads + the mention-fanout send). session.ts drops ~590 lines.
The two dispatch switches collapse into one: schedule/* was previously reached only
via the chat dispatcher's default fall-through arm — now all 21 types are explicit
delegation cases, removing that fragility.
Behavior-preserving: a new routing test drives the real Session.handleMessage for all
21 types (guards against a silently-dropped case), green before and after the move; a
collocated subsystem test covers the mention-fanout send seam, the fanout-limit error
code, the self->agent target remap, and the toScheduleSummary runs-stripping.
* refactor(server): extract provider catalog handlers into ProviderCatalogSession
Move the 8 provider-catalog handlers (model/mode/feature listing, providers
snapshot pull/refresh, diagnostic, usage) plus emitProviderDisabledResponse,
getProviderSnapshotEntryForRead, buildDraftAgentSessionConfig, the two mode-icon
downgrade helpers, and the providers_snapshot_update PUSH wiring out of session.ts
into session/provider/provider-catalog-session.ts behind a narrow
ProviderCatalogSessionHost seam — the same deep-module pattern as ChatScheduleLoopSession,
CheckoutSession, and VoiceSession.
The PUSH (start) and every PULL gate provider visibility and downgrade mode icons
through the SAME injected predicates (isProviderVisibleToClient + supportsCustomModeIcons),
both reading appVersion/clientCapabilities live — the COMPAT invariant the shell could
only enforce by code proximity before. dispatchProviderMessage collapses to delegation;
start()/dispose() wire into subscribeToOptionalManagers/cleanup at the same ordinal.
session.ts drops ~372 lines. Behavior-preservation gate (provider routing through the
real Session.handleMessage) green before and after; full session.test.ts 115/115; new
collocated subsystem test covers the PUSH/PULL parity seam, the disabled-provider path,
and the usage/feature error envelopes.
* refactor(server): extract workspace file-access handlers into WorkspaceFilesSession
Move the file-explorer, file-upload, file-transfer-frame, project-icon and
file-download-token handlers out of session.ts into
session/files/workspace-files-session.ts behind a narrow WorkspaceFilesSessionHost
seam (emit/emitBinary/hasBinaryChannel), matching the deep-module-with-Host shape
used by CheckoutSession/ChatScheduleLoopSession/ProviderCatalogSession.
The new module owns the FileUploadStore and is injected the daemon's
DownloadTokenStore; the shell's dispatch cases and the file_transfer binary route
collapse to delegation. The five handlers touch no workspace-git observer,
registry or subscription state, so this is a clean self-contained slice of the
workspace domain. session.ts drops 240 lines (7316 -> 7076).
Behavior-preservation tests (file explorer list/read/binary, download token
success + empty-cwd, project icon, upload round-trip) added to session.test.ts and
run green against the pre- and post-refactor Session; a collocated
workspace-files-session.test.ts exercises the module via a fake host with real
upload/download stores (no module mocks).
* refactor(server): extract agent-config setters into AgentConfigSession
Move the four agent-config setters (set_agent_mode/model/feature/thinking)
out of the session god-object into session/agent-config/, collapsing their
four near-identical try/log/emit envelopes into one applyConfigChange helper
behind a narrow { emit } host plus an AgentConfigOperations port.
Behavior-preserving: the dispatch cases delegate unchanged, setAgentModeCommand
still routes the mode path, and model/feature still emit no notice. Adds the
previously-absent failure-envelope coverage: 8 gate tests in session.test.ts
(success + forced failure per setter, asserting the *_response envelope and the
activity_log-before-response ordering) green before and after the carve, plus a
collocated agent-config-session.test.ts (typed fake operations + fake host, no
module mocks). session.ts drops ~173 lines.
* refactor(server): extract project-config handlers into ProjectConfigSession
Move read/write_project_config handlers, their failure emitters, and the
root-resolution helpers (resolveKnownProjectRootForConfig, canonicalizeConfigRoot,
stripTrailingPathSeparators) out of session.ts into session/project-config/, behind
a narrow { emit } host plus a projectRegistry port — matching the established
session/<domain>/ pattern. Also corrects a misfiling: project-config was dispatched
inside dispatchAgentConfigMessage despite being its own domain.
session.ts drops 155 lines (6903 -> 6748). The handleMessage-driven gate in
session.test.ts (project config RPC authorization) is green before and after; new
collocated project-config-session.test.ts covers the subsystem with a fake host and
fake registry over a real filesystem (no module mocks).
* refactor(server): extract daemon status/pairing handlers into DaemonSession
Move the two daemon.* RPC handlers (daemon.get_status, daemon.get_pairing_offer)
out of session.ts into session/daemon/daemon-session.ts behind a narrow
{ emit } host + injected reads (paseoHome, serverId, daemonVersion,
daemonRuntimeConfig, listProviderAvailability). These were misfiled inside
dispatchAgentConfigMessage despite being daemon- not agent-scoped; the slice
also removes the now-dead serverId/daemonVersion/daemonRuntimeConfig fields and
the getPidLockInfo/generateLocalPairingOffer imports from the god-file, and
collapses the inline daemonRuntimeConfig type into a named DaemonRuntimeConfig.
The daemon-config get/set handlers stay inline: daemonConfigStore is a
listener-bearing, cross-domain hub (read by structured-generation,
websocket-server, bootstrap, archive-if-safe), so moving it would double-own it.
session.ts: 6748 -> 6662 lines. Gate: new session.test.ts daemon round-trips
(status success + listing-rejects fallback, pairing relay-disabled) pass before
and after; new collocated daemon-session.test.ts (fake host + injected reads
over a real temp dir, no module mocks) 4/4. typecheck/lint/format clean.
#1640 extracted the voice subsystem to server/voice/ (a peer of session.ts);
#1644 extracted the checkout read side to session/checkout/. The two carves
landed in inconsistent homes, so there was no single place to find "what has
session.ts been carved into."
Relocate server/voice/ -> server/session/voice/ so all session subsystems share
one home alongside session/checkout/. Pure relocation: git-tracked renames with
import-depth bumped one level (../ -> ../../); the only external touch point is
the import path in session.ts. Behavior preserved verbatim — the 17 voice unit
tests pass unchanged before and after.
Also record the actual shipped layout and the next carve (the checkout mutation
handlers still inline in session.ts -> session/checkout/checkout-session.ts) in
the decomposition plan, and fix its now-stale voice path.
Extract the checkout read & live-stream side (status, branch validate/suggest, manual refresh, diff + status subscriptions) out of session.ts into session/checkout/checkout-session.ts behind a narrow CheckoutSessionHost seam and a CheckoutDiffSubscriber port. session.ts -234 net lines; 12 unit tests via injected fakes (no module mocks); 6 session tests moved onto the public handleMessage path. Also fixes a pre-existing tilde-resolution bug in the refresh handler (TDD).
* refactor(app): move workspace setup-status fetch into the setup store
The workspace screen ran a fetch-once-and-store workflow inline in a
useEffect, deduped by a hidden requestedWorkspaceSetupStatusKeyRef state
machine and a manual cancellation flag. The fetch + dedup + response
validation only existed inside the component, so it could only be
exercised through E2E.
Move the workflow into useWorkspaceSetupStore as an idempotent
ensureSetupStatus action with the daemon client injected as a port. The
store owns the in-flight dedup (requestedKeys) and clears the marker on
error/removal so a later attempt retries. The component effect now just
delegates; the ref and cancellation flag are gone.
This makes the workflow unit-testable with a fake client (no mocks) and
is the first slice of pulling business logic out of the workspace-screen
god component.
* fix(app): release the setup-status in-flight marker on every settle
Greptile P1: when fetchWorkspaceSetupStatus returned a null snapshot or a
mismatched workspaceId, the key stayed in requestedKeys forever, so a later
mount could never retry — a regression versus the old component-scoped ref,
which reset on remount.
Make requestedKeys a pure in-flight marker: add it before the fetch and
release it in a finally once the request settles (success, ignored, or
error). A settle that stored no snapshot leaves no marker, so the next call
retries; once a snapshot lands, the snapshots[key] guard prevents redundant
refetches. This also lets removeWorkspace/clearServer drop their now-redundant
requestedKeys pruning.
Adds tests for retry-after-null-snapshot and retry-after-mismatched-workspace.
* refactor(server): extract voice mode subsystem into VoiceSession
session.ts was a 10.5k-line god object and the most-churned file in the
repo. ~1,100 of those lines were an entire voice/audio subsystem — the
STT/TTS/dictation managers, the barge-in audio-buffering state machine,
voice-turn orchestration, and the MCP voice bridge — interleaved field by
field and method by method with workspace/git/agent/provider concerns.
Move the whole subsystem into a new VoiceSession deep module. Session now
holds one `voiceSession` field, constructs it once, and delegates the
voice/dictation/abort message types, the permission auto-allow gate, and
cleanup to it. VoiceSession owns all 18 voice fields and ~25 methods and
reaches back into the agent run only through a narrow VoiceSessionHost
seam (emit, loadAgent, reloadAgentSession, sendSpokenInput,
interruptAgentIfRunning, hasActiveAgentRun).
Behavior is preserved (verbatim method moves); deleting voice-session.ts
now removes the feature mechanically. The voice unit tests drive the
VoiceSession boundary instead of reaching into Session internals, so
future voice tests can construct a VoiceSession with a fake host.
session.ts: 10,468 -> 9,272 lines.
* refactor(server): move VoiceSession into voice/ and test it at the boundary
Address Greptile review on #1640:
- Move voice-session.ts into the existing voice/ subdirectory, alongside
voice-turn-controller.ts, instead of adding another peer to the 30+ file
server/ directory. The directory now carries the domain.
- Add voice/voice-session.test.ts beside the module: it constructs a
VoiceSession with a fake VoiceSessionHost and drives it through the public
API (handleSetVoiceMode, then turn-detection/STT events), asserting on the
host seam (sendSpokenInput) and emitted messages. No Session, no private
field/method access.
- Remove the voice tests from session.test.ts that reached through Session's
private voiceSession field into VoiceSession internals.
Same three behaviors are covered (streaming final -> agent, finalization
timeout empty path, low-confidence filtering); driving handleSetVoiceMode
additionally exercises the enable path the old tests bypassed.
Covers providers showing "Not installed", the login-shell/PATH
mismatch behind agents and terminals not seeing your commands, and
restarting the daemon after config changes.
Commit 25252d1b8 added AgentManager.unarchiveSnapshot, now called from
the shared unarchiveAgentState helper. The hand-rolled manager doubles in
the session-workspaces and import-sessions suites were not updated, so the
refresh/import RPC paths hit an unstubbed method and failed. Add the
method to those doubles; no production change.
The wire schema already allowed completed-turn usage, but rejected active usage updates before clients could see them. Accept usage_updated payloads and preserve turn ids on streamed turn events.
Sidebar project lists can be partial while hosts or workspace data are changing. Preserve collapsed preferences instead of treating a temporarily missing project as a request to expand it.
Unarchive provider storage before clearing Paseo's archived flag so failed native restores keep the snapshot archived. Keep initial history sync errors visible instead of falling back to the loading state.
Task-level usage reports processed work, not the parent session's active context. Prefer current context data and parent stream usage before falling back to result totals.
Record provider-owned helper processes (currently the OpenCode `serve` helper) in a daemon ledger and reconcile it in the background on startup: terminate validated leftovers, drop dead or PID-reused records without killing anything, and keep a record whose process can't be inspected for the next reconcile. Termination stops as soon as the process exits instead of always escalating to SIGKILL.
Calm the usage popover and settings card: smaller, muted provider logo
and name, more space between the header and the usage bars.
The popover divider used the `border` token, which equals the popover
background in dark themes and was invisible. Switch it to `borderAccent`
(the token the popover outlines itself with) and span it edge to edge.
Shrink the context meter ring and, while a session is active but usage
hasn't arrived yet, render a track-only ring in the final footprint so
the real ring fades in without shifting siblings. Agents that never
report usage still render nothing.
Expose the workspace id from worktree creation and let agent creation attach to an existing workspace, so callers can split worktree setup from agent launch without reusing the caller workspace.
Keep worktreeSlug as the worktree path slug and add branchName for branch-off targets.
Resolve exact suffix path queries before falling back to broad search, and keep broad hidden-directory traversal limited to known workspace config dirs.