Commit Graph
5334 Commits
Author SHA1 Message Date
Mohamed Boudra 5c581c2486 fix(server): convert content-search coordinates in one pass per file
Translating ripgrep byte offsets by decoding and splitting the whole prefix
and suffix for every occurrence costs the file per match. A 1,001,400-byte
file of a million empty lines followed by 200 matches blocked the host for
5,888ms and then answered "ok", 888ms after its own five-second budget.

A single forward cursor per file answers every occurrence, decoding each
needed line once and advancing the UTF-16 column from the previous match on
that line. Byte bounds, saved-revision validation, CR/CRLF/LF lines, BOM
handling, columns and original match text are unchanged.

Conversion also checks the operation deadline directly. The timer alone
cannot end an operation that overruns inside synchronous conversion, because
cleanup clears it before it can run.

Also scope the Windows skip in the content-search browser spec to the
literal-backslash fixture. At file scope it annotated the whole suite and
disabled all 12 journeys on Windows.
2026-09-10 15:17:14 +02:00
Mohamed Boudra 4e9dbc7f54 fix(app): host the desktop Command Center in the shared overlay root
A result row's exact path belongs in a tooltip: a path is often wider than the
result column, and two files can share both base name and nearest parent, so
no row label can carry it. The app's Tooltip could not serve that, and the
reason was the Command Center itself.

Desktop web rendered the panel through React Native Web's Modal, which paints
in a plane of its own above the shared overlay root. A tooltip portals into
that root, so it could never cover the panel however high its own z-index.
adaptive-modal-sheet already hosts desktop modals in the overlay root, which
is what docs/floating-panels.md prescribes; the Command Center now does the
same and native keeps the Modal. Its existing layer, focus registration,
backdrop and Escape handling are unchanged.

Proving it needed care. elementFromPoint reports the row either way, because a
tooltip is deliberately pointer-transparent, and sampling a pixel proves
nothing when the tooltip surface and the panel are both white. What settles it
is whether the pixels inside the tooltip's own rectangle change when it opens:
they do not under the Modal, and they do in the overlay root.

Replaces the hover text swap added in the previous commit, which only widened
the label inside a fixed-width column and still elided a genuinely long path.

Desktop web loses the Modal's fade-in; the panel now appears immediately.
2026-09-10 14:44:59 +02:00
Mohamed Boudra c33c1d0855 fix(app): spell out a result's path when its label cannot
A row shows the file name and its nearest parent, which is not always enough:
packages/app/src/utils/index.ts and packages/server/src/utils/index.ts render
identically. Pointing at a row, or selecting it, now replaces the elided
parent with every directory above the file, so a pointer user reads the exact
workspace-relative path without the preview getting a header back. At rest the
column stays scannable. The accessible name was already exact and is unchanged.

The app's Tooltip was tried first and cannot serve this surface: it portals
into the shared overlay root, which is pinned to the browser/overlay plane at
z-index 1, while the Command Center renders through React Native Web's Modal
at z-index 9999. The tooltip mounts, measures and reports itself visible, but
elementFromPoint at its own coordinates returns the row behind it, so a reader
never sees it. Every other desktop overlay in the app portals into the overlay
root instead of using RN's Modal, which is what docs/floating-panels.md
prescribes; moving the Command Center onto it would fix this for every scope
and is a larger change than one row affordance.
2026-09-10 14:17:25 +02:00
Mohamed Boudra 770648327f fix(app): finish desktop content search
Reopening the same occurrence left the pane where the reader had scrolled it.
A file target is identity-stable, so the second activation reused the tab and
looked like a no-op; the existing openers avoid this by asking for navigation
after opening, which the search callback had assembled around. The per-tab
navigation revision now lives with the tabs in the layout store, one
open-a-workspace-file command carries the whole recipe, and the explorer,
filename search and content search all go through it. The editor instance,
its dirty buffer and its history are untouched.

Result rows now read as three things rather than one. The match is foreground
on the highlight, the rest of the matched line is its context, and the
location is context for that. Rows are 44px instead of 56px, so more
occurrences fit without shrinking the text.

Row identity was the worst of it: a flat occurrence list is full of repeated
base names, and an end-truncated path threw away exactly the parents that
tell them apart, so four different index.tsx rows looked identical. The row
keeps the file name and its closest parent, marks the elided head, and pins
the name so only the parent may shrink.

The desktop preview drops its header. The selected row names the file and
Enter opens it, so a repeated title and an Open button were duplicate chrome.
The compact flow pages away from the list and keeps both.

The daemon end-to-end assertion expected an empty result without the
per-file ceiling the host reports, so it failed before reaching its file-read
step. Fixtures that can only exist on POSIX — a file named with a backslash,
a PATH built from which and symlinks — are skipped on Windows, and
toWorkspaceRelativePath gets separator coverage that runs everywhere.

Removes the native row-geometry estimator added earlier in this branch. It
predicted platform text layout from character counts and never agreed with
what Android drew. Native source returns to the behaviour it had before.
2026-09-10 14:00:09 +02:00
Mohamed Boudra 2a2362f5cf fix(app): reveal a deep source match on native
Selecting a match 1,201 lines into an ordinary TypeScript file showed line
1,103 on an Android device. The match was never on screen, so the preview and
Open flow could not deliver the occurrence they promised.

