* fix(chat): keep attachments visible after creating agents
Agent creation could receive the canonical user message before the client knew the new agent ID. Hand the local rich presentation to that message and preserve it through later timeline hydration.
* refactor(chat): clarify local message presentation naming
* fix(chat): clear pending state after message handoff
* fix(codex): hide shell launchers from command summaries
Codex only unwrapped POSIX shells from /bin. Commands launched through /usr/bin/zsh therefore exposed the wrapper in the tool-call row.
* test(codex): preserve shell wrapper path coverage
* fix(app): keep stream controls clickable under scroll button
The full-width animated overlay won browser hit testing even outside the circular control. Keep positioning on a pass-through container and animate only the button-sized child.
* test(app): preserve scroll hit-test diagnostics
* test(app): align scroll hit-test fixture deterministically
* test(server): include OpenCode message IDs in subagent assertions
Selectable table cells each installed a window-level gesture recognizer, causing every touch to fan out across the entire table. Keep prose selectable while rendering iOS table text without selection.
* fix(chat): show fork controls for every provider
Real assistant messages require stable identity so provider-agnostic chat actions can address the selected turn. Preserve provider IDs where available and generate stable adapter-owned IDs otherwise.
* test(opencode): expect assistant message identities
* test(pi): cover assistant updates without message start
* fix(providers): preserve late assistant identities
* fix(opencode): track fallback compaction identities
* fix(server): bound shell tool output in timelines
Provider tool output could enter timeline persistence and live streams without a size limit. Bound canonical shell output before coalescing, storage, history hydration, and dispatch so every provider follows the same 64 KiB budget.
* refactor(server): slice oversized tool output directly
* fix(server): cover imported and failed shell output
Nested native stacks fell back to the navigation library's light background during cross-stack swaps. Share the active app surface color across every stack without subscribing route layouts to all theme runtime changes.
* feat(app): expose archive for every workspace
Workspace actions now archive one workspace by id. The daemon remains responsible for backing-specific cleanup, including removing a managed worktree only after its final workspace is archived.
* refactor(app): clarify archive descriptor name
* fix(app): preserve workspace archive safety
* fix(app): allow archive without upstream
* refactor(app): remove stale archive callback
* test(app): cover workspace archive ownership
* fix(server): stop reconnects reviving agents during shutdown
Shutdown previously took its agent snapshot while reconnects and in-flight registrations could still add or repersist agents. Gate ingress and registration first so closed agents cannot return during teardown.
* fix(server): drain late sessions during shutdown
* fix(app): preserve loader behavior in retained panels
Keep the running footer mounted across panel switches. Inactive loaders stop their animation work, then realign to the original wall-clock phase before they become visible.
* perf(app): remove inactive loader wrapper worklet
* perf(app): pause hidden turn elapsed timers
* perf(app): reduce loader animation work
Keep one wall-clock-aligned clock for visible loaders and detach retained
hidden loaders without unmounting them. Preserve the 950 ms sequence and
reduced-motion behavior.
Plain chat views started the history virtualizer even without virtualized rows. Its delayed scroll callback could outlive the mounted view and make the app test job fail during environment teardown.
* fix(app): stabilize streamed chat rendering
Use deterministic Markdown AST keys and preserve unique row identities when an assistant message resumes after a tool. Keep native gesture and plan-card children correctly keyed to avoid unsupported Fabric reconciliation paths.
* fix(app): stabilize retained native panels
Keep retained workspace roots in a stable native order and make active
panels present in the selection commit. Hidden panels now stop expensive
subscriptions and animations without losing mounted state.
* fix(app): harden retained panel activity
Project picker tests had depended on a placeholder changed for fuzzy search. Use a dedicated input test ID so product copy no longer breaks the selectors.
* fix(codex): keep parent turns active during sub-agent work
Codex multi-agent activity is thread-scoped. Treating child events as root
activity could end the foreground turn early and splice child output into
parent messages.
Route legacy and current activity shapes through stable child identities and
preserve message boundaries across live and resumed turns.
* fix(codex): preserve sub-agent details across stream chunks
Keep empty activity paths explicit and normalize the synthetic assistant
boundary for every chunk in the active message.
* fix(codex): route legacy child events by thread
* fix(codex): stabilize sub-agent activity lifecycles
* fix(codex): retain child tool activity and terminal state
* Hide keyboard shortcut badges in workspace menu on native
The archive item's shortcut badge (e.g. Ctrl+Shift+⌫) rendered in the
workspace kebab/context menu on mobile, where there's no physical
keyboard to invoke it. Gate the trailing badge behind !isNative in both
kebab menu variants.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(app): hide native shortcuts in every workspace menu
Workspace menus were duplicated across project and status views, so the status path missed the native gate. Centralize the menu so every sidebar view shares the same shortcut policy.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* perf(app): keep large sidebars responsive
Sidebar rows rescanned every agent for each session update, multiplying work with both workspace and agent count. Build workspace activity once and share the derived sidebar model so updates scale with the underlying collections.
* test(app): use valid agent fixture for workspace restore
* refactor(app): keep sidebar model surface focused
* fix(app): keep mobile sidebars synced with store
Gesture-owned skip flags could swallow the next store sync after a no-op or overlapping gesture, leaving React state and Reanimated shared values split. Always reconcile both mobile sidebars from the panel store after gesture animations.
* test(app): keep sidebar sync coverage pure
* fix(app): keep sidebar close transitions idempotent
* fix(app): keep mobile sidebars stable through interruptions
Independent React and worklet state let stale gesture or animation callbacks
overwrite newer panel commands. Use one revisioned target and one transient
motion runtime so outdated callbacks cannot restore an earlier panel.
* fix(pi): avoid raw images for text-only models
Pi text-only models reject image content, and Pi persists the rejected raw image in session history. Materialize those image prompts to local files and send path hints while preserving raw images for vision-capable models.
* fix(pi): require explicit vision support
Default unknown Pi model capabilities to the text-hint path so raw image forwarding only happens for models that declare image input. Also keep materialized image files in a private per-user temp directory with restrictive file modes.
* fix(pi): harden materialized image temp dirs
* test(server): accept per-user image attachment dirs
* fix(server): use private temp dirs for materialized images
* fix(server): refresh missing image temp cache
* test(server): clean image temp dirs
* feat(app): restore compact Changes diff controls
Keep the folder tree behind a view toggle while preserving the flat file list as the default.
Move secondary diff options into the overflow menu so the Changes toolbar stays compact.
* fix(app): mirror whitespace diff option copy
* fix(app): polish diff controls review follow-up
* fix(android): handle denied voice audio focus
Android can deny voice-mode audio focus while another system audio owner, such as an incoming call, is active. Treat that as an interruption so resume does not crash the app and JS stops voice mode coherently.
* fix(voice): keep interruption state consistent
Make native interruption handling payload-aware so iOS resume events do not stop the shared JS runtime, avoid ending voice mode for duck-only Android focus changes, and roll host voice mode back if capture fails after startup enabled it.
* fix(voice): avoid duplicate interruption handling
Keep exactly one Android audio-focus request active at a time and leave JS capture state untouched for non-terminal interruption events.
* fix(android): abandon blocked audio focus requests
When Android denies or blocks voice audio focus, abandon the pending focus request so delayed focus gain cannot arrive after voice mode has already stopped.
* fix(voice): handle interrupted dictation capture
Stop Android resume from replaying after recording restart fails, and propagate native blocked interruptions into dictation so users do not remain in a stale recording state.
Daemon-advertised hostnames still name new/default hosts, but connection refreshes and re-pairing no longer overwrite labels that users already changed.
Drop advertised MCP output schemas (no supported harness forwards them to the model) and remove the redundant wait_for_agent tool. Core agent tool catalog drops ~20.5k -> ~5.6k estimated tokens; ~24.9k -> ~9k with browser tools. Runtime structuredContent is preserved via server-side text serialization. Also corrects the agent-wait timeout guidance for blocking callers.
* feat(settings): consolidate daemon management into the host page
The app-level Daemon section and the localhost host page rendered the
same daemon card in two places. The host page is now the single home:
the Host sidebar group leads with Overview, and a desktop-only "Enable
built-in daemon" option in the host switcher recovers a removed
localhost connection. Old settings/daemon deep links redirect
(COMPAT-tagged).
* fix(settings): hide daemon recovery option when the server id probe fails
Review feedback: !query.isLoading treated an errored IPC probe as
resolved, so the recovery affordance could show without knowing the
local server id. Replace the resolved-boolean hook with a single
discriminated state hook (loading | error | resolved) — errors hide the
affordance and fall back to the settings root in the redirect. Also
collapses the two query subscriptions per caller into one.
* fix(settings): keep daemon recovery reachable when no hosts remain
Review feedback: removing localhost as the only host takes the settings
sidebar down its no-host branch, which never mounts the host picker —
so the "Enable built-in daemon" option was unreachable in the most
common recovery case. The sidebar now owns the single hook call, passes
it to the picker, and renders the same action as a sidebar row next to
"Add host" when no hosts exist.
* test(e2e): update settings host header to Overview
Test drift: the host section's sidebar/header label was intentionally
renamed to "Overview"; the spec still asserted the old "Host" title.
Also assert the retired app-level Daemon row stays absent.
* feat(schedules): per-run workspaces, isolation and archive controls
Scheduled agents never appeared in the sidebar: each schedule reused one
stamped workspace and archived the run's agent on completion. Now every run
mints its own workspace — the entity the sidebar shows — with per-schedule
controls for isolation (local or worktree) and whether to archive that
workspace when the run finishes.
The schedule form is rebuilt on a non-React form model (open/commands/close)
to end the flicker, edit-into-create contamination, and multi-host hydration
bugs that came from effect-choreographed shared state. That lands the reusable
foundations it needed: a form kit over a single control-geometry owner, a
data-access taxonomy (replica/snapshot/fetch) with real load-state semantics,
and lint/boundary guardrails so new screens inherit the paved road.
Also fixes desktop combobox popovers truncating options when the trigger is
narrower than the content. New schedule protocol fields are optional on both
create and update, so old and new clients/daemons stay compatible.
* test(app): complete mock themes for control-geometry tokens
switch and terminal-profile-edit-modal now style through
createControlGeometry, which reads theme.borderWidth[1], theme.opacity[50],
and theme.colors.borderAccent. The hand-rolled mock themes in these two
suites lacked those tokens, so the eager StyleSheet.create mock threw at
import (collection error), not on any assertion. Add the missing tokens.
* fix(schedules): address review findings on run cleanup, edit isolation, load error, sheet dismiss, and preference hydration
- Server: archive the run's workspace even when agent creation fails, so a
misconfigured provider no longer orphans a sidebar workspace/worktree.
- Edit form: keep a stored worktree isolation while worktree eligibility is
still resolving, instead of silently downgrading a saved schedule to local.
- Schedules screen: show the load error/retry UI when every host fails, rather
than spinning forever behind the loading branch.
- Form sheet: fire the parent onClose on native gesture/backdrop dismiss so the
sheet closes instead of re-opening.
- Form model: apply late-loading saved preferences to untouched create-mode
fields (never to edited schedules or user-modified fields).
* fix(schedules): review batch — reconnect resubscribe, worktree prune, Android dismiss, cadence and provider edit safety
- Push-router: re-send terminal and checkout-diff subscriptions after a
reconnect; the old per-hook effect resubscribed on isConnected and the
extracted router did not, so daemon restarts silently stopped terminals_changed
pushes (root cause of the terminal-activity e2e failures).
- Server: pass the run's source repo root when archiving worktree runs so the
worktree is unregistered (git worktree remove/prune), not just deleted; also
archive the workspace when agent creation fails even with archive-off (an
agentless workspace has nothing to inspect).
- Sheet: RN Modal onDismiss is iOS-only — notify dismiss once-per-close on the
Android native path too; replace the JSDOM component test with a real
Playwright dismiss spec and inline the trivial dismiss decision.
- Form model: editing an interval schedule no longer rewrites its cadence to
cron unless the cadence UI is touched (90-minute intervals have no cron
equivalent); switching projects clears stale provider/model state and gates
submit until the new host's snapshot resolves.
* chore(lint): drop custom lint wrapper and boundary script, plain oxlint
The import bans stay in .oxlintrc.json (oxlint-native no-restricted-imports).
The receiver-sensitive checks the custom script enforced are not worth a
parallel lint pipeline; if they come back it will be as oxlint rules.
* fix(schedules): crash-safe run cleanup, capability-gated run options, resilient mutations
- Persist the run's workspace/agent ids on the running-run record so a daemon
crash mid-run can be recovered: restart archives the interrupted run's
workspace under the same policy as normal cleanup (agentless always archives,
otherwise archiveOnFinish is respected).
- Hide Archive on finish (and omit archiveOnFinish/isolation from payloads) on
hosts without workspaceMultiplicity — an old daemon ignores the fields, so
offering the switch there would lie.
- Optimistic pause/resume/delete no longer throw when the schedules cache holds
a still-connecting entry alongside loaded data.
- Mode field is gated on provider mode options, not a selected model, so
model-less providers can pick their advertised modes.
* fix(diagnostics): include desktop app logs
Settings diagnostics now collect the Electron main-process log separately from daemon.log so desktop startup and environment-probe failures are included in app reports.
* refactor(desktop): share log tail reader
* fix(diagnostics): surface desktop app log read errors
* refactor(diagnostics): tighten desktop report API
* fix(desktop): retry login shell env resolution
Finder-launched desktop sessions can inherit launchd's minimal PATH if the login shell probe times out or never prints its marker. Retry once with a non-interactive login shell so profile PATH still has a chance to apply.
* fix(desktop): cap login shell probe retry budget
* fix(desktop): repair csh login env retry
* test(desktop): pin login shell env retry platform
* refactor(desktop): use object params in shell env helpers
* fix(worktree): preserve PATH for lifecycle scripts
Lifecycle command strings were running through login shells, which could rewrite PATH before setup commands saw the daemon environment. Use the shared stable script-shell helper without login startup files, and route related project-authored command strings through it.
* fix(worktree): ignore Bash startup env hook
Project command strings should not source shell startup hooks before they see Paseo's supplied environment. Strip BASH_ENV from lifecycle, loop verify, and ACP string-command shells and pin the behavior in setup tests.
* fix(worktree): resolve Bash through PATH
* fix(worktree): preserve Windows cmd command strings
* test(acp): expect Windows cmd terminal strings
* feat(browser): generalize browser automation hosts
Rename the browser automation client capability to a host-neutral payload, advertise supported commands, and route tab-scoped commands by browserId affinity so future browser hosts can register without another protocol shape change.
* fix(browser): harden browser host registration
Reject unusable browser host capabilities, clean up pending requests when a host registration is replaced, and defer list_tabs affinity updates until every host in the aggregation succeeds.
* fix(browser): preserve host tab affinity across reconnects
Keep stranded tab ownership tied to the disconnected host so the same host can reclaim open tabs after reconnect, and tolerate future command names in browser_host capability payloads while retaining only known commands.
* fix(docker): build images from source
Collapse the npm-install and source-build image paths into one Dockerfile so every Docker image is built from the checked-out workspace packages. Include procps in the runtime image because provider process cleanup depends on ps through tree-kill on Linux.
* fix(docker): include native speech packages
* fix(docker): cover source build inputs
* fix(docker): ignore local agent artifacts
* fix(browser): stop new MCP browser tabs stealing focus
browser_new_tab used openTabFocused, switching the workspace UI to
the new tab and interrupting whatever the user was looking at.
Automation commands don't need visual focus to work: webviews stay
resident and paintable regardless, so open the tab in the background
via openTabInBackground instead.
* fix(browser): stop background MCP tabs hijacking Reload
openBrowserTabForRequest still called setWorkspaceActiveBrowser for
tabs opened in the background, so the desktop Reload/Force Reload
menu (which targets getMostRecentWorkspaceActivePaseoBrowserWebContents)
could reload an agent's hidden tab instead of whatever the user was
looking at. Automation commands never needed this: every tab-scoped
MCP tool takes an explicit browserId, and the existing
useSyncWorkspaceActiveBrowser hook already keeps "active browser" in
sync with real UI focus. Drop the redundant call.
* fix(providers): show Claude Ultra Code by capability
Expose Ultra Code for xhigh-capable Claude models and keep first-party Claude model metadata in one manifest so future model updates do not need scattered feature lists.
* fix(providers): preserve Claude fast mode aliases
Normalize versioned Claude model IDs through the manifest before checking fast-mode support so dated Opus IDs keep the same feature surface as their base model.
* fix(providers): keep Claude fast mode first-party
Anchor Claude model normalization so custom settings models that merely contain first-party IDs do not inherit manifest-only fast mode.
* fix(providers): preserve Claude runtime aliases
Keep Fast Mode capability checks strict to manifest IDs while allowing provider-form Claude runtime model strings to resolve known model metadata.
Resident nodes restored from the parking host could carry stale offscreen styles after hot reload, so reapply the permanent parking path before returning them.
Delete the renderer capture prep handshake and keep screenshots on the serialized invalidate/retry path. The capture harness now defaults to the production P1 attach-off parking check.