mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-28 04:03:05 +00:00
32356db309f8dafefdd08847d7be3ca3b1dc1321
84
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
793b16c0df |
fix(relay): protect sessions against replayed commands (#7610)
Protect relay sessions against replayed commands with authenticated Noise NK negotiation, ordered records, and identity-bound security pins. Preserve legacy admission during migration and add daemon enforcement policy. All 21 CI checks passed. General engineering review is complete; specialist cryptographic review of the handwritten Noise implementation and static-key reuse remains required before release or deployment. Closes #359 |
||
|
|
12e7a448f0 |
fix(app): open notification agents once in their current workspace (#7612)
* fix(app): open notification agents once in their current workspace Keep tap ownership across Activity and React remounts, clear native responses, and resolve agent workspaces through existing navigation after startup settles. Co-authored-by: Kassam Khoja <kassam@shopwrk.com> * fix(app): hydrate agent targets with the compact sidebar closed Hold directory demand for the agent route while resolving its workspace; the compact sidebar does not supply demand before it opens. * fix(app): keep deferred workspace opens in the layout model Key pending tab opens by workspace so redirects and retained screens cannot lose or consume another workspace's target. Legacy open URLs seed this state at their route boundary. * docs(app): document notification tap ownership and native replay * fix(ci): route feature-owned Electron sources to desktop checks * refactor(app): separate notification API from React integration * fix(app): consume pending opens before painting the restored tab A legacy open link showed the previously focused terminal for a frame before switching to the requested terminal. Seed and consume the pending open in layout effects so a ready target switches before paint. * fix(e2e): focus the visible retained terminal surface --------- Co-authored-by: Kassam Khoja <kassam@shopwrk.com> |
||
|
|
91dca5702b |
Connect saved hosts and delegate agents across daemons (#7370)
* feat(hosts): connect to saved hosts through a daemon * fix(hosts): validate safe targets and verify encrypted relay routes * test(hosts): include relay coverage in existing CI contracts * fix(cli): preserve validated addresses in daemon command hints * fix(hosts): share daemon links across isolated routed sessions Multiplex logical sessions over the monitored daemon connection and wrap original binary bytes in routing headers. Admit channels under their authenticated parent and current grants; preserve scoped delivery and physical send completion. * fix(hosts): bound encrypted overload and initial acquisition Check encrypted wire capacity before the async channel converts a resource limit into a physical connection error. Bound initial routed acquisition with the existing client timeout, then enable reconnect after admission. * fix(hosts): keep compact row actions clear of host labels Give compact connected-host rows separate text and action widths so long names and producer labels cannot compete with primary controls. * refactor(hosts): remove management settings UI from phase one Keep connected-host management in the CLI and shared SDK while preserving app saved connections and plugin access. * test(hosts): control physical write backpressure across platforms Hold real TCP writes until assertions finish instead of assuming a paused receiver exhausts every platform receive buffer. Preserve physical send settlement, byte identity, capacity and sibling checks. * refactor(hosts): use the ordinary client contract over channel transports Keep queuing, permission codes and reconnection independent of transport selection. Track submitted request outcomes generically while retaining connection error codes and structured causes. * test(e2e): keep transient developer toasts from stalling checks Concurrent locator handlers can wait for a dismiss button another handler removed. Dismiss existing toast DOM at document lifetime, including its open shadow root, without intercepting app actions. * Create, prompt, and monitor agents across connected hosts (#7507) * feat(hosts): delegate agents across connected hosts Resolve MCP host operations through in-process commands for self and the existing routed client transport for other destinations. Keep ordinary parent labels, app sessions and idle finish notifications. * test(app): normalize subagent fixtures through snapshots * docs(protocol): clarify same-host parent labels * fix(hosts): resolve subagents through destination sessions A child address identifies its destination regardless of the connection used. Reuse the existing host candidate selection for discovered routes and direct connections, and keep navigation and row actions independent of the parent transport. * fix(hosts): keep routed destinations distinct during discovery A routed candidate names only the origin daemon. Match it within the named destination profile so two destinations discovered through the same origin retain separate identities and connections, including existing saved routes. * feat(hosts): prompt and query agents on selected hosts Extend the existing MCP host operation boundary with prompt, status, activity and list operations. Local calls retain their handlers and notification owner; selected destinations use ordinary client RPCs and the existing idle finish wait. * test(e2e): preserve toast coverage with CSS hiding * docs(hosts): translate connected host references |
||
|
|
006fd053ac |
Record Android freezes in opt-in diagnostic builds (#7456)
* feat(app): record Android stalls in opt-in diagnostic builds * refactor(app): own watchdog sinks and bounded log storage * fix(app): preserve cumulative browser profiling history |
||
|
|
bd15e40e94 |
Serve paseo.sh in Simplified Chinese (#7373)
* feat(website): serve paseo.sh in Simplified Chinese
Every page of the website now has a Simplified Chinese version under
/zh-CN: the interface, docs, blog posts, comparison pages, legal pages,
all changelog releases and the homepage mockup. English URLs are unchanged.
The URL alone decides the language. There is no browser-language redirect
and no stored preference, because either would have to redirect the
unprefixed English URLs that people share and search engines index.
Internal links carry the page's language instead, and hreflang alternates
point Chinese search results at /zh-CN.
Translated headings pin the English anchor with {#id}, so section links
work in both languages. content-translations.test.ts fails when a page
has no translation or a translation's headings drift from the English.
Changelog releases and plugin listings without a translation render
whole in English under a notice, so a release never waits on one.
Plugin manifests and the registry gain optional translations and
OVERVIEW.zh-CN.md fields; older clients and registries ignore them.
* feat(website): replace the native language select with an EN/ZH menu
The header showed an OS-native select whose popup did not match the site.
The trigger now shows the language abbreviated (EN, ZH) and opens a menu in
the app's dropdown style that names each language in itself, with its name
in the page language underneath. Rows are real links to the same page in
that language; arrows, Escape, outside presses and taps behave as a menu
button, and the menu keeps itself on screen when a narrow header pushes the
trigger toward an edge.
Also drops the stray space after the full-width comma in the Chinese
homepage subtitle.
* docs(website): translate main's new docs and 0.12.0 release notes
Main added Keep run workspaces to the schedules docs, rewrote plugin icons
around inline SVG, and promoted 0.12.0 with new entries. Bring the Chinese
pages and changelog in line so no section falls back to English.
* fix(server): leave plugin listing translations to the registry
The daemon rejected manifests whose translations the registry would refuse,
so a plugin main installs could fail to load here. Paseo never reads
translations; the registry validates them at publish. Docs now say which
checks are automated at publish and which are human review.
* docs(plugins): describe the registry's final overview translation discovery
* style(server): format manifest test
* test(app): measure sidebar rows one at a time
After a reload the developer error toast appears while expectVerticalOrder
measures every row at once. Each concurrent boundingBox runs the toast
locator handler, and only one of them resumes, so the poll timed out on
every attempt since #7424. Measuring sequentially keeps one action in flight.
* test(app): return sidebar row measuring to main
#7480 hides the developer toast instead of running a locator handler, so
measuring the rows concurrently no longer races it. Reverts
|
||
|
|
eb1a8a2ec7 |
fix(markdown): one renderer on react-native-enriched-markdown for iOS, Android and web (#6973)
* fix(app): group native assistant prose into selectable runs * fix(app): render selectable native prose with enriched markdown * docs: explain native prose selection boundaries and tradeoffs * fix(app): preserve markdown semantics and render limits in prose runs * docs: record native prose compatibility fixes and heading limits * fix(app): allow additive task lists and admonitions in native prose * fix(app): preserve inline image formatting and reference scope * fix(app): preserve markdown source and file actions in native prose Prepare original source blocks with source-position edits from the assistant parser. Keep reference scope and live/history projection consistent, and retain link text when dispatching workspace file actions. * fix(app): preserve native prose entities and links while bounding preparation cost * fix(app): preserve provisional syntax at cached reveal boundaries * fix(app): bound prose append work and preserve markdown edge cases * fix(app): preserve home paths and normalized prose links * fix(app): preserve parser-owned links and streaming tilde tails * fix(app): keep growing markdown on the streaming block renderer * fix(app): preserve bracketed IPv6 links in native prose * fix(app): skip prose classification for blocks over the render cap * fix(app): share selectable native prose across Markdown surfaces * fix(app): preserve Markdown containers before grouping prose * revert(app): remove shared prose-run renderer changes Reverts ab4737bb2 and ac47e1063 before replacing the Markdown engine across all platforms. * feat(markdown): use enriched renderer across all platforms * test: verify library image controls and isolate website config * fix(markdown): preserve file links and image revisions * fix(markdown): restore diagrams and preserve link origin * fix(markdown): retain diagram identity and reference scope * fix(markdown): avoid empty web history measurements * fix(markdown): preserve authored empty link titles * test(markdown): exercise find across whole-message history * fix(markdown): preserve encoded image paths after rebase Keep the shared renderer image normalization and file-read regression coverage that originally lived in the main integration merge. * fix(markdown): derive table and task list colours from the theme * fix(markdown): restore selection highlight on web * fix(markdown): restore file link hover tooltips on web * fix(markdown): normalize inline HTML before rendering * fix(markdown): create renderer types when applying package patch The published nightly omits both MarkdownRenderers type files. Represent them as new files so patch-package succeeds on a fresh npm install, matching the already patched development package. * fix(markdown): keep the renderer tree stable while images resolve * fix(markdown): size inline images to the line and collapse failed loads * fix(markdown): normalize only parsed HTML nodes * fix(markdown): keep the first image reference definition * fix(markdown): preserve complete assistant messages on native * fix(markdown): classify web links explicitly and complete renderer types * fix(markdown): preserve spacing between HTML elements * fix(markdown): normalize HTML following code elements * fix(markdown): preserve literal image fallback labels * fix(markdown): retry transient image decode failures * docs(markdown): record native selection and image limitations * fix(markdown): preserve first definitions across Mermaid segments * fix(markdown): reserve pending images and harden web rendering * test(markdown): cover state retention during image arrival * fix(markdown): preserve reader position while responses grow Keep the same message component boundary when a live row becomes retained history, preserving Mermaid runtimes and selection nodes. Observe paced child text commits at the web viewport so sticky-bottom follows before the next frame. * test(markdown): require growth while holding a streaming paragraph * test(markdown): isolate image arrival from reload positioning Mounted row totals change as history virtualizes, so observe the new response and tool directly. Hold the real PNG response after reload until the reading position settles, then verify its reserved height and viewport instead of attributing initial bottom-follow positioning to image decoding. * fix(markdown): preserve adjacency around inline HTML * fix(markdown): keep summary block boundaries outside bold text * perf(markdown): scan HTML block boundaries incrementally * test(markdown): gate delivery while establishing a held reader * test(markdown): name shared renderer setup and file actions * test(markdown): assert linear HTML boundary work without timing * test(markdown): check large HTML table output without observing built-ins * fix(markdown): restore accentBright link colour * fix(markdown): require letters and known extensions in bare path recognition * fix(markdown): match main's blockquote, bullet and h6 colours * fix(android): keep inverted chat still during text selection React Native decomposes Android list inversion into a 180-degree rotation with positive scaleY, so the existing visibility-scroll guard never recognized the inverted viewport. Inspect the composed matrix and reproduce the actual list transform through the native view manager in regression tests. * fix(markdown): linked inline code keeps inline code size * fix(markdown): reserve a placeholder box with an image glyph while images load * fix(markdown): bare prose paths need an extension, line suffix or depth Slash-separated English pairs were recognized as file links in prose. Require stronger path intent there while preserving the existing inline-code recognition rule. * fix(markdown): horizontal blocks own their drags on compact layouts * perf(markdown): keep native streaming updates in the unfinished tail Native markdown reparses each updated view in full. Keep parser-closed blocks in a memoized head during paced reveal, then reunite completed prose for cross-paragraph selection. Preserve trailing spacing and source indentation at the split. * perf(markdown): reuse Android measurements across view mounts * test(markdown): bare Fabel/model and daemon/client stay plain text when copied |
||
|
|
e135e9c101 |
Open files and subagents in a closable sheet on phones (#6827)
* feat(app): open files and subagents in a closable sheet on compact On phones, opening a file, a provider subagent, or a Paseo subagent now shows it in a bottom sheet over the current tab instead of adding a tab, so the tab strip stays for terminals and root agents. Paseo subagents get "Open as tab" to move the same tab (same tabId and state) into a full tab with a composer; agents in a sheet hide the composer. The decision lives in the workspace layout store's openTab, driven by a per-kind `compactSheet` rule in the panel manifest, so every open path (chat links, Explorer, subagents track, notifications, plugins) follows it without call-site branching, and a new kind opts in with one manifest entry. The app shell publishes the layout mode (compact / single-pane / split-panes) once, so callers stop passing isCompact for placement. A sheet holding unsaved edits is never implicitly displaced; later opens go to tabs underneath, and closing it goes through the existing discard guard. The editor host is position: sticky because CodeMirror's scrollIntoView otherwise scrolls the bottom sheet's container and drags the sheet to full height. * test(app): fail the compact sheet snap check when the sheet is oversized --------- Co-authored-by: paseo-bot[bot] <paseo-bot[bot]@users.noreply.github.com> |
||
|
|
7b0cace991 |
Restore Antigravity with Full access and session warnings (#5795)
* Revert "Remove the unreleased Antigravity provider plugin (#5781)"
This reverts commit
|
||
|
|
9d9d9af993 |
Remove the unreleased Antigravity provider plugin (#5781)
* refactor(providers): remove the unreleased Antigravity plugin Remove the provider at the owner’s request because headless command approvals are unsupported. Preserve Muse, shared plugin infrastructure, and earlier editor and terminal integrations. * test(plugins): keep builtin startup coverage generic |
||
|
|
8332f4d1ff |
feat(muse): add Muse Code as a built-in provider plugin (#5719)
* test(muse): record real host protocol fixtures * feat(muse): add built-in MSP provider Co-authored-by: millerben95 <millerben95@users.noreply.github.com> * fix(muse): expose provider settings in the app The daemon registered host settings without a client contribution, leaving sandbox, network, and workspace trust unreachable. Share the definition with a settings screen and expose it through Command Center even when installed plugins are disabled. * docs(muse): publish setup and provider support * test: correct runtime fixtures and restart readiness * fix(muse): hide reminder housekeeping from the timeline * fix(muse): use the official Muse Code mark Source: https://muse.ai/images/favicon/app-squiggle.svg Preserve the official silhouette path verbatim. Remove its gradient definition and use currentColor, scaling the original 100-unit coordinates into the 24-unit viewBox. Black silhouette renders match pixel-for-pixel. * fix(muse): adapt to current plugin APIs Use the screen API introduced with sidebar plugin items. Usage reports now come from source account discovery; remove the retired agent usage-reference request and headline marker, and exercise discovery through the existing provider fixtures. --------- Co-authored-by: millerben95 <millerben95@users.noreply.github.com> |
||
|
|
d1a53255fc |
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 |
||
|
|
94fc4e5d49 | docs(release): document temporary next and hotfix branches (#4651) | ||
|
|
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> |
||
|
|
63587a9c03 |
feat(server): establish semantic session permissions (#3981)
Keep trusted-client and Hub behavior stable while replacing RPC-name grants with a semantic authorization boundary. This establishes the operation inventory needed for future principal, pairing, and resource-scoped access work. |
||
|
|
f6288629ff |
Fastlane metadata format for F-Droid releases (#2501)
* add f-droid metadata support * share version code calculation with website * preserve blockquoted sections in changelogs * refactor(android): simplify stable F-Droid metadata Generate store changelogs only for stable releases and keep native version-code ownership in the existing release module. * test(android): run F-Droid changelog suite in CI --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> |
||
|
|
e22ad117fd |
Stream assistant text at a steady rate instead of in arrival lumps (#3612)
* Stream assistant text at a steady rate instead of in arrival lumps Assistant text arrived in lumps and was painted that way. The daemon coalesces one message per 60ms window, and that window carries however many characters the model produced in it, which swings by an order of magnitude within a turn. Painting each delta as it landed made the size of those lumps visible. Three changes: - The agent stream coalescer is now leading + trailing. The first delta after an idle window flushes synchronously instead of waiting a full window, matching the invariant TerminalOutputCoalescer already documents. The window stays at 60ms; shrinking it would raise message rate on a loop that already contends with terminal frames and relay encryption, without making the stream smoother. - The client reducer queue commits on a frame instead of an arbitrary 48ms timer, with the timer kept as a ceiling so a hidden tab still advances. - Assistant and reasoning text are revealed at a rate derived from the backlog rather than from arrival timing. Arrival changes the target, not the rate. The store keeps the full text and only the rendered slice is paced, so copy, selection, the chat outline, and scroll geometry all stay consistent with what is on screen. The leading flush means a burst's first chunk lands as its own timeline row. The projection already merges contiguous same-turn rows and clients read the projected timeline, so history is unchanged; the server tests that assert on raw rows were updated for the extra row. Measured on Expo web against a local dev daemon. Mock bursty-stream provider, 6s samples, paint-on-arrival vs paced: frames that advanced the text 55 -> 244, chars-per-frame CV 2.66 -> 1.44, gap p50 100ms -> 17ms. Real Claude Haiku agent over 52s: 84% of frames advance the text, CV 1.29, gap p50 and p95 both 17ms (one frame at 60Hz). Adds a bursty-stream mock model with seeded burst sizes, a gated browser perf spec, and docs/agent-stream-performance.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Address review: safe reveal boundaries and a testable seam Two findings from the PR review. Unicode boundaries. Slicing the revealed text at an arbitrary index could strand a lone surrogate, which paints as a replacement glyph, or split an emoji ZWJ sequence so a family briefly rendered as its members. Both were visible for a frame or two mid-stream — the exact flicker the paced reveal exists to remove. The rendered cut is now pulled back off surrogate pairs, combining marks, variation selectors, skin tone modifiers, and ZWJ joins. The reveal counter stays monotonic and only the painted boundary moves, so a long cluster cannot stall the reveal. Test placement. The hook test used JSDOM, @testing-library renderHook, and monkey-patched requestAnimationFrame, all of which docs/testing.md rules out; it prescribes fixing the missing seam instead. The reveal policy is now a pure state machine in text-reveal.ts (begin/retarget/advance/ complete/visible) covered by plain unit tests with no environment, and the hook is reduced to the frame-clock binding. Rendered behavior is left to the browser spec, which no longer carries frame sampling and statistics in its body — those moved to e2e/support/helpers/stream-smoothness.ts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Measure total painted text, not the tail message The smoothness sampler read the length of the last assistant-message element. A real turn emits many assistant messages, so that element keeps changing identity and its length is not monotonic — handovers read as resets, and a stream that was plainly moving on screen reported almost no growth at all. Sampling the total across every assistant message fixes it. This invalidated the numbers previously recorded for the real-agent run, so the doc table is replaced with a fresh A/B taken with the corrected sampler against a Claude Haiku agent: frames that advanced the text 6% -> 87%, chars-per-frame CV 4.11 -> 1.86, gap p50 317ms -> 17ms, p95 383ms -> 17ms. The paced column also reported zero Unicode defects across a stream containing emoji, which exercises the reveal boundary clamp end to end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * perf(agent-stream): harden paced text reveal Use standards-compliant grapheme segmentation when available and preserve full arrivals on older runtimes. Cap reveal commits at 60Hz and measure the real browser rendering path so high-refresh displays stay smooth without rendering at hardware frame rate. * Hold back trailing fragments split by arrival boundaries The reveal boundary is not the only place a grapheme gets cut. The daemon's coalescing window ends a delta wherever the window closes, so a caught-up reveal can be handed half a cluster and paint it. Measured on a mixed-script Haiku stream: five frames in ~350 ending in a single regional indicator, i.e. half a flag on screen for a frame. While the turn is streaming, a trailing fragment that cannot stand alone — a dangling high surrogate, a trailing joiner, an odd regional indicator — now waits for the rest of its cluster. Once the turn ends everything received is painted, because nothing more is coming. Complements the grapheme-boundary clamp, which handles the reveal cut; this handles the arrival cut. The same stream now measures one such frame in ~400 rather than five. The remainder is a flush boundary that coincides with the end of an assistant message, putting the two halves in different messages, which the client cannot rejoin — that one belongs in the coalescer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> |
||
|
|
c914bcb44b | feat(app): make Explorer a first-class pane host (#3826) | ||
|
|
19a7d26349 |
feat(app): make the side panel user-directed (#3605)
* feat(app): let the file panel close its tree rail The file panel's tree rail was permanent on desktop — the changes panel had a folder-tree toggle in its toolbar, the file toolbar had nothing, so the rail was there whether you wanted it or not. The changes panel's private DesktopTreeToggle becomes a shared TreeRailToggle so both toolbars render the same affordance; that also drops the treeToggleStyle prop threaded through ChangesToolbarProps, which recomputed exactly what the component already built internally. The i18n keys move out of workspace.git.diff into workspace.tree, since a shared component reaching into the git namespace would rot. The two rails keep independent flags — the file rail in panel-store, the changes rail in useChangesPreferences — so closing one leaves the other alone. FileTreeToggle wires itself to the store rather than taking a callback: the bar renders several components below FilePanel, where the rail actually lives. Both rails also get narrower (320 -> 260 default, 200 -> 180 minimum). The persist version bump resets treeRailWidth on purpose: nearly every existing install carries the old 320 default, so without the reset the narrower rail would only ever reach new users. Anyone who dragged their rail to a deliberate custom width loses it once. Note that MIN_CONTENT_WIDTH still clamps the rail to `containerWidth - 240`, so in a narrow pane the new default never applies — that floor, not the default, is what sets the width there. * feat(app): make the side panel an empty workspace surface The fixed, hideable side panel keeps supporting context beside an agent without seeding content. Pane-local choices stay local, while implicit supporting opens follow a persisted routing preference. * test(app): exercise side panel policy through its capability boundary The browser rail journey already covers the real tree-toggle behavior. Side-panel policy tests now pass their platform capability through the public boundary instead of replacing a production module. * test(app): expect an empty side panel after upgrade * test(app): expect an empty first split pane * test(app): account for the empty side panel in split retention * feat(app): make workspace panel tabs independent Give explicit New actions fresh tab instances, keep panel state opaque to tab management, preserve File navigation context, and stabilize uncached File transitions. * fix(app): preserve file conflicts during preview refresh Keep the current preview mounted for same-target reads and conversions so the editor retains its conflict observation state. Clear retained previews only when the file target changes. |
||
|
|
23853a1515 |
Add managed local plugin lifecycle (#3222)
* feat(plugins): add local plugin surfaces and RPCs Keep plugin execution scoped to its daemon while allowing one local TypeScript entry point to contribute validated RPCs and native client surfaces. Paseo owns host selection, navigation chrome, and per-installation query caches. * feat(plugins): add Linear attachment source * fix(nix): refresh npm dependency hash * test(server): exclude internal sessions from Hub counts * test(server): distinguish Hub execution sessions * feat(plugins): make local plugins explicitly opt-in Keep plugin loading dormant until configuration enables it, and collect unsupported examples under one directory. * refactor(plugins): establish host boundaries * fix(nix): refresh plugin dependency hash * fix(plugins): complete packaging and startup rollback * feat(plugins): add managed local lifecycle Make trusted local plugins recoverable and authorable through daemon-owned lifecycle operations, host settings, and a capability-gated CLI. Reloads fully stop the previous plugin before starting new code, while generated projects provide strict TSX checking without changing runtime dependency ownership. * fix(nix): refresh plugin dependency hash |
||
|
|
48e65b407f |
Replace Parcel Watcher with Custom Watcher (#3107)
* fix(server): replace wedging file observation Native watcher teardown could block the daemon main thread during workspace archive. Own recursive observation behind an awaited lifecycle boundary, retain an isolated comparison harness, and gate correctness, resources, and sustained CPU across platforms. * test(server): accept root watcher evidence Native recursive watchers may coalesce ignored descendant changes into a root event. Assert that ignored paths stay excluded while a nonignored sentinel proves delivery remains live. * test(server): preserve watcher comparison evidence Record Parcel delivery misses without weakening the production observer gate, and allow the Windows stress test to use its declared polling window. * fix(server): reconcile native watcher omissions Back macOS and Windows recursive watching with a bounded namespace inventory so coalesced create and delete events converge without unbounded rescans. * test(server): compare worktree filesystem identity Windows can return the same worktree through 8.3 and long path spellings. Preserve cardinality and branch checks while comparing the real path. * perf(server): bound native inventory audits Reduce full native inventory cadence while preserving a six-second convergence bound, and gate setup readiness and event-loop p99 against platform-appropriate budgets. * build: refresh Nix npm dependency hash * perf(server): scope native file reconciliation * perf(server): defer native safety audits until quiet * fix(server): move native safety deadline during audits * perf(server): audit native inventories after settled churn * fix(server): reconcile native root evidence promptly * perf(server): deduplicate native audit scheduling * perf(server): coalesce native event clusters * perf(server): suppress fresh native change scans * test(server): separate burst recovery from sustained churn * test(server): stabilize observer performance gates * perf(server): skip fresh native change scans * fix(server): preserve nested native change audits * perf(server): coalesce native repair scans * fix(server): reconcile native directory moves promptly * test(server): bound observer burst repair cost * refactor(server): own file observer lifecycle * build: refresh Nix npm dependency hash * ci: fold observer coverage into server suite * fix(server): await file observer shutdown |
||
|
|
3d420720c5 |
docs(hub): document workflow security boundaries (#3032)
* docs(hub): add security guidance for workflows * docs: register Hub security guide |
||
|
|
ab8151d33f |
feat(app): sidebar display preferences, a shared menu engine, and a colour pass
Sidebar polish round. Menus. `dropdown-menu` and `context-menu` were two copies of one engine that differed only in how the trigger fires — identical from `computePosition` down — and neither could nest. Both are now thin wrappers over `components/ui/menu/`, which owns anchoring, a pure page-stack navigation model, and one presentation fork: flyout on pointer, pushed pages in a sheet on compact. All 31 call sites are unchanged. Items got a rounded inset fill, a visible separator, and `selected` (a check) split from `active` (a fill). Sidebar display preferences. The flat wall of four sections becomes one row per decision with its value, and per-feature toggles over what a workspace row shows: host, pull request, checks, scripts. Each is independent — a ticked toggle that draws nothing is a lie. `display-preferences/` hides that grouping lives in zustand while title source and row items are app settings. Timestamps. "Last activity" now ticks itself. The label rounds to the minute and never shows seconds; past a week it becomes a date. The timer lives in the leaf `<Text>`, at most one per resolution tier shared by every row, and a tick only re-renders when the string actually changed — so a memoized row that legitimately never re-renders still stays current. Colour. Full status tier regenerated at 90% of gamut max (was 85), same rule as the muted tier, lightness and hue untouched. File icons are pulled toward neutral in OKLab behind `getFileIconSvg` — material-icon-theme picks its palette for a tree where the icon is the only colour on the row, and ours is not. New/deleted files use the forge square-plus/square-minus glyphs at muted weight instead of tinted pills. Project icons round a little more; uploaded icons never do. The project status badge is a knockout again: it fills with the colour of the row behind it, so the ring reads as a gap in the icon rather than a white halo. File rows. The always-visible kebab is gone from the file explorer and diff tree in favour of right-click, which also reclaims the reserved action slot. |
||
|
|
5a30affd7f |
Skip unrelated required checks without losing their names (#2542)
* fix(ci): skip unrelated required checks cleanly Matrix jobs had to start no-op runners to preserve interpolated required-check names. Static named jobs let GitHub report genuine skipped conclusions while keeping those names stable. * fix(ci): gate changes to install patching script * fix(ci): route cross-package test contracts narrowly * fix(ci): keep routed contracts merge-blocking * fix(server): exclude nested test utilities from builds * fix(ci): cover app-owned native and browser domains * refactor(ci): align tests with runtime domains Own browser specs, desktop specs, and Electron-only app code by stable directories so PR routing does not depend on filename conventions. Keep desktop integration coverage inside the required desktop check. * fix(ci): keep routing and desktop suites isolated The routing regression must run before dependency installation, and desktop Vitest must not collect Playwright-owned specs. * fix(ci): isolate desktop e2e runtime * perf(test): isolate Playwright workers * perf(test): parallelize Playwright within specs * fix(test): keep Playwright file scheduling * fix(test): share dynamic Metro fixture * fix(test): preserve own-host restart fixtures * fix(ci): preserve routed contracts after rebase * Stabilize Playwright wheel-scroll assertions |
||
|
|
588fdc794b |
docs: rewrite contributing guide, add QA and compatibility docs
Issues are bug-only now. The feature-request template is gone, requests opened as issues get closed automatically, and feature talk moves to product discussions on GitHub Discussions and Discord. "Product discussion" is the term everywhere, recorded in the glossary. Contributing states the QA evidence bar and links out instead of teaching; docs/qa.md carries the depth. The protocol back-compat rules moved out of CLAUDE.md into docs/protocol-compatibility.md, since that constraint is invisible in development where both sides always run the same version. CLAUDE.md gains doc-writing and voice rules to keep docs from accumulating appended paragraphs. design.md gains an alignment section (glyph rails) and a layout-shift rule. |
||
|
|
a8ebd390fa |
feat(forge): pluggable forge abstraction + GitLab and Gitea/Forgejo/Codeberg (#1913)
* refactor(forge): forge-neutral foundation (GitHub-only) Decouple git-hosting from GitHub behind a neutral abstraction (issue #1616), GitHub-only for now; existing GitHub behaviour is unchanged. - Forge manifest, neutral ForgeService contract, forge registry + resolver, and a client forge-module registry. - GitHub code renamed to the neutral shape; PR/Issue attachment wording preserved. - forge.search.response enums parse tolerantly (unknown kind/auth state degrade instead of breaking the client). - createPullRequest reports typed CLI/auth errors instead of a generic message. - forge-resolver host/remote caches are LRU-bounded. - Forge host trust is explicit: only a known cloud host or a CLI-authenticated host is ever talked to; an unauthenticated GitHub Enterprise host fails resolution instead of routing to github.com. - Docs: forge-providers guide, glossary and i18n forge-copy conventions, architecture and rpc-namespacing terminology. - Vitest React Native mocks (unistyles, svg, linking, lucide) consolidated into shared aliased test-stubs. * feat(forge): GitLab adapter, forge-aware UI, pipelines and approvals GitLab adapter over the glab CLI on the neutral contracts: MR status, forge-aware UI, pipeline tree, and N-of-M approvals. - threadIsResolved is part of the neutral timeline item. - Pipeline load failures show an error instead of an empty section. - Manual pipeline jobs render as pending. - Fork/detached MR head pipelines are fetched by MR iid (glab ci get --merge-request). * feat(forge): Gitea family adapter (Gitea, Forgejo, Codeberg) One adapter over the tea CLI serving Gitea, Forgejo, and Codeberg on the neutral contracts. - CI status aggregates commit statuses and Actions runs together. - Gitea's terminal "warning" state maps to failure on server and client. - Gitea Actions check details are reachable from the PR pane by workflowRunId. * refactor(forge): localize compatibility handling * test(forge): expect normalized GitLab facts --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> |
||
|
|
6af1379357 |
Stop mobile sidebars getting stuck out of sync (#1953)
* fix(app): keep mobile sidebars synced with store Gesture-owned skip flags could swallow the next store sync after a no-op or overlapping gesture, leaving React state and Reanimated shared values split. Always reconcile both mobile sidebars from the panel store after gesture animations. * test(app): keep sidebar sync coverage pure * fix(app): keep sidebar close transitions idempotent * fix(app): keep mobile sidebars stable through interruptions Independent React and worklet state let stale gesture or animation callbacks overwrite newer panel commands. Use one revisioned target and one transient motion runtime so outdated callbacks cannot restore an earlier panel. |
||
|
|
d9b65e269f |
Scheduled runs each get their own workspace in the sidebar (#1934)
* feat(schedules): per-run workspaces, isolation and archive controls Scheduled agents never appeared in the sidebar: each schedule reused one stamped workspace and archived the run's agent on completion. Now every run mints its own workspace — the entity the sidebar shows — with per-schedule controls for isolation (local or worktree) and whether to archive that workspace when the run finishes. The schedule form is rebuilt on a non-React form model (open/commands/close) to end the flicker, edit-into-create contamination, and multi-host hydration bugs that came from effect-choreographed shared state. That lands the reusable foundations it needed: a form kit over a single control-geometry owner, a data-access taxonomy (replica/snapshot/fetch) with real load-state semantics, and lint/boundary guardrails so new screens inherit the paved road. Also fixes desktop combobox popovers truncating options when the trigger is narrower than the content. New schedule protocol fields are optional on both create and update, so old and new clients/daemons stay compatible. * test(app): complete mock themes for control-geometry tokens switch and terminal-profile-edit-modal now style through createControlGeometry, which reads theme.borderWidth[1], theme.opacity[50], and theme.colors.borderAccent. The hand-rolled mock themes in these two suites lacked those tokens, so the eager StyleSheet.create mock threw at import (collection error), not on any assertion. Add the missing tokens. * fix(schedules): address review findings on run cleanup, edit isolation, load error, sheet dismiss, and preference hydration - Server: archive the run's workspace even when agent creation fails, so a misconfigured provider no longer orphans a sidebar workspace/worktree. - Edit form: keep a stored worktree isolation while worktree eligibility is still resolving, instead of silently downgrading a saved schedule to local. - Schedules screen: show the load error/retry UI when every host fails, rather than spinning forever behind the loading branch. - Form sheet: fire the parent onClose on native gesture/backdrop dismiss so the sheet closes instead of re-opening. - Form model: apply late-loading saved preferences to untouched create-mode fields (never to edited schedules or user-modified fields). * fix(schedules): review batch — reconnect resubscribe, worktree prune, Android dismiss, cadence and provider edit safety - Push-router: re-send terminal and checkout-diff subscriptions after a reconnect; the old per-hook effect resubscribed on isConnected and the extracted router did not, so daemon restarts silently stopped terminals_changed pushes (root cause of the terminal-activity e2e failures). - Server: pass the run's source repo root when archiving worktree runs so the worktree is unregistered (git worktree remove/prune), not just deleted; also archive the workspace when agent creation fails even with archive-off (an agentless workspace has nothing to inspect). - Sheet: RN Modal onDismiss is iOS-only — notify dismiss once-per-close on the Android native path too; replace the JSDOM component test with a real Playwright dismiss spec and inline the trivial dismiss decision. - Form model: editing an interval schedule no longer rewrites its cadence to cron unless the cadence UI is touched (90-minute intervals have no cron equivalent); switching projects clears stale provider/model state and gates submit until the new host's snapshot resolves. * chore(lint): drop custom lint wrapper and boundary script, plain oxlint The import bans stay in .oxlintrc.json (oxlint-native no-restricted-imports). The receiver-sensitive checks the custom script enforced are not worth a parallel lint pipeline; if they come back it will be as oxlint rules. * fix(schedules): crash-safe run cleanup, capability-gated run options, resilient mutations - Persist the run's workspace/agent ids on the running-run record so a daemon crash mid-run can be recovered: restart archives the interrupted run's workspace under the same policy as normal cleanup (agentless always archives, otherwise archiveOnFinish is respected). - Hide Archive on finish (and omit archiveOnFinish/isolation from payloads) on hosts without workspaceMultiplicity — an old daemon ignores the fields, so offering the switch there would lie. - Optimistic pause/resume/delete no longer throw when the schedules cache holds a still-connecting entry alongside loaded data. - Mode field is gated on provider mode options, not a selected model, so model-less providers can pick their advertised modes. |
||
|
|
9968f2ba50 |
Speed up inbound WebSocket validation (#1895)
* perf(protocol): generate inbound ws validators * perf(client): use generated ws validation * docs(protocol): document generated validation * fix(protocol): make validator generation source-only * test(protocol): cover explicit provider model normalization * fix(protocol): preserve inbound compat defaults * fix(protocol): make validator import rewrite portable * fix(protocol): harden validation safety checks * fix(protocol): guard generated validator boundaries * fix(protocol): normalize legacy inbound defaults * fix(protocol): simplify generated validation safety net * fix(protocol): keep cold typecheck source-only * fix(protocol): encapsulate validator codegen * fix(protocol): bootstrap source-alias typechecks * fix(app): keep source aliases out of bundler config * fix(protocol): harden validator codegen packaging * fix(protocol): own zod-aot patches in generator * fix(protocol): trim validator safety net * fix(client): normalize provider updates before dispatch * fix(protocol): keep validator generation out of install |
||
|
|
9430a897e8 | Fix browser screenshot capture for parked webviews | ||
|
|
cb212b4e5c | Fix mobile launch with a saved workspace | ||
|
|
2c9db5279c |
Run Paseo from an official Docker image (#1740)
* Add Docker images and agent Docker Mods Ship official container images that run the Paseo daemon headless. One Dockerfile parametrized by BASE_IMAGE covers Debian 12/13, Ubuntu 22.04/24.04 and Alpine; it bundles Node 22, the npm-published server + CLI, a vendored s6-overlay as PID 1, and a small Docker Mods loader. Agents are chosen at runtime via DOCKER_MODS (pipe-separated mod images). Each mod is a FROM scratch image carrying only an install hook that runs `npm install -g <agent-cli>`; the loader pulls the layers from the registry, extracts them, and runs the hook before the daemon starts, so any requested agent is on PATH when Paseo probes provider availability. - docker/base: Dockerfile, install scripts, s6 services, mods loader - docker/mods/*: claude-code, codex, copilot, opencode, pi - docker/docker-compose.example.yml + docker/README.md - .github/workflows/docker.yml: multi-arch (amd64/arm64) buildx matrix, publishes to ghcr.io/getpaseo on version tags - docs/docker.md + CLAUDE.md docs index row * feat(docker): print pairing QR and link on daemon startup Add an s6 oneshot service that waits for the daemon to listen, then runs `paseo daemon pair` so the pairing QR code and link surface in the container logs. Best-effort: never blocks boot, skips gracefully when relay is disabled. Opt out with PASEO_PAIRING_QR=0. * build(docker): add Arch image support * ci(docker): build Arch without Buildx * docs(docker): document paseo env contract * feat(docker): add opt-in sudo mode * docs(docker): link env references * fix(docker): create agent config dirs * fix(docker): default home to /home/paseo * docs(docker): document agent auth setup * docs(docker): document relay port setup * fix(docker): install Node from tarball * docs: add Docker quick start * docs(docker): remove legacy home example * docs(docker): set container hostname * fix(docker): prepare opencode storage * fix(docker): allow paseo login shell * fix docker opencode permissions * ci(docker): use Node 24 actions * fix(docker): install bzip2 runtime tools * fix(docker): update Pi mod package * fix(docker): quiet default daemon logs * fix(docker): split home from state Docker images now keep HOME at /home/paseo and store Paseo daemon state under /home/paseo/.paseo by default. Existing volumes can keep the old layout by setting PASEO_HOME=/home/paseo. * fix(docker): keep mods out of paseo home * ci(docker): skip alpine arm64 builds * fix(docker): address review findings * fix(docker): verify s6 overlay downloads * fix(docker): honor custom healthcheck port * fix(docker): fail on mod extraction errors * feat(docker): add official container image Ship a focused daemon image with the bundled web UI enabled and document extending it with agent CLIs. * ci(docker): publish images only on stable releases * fix(docker): check daemon health over HTTP --------- Co-authored-by: Herbrant <cdavide98carnemolla@gmail.com> |
||
|
|
0d76654ba0 |
Show activity indicators for terminals and their workspaces (#1507)
* feat: show activity indicators for terminals and their workspaces A terminal surfaces whether the agent running inside it is working, idle, or waiting for input as a tab dot, and that state rolls up into the owning workspace status — so an agent run inside a terminal reads the same as a native agent. Finishing a turn raises a "Terminal finished" notification that routes back to the workspace terminal tab. Activity is reported by the agents, not parsed from terminal output: Claude and Codex command hooks and an OpenCode plugin post coarse state to a local daemon endpoint, and a source-agnostic per-terminal tracker maps it to working/idle/attention. Hook installation is opt-in and off by default because it edits your real agent config files; enable it under a host's Terminal settings and Paseo installs the hooks globally, runs them via `paseo hooks <agent> <event>`, and removes its own hooks when you opt back out. Installation and reporting are idempotent and fail-open, so a missing or failing hook never breaks the user's shell. * Fix terminal activity routing and CI test Test drift: anchor the provider-leak test to the repo root instead of process.cwd() so it passes under the server workspace CI runner. Also restore auth-first JSON parsing outside the terminal-activity route and route subdirectory terminal activity to the deepest active workspace. * Fix workspace resolver regression Code drift: terminal activity routing needs parent-workspace prefix matching, but agent directory and historical fetches keep exact registered workspace semantics, including archived exact matches. * Narrow terminal/draft workspace subscriptions to the fields they use terminal-panel and the draft agent tab subscribed to the whole workspace descriptor via useWorkspace but read only workspaceDirectory (plus projectKind in TerminalPanel, plus id in the draft tab), so they re-rendered on any field change of that workspace (status, diffStat, scripts). Switch them to the existing useWorkspaceDirectory / useWorkspaceFields hooks so they re-render only when the fields they actually read change. No behavior change. * Make TerminalActivity.state forward-compatible A strict z.enum rejects the whole payload when a newer daemon reports a terminal activity state this client predates, breaking the protocol's old-client-parses-new-daemon contract (the activity field has no .catch at its embedding in messages.ts, so the failure bubbles up). Degrade unknown states to "idle" (no indicator, no notification) via .catch, matching the enum-with-catch pattern used elsewhere in the protocol. Add a forward-compat regression test. |
||
|
|
72b67f48e3 |
Eliminate spiky terminal lag under load (#1500)
* Eliminate spiky terminal lag under load
Terminal output stuttered during heavy output and when the daemon was
busy. Two compounding causes, both confirmed by measurement:
- Every 256KB of output, the daemon dropped pending frames and re-sent a
full cell-grid snapshot (cloned across IPC, stringified to JSON) — a
10MB build did this ~40 times. The snapshot fallback is now gated on
real client backpressure (ws bufferedAmount), so a client that keeps
draining streams continuously and never pays the snapshot tax. This
also removes the periodic GC hitch the churn caused.
- Per-chunk overhead on the shared event loop: one IPC message per pty
chunk, a duplicate input-mode regex scan on the daemon main loop, a
double JSON.stringify per outbound message, and 16KB string realloc
per chunk. Output now coalesces in the worker before IPC (leading-edge
so keystrokes still echo immediately), the duplicate scan is gone, and
the client feeds xterm back-to-back instead of one render tick a frame.
Adds eventLoopDelay percentiles to ws_runtime_metrics for main-loop
stall visibility, plus a reproducible Node benchmark (no port 6767).
Measured: echo p50 7.7ms to 2.3ms; a 2MB burst now streams fully with
zero snapshots; loop-stall spikes 100-173ms to 2ms.
* Guard trace-level check against partial logger stubs
The isLevelEnabled gate added for the emit() perf fix crashed every
session test that injects a hand-rolled logger stub (10 of them omit
isLevelEnabled). Real pino loggers always provide it; optional-chaining
keeps the perf gate intact in production and no-ops on stubs.
* Make recent-output exit-summary test deterministic
The added test self-exited the child immediately after writing 3000
lines, then asserted the newest line was in the exit summary — but the
summary reads the headless xterm buffer, which parses writes
asynchronously, so a loaded CI runner saw a stale buffer (line-2707 not
line-2999). Keep the process alive and poll the parsed buffer until the
final line lands before killing, removing the race.
* Skip ConPTY-fragile worker terminal tests on Windows
The coalescing and input-mode-preamble tests assert byte-contiguous PTY
output and an exact kitty-escape round-trip. Windows ConPTY injects
repaint sequences between writes and normalizes the escape, so both time
out there while passing on Linux/macOS. Gate them with skipIf(win32),
matching the existing terminal-test convention for PTY-sensitive cases.
* Apply terminal barriers immediately when no writes are pending
A barrier op (snapshot/restore/clear) only needs the sentinel-write gate
to wait out plain writes still parsing in xterm's buffer. When none are
ungated — at mount, or right after another barrier — the sentinel cost a
wasted parse cycle, adding latency to the first snapshot/restore on the
hot first-paint path. Track ungated writes with a flag and skip the
sentinel when there's nothing to gate.
* Fix mobile startup saved-host e2e
* Unskip Windows worker terminal coverage
* Tighten worker coalescing assertion
* Reset terminal ungated writes on unmount
* Fix terminal regressions found in adversarial review
Three issues this PR's terminal changes introduced:
- Worker snapshot could duplicate coalesced output. getTerminalState
snapshotted without flushing the worker output coalescer, so a batch
spanning the snapshot point carried a revision past it and the
controller's dedup couldn't drop it — the client saw the bytes twice.
Flush before snapshotting.
- Relay clients lost backpressure protection. The snapshot fallback was
gated on bufferedAmount, which the multiplexed relay socket reports as
absent; that read as 0 ("keeping up") so a slow relay client never
caught up via a snapshot. Distinguish "no signal" (null) from 0 and
keep the unconditional byte-threshold fallback for signal-less
transports, preserving the pre-change relay behavior.
- Recent-output buffer was no longer a hard cap. A single chunk larger
than the limit was retained whole; slice its tail.
Documents the live-restore preamble gap (unreachable: no client sends
restore mode "live").
* Skip input-mode preamble test on Windows ConPTY
CI confirmed Windows ConPTY normalizes away the kitty keyboard escape
the child writes, so it never reaches the worker's input-mode tracker
and the preamble stays empty — the test times out. ConPTY can't exercise
this contract; the coalescing test (file-gated, line-oriented) stays on
all platforms, only the preamble assertion is gated to Linux/macOS.
|
||
|
|
6524677960 | Document checkout-local dev home defaults | ||
|
|
f1a20a9fe9 |
Add configurable service proxy (#1280)
* Add configurable service proxy * Fix service proxy URL used when opening scripts with a public base URL When a public proxy URL was configured, the View link was falling through to direct connection logic and opening the wrong URL. Move the public URL early-return before connection-type checks, and remove the now-dead duplicate check further down. * Add service proxy documentation Covers config options, URL generation format, wildcard DNS setup, and reverse proxy Host header requirements. * Cap combined public service label and document hostname format * Address service proxy review feedback |
||
|
|
53c14d9855 |
Extract client SDK package (#1052)
* Extract client SDK package * Polish SDK client identity defaults * Build client before dependent CI jobs * Restore daemon client server export * Extract protocol and client SDK packages * Fix provider override schema validation * Fix app test daemon client imports * Simplify workspace build targets * Fix CLI test server build bootstrap * Run SDK package tests in CI * Fix rebase package split drift * Restore lockfile registry metadata * Update SDK config test for prompt default * Move terminal stream router test to client package * Fix rebase drift for protocol imports * Fix SDK agent capability fixture * Restore legacy server client exports * Fix server export compatibility test * Advertise custom mode icon client capability * Remove server daemon-client exports * Format rebased mode control import * Fix rebase drift for protocol imports Files added by upstream PRs (#893, #1147, #1154) referenced the pre-split shared/ paths that this branch moves into @getpaseo/protocol. Redirect those imports to the protocol package so typecheck stays green after the rebase. |
||
|
|
008e4e846f |
fix(app): make slash command popover interactive on Android
The popover was positioned `bottom: 100%` of its parent inside the composer and worked on iOS/web; Android's hit-test by parent bounds drops touches on children that overflow their parent, so taps and scrolls went through to the chat behind it. Move the popover into a `<Portal>` at the app root and apply the same Reanimated keyboard SharedValue as the composer so it tracks the keyboard in lockstep. New `docs/floating-panels.md` captures the gotchas (Android hit-test, Portal lifecycle/transforms, status-bar offset, the two-measurement flash) and the canonical files. |
||
|
|
c88f6fb2c2 | docs: update built-in provider references (#1105) | ||
|
|
57db997028 |
docs: lock down hover pattern, fix user message bubble flicker
The user message bubble had hover on a `Pressable` wrapping the bubble, with the timestamp and copy button as siblings outside it — moving the cursor onto them lost hover. Wrapping both in a single Pressable then flickered, because `TurnCopyButton` is itself a Pressable and nested Pressables fight over hover state. Rewrite `UserMessage` to match the workspace row pattern: hover-tracking on a plain outer `View` with `onPointerEnter`/`onPointerLeave`, nested Pressables freely inside. Pointer events are non-bubbling DOM events, so descendants can't disrupt the outer hover. Rewrite `docs/hover.md` with the workspace row as THE canonical pattern, not one option among several. Names the three failure modes that come from skipping it — including "nested Pressables fight over hover" — and explicitly bounds when `Pressable.onHoverIn`/`onHoverOut` is acceptable (self-styling only). Update `constants/platform.ts` so its hover guidance points at the doc instead of recommending the broken pattern. |
||
|
|
eb7fa02ac8 |
Surface GitHub auto-merge actions (#1001)
* Surface GitHub auto-merge actions * Namespace GitHub auto-merge RPC * Update workspace git refresh test expectation * Avoid redundant GitHub refresh queue |
||
|
|
5b884aee0c |
Import existing Claude/Codex/OpenCode sessions (#766)
* Restrict provider import fan-out to enabled, non-derived allowlist Move provider eligibility from a session-level post-filter to the agent manager's fan-out so disabled providers are never spawned (fixes the Gemini-disabled hang) and derived providers (like a custom Z.AI extending Claude) don't duplicate their base provider's sessions. Add an IMPORTABLE_PROVIDERS allowlist (claude, codex, opencode); ACP-based providers are excluded from import discovery entirely. * Filter Codex persisted threads by cwd before hydrating turns Codex's listPersistedAgents previously called thread/read includeTurns on every thread returned by thread/list, even when the import sheet was scoped to one cwd. With many recent threads outside the scope, that fan of reads dominated the import-sheet open latency. Add an optional cwd hint to ListPersistedAgentsOptions, thread it through the agent manager, and have Codex pre-filter the cheap thread/list rows by cwd before scheduling the per-thread hydration. Widen the thread/list window when filtering since most rows will be from other cwds. Other providers ignore the hint. * Fan out import session fetch per enabled provider Sheet now reads the providers snapshot already warmed by the workspace screen, filters to enabled importable providers (claude/codex/opencode), and issues one fetchRecentProviderSessions request per provider via useQueries with limit 15. Results are merged, deduped by handle, and sorted by last activity. Falls back to a single unfiltered request when the daemon does not expose a provider snapshot (older servers). Per-provider failures show inline so a single broken provider does not hide the rest. * Add provider filter badges to import sheet Show "All" plus one badge per enabled importable provider above the session list when more than one is available. Single-select narrows the already-merged list — no refetch. Each row now shows the provider's icon next to its label so multiple providers stay visually distinct. Selection resets when the sheet closes. * Distinguish already-imported empty state in import sheet Track how many descriptors were dropped because their handle was already imported, surface the count back to the client as an optional `filteredAlreadyImportedCount`, and switch the sheet copy to "All recent sessions are already imported." when the merged list is empty solely due to that filter. * Cover already-imported provider session filtering * List persisted OpenCode sessions for import * Revalidate import sessions when sheet reopens * Add agent persistSession plumbing * Drop redundant visibility-transition refetch and test boilerplate The import sheet's queries are gated on `enabled: visible`, and react-query already refetches stale data when `enabled` flips on, so the manual `wasVisibleRef` + `invalidateQueries` effect was redundant. Collapse the two `selectedProvider` reset effects into one. Drop `expect(...).toBeTruthy()` after `findByText` / `getByText` — those helpers throw on miss, so the assertion was noise. Drop `| null` from `buildOpenCodePersistedAgentDescriptor`'s return type (it never returns null) and the dead `Boolean(descriptor)` filter at the caller. * Add provider-opaque session import path Introduces fetchRecentProviderSessions on the daemon client plus an importAgent overload accepting providerId/providerHandleId, and a projection helper that renders persisted descriptors as provider-opaque recent-session payloads with prompt previews. * Unslop the import-session feature Centralizes the importable-providers list, normalizes import_agent_request shape at one boundary, and removes the per-provider fan-out fallback in the import sheet. Old daemons without features.providersSnapshot now show "Update host" instead of being served a degraded path. Replaces internal-helper unit tests on the OpenCode and Codex providers with boundary tests through listPersistedAgents; makes storageRoot injectable on the OpenCode provider so the boundary test can target a temp directory. Splits the CLAUDE.md back-compat rule into two contracts: protocol stays backward-compatible (always); features may require new daemons (no fallback paths). Tags the two new compat shims with COMPAT(name) and target removal dates. * Extract importable-session listing into its own module Move listImportableProviderSessions out of session.ts and drop the unused generic listPersistedAgents from AgentManager. |
||
|
|
c097678cdf |
Surface paseo subagents in a collapsible section above the composer (#532)
* Surface paseo subagents in a collapsible section above the composer
Subagents no longer auto-open as tabs. Each parent agent's pane shows
a compact tab attached to the top of the composer, listing its direct
children with name and status; clicking a row opens or focuses the
subagent's tab. The section is provider-neutral so Claude Code Task
and Codex subagent sources can feed the same UI later. Reconciliation
now separates pruning (`activeAgentIds`) from auto-opening
(`autoOpenAgentIds`) so manually opened subagent tabs survive.
* Tighten subagents composer code from review
Collapse duplicate helper in workspace reconciliation, swap a hand-rolled
shallow comparator for fast-deep-equal (matching repo convention), drop
an unused React default import and a single-use formatCount helper, and
extract a shared PaneOverrides type in the agent-panel test.
* Fix oxlint failures in subagents code
Switch type aliases to interfaces, hoist inline JSX styles/handlers in
SubagentsSection to satisfy react-perf rules, and lift the test
makeAgent helpers onto AGENT_DEFAULTS so their cyclomatic complexity
drops below the configured cap.
* Add missing unistyles mock exports after rebase
After rebasing onto main, SubagentsSection now imports both
useUnistyles and (transitively, via WorkspaceTabIcon) withUnistyles.
Two test mocks needed updating to expose them.
Test drift fix: the implementation contract is correct; the local
react-native-unistyles mocks were stale.
* Tuck subagents tab under the composer
Subagents tab now grows visually from beneath the composer's rounded
top edge: the surface overlaps the composer by the corner radius so
the rounded curve nests into the tab. Composer's top padding is
removed so the tab sits flush against the input box with no
click-dead gutter. The tab is full-width in both states, the header
label drops the medium weight to follow design-system §3 (content
text inside a surface is normal), and hover covers the entire
visible tab when collapsed but stays scoped to the header row when
expanded.
* Decouple subagent close-tab from archive
Closing a subagent's tab no longer archives the agent — the tab is
removed from the per-client layout, the agent stays in the parent's
track until explicitly archived. Track rows gain an archive button (X)
to make the lifecycle gesture explicit. AgentManager.archiveAgent now
cascades to children carrying the paseo.parent-agent-id label so
subagent fleets don't outlive their orchestrator.
Trade-offs documented in docs/agent-lifecycle.md, including the known
limitation that handoff agents launched via the same MCP path get
cascade-archived alongside true subagents until a richer relation
model lands.
* Stabilize subagent archive button hover
Apply the sidebar workspace row pattern to fix two issues with the
new archive button: the row no longer drops hover when the pointer
moves onto the button, and the button slot reserves layout space
when invisible so adding/removing it doesn't shift the row.
The wrapper View tracks hover via onPointerEnter/onPointerLeave
(workspace rows do the same), and the slot stays mounted with
opacity 0 + pointerEvents="none" when the button is hidden. Also
swap the X icon for the Archive icon to match the gesture's intent.
* Match design language for subagent archive button
Hover state now changes the icon color from foregroundMuted to
foreground, matching the project-row trailing-action pattern in
sidebar-workspace-list.tsx. Drops the surface3 background hover
that didn't match the design language.
Adds a tooltip ("Archive subagent") on the trigger using the
canonical Tooltip + TooltipTrigger asChild pattern. Disabled when
the slot is hidden so it never appears unbidden.
* Slot subagents into one module + harden cascade
Reshape the subagents-in-composer feature so it lives behind one
module and integrates at a few explicit sites instead of being
smeared across generic files.
- Extract `PARENT_AGENT_ID_LABEL` to `packages/server/src/shared/agent-labels.ts`. Sweeps 16 literal sites across server, client, CLI, and tests.
- `packages/app/src/subagents/` is now a deep module: `index.ts` is the only React entry (`SubagentsSection`, `useArchiveSubagent`, `useSubagentsForParent`, `selectSubagentsForParent`, `resolveCloseAgentTabPolicy`, `shouldAutoOpenAgentTab`); `policies.ts` is a second designed entry for non-RN data consumers. Internal files renamed (`section.tsx`, `select.ts`, `close-tab-policy.ts`, `auto-open-tab-policy.ts`, `use-archive-subagent.ts`).
- Move the 22-line subagent archive flow out of `agent-panel.tsx` into `useArchiveSubagent`. Agent-panel becomes a one-line consumer.
- Move the close-tab subagent branch out of `workspace-screen.tsx` into `resolveCloseAgentTabPolicy` (discriminated union).
- Move the auto-open exclusion rule out of `workspace-agent-visibility.ts` into `shouldAutoOpenAgentTab`.
- Theme chevrons in `section.tsx` via `withUnistyles`; drop the forbidden `useUnistyles()` call from the hot path.
Server-side cascade hardening:
- Single write path: extract `markRecordArchived` and route both `archiveAgent` and the off-memory cascade branch through it. Off-memory cascaded children now notify subscribers (previously silent).
- Cascade no longer swallows child failures with `try/catch warn`. Failures propagate.
- Cascade test extended from 1 weak assertion to 5 contract tests: full archive shape (archivedAt, normalized lastStatus, requiresAttention=false), running child runtime stop, off-memory branch, subscriber notification for in-memory and off-memory children, partial-failure surfacing.
Net diff vs the start of this PR: -1037 / +444 lines. The feature now slots into the repo at four explicit sites: `agent-panel.tsx` (3 imports + a hook + the section), `workspace-screen.tsx` (one policy import), `workspace-agent-visibility.ts` (one predicate import), and `agent-manager.ts` (the cascade plus the shared label constant).
|
||
|
|
3a5d999006 |
docs: refresh CLAUDE.md doc index, fold design.md into design system
- Make `docs/` the explicit source of truth: "the docs"/"check the docs" mean this directory, not the web. - List all 16 docs in CLAUDE.md (was 8); group by purpose so unistyles, glossary, data-model, providers, etc. are discoverable. - Delete the old design.md (designing-features prose) and rename design-system.md → design.md so design tokens own the name. |
||
|
|
906205f97a |
Add browser pane element picker (#670)
* feat: add browser pane element picker Port the browser pane from PR #198 and route picked elements through workspace attachments so the context follows every agent composer in a workspace. Co-authored-by: Jason Kneen <jason.kneen@bouncingfish.com> * docs: document electron platform overlays * fix: harden browser pane navigation * fix: polish browser tab interactions * fix: route browser pane focus shortcuts --------- Co-authored-by: Jason Kneen <jason.kneen@bouncingfish.com> |
||
|
|
dec47d72d9 |
docs: lowercase internal docs + migrate website docs to public-docs/ (#634)
* docs: rename to lowercase + drop leftover plans Rename docs in docs/ to lowercase kebab-case for consistency and update all references in CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md, packages/server/CLAUDE.md, and inter-doc links. Drop two leftover design plan docs: - docs/ATTACHMENT_BASED_REVIEW_CONTEXT_PLAN.md - docs/plan-approval-normalization.md * docs: drop stale uppercase entries from case-insensitive rename * feat(website): power /docs from public-docs/ markdown tree Move website docs out of TSX route components and into a root-level public-docs/ directory of plain markdown files with frontmatter (title, description, nav, order). - Add packages/website/src/docs.ts loader using import.meta.glob with ?raw to compile the markdown into the bundle at build time. - Replace the 9 hand-written docs/*.tsx routes with a single $.tsx catch-all that renders any slug via react-markdown. - Drive the docs sidebar nav from frontmatter order/nav. - Auto-discover docs routes in vite.config.ts so the sitemap stays in sync without manual edits. * fix(website): bind dev server to 0.0.0.0 so port collisions trigger fallback `host: "127.0.0.1"` (or unset) lets macOS coexist with another process holding an IPv6 dual-stack `*:8082` socket, so Vite never sees EADDRINUSE and silently binds alongside it. Forcing IPv4 wildcard makes the conflict real, and Vite's default `strictPort: false` falls through to the next free port. * fix(website): restore docs page styling after markdown migration Add a .docs-prose class that mirrors the styling the original docs/*.tsx components hand-rolled (h1/h2/h3 sizes, paragraph/list spacing, link colors, code blocks, callout-style blockquotes). ReactMarkdown was emitting unstyled HTML because the previous wrapper class only had inline-code rules — headings and code blocks fell back to user-agent defaults. |
||
|
|
6a61066e2c |
fix(desktop): warn about Rosetta installs on Apple Silicon (#621)
* fix(desktop): warn about Rosetta installs on Apple Silicon * docs: note workspace build sync before type fixes |
||
|
|
480b18c500 | Document targeted npm lint and format scripts | ||
|
|
f9e18424e9 | Require lint after changes | ||
|
|
ce4b0d54c7 |
chore: reformat codebase with oxfmt
Apply prettier-compatible formatting across the repo to match the incoming oxfmt configuration. Mechanical reformat only — no logic changes. Covers YAML quote normalization, package.json key sorting, Markdown/TOML formatting, and minor TS whitespace tweaks. |
||
|
|
89b87c0235 | docs: add local test suite execution rules |