The cause, measured rather than assumed. The list was told every row is one
line of code tall. Probed on the device, a plain row is 17.52dp and a wrapped
row is 140.19dp, because a row is as tall as the text needs and ordinary source
lines wrap on a phone. Every wrapped line above the match therefore shrank the
computed offset, and the list scrolled to a fraction of the real distance.

Row geometry is now derived instead of assumed: one visual line is the reader's
code size rounded the way the device rounds it, and a line occupies as many
visual lines as it wraps onto, counted the way the platform wraps — greedily at
spaces, mid-word only for a word too long for a line. The column count comes
from measuring one monospace character at the current code size, so it follows
the Appearance setting rather than a constant.

Two smaller things fell out. The list renders only once that measurement
exists, because scrolling a list still sized for a provisional geometry gets
clamped to the shorter content and lands short. And the match is revealed near
the top with two lines of context rather than centred, since centring is
measured against the list's own box, which inside a sheet is taller than the
part the reader can see.

Verified on an Android device at the default code size and at 20px: preview
and Open both show the highlighted text, across files and between two
occurrences in one file.
2026-09-10 13:01:47 +02:00
Mohamed Boudra 6e57bb1e4f fix(app): tell the truth about search scope and file identity
Two reproduced failures from the full correctness review.

An empty result claimed the whole workspace had been read. Ripgrep never
opens a file above the host's per-file ceiling, so a workspace whose only
match lives in a large log answered "No matches" with nothing else said, and
adding a small match still reported a complete result. The response now
carries the ceiling the host actually applied, and the empty state names it.
Only the empty state, because that is the only place the UI claims
completeness; a populated list already claims nothing, and the occurrence-cap
notice stays what it was. No permanent scope strip returns.

A file literally named "a\b.txt" previewed itself and then opened "a/b.txt".
The client rewrote every backslash to a separator on paths the host had
already made canonical, so a valid Unix file name became a directory and a
different file opened under a false "File changed" notice. Identity is now
kept as the producer gave it: the daemon renders workspace-relative paths with
its own separator turned into "/" for content search the way the file explorer
already did, and the client only normalises separators for a path whose own
shape is Windows, never from the client's operating system.

Native source previews sized every row at a hard-coded 20px while the row is
really one line at the reader's configurable code size, so scrolling to a
match landed in the wrong part of the file. The height is derived from the
size the view is already handed. Revealing a match thousands of lines into a
long file remains broken on Android beyond this; that is recorded with device
evidence rather than claimed fixed.
2026-09-10 11:45:15 +02:00
Mohamed Boudra 6db8bd124f fix(app): put content search in the real Command Center
The content scope replaced the whole panel with a lookalike shell: its own
query field pinned to the bottom, its own scope button, and a permanent strip
naming the implementation ("Saved files up to 1 MiB · Ignore case"). Search
now uses the Command Center's own header, so every scope shares one input,
one chip, one loading slot and one key dispatcher. Content search contributes
only a body: results left, source preview right, in a wider panel.

The strip is gone. Idle shows nothing, an empty result set reuses the shared
"No matches", and loading and errors are the existing spinner and Retry.

Two row defects fell out of the wider layout. A deep path truncated its own
file name away, so a row could not be identified; the name and coordinates now
lead and the directory is what truncation eats. A match far into a long line
sat outside the row entirely; the run-up is trimmed when there is enough of it
to be worth trimming. The preview centres such a match horizontally instead of
leaving it clipped at the scroller edge.

Pane Find required pane context, which an overlay preview does not have, so
rendering a source view inside the Command Center threw. Find now belongs to
panes only and stands down elsewhere, and the file pane routes through the
source module instead of rebuilding its theme.
2026-09-10 10:13:15 +02:00
Mohamed Boudra eb1fe10375 feat(app): search saved workspace contents from Command Center 2026-09-10 09:38:19 +02:00
Mohamed Boudra db8c22e135 fix(app): stop the Find placeholder from hiding Go to line
Hiding `.cm-panels` suppressed every CodeMirror panel in the file editor,
including the Go to line dialog that Mod-Alt-g opens through the same
search keymap. Hide only the inert Find placeholder panel instead, by its
own class, and leave the shared panel container alone.
2026-09-09 20:51:02 +02:00
Mohamed Boudra 0c38afa76f fix(app): keep the active match visible and drop the Find button
The floating widget covered the active match whenever the match sat under
it and the document was too short to scroll clear. CodeMirror's own panel
scroll margin cannot reveal a first line, so the file integration now owns
placement: the widget renders as an overlay beside the editor, the model
measures it against the active match, and flips it to the opposite corner
only when the current corner collides. Hysteresis keeps navigation from
bouncing the widget between corners.

