Keep 0.7.2 apps working with 0.8 daemons - #4443
Merged
Merged
Conversation
Gate plugin timeline rows and blocked workspace setup statuses per client capability so app-store version lag does not make existing agents unloadable. Preserve canonical pagination and document the intended Hub provider-mode authority.
Contributor
|
| Filename | Overview |
|---|---|
| packages/server/src/server/session.ts | Centralizes capability-aware filtering of timeline items and per-socket adaptation of blocked workspace setup messages. |
| packages/server/src/server/selective-timeline-delivery.e2e.test.ts | Adds mixed-capability coverage across live delivery, fetches, provider subagents, rewind replay, and setup status; the previously reported test-style violations are resolved. |
| packages/protocol/src/client-capabilities.ts | Defines optional capability keys for the two v0.8 wire additions. |
| packages/client/src/daemon-client.ts | Makes current clients advertise support for plugin timeline items and blocked setup states. |
| packages/protocol/src/messages.wire-compat.test.ts | Documents and verifies the legacy incompatibility of plugin rows, identity merges, and blocked setup snapshots. |
| packages/server/src/server/wire-compat.test.ts | Verifies per-source setup adaptation and preservation of the canonical snapshot. |
| public-docs/hub/security.md | Clarifies that hub execution authority includes provider-mode configuration that may bypass approval prompts. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
D[Canonical daemon state] --> S[Session delivery boundary]
S --> C{Client capabilities}
C -->|plugin_timeline_items| N[Current client receives plugin rows]
C -->|No plugin capability| L[Legacy client receives filtered timeline]
C -->|workspace_setup_blocked| B[Current client receives blocked state]
C -->|No blocked capability| F[Legacy client receives failed compatibility state]
Reviews (2): Last reviewed commit: "test(protocol): clarify compatibility te..." | Re-trigger Greptile
capitally-dev Bot
pushed a commit
to panrafal/paseo
that referenced
this pull request
Sep 7, 2026
upstream/main: 469d512 Keep 0.7.2 apps working with 0.8 daemons (getpaseo#4443) fork-integration: 5f2480f fork-base: 3cf7930 branches: origin/add-daemon-loopback-password-bypass 0b036d8 origin/add-paseo-deeplink-hash-prompts 11c5a49 colonelpanic8:codex-astra-support 3810a85 ragokan:fix/claude-native-tool-details 1c80e6c stackia:fix/cursor-usage-official-display 625462e cleiter:plan-visible-after-answer 0164602 fidelix:fix/cursor-per-model-thinking 609a05d origin/paseo-ls/add-labels-filter c8a0eae origin/add-search-transcript-terminal 35eaed0 origin/feat/terminal-close-confirm 28cd035 origin/sidebar-grouping-toggle 561e9c3 origin/allowedorigins-domain-patterns 63db022 origin/history-navigation-shortcuts 755edb9 origin/host-selection-pill-color cb19f38 origin/vscode a1c01b8 origin/fix/plugin-host-react-free-sdk-import 08f836b origin/video-file-preview d3dd95c origin/assign-schedule-labels dc94c03 origin/usage-shortcuts f4b005a add-profile-selector-schedules 6b06ef8 fix-duplicate-chat-creation ad785e5 fix/timeline-image-width 8995bd4 colonelpanic8:codex-banked-resets 423dea1
capitally-dev Bot
pushed a commit
to panrafal/paseo
that referenced
this pull request
Sep 7, 2026
upstream/main: 469d512 Keep 0.7.2 apps working with 0.8 daemons (getpaseo#4443) fork-integration: 9b2f233 fork-base: 241616a branches: origin/add-daemon-loopback-password-bypass 0b036d8 origin/add-paseo-deeplink-hash-prompts 11c5a49 colonelpanic8:codex-astra-support 3810a85 ragokan:fix/claude-native-tool-details 1c80e6c stackia:fix/cursor-usage-official-display 625462e cleiter:plan-visible-after-answer 0164602 fidelix:fix/cursor-per-model-thinking 609a05d origin/paseo-ls/add-labels-filter c8a0eae origin/add-search-transcript-terminal 35eaed0 origin/feat/terminal-close-confirm 28cd035 origin/sidebar-grouping-toggle 561e9c3 origin/allowedorigins-domain-patterns 63db022 origin/history-navigation-shortcuts 755edb9 origin/host-selection-pill-color cb19f38 origin/vscode a1c01b8 origin/fix/plugin-host-react-free-sdk-import 08f836b origin/video-file-preview d3dd95c origin/assign-schedule-labels dc94c03 origin/usage-shortcuts f4b005a add-profile-selector-schedules 6b06ef8 fix-duplicate-chat-creation ad785e5 fix/timeline-image-width 8995bd4 colonelpanic8:codex-banked-resets 423dea1 KiraPC:feat/open-in-editor-remote-authority d32b112
12 tasks
12 tasks
omorsi45
pushed a commit
to omorsi45/paseo
that referenced
this pull request
Sep 8, 2026
* fix(protocol): keep 0.7.2 clients working with 0.8 hosts Gate plugin timeline rows and blocked workspace setup statuses per client capability so app-store version lag does not make existing agents unloadable. Preserve canonical pagination and document the intended Hub provider-mode authority. * test(protocol): clarify compatibility test preconditions
hteo1337
added a commit
to hteo1337/paseo
that referenced
this pull request
Sep 18, 2026
* fix(app): keep long agent timelines smooth across autonomous turns
Scope provider tool-call identity to its autonomous turn so repeated IDs remain distinct, lifecycle updates still merge correctly, and native timeline lists no longer receive duplicate keys.
* Let plugins provide coding agents (#4314)
Allow trusted local plugins to register complete coding-agent providers through the existing agent lifecycle. Includes direct and ACP implementations, SVG provider icons, custom timeline items, runnable examples, and public authoring documentation.
Closes #3547
Closes #3556
* Restore reliable macOS desktop updates (#4322)
* fix(desktop): restore macOS app updates
Electron 41's Squirrel helper can leave the update handoff dormant on current macOS, so Paseo quits cleanly but relaunches the old bundle. Electron 44 wakes ShipIt through its XPC service.
Include updater lifecycle events and ShipIt state in app diagnostics so future handoff failures identify the current version, target version, cache paths, and installer output.
* fix(desktop): harden updater diagnostics
Keep each ShipIt file's evidence available when another read fails, carry the service-selected version into updater lifecycle logs, and make clean workspace installs resolve the Electron runtime used by desktop tests.
* refactor(desktop): name updater install options
* refactor(desktop): name updater log payloads
* refactor(desktop): name clipboard payload
* fix(desktop): surface updater lookup failures
* test(desktop): await guest focus transition
* fix(desktop): guard macOS update compatibility
* test(release): validate desktop manifests before publish
* test(desktop): await populated daemon pid file
* fix(providers): preserve state across configuration reloads (#4332)
A provider configuration edit invalidated every cached catalog and could turn healthy providers into false availability errors under the resulting probe burst. Preserve unchanged registry entries, clients, loads, and snapshots, and keep the submitted draft snapshot stable while creation is pending.
* Preserve nested subagent ownership (#4321)
* fix(subagents): preserve recursive provider ownership
Provider events retained enough lineage to identify nested work, but the canonical descriptor discarded the direct owner. Preserve that relationship at ingestion so child timelines and hierarchy remain attached to the process that emitted them, including after history replay.
* test(e2e): clarify nested ownership journey
* test(e2e): separate nested ownership actions
* fix(subagents): retain nested ownership across replay
Wait and interaction events can reference descendants without spawning them. Preserve the original spawning owner and route persisted background notifications through that same relationship after history reload.
* test(subagents): hide provider turn plumbing
* fix(subagents): scope nested tracks to their host
Plugin-provided icons are host-specific. Carry the panel host through nested child tracks so the rebased ownership UI resolves the same provider presentation as its parent.
* fix(workspaces): preserve launched terminal focus
* Hide empty ACP sessions from session import (#4335)
* fix(server): filter empty ACP import sessions
ACP metadata probes can leave persisted sessions that later appear in import results. Replay available session history to exclude truly empty entries while retaining sessions whose history is unavailable, and close probe sessions before process teardown.
* fix(server): bound ACP session history loading
Keep import enrichment inside the provider listing deadline when session histories are inaccessible. Catalog abort cleanup now also waits briefly for an in-flight session creation so the resulting probe session can be closed.
* test(server): avoid wall-clock ACP assertion
Verify the history budget through the number of attempted protocol loads instead of elapsed process time, so scheduler delays cannot make the regression flaky.
* fix(server): allow slower ACP history loads
Give native session history more time to replay while retaining a shared enrichment budget below the provider listing deadline.
* fix(server): give ACP history replay enough time
Use the provider RPC timeout scale for native history loading, with a larger shared budget and provider-list deadline. Keep timeout coverage fast and deterministic with a controlled clock.
* fix(server): isolate generous ACP import deadlines
Keep the long listing deadline for the picker’s single-provider requests while preserving the short aggregate deadline for callers that fan out across providers. Restore deterministic coverage of unresolved history loads through fake timers and an injected clock boundary.
* fix(server): preserve ACP aggregate listings
* feat(client): expose agent permission responses through the public SDK (#3985)
* feat(client): expose agent permission responses
* test(client): verify permission response wire message
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* Fix agent reloads blocked by an active session writer (#4353)
* fix(server): keep agent reloads from overlapping session writers
Idle runtimes can still own their persisted session writer. Release the original runtime before resuming and retain failed cleanup for retry; a timeout must not authorize another writer.
* fix(server): honor closes queued during agent reload
Serialize public close with reload so a completed close cannot be undone by replacement registration. Keep archive cleanup inside its existing lifecycle operation.
* Address terminals by workspace ID in the SDK and CLI (#4358)
* feat(terminals): expose workspace ownership through the SDK and CLI
Directory-based terminal creation selected the oldest workspace sharing that path. Expose explicit workspace ownership in the shared public API and CLI, retain ownership and process directories in listings, and acknowledge terminal deletion after teardown.
* fix(file-observer): preserve nested shallow reconciliation scopes
A parent directory scan does not visit its descendants. Collapsing change scopes discarded nested scans and left coalesced native deletions unseen until a full audit.
* test(terminals): extract output waits from end-to-end scenarios
* Restore live sessions immediately after app resume (#4160)
* perf(app): restore live sessions immediately after resume
Foregrounding now reconnects every host without waiting for scheduled backoff, while cached turns, timelines, agents, and workspaces remain internally consistent until authoritative catch-up arrives. Replica persistence is keyed and incremental so ordinary updates avoid whole-directory serialization.
* fix(app): preserve turn ownership through session updates
Stream turn closure now wakes queued-message delivery, while same-turn snapshots retain local cancellation state. Offline liveness remains visible until an authoritative close arrives.
* fix(app): preserve pending chat cache writes and trim resume refactor
Directory baseline replacement must preserve independently accepted timeline changes until the deferred flush. Keep the reconnect and persistence fixes while removing the session-data facade and broad read-hook migration.
* fix(app): initialize reconnect policy from current app state
Initialize visibility before host bootstrap so a background mount cannot leave reconnect enabled. Seed sidebar fixtures through the workspace owner so cache hydration preserves their rows.
* test(app): verify submission settlement across reconnect
Keep submission acknowledgement separate from turn liveness: disconnect preserves the running turn until authoritative catch-up. Wait for confirmed running chrome before exercising queued cancellation so the alternate-send shortcut cannot race turn startup.
* docs: remove cache implementation recap
* Let Hub continue agents through the standard daemon API (#4354)
* feat(hub): continue agents through standard daemon operations
Authorize ordinary agent and workspace recovery operations with hub.execute. Keep conversation routing in Hub and persist generic creation and message receipts so retries cannot silently duplicate work.
* fix(hub): expose custom providers to continuing clients
Allow Hub to advertise provider support independently of the app version. Document the companion continuation policy and migration. Preserve newly observed files in the native watcher inventory so coalesced deletions remain visible to reconciliation, addressing the Windows CI failure.
* fix(hub): allow retries after confirmed local failures
Prepare message loading before recording dispatch intent. Remove failed creation receipts only when creation cleanup completed and no live or stored agent exists. Keep ambiguous provider deliveries fail-closed. Exercise native file observation with real filesystem dependencies.
* Let plugins contribute settings screens (#4357)
* feat(plugins): contribute settings screens and shared controls
* fix(plugins): localize settings and verify shared UI consumers
* test(composer): unmount draft test roots after each case
* test(app): keep launch terminals alive until cleanup
The fixed ten-second profile lifetime could expire before a busy CI browser attached or finished revisiting the composer. Keep the real process waiting for input until project teardown terminates it.
* test(server): await the initial snapshot before a buffered ref event
Watcher setup can read checkout facts before the initial refresh publishes a snapshot. Wait for publication before injecting the event so the test consistently exercises a second refresh.
* fix(desktop): preserve the packaged macOS Dock icon (#4389)
The startup override replaced the bundle icon with a flat PNG even in packaged builds. Limit the override to development so macOS retains the bundle icon appearance.
* fix(app): simplify Changes empty-state copy (#4393)
* Let plugins control modal layout, scrolling and clipboard actions (#4392)
* feat(plugins): support modal body layout, scrolling and clipboard actions
* fix(app): keep native scroll dependencies out of web imports
* fix(nix): sync dependency hash for plugin native types
* test(plugins): express modal QA as user journeys
* Remember model selections across workspace creation (#4401)
* fix(providers): remember model selections across workspace creation
Keep saved model intent independent of catalogue readiness and use the caller-owned host and directory from the first render. Share discovery through provider-owned keys while preserving execution context.
Give each target one committed snapshot, publish actual transitions atomically, and share catalogue content across client directory associations. Regression coverage exercises successful repeated creation and provider lifecycle boundaries.
* fix(providers): preserve shared cache bodies during eviction
Admit directory references with their bodies, retain reference recency during eviction and recovery, and remove the unused persisted index. Refresh known workspace catalogues concurrently.
Make repeated-creation picker states explicit and seed mode tests from the installed provider catalogue, preserving assertions across Codex versions.
* fix(providers): bound discovery independently per provider
Settings refresh repairs all remembered targets, which otherwise starts an unbounded number of discoveries. Admit work after catalogue-key deduplication and keep each provider’s budget independent so a stalled provider cannot block another. Preserve active accounting across configuration changes and settle obsolete queued requests during replacement and shutdown.
* feat(client): expose paseo.projects.subscribe() through the public SDK (#3983)
* feat(client): expose project update subscriptions
* docs(sdk): clarify project subscription initialization
* docs(sdk): document race-free project initialization
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(pi): settle autonomous Pi turns triggered by extensions (#3849)
* Settle autonomous Pi turns triggered by extensions
Treat Pi's agent lifecycle as authoritative for extension-triggered turns that do not have a Paseo provider turn ID. Keep locally handled extension commands immediate, but defer autonomous completion until agent_settled.
* Fail autonomous Pi turns on process exit
* Fix cancellation of autonomous Pi turns
* Synchronize Pi cancellation regressions with queue clearing
* Release Pi interruption state at its terminal boundary
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* Restore cached conversations before reconnecting (#4421)
* fix(app): restore cached conversations before reconnecting
Give each host one timeline write authority so cache restores, live events and fetched pages publish consistent rows and coverage. Persist a moving 40-entry restart page independently of loaded scrollback, and keep native navigation mounted while startup settings hydrate.
* fix(app): recover cache writes after initialization failures
Keep accepted writes pending and retry initialization without another UI update. Publish the storage index only after loading succeeds, while queued host identity changes still settle in order.
* test(app): wait for sidebar and workspace transitions
Viewport intersection and leaving the creation route can precede completed UI transitions. Wait for the layering hit test and selected agent tab before asserting or navigating again.
* fix(app): restore provider loading on native clients
The native AbortSignal lacks throwIfAborted, so successful provider responses failed before reaching every model selector. Preserve both cancellation boundaries using the supported aborted property and the existing query cancellation error.
* Declare and enforce plugin Paseo version requirements (#4430)
* feat(plugins): enforce declared Paseo version requirements
Treat manifests without a Paseo requirement as pre-0.8 plugins so the first breaking plugin release offers an explicit migration path. Check the daemon and app independently before plugin execution, and preserve installed Git revisions when updates are incompatible.
* fix(nix): refresh dependency hash for plugin requirements
* refactor(plugins): validate test manifest inputs and name compatibility input
* fix(app): restore coordinated bottom sheet gestures (#4431)
Supplying a list controls scroll-container ownership, not whether content can move the sheet. Preserve the shared expand-scroll-dismiss interaction for model selectors and plugin lists, with native gesture regressions and updated plugin guidance.
* Let plugins customize agent creation and observe lifecycle events (#4435)
* feat(plugins): expose agent and workspace lifecycle hooks
* test(plugins): verify lifecycle examples with version requirements
* fix(plugins): publish archive hooks for closed agents
* test(plugins): remove diagnostic catch from lifecycle test
* refactor(plugins): define hook handlers in lifecycle module
* Revert #4421 and narrow the cache restore fix (#4436)
* Revert "Restore cached conversations before reconnecting (#4421)"
This reverts commit 1c51b52d079977aea4c9c2b20243d30be112f5f1.
* fix(app): restore provider loading on native
* fix(app): preserve native navigators across appearance changes
Reapply the isolated appearance boundaries and native menu transform origin from #4421 so appearance hydration preserves native navigator and gesture host lifetimes.
* fix(app): retain cached baselines during live updates
Read the timeline concurrently with agent preparation and reconcile display-only cache rows beneath the live head using the existing reducer. Merge cached directory entries beneath live state while preserving the existing checkpoint race guard.
* feat(client): expose provider usage (#4062)
* feat(client): expose provider usage
* fix(client): gate provider usage by capability
* fix(server): support OpenCode 2 in terminal activity plugin (#4300)
* fix(server): support OpenCode 2 in terminal activity plugin
OpenCode 2 rejects V1 hook-object plugins, so the installed
paseo-terminal-activity.js failed to load on every OpenCode 2
session. Ship one definition object that serves both generations:
OpenCode 2 loads id + setup() and ignores the 1.x server entrypoint,
OpenCode 1 loads the same object through server().
* fix(server): preserve terminal activity across plugin generations
* test(server): drain persistence before reload test cleanup
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* Keep 0.7.2 apps working with 0.8 daemons (#4443)
* fix(protocol): keep 0.7.2 clients working with 0.8 hosts
Gate plugin timeline rows and blocked workspace setup statuses per client capability so app-store version lag does not make existing agents unloadable. Preserve canonical pagination and document the intended Hub provider-mode authority.
* test(protocol): clarify compatibility test preconditions
* fix(plugin): loosen the react peer to ~19.1.0 (#4100)
* fix(plugin): loosen the react peer to ~19.1.0
The plugin SDK pins its react peer to exactly 19.1.0, and that pin is
what a downstream `npm i -g @getpaseo/cli` resolves against. So every
user ends up with react 19.1.0 in their global node_modules, where
corporate dependency scanners flag it: the CVE-2025-55182 advisory range
covers 19.1.0 even though the vulnerability itself lives in
react-server-dom-webpack/-parcel/-turbopack, none of which the CLI
installs. The user is then left hand-patching node_modules or
uninstalling Paseo.
Widening the peer to ~19.1.0 lets a downstream install pick the latest
19.1.x patch (19.1.9 today) while this repo stays on 19.1.0, which the
root override still pins.
Keeping the repo itself on 19.1.0 is deliberate. react-native 0.81.5
embeds a 19.1.0 Paper renderer that hard-throws "Incompatible React
versions" on any exact mismatch, and it is reachable from Fabric builds
too: RendererImplementation.js requires the Paper shim unconditionally
in findNodeHandle, unstable_batchedUpdates, sendAccessibilityEvent and
three other entry points, and react-native-gesture-handler re-exports
findNodeHandle. Bumping the app's react is therefore not safe until
react-native moves.
The published CLI tree contains no react-native, so downstream installs
are not subject to that constraint.
Supersedes the earlier 19.1.5/19.1.9 version-bump approach on this
branch, which would have broken the mobile app.
* Update Nix dependency hash for plugin peer metadata
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(app): show workspace plugin panels in Explorer menu (#4446)
* fix(app): show workspace plugin panels in Explorer menu
Let the host-filtered launch catalog identify toggleable views and their targets. Explorer uses the same identity comparison for every view while plugin panels remain workspace-scoped.
* test(app): express Explorer panel regression as user steps
* Keep daemon reconnect running while the app is backgrounded (#4444)
* fix(app): keep reconnect enabled while backgrounded
Visibility should flush cached state and accelerate foreground recovery without disabling normal reconnect backoff. Preserve healthy sockets and retry whenever the OS permits execution.
* test(app): clarify the background reconnect journey
* test(app): name reconnect screenshot helper inputs
* test(app): wait for stable content before delayed-history scroll
* chore(app): keep reconnect QA evidence in the PR body
* Enforce shared, client, and server plugin SDK boundaries (#4347)
* fix(plugins): keep React out of the server plugin host
* fix(plugins): align SDK guidance and tests with runtime boundaries
* fix(plugin): loosen the react peer to ~19.1.0
The plugin SDK pins its react peer to exactly 19.1.0, and that pin is
what a downstream `npm i -g @getpaseo/cli` resolves against. So every
user ends up with react 19.1.0 in their global node_modules, where
corporate dependency scanners flag it: the CVE-2025-55182 advisory range
covers 19.1.0 even though the vulnerability itself lives in
react-server-dom-webpack/-parcel/-turbopack, none of which the CLI
installs. The user is then left hand-patching node_modules or
uninstalling Paseo.
Widening the peer to ~19.1.0 lets a downstream install pick the latest
19.1.x patch (19.1.9 today) while this repo stays on 19.1.0, which the
root override still pins.
Keeping the repo itself on 19.1.0 is deliberate. react-native 0.81.5
embeds a 19.1.0 Paper renderer that hard-throws "Incompatible React
versions" on any exact mismatch, and it is reachable from Fabric builds
too: RendererImplementation.js requires the Paper shim unconditionally
in findNodeHandle, unstable_batchedUpdates, sendAccessibilityEvent and
three other entry points, and react-native-gesture-handler re-exports
findNodeHandle. Bumping the app's react is therefore not safe until
react-native moves.
The published CLI tree contains no react-native, so downstream installs
are not subject to that constraint.
Supersedes the earlier 19.1.5/19.1.9 version-bump approach on this
branch, which would have broken the mobile app.
* Enforce example import ownership including client types
* Update Nix dependency hash for plugin peer metadata
* Group plugin APIs under client and server entry trees
* Resolve plugin type dependencies through declarations
* Validate declarations for implicit plugin type imports
* Preserve optional imports and validate declaration references
* Refresh Nix hash for plugin declaration resolver
* Canonicalize plugin paths consistently on Windows
* Normalize plugin ownership paths across resolver boundaries
---------
Co-authored-by: liujin0506 <liujin0506@qq.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* Accept prerelease Paseo versions in plugin requirements and prepare plugin docs for the 0.8 beta (#4452)
Accept plugin requirements against the runtime version or its stable core while preserving the legacy version boundary. Validate all example manifests, correct the ACP example metadata, and prepare plugin author documentation for the beta.
* Keep the mobile terminal keyboard open while typing (#4469)
* fix(app): keep terminal keyboard focus stable
Clearing the terminal buffer remounted the shared native input, triggering an IME hide/show cycle. Keep text replacement in place and make the composer explicitly reset its editor to preserve multiline layout clearing.
* test(app): verify terminal keyboard continuity on Android
Replace the new JSDOM regression with a real Android harness. It detects focused native input replacement and IME hide/show requests through terminal toolbar controls, and fails with the previous remount behavior.
* docs(changelog): add 0.8.0-beta.1 entry
* chore(release): cut 0.8.0-beta.1
* fix: update lockfile signatures and Nix hash [skip ci]
* fix(release): run release scripts as main on Windows runners (#4474)
The ESM main-module guard compared import.meta.url with "file://" + process.argv[1],
which never matches on Windows (file://D:\a\... vs file:///D:/a/...). On the Windows
desktop release job, scripts/github-release.mjs therefore printed nothing and
gh release upload received an empty release lookup ("release not found"), leaving
v0.8.0-beta.1 without a Windows build. Compare resolved paths instead, shared by
every release script that runs as a CLI.
* fix(acp): keep vendor configuration notifications atomic (#4479)
ACP SDK extension dispatch can finish after a later response, letting vendor config notifications escape commit or rollback depending on pipe chunking. Consume synchronous vendor transforms at the stream boundary before forwarding later responses, and reproduce coalesced single and burst notifications through a real subprocess.
* fix(client): keep daemon connections lean by default (#4470)
Move supported protocol capabilities into client defaults and make session traffic subscription-driven. Preserve timeline readiness and reconnect recovery across SDK, CLI, and plugin consumers.
* Make orchestration setup, profiles, and agent communication discoverable (#4486)
* docs(orchestration): lead with capabilities and practical delegation
* docs(orchestration): correct model requirements and agent list scope
* Fix Codex continuation after workspace archive (#4512)
* Fix native writer ownership when restoring archived agents
* Keep archive QA mechanics in a scenario helper
* Initialize Hub with organization triggers (#4517)
* fix(cli): initialize current Hub triggers
Hub init still authored and deployed project bundles after hosted configuration moved to organization triggers. Generate the self-contained trigger contract and keep project bundle deployment behind the existing --project path.
* fix(cli): harden Hub trigger setup
* docs(hub): align public setup guides with organization triggers
* Allow finished workspaces to be marked unread (#3603)
Restore unread attention on finished workspaces and preserve the reminder when leaving the current workspace. Cover desktop and compact read flows, and retain change-only file paths for deletion observation.
Co-authored-by: Edi Hasaj <34984294+edihasaj@users.noreply.github.com>
* fix(app): preserve plugin provider icons in model buttons (#4549)
* fix(app): preserve plugin provider icons in model buttons
Pass the selected host to the composer glyph so it resolves the same plugin SVG as the model picker. Require the nullable glyph host prop to catch omitted identity at compile time.
* test(app): isolate plugin icon regression setup
* fix(app): show loading state for restored terminal titles (#4553)
Restored tabs render before the terminal list arrives. Mark their titles as loading until metadata is available so they do not briefly display the generic terminal name.
* docs(product): clarify core philosophy and contribution expectations
* docs(contributing): route bugs and product discussions to GitHub
* feat(app): read the changelog in the app instead of the browser (#4576)
What's new opens an adaptive sheet — bottom sheet on compact, centred card on
desktop — that fetches CHANGELOG.md from the repository and renders it with the
app's own chrome. The desktop update callout, the sidebar help menu, and a new
row in Settings → About all open it; the sheet header links out to
paseo.sh/changelog.
The parser commits to two facts only: `##` starts a release and `###` starts a
section. Section titles are read from the document rather than matched against a
list, and everything under a section passes through verbatim to the Markdown
renderer, so a future callout, block quote, code fence or image needs no parser
change. Fences are tracked because a fenced sample is the one thing that can
fool a line scan.
The document is fetched rather than bundled because the app has to describe a
release that shipped after it was built, which is the update callout's whole
purpose. It goes straight to the repository rather than through a host: the
notes describe the app, and routing them through a daemon would make them
depend on which host happens to be connected. Nothing is fetched or parsed
until the sheet opens.
The latest release renders as soon as there is one and the rest of the first
page follows in a transition, so React slices that work around the opening
animation instead of committing hundreds of list items at once.
* feat(plugin): add shared header buttons and composer controls (#4577)
* feat(plugin): add shared header buttons and composer controls
* fix(plugin): isolate button registration imports and settle captures
* test(plugin): express button journeys through domain helpers
* Reduce stalls when opening large diffs (#4574)
* perf(app): reduce avoidable work when opening large diffs
Large snapshots were recursively reconstructed for reference stability, while native scroll worklets captured complete file models. Preserve identity at the data producers and prepare text for the visible window without rebuilding unchanged files.
* test(app): await commit diff query completion without polling
* chore(app): remove profiling artifacts from the source tree
* feat(website): link the community plugin directory
Add a paseo.cafe link to the Plugins card on the landing page and a
section at the top of the related projects doc. The card link is accented
so it reads without hover; the card now takes a list of links instead of
one hardcoded link.
* feat(app): add pane Find to file source (#4586)
Make Find discoverable at the top right of editable and read-only web file panes, using shared controls backed by CodeMirror search. Preserve literal case-insensitive buffer search, keyboard focus and navigation, optional replacement, Undo, and save.
* revert(app): undo file Find merge pending maintainer review
Reverts 69510ac72f41ed50bcc596a8513866dafb1ea8b5. The feature must return to an open pull request for maintainer review.
* fix(app): don't steal focus when workspace creation finishes late (#2986)
Preserve the selected workspace when delayed creation finishes, while still starting the requested agent or terminal. Keep normal navigation when the initiating screen remains active, and preserve newer draft text and context.
Verified with before/after recordings, ten passing real-browser journeys using isolated daemons, focused regression tests, green CI, and independent review.
QA: https://github.com/getpaseo/paseo/tree/qa/workspace-focus-2986
Co-authored-by: Christoph Leiter <christoph@leiter.io>
* Show Codex questions while the agent keeps working (#4587)
* fix(codex): show asynchronous questions and preserve their lifecycle
* fix(server): reject concurrent replies to the same question
* test(codex): use native temporary paths in question fixtures
* fix(claude): open a single compaction marker per compaction (#4391)
Suppress repeated Claude compaction heartbeat markers at the provider and re-arm marker creation at compaction and turn boundaries. Verify emitted events through the public session interface, including subsequent compactions after interrupted or abandoned turns.
Closes #4390.
Co-authored-by: Tom Gringauz <17593920+tomgrin10@users.noreply.github.com>
* docs(hub): document configurable startup timeout (#4601)
* docs(hub): document configurable startup timeout
* docs: keep startup timeout guidance in one place
* fix(server): align Codex Fast with documented model support (#4640)
The prefix allowlist hid Fast for GPT-6 Astra and exposed it for models outside documented Codex Fast support. Use explicit model IDs and avoid claiming a universal usage multiplier.
* docs(hub): explain pending dashboard navigation (#4608)
* docs(hub): remove navigation implementation FAQ (#4645)
* docs(hub): explain pending dashboard navigation
* docs(hub): remove navigation implementation FAQ
* fix(plugin-provider): recover stale sessions after reload (#4629)
* fix(plugin-provider): recover stale sessions after reload
* fix(plugin-provider): auto-recover stale sessions on next prompt
* fix(plugin-provider): retry stale prompt streams
* fix(plugin-provider): recover replacement prompts
* test(plugin-provider): await runtime retirement
---------
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* Document scoped credentials during Hub continuation (#4607)
* docs(hub): explain scoped credentials during agent continuation
* docs(hub): align credential lifetime across trigger guides
* docs(hub): centralize continuation credential lifecycle
* docs(hub): keep continuation guidance focused on agent reuse
* docs(hub): state when shared tokens are revoked
* docs(release): document temporary next and hotfix branches (#4651)
* fix(desktop): restore Linux launcher identity (#4649)
* fix(desktop): restore Linux launcher identity
Electron now derives Linux window identity from the scoped package name before app startup. Pin the existing Paseo.desktop identity in packaged metadata and check the built archive in the packaged-app smoke test.
* test(desktop): declare packaged smoke archive dependency
* fix(desktop): update Nix hash for smoke dependency
* docs(changelog): prepare 0.8.0 stable release
* chore(release): cut 0.8.0
* fix: update lockfile signatures and Nix hash [skip ci]
* fix(desktop): throttle hidden browsers between screenshots (#4646)
* fix(desktop): throttle hidden browsers between screenshots
Keep Chromium background throttling enabled outside serialized captures. Temporarily enable frame production and wait for fresh paint so hidden-window screenshots remain usable without continuously animating idle guests.
* fix(desktop): share screenshot deadline across paint and capture
* test(desktop): wait for browser fixture readiness before sampling
* test(desktop): exit Electron after screenshot verification
CI completed every screenshot assertion but remained alive after app.quit. Exit the standalone test explicitly and keep its deadline active until process shutdown.
* test(desktop): clean browser profile after Electron exits
Explicit Electron exit skips quit handlers, and removing the profile before shutdown lets Chromium recreate it. The launcher owns the temporary profile and removes it after the child exits.
* test(desktop): reuse browser E2E for hidden screenshot regression
Exercise the running app and browser MCP tools within the existing browser journey. Remove the separate Electron fixtures, npm script, and CI step.
* Speed up cold diffs and wrapped text measurement (#4676)
* perf(app): optimize wrapped measurements and checkpoint native layout experiment
Compute full grapheme geometry only for final wrap fragments. Preserve shaped-text search behavior while bounding additive wrap probes. Keep the Android background layout experiment isolated from the normal diff renderer; native renderer integration and iOS bindings remain unfinished.
* perf(server): reduce cold diff generation work
Keep active diff reads owned across invalidation and wait for the initial watcher inventory before cold generation. Batch patch and blob reads while preserving per-file size isolation, bounded output, and daemon responsiveness. Avoid constructing fallback tokens when full-file highlighting already covers both sides.
* test(server): exercise oversized highlighted diff rejection
The single-line fixture now bypasses highlighting under the long-line guard, so it no longer reaches the structured payload limit. Use short dense lines to keep testing oversized rejection and continued socket usability.
* fix(server): queue fresh diffs for forced reads
A forced request following a repository mutation must not reuse an older active build. Invalidate that result and coalesce waiting callers onto one subsequent build. Give the cache a named object input and completed-value type.
* test(server): share deferred diff loader setup
* test(server): name completed diff cache setup
* refactor(server): use aliases for new diff imports
* fix(server): keep shipped diff imports resolvable by Node
The TypeScript build preserves path aliases without a runtime resolver. Restore relative imports for shipped code and declarations, and correct the server import guideline that prompted the regression.
* fix(subscriptions): isolate observers and silence unrequested feeds (#4596)
Give each observation a server-issued ID owned by its physical connection. Preserve snapshot-only reads and combined list-and-subscribe, source-bound replies, lifecycle cleanup and legacy wire compatibility.
* Reload to the project picker after an app error (#4598)
* fix(app): reload to the project picker after a render error
Keep the recovery boundary above the router so Reload starts a new navigation tree at the picker instead of remounting the failed workspace. Preserve saved layouts and normal startup restoration.
* test(app): exercise reload through fixture routes
* fix(daemon): keep lifecycle operations on the selected instance (#4575)
* fix(daemon): keep lifecycle operations on the selected instance
Restart reconstructed launch inputs and address fallback could redirect a home-selected shutdown to another daemon. Let supervisors publish bound endpoints, retain their worker restart contract, and share captured local lifecycle ownership between CLI and Desktop.
* test(daemon): verify bounded desktop readiness and cancellable artifacts
* test(cli): verify reload target hints and durable offline pairing
* test(daemon): align lifecycle fixtures with ownership and targeting
* fix(daemon): preserve reachability when status details time out
Provider discovery can outlast a status request after authentication succeeds. Keep local connection facts separate from unavailable details, while explicit remote queries retain their error outcome.
Migrate CLI and packaged Desktop fixtures to persistent managed configuration and explicit targets. Isolate generic Git observation from forge discovery and reuse disposable CLI homes on Windows.
* test(daemon): observe restart through the worker status RPC
The bounded CLI status observation may omit worker details while an authenticated connection remains healthy. Exercise the app-style restart through its full RPC and retain the replacement observation, with a real slow provider probe covering the distinction.
* fix(desktop): preserve Linux sandboxing wherever the host allows it (#4447)
* fix(desktop): preserve Linux sandboxing wherever the host allows it
Chromium checks its Linux sandbox before main.js, while package installation probes run as root and miss AppArmor restrictions on desktop users. Choose the launch flags before exec and always install the SUID alternative for native packages. Test published artifacts as an ordinary user with namespaces both denied and allowed; never repair permissions in the smoke harness.
* fix(desktop): retain the Linux release build baseline
* fix(ci): allow canceled desktop smoke uploads to stop
* test(desktop): verify the installed RPM sandbox under AppArmor
* fix(ci): resolve packaged desktop dependencies before installation
* chore(desktop): keep QA evidence out of the repository
* fix(app): let touch workspace titles use the available width (#4698)
* fix(app): let touch workspace titles use the available width
Omit trailing stats when the permanent touch menu replaces them. Keep concealed stats in layout only for temporary desktop shortcut hints, preserving hover and shortcut geometry.
* test(app): express sidebar width checks as user journeys
* docs(changelog): clarify macOS version requirement (#4726)
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* docs(changelog): update generated release notes (#4728)
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(agents): restore archived Codex workspaces without losing focus (#4736)
* fix(agents): restore archived Codex workspaces without losing focus
History selection must survive without cached agent records, and reading an archived Codex thread must not acquire an interactive writer. Keep workspace restore explicit, serialize native lifecycle transitions, and leave transcript lifetime with its owner.
* test(app): express Codex restore specs as user journeys
* test(app): finish restore fixture cleanup after teardown errors
* test(app): align restore coverage and await fixture readiness
Workspace restore leaves agents archived until explicit Unarchive. Keep that distinction covered for multiple agents. Compile the recovery fixture before its interaction test starts, and wait for sidebar agent hydration before asserting tab presence.
* fix(app): keep incomplete Markdown formatted while streaming (#4742)
* fix(app): keep incomplete Markdown formatted while streaming
Treat unfinished formatting at the growing assistant-message tail as provisional so delimiters and link destinations do not flash while tokens arrive. Completed messages retain ordinary Markdown parsing.
* test(app): express streaming Markdown checks as user actions
* fix(server): show the right thinking options for each Cursor model (#4180)
* fix(server): probe Cursor thinking options per model
Cursor ACP only reports thought_level for the current model, so the catalog was copying Haiku's Off/On onto Grok 4.6 until the next probe happened to land on Grok.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(server): drop inherited thinking options when an ACP catalog probe fails
A rejected model switch was leaving the probe session's thinking list on the failed model, so Grok could still advertise Haiku's Off/On.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(server): probe per-model thinking when session/new has no thought_level
Composer/Auto as the Cursor ACP default omits thought_level, so the catalog helper skipped every model switch and Grok kept an empty thinking list.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(server): discover model options without changing preferences
Use the provider model catalog extension instead of probing model switches that persist user preferences. Keep provider-specific discovery in each shim and validate extension responses at the boundary.
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* Keep older daemons usable and let clients choose timeline recovery (#4737)
* fix(subscriptions): preserve older-host workflows and explicit timeline recovery
Keep established app and SDK workflows available on older daemons without emulating independent directory filters. Restore live timeline membership without choosing the consumer's history recovery policy. Preserve daemon filesystem paths separately from legacy workspace IDs.
* fix(client): keep legacy workspace replies scoped to the requested page
* fix(agents): keep rejected plans collapsed in conversation order (#4756)
Keep plan proposals at their original timeline position through approval, rejection, and cancellation. Make plan cards collapsible while retaining readable history after follow-up prompts and daemon restart.
* fix(app): restore Android clipboard image pasting (#4758)
Decode clipboard image data URLs locally because native fetch rejects them. Add native-runtime and byte-integrity regression coverage, and clarify empty-clipboard guidance.
* fix(app): keep growing composers below the header (#4824)
* fix(app): keep growing composers below the header
Native keyboard translation left the dock at full height, so intrinsic composer growth could extend behind the header. Reserve the keyboard destination at motion boundaries and bound the composer to its stationary viewport while preserving native input scrolling and animation.
* docs: reuse existing test suites and automation
* fix(agents): prevent OOM from cumulative tool history (#4838)
* fix(agents): prevent OOM from cumulative tool history
Repeated progress updates retained complete historical copies of growing tool output. Retain projected items on ingestion and serve them for every history fetch, preserving source sequence coverage for catch-up. Keep intermediate payloads only in streaming; confine compatibility degradation to child transcripts and overwritten fork checkpoints.
* test(client): expect projected subagent capability in hello
* fix(app): report unexpected child history failures
Keep connection failures recoverable while forwarding unexpected fetch and reconciliation errors to diagnostics. Name observer arguments to keep server and agent identities explicit.
* test(agents): assert projected history after rewind
* docs(protocol): tag projected child compatibility gates
* fix(creation): prevent duplicate workspaces and agents on retries (#4442)
* fix(creation): prevent duplicate workspaces and agents on retries
* test(creation): cover plugin hooks across receipt replays
* fix(creation): recover draft retries without accepting partial workspaces
* refactor(creation): name receipt inputs and tag compatibility gate
* fix(file-observer): track files announced by native change events
* fix(creation): keep initial prompts in daemon-owned creation requests
* fix(creation): reuse connection-owned progress and recovery
* test(creation): adapt handoff gates to combined requests
* ci(desktop): avoid xz compression for smoke packages
* ci(desktop): pass valid RPM compression through FPM config
* refactor(creation): give legacy and modern requests one identity owner
* fix(creation): log observer failures without interrupting creation
* fix(app): restore Android tablet model picker interactions (#4845)
Restore provider/model selection in Android tablet modal windows by giving both native modal hosts their own gesture-handler root. Preserve phone sheet and web portal behavior.
Include native regression scripts, test dependency resolution, and verified CI test corrections. Generated media and logs are excluded. Refs #4475 and #4377; consolidates #4721 and #4771.
Co-authored-by: Mike P. Kuhl <mkuhl@softmachine.at>
Co-authored-by: Cameron Chin <cjcrjc@users.noreply.github.com>
* docs(skills): clarify workspace comparison base selection (#4860)
* fix(ci): consolidate E2E repairs and wait for Metro readiness (#4858)
* fix(desktop): wait for Metro HTTP readiness before launching Electron
Expo briefly listens while probing port availability. A successful TCP connection can therefore launch Electron before Metro starts and leave it exiting on connection refusal. Require the configured renderer origin to report the running packager status.
* test(ci): consolidate fixture and readiness corrections
Await the connection and UI states each journey owns, isolate Git fixture contents before observation, and hold real Markdown frames at asserted checkpoints. Keep creation completion diagnostics explicit without claiming the original Windows failure is fixed.
* test(desktop): describe Metro readiness through HTTP states
Keep protocol mechanics in the fixture and assert readiness only after a successful packager response. Cover non-success status and unrelated HTTP content without inline synchronization in the test body.
* Prevent duplicate catalog choices from crashing agent drafts (#4839)
* fix(providers): keep duplicate catalogs from breaking agent drafts
Normalize model identities before publication and when materializing older
catalogs, preserving the first definition and configured overrides. Keep
explicit default variants separate from the base model selection.
* fix(providers): preserve saved variant identities across resumes
Persist the selection encoding so legacy names remain literal. Refresh provider persistence when a selection changes, before another prompt is sent.
* test(app): let fixtures own catalog journey cleanup
* fix(providers): preserve the existing default variant contract
Keep one Default catalog choice and let the provider resolve an omitted variant. Remove the encoding and persistence machinery; other variant IDs retain their literal meaning.
* test(creation): expose failed completion after conflicting requests
Creation returns a failed snapshot instead of rejecting. Assert the result before its side effects so platform failures report the underlying error.
* test(app): wait for the seed client after daemon restart
The browser and seed client reconnect independently. Wait for the seed connection before issuing post-restart RPCs instead of assuming browser readiness covers both clients.
* test(app): isolate visible prompts and checkout diff fixtures
Inactive tabs retain message DOM, so prompt assertions must inspect visible rows. Ignore the local fixture remote before observation rather than deleting it after the daemon has started tracking the checkout.
* fix(file-observer): preserve final deletions when batching events
A duplicate creation notification followed by deletion was collapsed into an update, hiding final absence from consumers that distinguish deletes. Keep final deletes while retaining update semantics for atomic replacement.
* fix(desktop): wait for Metro HTTP readiness before launching Electron
Expo briefly listens while probing port availability. A successful TCP connection can therefore launch Electron before Metro starts and leave it exiting on connection refusal. Require the configured renderer origin to report the running packager status.
* test(ci): consolidate fixture and readiness corrections
Await the connection and UI states each journey owns, isolate Git fixture contents before observation, and hold real Markdown frames at asserted checkpoints. Keep creation completion diagnostics explicit without claiming the original Windows failure is fixed.
* fix(server): discover fork PRs with URL-valued branch remotes (#4862)
Resolve direct repository URLs as well as named remotes when deriving checkout PR identity, preserving fork-owner filtering and batched polling.
* fix(plugins): expose server settings access (#4674)
* fix(plugins): expose server settings access
* fix(plugins): isolate settings subscriber state
* test(plugins): read persisted settings before clients connect
* test(app): reconnect seed client after old daemon restart
* test(server): drain file creation events before deletion assertions
* test(creation): expose errors when completion fails
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(plugins): transform whole timeline items before grouping and Markdown splitting (#4675)
Send every original tool call and each whole accumulated assistant message through plugin transformers before display grouping or Markdown splitting. Preserve streaming callbacks, original payloads, native Markdown block identities, and chat outline reading position. Reject legacy fragment caches and retain failing-first plugin-interface and browser regressions.
Fixes #4673. Fixes #4789. Consolidates #4820.
Co-authored-by: Matt Cowger <matt@cowger.us>
Co-authored-by: Jegor Kitskerkin <jegor.kitskerkin@gmail.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* feat(app): give compact Changes an overview and a straight rail (#4861)
* feat(app): give compact Changes an overview and a straight rail
Three fixes to the Explorer's Changes tab on phones, where there is no
tree rail and the diff is the only view of what changed.
No placeholder stat while the sidebar slides in. `useCheckoutDiffQuery`
reported `isLoading` only when the query was enabled and connected, so an
inactive retained panel settled on `files: []`. The comparison toolbar
summed that into "+0 -0" and the body rendered "No changes" for a diff
nobody had fetched yet, both replaced a moment later by real numbers. The
result now derives from the payload alone: no payload means loading,
whatever the query is doing. Callers already handle loading correctly, so
nothing downstream needed a special case.
Jump to file. A floating action button over the diff opens a sheet with
the same changed-files tree the desktop rail renders; tapping a file
closes the sheet and scrolls the diff there. `ChangedFilesTree` moved out
of `diff-pane.tsx` into its own module so both surfaces own one tree, and
its list is now the sheet-aware one so it scrolls with the sheet gesture
on native. `DiffDocument` takes a `contentInsetBottom` so the button
never traps the last line.
Rails on the compact header. The tab labels sat at 20dp and the close X
at 16dp while every toolbar row below them used 12dp leading and 8dp
trailing. The close button was also a hand-rolled Pressable rather than a
ToolbarButton. `paneContentToolbarTrailingPadding` now takes whether the
row's last control is a bare glyph or a framed one: a glyph's hitbox
overhangs its ink so the row pads by less than the rail, but the compact
git-actions trigger paints its own frame and was landing flush against
the screen edge. The commits header chevron moved to the depth-0 tree
rail. Desktop geometry is unchanged.
* fix(app): preserve Explorer touch targets and reset file overview
* fix(app): preserve diff selections across repaints
Header typography refreshes and height resizes changed the repaint callback
and inadvertently cleared active source selections. Keep selection updates
stable while retaining explicit source and selection-layout invalidation.
* test(app): express diff copy regression as user actions
* test(app): express file overview flows as user actions
* fix(app): prevent and recover workspace layout crash loops (#4844)
* fix(app): prevent and recover workspace layout crash loops
* fix(desktop): capture resized browser tabs without hanging
A resident guest can paint fresh pixels after resize while capturePage's
native surface-copy request remains pending. The same failed guest returns
a fresh frame through frame subscription, so waiting for resize or another
animation frame does not correct the failing copy request.
Capture one subscribed frame within the existing five-second budget and
release the subscription on completion or cancellation. Preserve temporary
frame production and its original throttling policy. Remove viewport copy
retries and E2E screenshot polling; assert the resized image dimensions.
Refs #4866. Three full real-Electron browser journeys passed under Xvfb,
including physical clicks and fresh screenshots while the window is hidden.
* test(app): choose Agent before exercising the runtime catalog
The newly merged catalog journey assumed an empty workspace opened an agent draft tab. This branch opens the New launcher, so create the agent draft tab explicitly before checking its composer and model choices.
* fix: settle closed browser captures and validate persisted layout fixtures
Destroying a guest during frame subscription left its screenshot pending until the capture deadline and blocked queued captures. Reject at destruction, remove the listener on every exit, and return browser_tab_closed. The regression proves immediate settlement and queue release.
Reuse the unchanged persisted layout schema from a UI-independent module so the recovery fixture validates storage before editing it.
* test(app): encapsulate Explorer layout setup and cleanup
Keep persisted layout schemas and corrupted-state construction inside the seedCorruptedWorkspaceLayout helper. Use the existing withWorkspace fixture for cleanup in all four Explorer journeys so test bodies follow a straight setup, action and assertion flow.
* test(app): target the selected workspace in layout recovery
The corruption helper selected the first persisted entry, so a multi-workspace journey could leave the intended workspace untouched. Resolve its exact persistence key from the requested workspace ID and fail setup when absent. The failing-first journey now preserves an earlier workspace with an agent draft tab while recovering the selected workspace.
* Smooth streaming replies with a continuous left-to-right fade (#4859)
* feat(app): reveal streaming text as one continuous fade front
Streaming assistant text is released by word and every character fades in
over the same 150 ms envelope from a start time owned by the message. Start
times never decrease in reading order, so a line paints as a single front
moving left to right, however the text is chunked. Per-word envelopes with an
internal stagger let the next word's first letter overtake the previous word's
last letter, which read as holes and flashing words at the real frame rate;
100 ms frame strips hid it.
Native renderers receive the fade ranges as a prop on a host view wrapping the
root Text, so ranges and text land in the same Fabric mount transaction. The
earlier binding sent ranges through a separate async module call keyed by view
tag and had to reconcile them against the view's text; on a fast phone the UI
thread lags JS by several commits, mounts and calls interleave, and every
reconciliation heuristic guessed wrong. Native code no longer infers newness
or creates start times.
The mock load-test provider now separates cycles with a blank line so a cycle
heading is not appended to the previous paragraph.
* fix(ci): refresh streaming dependencies and isolate E2E setup
* test(app): distinguish fade cleanup from Markdown replacement
* test(app): detect restarted fades during Markdown updates
* docs(app): keep streaming guidance focused on constraints and QA
* test(app): control animation time in Android fade assertions
Fixed wall-clock sleeps let emulator scheduling select the sampled animation frame. Inject the animator time source and drive paint at exact timestamps within one UI-thread operation; production keeps the system clock. Verify the mid-fade sample and the exact cleanup boundary without sleeps, polling, or retries.
* fix(test): compile the desktop renderer before lifecycle checks
The lifecycle fixture treated Metro /status as app readiness. Its first
Settings click therefore spent its 90-second UI budget compiling the cold
bundle. An isolated cold-cache run restricted to one CPU reproduced the
click timeout before the app runtime had loaded.
Reuse the browser suite's existing bundle warmup before launching Electron,
and preserve Metro output in the lifecycle artifacts. The UI timeout,
build-request timeout, lifecycle assertions and product behavior are unchanged.
CI did not preserve Metro output, so its precise startup timing is not
recoverable from the failed run. The retained log makes subsequent startup
failures diagnosable without relying only on an empty renderer text dump.
Validation: three complete lifecycle E2E runs under private Xvfb displays;
existing Metro readiness tests; app typecheck; targeted lint; root format.
* test(file-observer): await burst delivery through native recovery
The deletion phase required every event within 10 seconds, but native
coalescing recovery uses a 30-second safety audit followed by the existing
reconciliation debounce. An idle diagnostic excludes that delayed audit
and therefore cannot act as a completion barrier.
Await the required delivered events instead, preserving every deletion
assertion and the existing overall test watchdog. A real-filesystem test
with controlled native notifications reproduces 95 outstanding paths at
the old deadline and verifies completion when the safety audit delivers
them. No production observer behavior changes.
* test(file-observer): verify burst recovery through public events
The controlled-clock regression constructed a private native backend,
bypassing the interface used by consumers. Consolidate recovery coverage
into the public observer integration test instead: drive real writes,
deletes and directory renames, then await every required public event,
including the 450 surviving files at their renamed paths.
No public capability was added: subscribe callbacks already express
completion. No production behavior or test timeout changes.
* test(app): drive Android fade tests through the native host
The instrumentation suite constructed TailFadeInAnimator directly, advanced
its private clock, and counted FadeInSpan instances. It now mounts the
WordFadeHost that React mounts, feeds it the ranges prop the bridge sends,
and asserts rendered pixels: hidden before a start time, a monotone front
inside and across words, settled frames equal to the baseline, and no frame
callbacks left after settling. Span counts and internal timing state are no
longer asserted.
Choreographer cannot be advanced deterministically from a test and the fade
epoch is the message's wall-clock start time, so the host takes a
WordFadeFrameClock (time plus frame callbacks) as its smallest public seam;
production keeps the Choreographer-backed default.
Fixture text is sized in pixels so every sample stays on one line at any
density; at 420 dpi the 32sp fixture wrapped and the column helper summed
glyphs from the first line.
* Keep open chats current and show reconnection progress (#4863)
* fix(app): keep open chats current and show resume progress
Keep open-chat delivery independent of mounted views, freeze hidden chat rendering, and catch up visible chats before hidden ones. Distinguish host reconnection from timeline freshness so cached messages do not silently appear current after resume.
* fix(app): prevent sync status from overlapping action errors
Share the existing panel toast slot so action errors remain readable during reconnect and catch-up. Verify the interaction and draft retention in the real browser, preserve the tested freeze transition, and refresh the Nix dependency hash for react-freeze.
* test(hub): expose complete creation and message retry failures
Partial response assertions hid the creation error and allowed duplicate responses with incomplete agent data. Check complete success envelopes, the shared agent snapshot, and null message errors so future failures print the actual error payload.
* fix(app): dismiss retained chat menus before freezing
An async fork navigates before its closing state commits, so freezing the source chat could leave its portal backdrop blocking the destination. Remove the shared menu surface in the inactive commit before suspension.
Keep regression assertions aligned with open-chat subscriptions, per-agent incremental recovery, plugin contribution lifetimes, and rendered image completion.
* test(app): expect the reloaded plugin to contribute its pill again
A full reload discards the pressed pill with the page, then evaluates the plugin again and its agents snapshot re-adds the pill. The old count-zero assertion only passed on main because the chat was not ready yet when the URL settled; open chats now subscribe from session start, so the pill is rendered by then.
* fix(app): suspend hidden chats on web only
react-freeze is Suspense with a never-resolving thenable. Suspending a committed subtree hides its host nodes, tears down layout effects, and detaches refs, which changes Fabric ownership of the retained chat. Split the boundary by platform: the web file keeps the two-commit freeze, the native file keeps the chat mounted and live, and the retained-panel signal remains the only thing that stops background work. Restore the mobile-panels rule the branch had rewritten.
* fix(creation): never read a receipt while replacing it
Admission reads the creation receipt while the runner replaces it atomically. On Windows a rename over a file this process still has open for reading fails with EPERM, and that error marked an otherwise completed creation as failed (#4865). Queue reads and writes per receipt so one owner never overlaps them, and serialize the record when the write is queued because the runner mutates it afterwards. No retry on EPERM: the conflict is removed rather than tolerated.
Linux cannot reproduce the rename failure, so the Windows server-tests job (creation/index.test.ts and hub/execution-session.websocket.test.ts) is the verification for this change.
* fix: update lockfile signatures and Nix hash [skip ci]
* fix(app): stop the streaming Markdown spec at a word boundary (#4875)
The mock provider splits "**Bold" into two frames, "**Bo" and "ld". The
helper forwarded both back-to-back and asserted the strong span reads
"Bold", which only holds when the browser commits both frames to the
store in one reducer flush and the message first mounts with "**Bold".
When a frame boundary lands between the two tasks the message mounts
with "**Bo" and word pacing holds "ld" until the whitespace after it
arrives, so the span reads "Bo" until the gate releases. That is the
designed behaviour of the reveal, and the batching is up to the
browser's task scheduling.
Stop after "**Bold text" instead: the space after "Bold" ends the word,
so every batching reveals "Bold" and never more than "text". Forcing the
frames into separate commits reproduced the CI failure before the change
and passes after it.
* ci: simplify and de-flake the test suite (#4868)
* chore(ci): audit packaging costs and repeated test execution
Read-only audit: Linux packaging and smokes consume 12-14 minutes; all three CLI shards repeat the 294-test source suite and build the server twice. Preserve the 17 required contexts, move packaging to main, run CLI source tests once, and consolidate repeated E2E setup into journeys. Historical logs and the complete audit were reported outside the repository.
* ci: move packaging to main and remove repeated CLI suites
PR runners were rebuilding distribution artifacts and executing the entire CLI source suite on all three E2E shards. Keep packaging coverage after merge, let the local E2E runner own its dependency build, and execute source tests once in an existing required check. Root typecheck already validates plugin examples.
* test(ci): consolidate journeys and remove repeated startup work
Run user journeys with shared setup, retain one Hub CLI contract, and weight the existing Playwright shards using observed costs. Remove implementation-only and ad-hoc measurement checks. Await root-watch closure and publish creation failure commands atomically so teardown and failure injection have completion boundaries.
* test(hub): remove incidental observation and speculative shutdown changes
Hub control RPCs need a connected client without an agent observation. Preserve th…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linked issue
Pre-release compatibility review. Refs #3411, #4215, #4354, #4242, #4436.
Type of change
Reasoning
A phone app can remain on 0.7.2 for weeks after its desktop host updates to 0.8. Plugin timeline rows currently make that app reject timeline fetches and drop live events. Fork-PR setup also introduces a status that the old app cannot parse.
The daemon now checks
hello.capabilities.plugin_timeline_itemsbefore delivering plugin rows through live streams, canonical/projected fetches, rewind replay, and provider-subagent streams/fetches. Filtering merged plugin rows also removes their newcollapsed: "identity"value from old-client responses. Pagination windows and cursors remain canonical. Current clients advertise support.hello.capabilities.workspace_setup_blockedpreservesblockedfor current clients and maps it tofailedwith update/approval guidance for old clients. Both setup progress and status responses are adapted per socket, including sockets sharing one retained session. Stored state stays unchanged. External agent-tool and MCP worktree broadcasts also enter this boundary:emitExternalSessionMessage→wsServer.broadcast→Session.publish→Session.emit.The five incorrect
v0.7.3feature tags present in this checkout now sayv0.8.0. Review of #4354 and #4242 confirms thathub.executeintentionally has daemon-wide agent authority. Authorization remains unchanged; the public security guide explicitly includes model, thinking, and provider permission modes that bypass approval prompts, distinguishing those from daemon credential permissions.Goals
Non-goals
readTimelinealready deletes undecodable cache rows and returns a cache miss (Revert #4421 and narrow the cache restore fix #4436).z.union([knownItems, z.looseObject({type: z.string()})])compiles with the pinned zod-aot, but also accepts malformed known items. Its overlapping string tag would spread loss of type narrowing across shared protocol consumers. Excluding known tags and introducing a separately normalized client wire type is larger than the small release fix requested here. Wire validation remains strict.QA
CI passed on
5518eacab: CI completed successfully with Linux/Windows server tests, SDK tests, all four Playwright shards, typecheck, lint, and formatting. Docker and Nix Linux/macOS builds also passed. All applicable checks are green; unrelated jobs were skipped by path filtering. Review threads are resolved.Failing first: added the plugin mixed-socket repro, ran it before changing the gate, and observed:
Added the blocked-setup mixed-socket repro before implementing its gate:
Targeted automated checks: only the four changed test files were run, never the full local suite.
The WebSocket coverage exercises persisted plugin appends, both timeline projections, mixed capabilities on a shared session in both connection orders, provider-child live delivery and fetches, and real rewind reconstruction from a controllable provider adapter. The setup progress test publishes through the public session boundary and verifies per-source adaptation without mutating the snapshot. The existing attention test needed workspace ownership and directory subscriptions added to its fixture to match the current delivery contract; it now passes without changing attention behavior.
Manual published-client QA: installed npm's
@getpaseo/client@0.7.2into a separate temporary prefix, including its published 0.7.2 protocol dependency. A temporary source harness startedcreateTestPaseoDaemonon127.0.0.1:0, with an isolatedPASEO_HOME, daemon version0.8.0-beta.1, and a real Codex provider. Connected the published old client and current source client before creating the agent.The current
plugin-examples/timeline-itemsexample is client-only. Copied it into the isolated directory and added this temporary server contribution so its rows were persisted through the actual plugin-session append RPC:Installed that copy with
installDirectoryPlugin. Its temporary manifest requirement was>=0.8.0-beta.1; the original example's stable>=0.8.0requirement correctly rejects the beta. No example files were changed in this PR. Sent the real agent:Reply with exactly COMPAT_OK. Do not use any tools.Commands and observed output (temporary paths and the generated agent ID omitted):
Also seeded persisted fork provenance before daemon startup, then fetched setup status through both real clients:
{ "old": { "status": "failed", "error": "Workspace setup is blocked pending approval of code from a fork pull request. Update Paseo to review and run setup." }, "current": { "status": "blocked", "error": null, "blockedSource": { "kind": "change_request", "forge": "github", "number": 42, "headRepository": "contributor/project" } } }This verifies the restart/status path with fork provenance; an actual remote fork checkout and its live setup UI were not exercised. Live setup progress is covered at the session boundary. Only the isolated daemon was stopped; existing services were untouched. The temporary harness is not part of the diff.
Fallback compiler probe:
Compatibility in the other direction is unchanged: new clients only add optional hello capability keys, which older daemons already accept. No wire member becomes required, narrower, or removed. The remaining risk is another newly introduced union member elsewhere; this PR verifies the two release findings above, not an exhaustive historical protocol audit.
Checklist
npm run typecheckpassesnpm run lintpassesnpm run formatpasses