Also removes the standalone Find button shown while Find is closed, per
user direction. Cmd/Ctrl+F is the entry point.
2026-09-09 20:38:48 +02:00
Mohamed Boudra 7b813f7f58 fix(app): float the pane Find widget and tighten its chrome
Find pushed the first lines of the file down, ran 480px wide with 32px
ghost buttons, and let the replacement input start left of the query
input. Float the CodeMirror top panel out of flow so the widget overlays
the top right of the pane, move the disclosure toggle into a gutter that
spans both rows so the two fields share a leading rail, put the match
count inside the query field, and drop the icon controls to the shared
pane-toolbar chrome the file bar above already uses.
2026-09-09 20:09:55 +02:00
Mohamed Boudra d014cfdb2f feat(app): add pane Find to file source 2026-09-09 19:40:57 +02:00
Mohamed Boudra ec5ab3fc4d revert(app): undo file Find merge pending maintainer review
Reverts 69510ac72f. The feature must return to an open pull request for maintainer review.
2026-09-09 19:39:14 +02:00
Mohamed Boudra 69510ac72f 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.
2026-09-09 19:28:36 +02:00
Mohamed Boudra 433e67b18b 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.
2026-09-09 16:06:08 +02:00
Mohamed Boudra d311797653 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
2026-09-09 16:05:44 +02:00
Mohamed Boudra 0b3d82a469 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
2026-09-09 15:39:12 +02:00
Mohamed Boudra 509a3e17bd 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.
2026-09-09 15:23:01 +02:00
Mohamed Boudra 19bd6e5909 docs(contributing): route bugs and product discussions to GitHub 2026-09-09 15:14:07 +02:00
Mohamed Boudra 16bfff8e0f docs(product): clarify core philosophy and contribution expectations 2026-09-09 15:04:06 +02:00
Mohamed Boudra fdf3b4b47f 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.
2026-09-09 11:30:28 +02:00
Mohamed Boudra 96ea6698d7 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
2026-09-09 11:06:29 +02:00
Edi Hasaj 85cd969321 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>
2026-09-09 11:01:08 +02:00
Mohamed Boudra c172076bff 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
2026-09-09 09:15:28 +02:00
Mohamed Boudra 613cbbe9ef Fix Codex continuation after workspace archive (#4512)
* Fix native writer ownership when restoring archived agents

* Keep archive QA mechanics in a scenario helper
2026-09-09 08:51:55 +02:00
Mohamed Boudra da8c1b5c94 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
2026-09-08 13:40:21 +02:00
Mohamed Boudra 18aaec2772 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.
2026-09-08 12:59:25 +02:00
Mohamed Boudra 158525624f 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.
2026-09-08 12:59:09 +02:00
Mohamed Boudra 47171b419b 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.
desktop-windows-v0.8.0-beta.1
2026-09-08 11:07:13 +02:00
paseo-ai[bot] f809651876 fix: update lockfile signatures and Nix hash [skip ci] 2026-09-08 08:29:09 +00:00
Mohamed Boudra 4eab53e24e chore(release): cut 0.8.0-beta.1 android-v0.8.0-beta.1 v0.8.0-beta.1 2026-09-08 10:19:53 +02:00
Mohamed Boudra 341a86aa52 docs(changelog): add 0.8.0-beta.1 entry 2026-09-08 09:51:21 +02:00
Mohamed Boudra 1622da40d9 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.
2026-09-08 09:48:48 +02:00
Mohamed Boudra 229f3cd5a8 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.
2026-09-08 08:30:52 +02:00
1f5b6143d6 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>
2026-09-07 23:42:28 +02:00
Mohamed Boudra d779514f10 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
2026-09-07 23:34:37 +02:00
Mohamed Boudra 0859a67ab1 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
2026-09-07 21:46:59 +02:00
LiuJingandMohamed Boudra 56fd50988c 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>
2026-09-07 21:40:30 +02:00
Mohamed Boudra 469d51289b 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
2026-09-07 21:26:30 +02:00
Karan SharmaandMohamed Boudra a7a708bec9 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>
2026-09-07 21:08:50 +02:00
Ivan Brykov c43df5d4c3 feat(client): expose provider usage (#4062)
* feat(client): expose provider usage

* fix(client): gate provider usage by capability
2026-09-07 19:52:34 +02:00
Mohamed Boudra c46aee4e6a Revert #4421 and narrow the cache restore fix (#4436)
* Revert "Restore cached conversations before reconnecting (#4421)"

This reverts commit 1c51b52d07.

* 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.
2026-09-07 19:18:54 +02:00
Mohamed Boudra 9c8d775895 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
2026-09-07 19:17:42 +02:00
Mohamed Boudra 8c89e72a06 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.
2026-09-07 17:39:17 +02:00
Mohamed Boudra a5d2b7d863 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
2026-09-07 17:20:19 +02:00
Mohamed Boudra 1c51b52d07 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.
2026-09-07 15:52:21 +02:00
Michael JaklandMohamed Boudra c424f82922 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>
2026-09-07 08:57:01 +02:00
Omer Cohenandpaseo-bot[bot] b403dea32b 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>
2026-09-06 21:40:23 +02:00
Mohamed Boudra dd7671be20 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.
2026-09-06 20:29:41 +02:00
Mohamed Boudra 38c22139bb 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
2026-09-06 13:48:29 +02:00