Commit Graph
543 Commits
Author SHA1 Message Date
Mohamed Boudra 73390346ca fix(ios): address clipped chat and Markdown text (#6445)
* Revert "fix(app): reflow iPad chat text after pane resize (#5716)"

This reverts commit 5715b5ba62.

* docs(changelog): describe iOS text clipping fix for 0.11.2

* chore(release): cut 0.11.2
2026-10-09 16:35:43 +01:00
0009a7e717 feat(workspaces): add background visibility (#5039)
* feat(server): let clients create internal agents

The daemon's own branch-name and commit-message generators run as
internal agents: never persisted, listed, flagged, announced, or seen by
plugin lifecycle hooks. Nothing outside the daemon could ask for one,
because the wire config schema strips `internal` and the agent.create
transform re-pins it. A plugin that runs a one-turn helper per event
therefore left an archived session in History every time.

Add an optional `internal` flag to `create_agent_request` (inherited by
`agent.create.request`), copy it onto the config in the session's create
path, and skip provider session persistence for it like the generators
do. The client SDK forwards the flag on both creation paths and gates it
once on `server_info.features.internalAgents`.

Two daemon paths assumed every agent is public and had to follow:
`resolveAgentIdentifier` now accepts a live agent by exact id, since the
id set it matched against came from listings that exclude internal
agents and left `wait_for_finish` answering "Agent not found"; and
archiving an internal agent now closes it without writing the record
that `registry.applySnapshot` used to leave behind, so `autoArchive`
does not accumulate hidden files on disk.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(server): keep internal agents whole through failure, archive, and wait

Review follow-ups for internal agents:

- A create that fails after the runtime is registered now closes the
  internal agent before rethrowing, so a hidden runtime cannot outlive
  the failed request. Public agents keep today's behaviour.
- Archiving an internal agent cascades to attached children like any
  parent. cascadeArchiveChildren takes the parent record instead of
  reading it back from storage, where an internal agent never is.
- The archived snapshot and last message of an internal agent stay
  readable by exact id for ten minutes, so create-then-waitForFinish
  returns the finished turn whether the wait lands before or after an
  auto-archive. Storage plays that role for public agents.
- The internal-agent e2e scenarios live in the existing
  create_agent_request lifecycle suite rather than a new file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(server): cascade to live internal children and keep a failed internal create's worktree until it closes

Internal children never reach storage, so the parent archive cascade scans
live agents for them as well. A failed internal create now clears the
created id only after the runtime is archived, so worktree cleanup cannot
delete the directory under a runtime that would not close. Stored agent
payloads carry lastError, so a retained failed helper reports its error
through wait_for_finish instead of the generic message.

* feat(cli,mcp): expose internal agents and document --no-wait

paseo run gains --internal and create_agent gains internal, both forwarded
to the create request. --no-wait becomes the documented spelling for
returning immediately, matching paseo send; --background and --detach stay
as hidden aliases under COMPAT tags.

* test: follow the --no-wait rename and stub getRetiredInternalAgent

* feat: list internal agents on request and add internal workspaces

Internal agents stay hidden by default, but a caller can now ask for
them: `fetch_agents_request` takes `filter.includeInternal`, snapshots
carry `internal: true`, and `paseo ls --internal` shows them in an
INTERNAL column. The app never passes the flag.

Workspaces get the same flag. `workspace.create.request` with
`internal: true` persists a hidden workspace: listings, update
subscriptions, sync reads, empty-project counting, open-by-path, MCP
`list_workspaces`, and plugin workspace hooks all skip it unless the
caller opts in with `filter.includeInternal`. Every agent created inside
an internal workspace is internal and unpersisted; AgentManager applies
that from the workspace id before the plugin hook so creators cannot
opt out. Archive and shutdown enumerate internal agents so nothing
inside an internal workspace outlives it.

Surfaces: `paseo workspace create --internal`, `paseo workspace ls
--internal`, `paseo run --internal --new-workspace`, MCP
`create_workspace({ internal })`, SDK `workspaces.create({ internal })`
and `list({ filter: { includeInternal } })`. Clients gate on
`features.internalWorkspaces`.

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(workspaces): add background visibility with inherited creation defaults

* fix(cli): resolve workspace callers on the selected daemon

* test(workspaces): keep caller integration coverage in server suite

* test(cli): exercise workspace callers through CLI integration harness

* test(mcp): remove obsolete execution flags from browser fixtures

---------

Co-authored-by: marvin-ambrozio <marvin@gustavo.eng.br>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-10-09 09:31:50 +01:00
Mohamed Boudra ab10a6694c chore(release): cut 0.11.1 2026-10-07 22:30:39 +02:00
Mohamed Boudra 22488d4502 chore(release): cut 0.11.0 2026-10-07 15:27:08 +02:00
paseo-bot[bot] 2974d7c960 fix(desktop): keep browser_type text out of the focused Paseo composer (#6207)
browser_type committed its text with CDP Input.insertText on the tab's
debugger. Chromium commits that text into the focused element of the
whole Paseo window, so when the user was typing in an agent composer
and the browser tab did not hold focus, the agent's text landed in the
composer instead of the page.

Insert the text with the tab's own webContents.insertText, which
commits into the tab's focused element regardless of window focus.

Fixes #6182

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-06 11:59:59 +02:00
Mohamed Boudra 15d774d4a1 chore(release): cut 0.11.0-beta.5 2026-10-06 00:51:38 +02:00
Ruichen ZhouandMohamed Boudra 84054f8d72 fix(desktop): restore titlebar dragging after scrolling chat (#5233)
* fix(app): restore desktop screen header dragging

* fix(desktop): keep the titlebar draggable after scrolling chat

* fix(desktop): preserve global control exclusions without blocking chat headers

Keep programmatic focus containers out of the global interactive no-drag rule. Retain semantic controls everywhere, including window-owned sidebar toggles and negative-tabindex buttons. Verify both header rows and independently positioned controls with native input.

Co-authored-by: Ruichen Zhou <ruichenzhou@outlook.com>

* fix(desktop): isolate chat content from window drag regions

Electron includes scrolled interactive rectangles outside their viewport in window hit testing. Reset window regions at the chat viewport while preserving the global control exclusions for chrome and portals.

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-10-05 23:09:19 +01:00
Callum WongandMohamed Boudra db303be3c2 fix(nix): ship resolved daemon dependencies and working terminals (#5523)
* fix(nix): ship @getpaseo/server's main export in the desktop closure

The desktop's runtime-paths.js locates the server package root with
require.resolve("@getpaseo/server"), which resolves through the exports
map to dist/server/server/exports.js. The desktop never loads that file,
so nft doesn't trace it, and the Linux Nix desktop build fails to start
the daemon with MODULE_NOT_FOUND.

* fix(nix): retain the server resolution anchor for CLI and desktop

Run the existing Nix daemon smoke through the built CLI so package-root resolution is exercised before readiness and HTTP checks.

Co-authored-by: Callum Wong <mail@callumwong.com>

* fix(nix): ship complete terminal runtimes

Resolve runtime dependency edges at their importing modules so package links and exports survive closure tracing regardless of npm hoisting. Use the native loader's selected files and verify terminal commands and hook CLI execution through the built package.

Co-authored-by: Callum Wong <mail@callumwong.com>

* test(desktop): make packaged CLI path expectations portable

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-10-05 19:25:48 +01:00
Mohamed Boudra 6b00310f7b chore(release): cut 0.11.0-beta.4 2026-10-05 04:02:22 +02:00
Mohamed Boudra 6166a7aca5 chore(release): cut 0.11.0-beta.3 2026-10-02 02:15:43 +02:00
Mohamed Boudra 8498c74169 chore(release): cut 0.11.0-beta.2 2026-10-01 16:25:00 +02:00
Mohamed Boudra 70df09daeb Restore desktop usage and subscription discovery across harnesses (#5844)
* fix(app): hide the sidebar Usage item while it has no summary

Without a summary window with data, the item fell back to a plain "Usage"
row that duplicated the footer's Usage icon. It now renders nothing, and the
footer rows leave it out so an otherwise empty rows block and its separator
disappear with it.

* fix(desktop): restore built-in usage in packaged apps

The external plugin compiler cannot traverse app.asar, and dependency packaging drops declarations needed by import validation. Ship complete built-in resources beside the archive and verify registration in the packaged desktop smoke check.

* fix(usage): restore login discovery across harnesses

Discover subscription accounts independently of agent sessions and retain each account’s logins in preference order so an unusable credential can fall back to another login for the same account.
2026-10-01 14:51:11 +01:00
Mohamed Boudra 4aa2f2817d chore(release): cut 0.11.0-beta.1 2026-10-01 10:48:28 +02:00
paseo-bot[bot] 5de45e2086 fix(desktop): capture the annotated element when the app is zoomed (#5703)
The browser pane measures the selected element in page CSS pixels, but
capturePage takes device-independent pixels. Paseo's zoom carries into the
in-app browser, so at any zoom other than 100% the screenshot attached to
an annotation was offset and undersized. Scale the region by the guest's
zoom factor before capturing.

Closes #5690

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-09-29 20:08:05 +02:00
paseo-bot[bot] 87818812ad Stop the desktop app from launching a process every second when no local daemon runs (#5636)
* fix(desktop): stop launching the CLI every second when no local daemon runs

The app polls the local daemon status once a second until it learns a
server id, and a stopped daemon has none, so the poll never ends. Each
poll ran `paseo daemon status` in a new process, which on Windows is
Paseo.exe and shows the busy cursor about once a second while the app
sits idle. The status handler now answers "stopped" in-process when the
home has no running daemon, which is what the CLI reports in that case.

* fix(desktop): report an errored daemon when its state file cannot be read

Reading the daemon instance record ran before the try in
resolveDesktopDaemonStatus, so an unreadable paseo.pid made the status
IPC reject instead of returning the errored status the CLI failure path
produced.

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-09-29 11:19:19 +02:00
Mohamed Boudra c481ecf3e1 chore(release): cut 0.10.0 2026-09-28 14:27:01 +02:00
Mohamed Boudra 52d345db7f chore(release): cut 0.10.0-beta.1 2026-09-27 13:51:11 +02:00
Mohamed Boudra d7b7016cc9 feat(settings): polish navigation and controls (#5459)
* feat(settings): polish navigation and controls

* test(settings): update navigation assertions
2026-09-26 20:26:04 +02:00
Mohamed Boudra e1c769c018 Make password-protected daemons work across local and remote connections (#5393)
* feat(auth): authenticate daemon clients through hello

Give local CLI and desktop clients a per-run credential while remote clients send the host password inside the protocol. Keep relay passwordless admission for this mobile release so existing saved hosts keep working until the follow-up enforcement release.

Move password recovery for saved hosts into host settings and keep pairing credentials in the add flow. Include isolated web QA evidence for both flows.

* fix(auth): preserve Hub bootstrap and local credential expectations

Hub-owned sockets already carry admission; keep their hello handling synchronous through session bootstrap while resolving credentials asynchronously for ordinary sockets. Keep the resolver out of lightweight configuration imports.

Update CLI expectations for same-home credentials, let a rejected connection yield to another usable connection, and clear pairing secrets when add-flow targets change.

* fix(pairing): retain passwords across edits to the same target

Track the last valid pairing host while a URI is edited. This preserves an entered password for whitespace and partial edits but clears it when a different host becomes the target.

* test(auth): align local credential checks across platforms

Expect the CLI to authenticate through the credential in its selected home, and verify POSIX file modes only where Node exposes them.

* feat(app): move host password into a settings row with a modal

The host page rendered an always-visible password form above Name on every
host. Replace it with a Password row under a Security section below
Appearance; tapping it opens a modal with Save and, when a password is saved,
Clear. The connection-error callout keeps its guidance and gains a Set
password button that opens the same modal. Drop the unused
pairing.hostPassword body/reopen/saveError strings.

* chore: remove committed QA screenshots

QA evidence stays out of the repository; the PR body describes it in words.

* feat(app): keep the host password out of the host page

The saved password is connection data, entered only when a host is added. A
Password setting on the host page reads as the daemon's own password, so the
row, modal, and callout button are gone. A rejected host shows the daemon's
reason with guidance to remove the host and add it again with the password.
useHostMutations no longer exposes setHostPassword; relay imports still set it
inside the runtime.

* feat(app): show host connection errors in an error alert

HostConnectionError renders the existing Alert with the connection error as
its title. Auth rejections add the re-add guidance as the description; other
errors show the title only. The Overview and Connections pages share it.

* style(app): soften alert borders to half-opacity accent

* feat(app): size alerts on the button scale

Alert takes a size prop (xs/sm/md/lg, default md) whose geometry lives next to
the button's in control-geometry: one font size for title and description, the
button icon size centered on the first text line, button padding and radius,
and a one-line alert as tall as the button of the same size. Consumers pick
sm among settings rows, cards, panels, and modals, and md when the alert is
the whole page or sheet state.

* style(app): roomier, rounder alerts with a tight title block

Alert sm padding returns to the original 12/16 and every size is at least
borderRadius.xl. Title and description sit a small gap apart with no line
height override. The icon slot carries a zero-width text strut in the alert's
font so the icon centers on the first line for titled, title-only, and
description-only alerts.

* refactor(app): align the alert icon with a centered lead row

Drop the zero-width strut. The icon and the alert's first text (title, else
description) share one row centered on each other; the description and actions
sit below, indented by the icon slot width plus gap so they start at the lead
text's left edge.

* refactor(app): drop the unused Alert icon override
2026-09-26 19:53:43 +02:00
Mohamed Boudra c67b7158b4 chore(release): cut 0.9.2 2026-09-24 12:05:08 +02:00
paseo-bot[bot] fbe5005aa1 Allow Backspace in custom keyboard shortcuts (#5224)
Capture modified Backspace shortcuts in the editor while keeping bare Backspace as the erase-last-combo key. Cover the reported Mac chord, its Windows Ctrl equivalent, and the erase behavior in the desktop renderer suite.

Closes #5215

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-09-23 22:39:00 +02:00
Mohamed Boudra 8186585201 chore(release): cut 0.9.1 2026-09-22 20:34:13 +02:00
Mohamed Boudra 7f7e60bcbb chore(release): cut 0.9.0 2026-09-22 12:25:40 +02:00
Mohamed Boudra e9d32a17d6 chore(release): cut 0.9.0-beta.2 2026-09-18 11:41:13 +02:00
Mohamed Boudra 7c1958f5b0 chore(release): cut 0.9.0-beta.1 2026-09-17 19:39:33 +02:00
Mohamed Boudra 92b92a81c8 perf(desktop): reduce daemon management memory overhead (#5007)
Desktop and CLI helpers imported daemon bootstrap through the server root, while supervisor configuration retained the full wire schema tree. Expose existing capabilities through narrow package entrypoints and share configuration schemas without loading message definitions. Guard the dependency trees without tree-shaking so unused exports cannot hide the runtime cost.
2026-09-17 15:18:12 +02:00
Mohamed Boudra c7db3c5a8c Let plugins open external links and workspace browsers (#4972)
* feat(plugins): open external links and workspace browsers

* fix(plugins): refuse invalid links and unavailable browser workspaces

* docs(plugins): clarify external links and browser navigation
2026-09-16 23:25:17 +02:00
Mohamed Boudra d8dd189b94 ci: simplify and de-flake the test suite (#4868)
* chore(ci): audit packaging costs and repeated test execution

Read-only audit: Linux packaging and smokes consume 12-14 minutes; all three CLI shards repeat the 294-test source suite and build the server twice. Preserve the 17 required contexts, move packaging to main, run CLI source tests once, and consolidate repeated E2E setup into journeys. Historical logs and the complete audit were reported outside the repository.

* ci: move packaging to main and remove repeated CLI suites

PR runners were rebuilding distribution artifacts and executing the entire CLI source suite on all three E2E shards. Keep packaging coverage after merge, let the local E2E runner own its dependency build, and execute source tests once in an existing required check. Root typecheck already validates plugin examples.

* test(ci): consolidate journeys and remove repeated startup work

Run user journeys with shared setup, retain one Hub CLI contract, and weight the existing Playwright shards using observed costs. Remove implementation-only and ad-hoc measurement checks. Await root-watch closure and publish creation failure commands atomically so teardown and failure injection have completion boundaries.

* test(hub): remove incidental observation and speculative shutdown changes

Hub control RPCs need a connected client without an agent observation. Preserve the failed-create subscription contract and remove product shutdown changes whose Windows cleanup cause was not reproduced.

* fix(client): clear the scheduled retry when a connect attempt opens a transport

An explicit connect() during reconnect backoff opened a new transport but
left the scheduled retry armed. When it fired on the healthy connection it
disposed the transport and reconnected, dropping requests in flight. This
is why owned-subscriptions-old-daemon could still fail after #4858 added
the explicit reconnect. attemptConnect now owns the rule ensureConnected
already applied; the new unit test fails on the old client.

* test(e2e): keep the completed-block, font-readiness, and live-page guards inside journeys

- agent-stream-ui: the auto-scroll journey anchors on the completed intro
  block by content and requires its Markdown root to stay the same mounted
  node while later blocks stream (#1989 guard, reshaped off the tail block).
- changes-pane: the code-typography journey holds font loads and requires
  no diff geometry before they resolve, then applies the font change.
- agent-timeline-pagination: the live-output page load runs against a
  thirty-minute mock stream and gates the release on the running turn, so
  the status assertion no longer races a ten-second turn.

* test(desktop): install the Electron binary once before the unit suite

Electron 44 downloads its binary on the first Node require("electron")
instead of at npm ci. Vitest workers importing electron-log/main raced
that download and extraction (File exists on dist/electron, then
"Electron failed to install correctly" in a sibling worker) on both
Ubuntu and Windows desktop-tests jobs. Run the package's idempotent
install-electron in one process before vitest forks.

* ci(desktop-packages): pin actions and smoke packaging-path pull requests

Pin checkout, setup-node, and upload-artifact to full commit SHAs. Keep
packaging off ordinary PR CI, but run the Linux package smoke on pull
requests that touch packages/desktop, nix, or the workflow so a packaging
or sandbox regression cannot land on main unchecked. The desktop release
workflow already builds and smokes its own artifacts on all platforms.
2026-09-15 13:26:41 +02:00
Mohamed Boudra 6215e08efe Smooth streaming replies with a continuous left-to-right fade (#4859)
* feat(app): reveal streaming text as one continuous fade front

Streaming assistant text is released by word and every character fades in
over the same 150 ms envelope from a start time owned by the message. Start
times never decrease in reading order, so a line paints as a single front
moving left to right, however the text is chunked. Per-word envelopes with an
internal stagger let the next word's first letter overtake the previous word's
last letter, which read as holes and flashing words at the real frame rate;
100 ms frame strips hid it.

Native renderers receive the fade ranges as a prop on a host view wrapping the
root Text, so ranges and text land in the same Fabric mount transaction. The
earlier binding sent ranges through a separate async module call keyed by view
tag and had to reconcile them against the view's text; on a fast phone the UI
thread lags JS by several commits, mounts and calls interleave, and every
reconciliation heuristic guessed wrong. Native code no longer infers newness
or creates start times.

The mock load-test provider now separates cycles with a blank line so a cycle
heading is not appended to the previous paragraph.

* fix(ci): refresh streaming dependencies and isolate E2E setup

* test(app): distinguish fade cleanup from Markdown replacement

* test(app): detect restarted fades during Markdown updates

* docs(app): keep streaming guidance focused on constraints and QA

* test(app): control animation time in Android fade assertions

Fixed wall-clock sleeps let emulator scheduling select the sampled animation frame. Inject the animator time source and drive paint at exact timestamps within one UI-thread operation; production keeps the system clock. Verify the mid-fade sample and the exact cleanup boundary without sleeps, polling, or retries.

* fix(test): compile the desktop renderer before lifecycle checks

The lifecycle fixture treated Metro /status as app readiness. Its first
Settings click therefore spent its 90-second UI budget compiling the cold
bundle. An isolated cold-cache run restricted to one CPU reproduced the
click timeout before the app runtime had loaded.

Reuse the browser suite's existing bundle warmup before launching Electron,
and preserve Metro output in the lifecycle artifacts. The UI timeout,
build-request timeout, lifecycle assertions and product behavior are unchanged.

CI did not preserve Metro output, so its precise startup timing is not
recoverable from the failed run. The retained log makes subsequent startup
failures diagnosable without relying only on an empty renderer text dump.

Validation: three complete lifecycle E2E runs under private Xvfb displays;
existing Metro readiness tests; app typecheck; targeted lint; root format.

* test(file-observer): await burst delivery through native recovery

The deletion phase required every event within 10 seconds, but native
coalescing recovery uses a 30-second safety audit followed by the existing
reconciliation debounce. An idle diagnostic excludes that delayed audit
and therefore cannot act as a completion barrier.

Await the required delivered events instead, preserving every deletion
assertion and the existing overall test watchdog. A real-filesystem test
with controlled native notifications reproduces 95 outstanding paths at
the old deadline and verifies completion when the safety audit delivers
them. No production observer behavior changes.

* test(file-observer): verify burst recovery through public events

The controlled-clock regression constructed a private native backend,
bypassing the interface used by consumers. Consolidate recovery coverage
into the public observer integration test instead: drive real writes,
deletes and directory renames, then await every required public event,
including the 450 surviving files at their renamed paths.

No public capability was added: subscribe callbacks already express
completion. No production behavior or test timeout changes.

* test(app): drive Android fade tests through the native host

The instrumentation suite constructed TailFadeInAnimator directly, advanced
its private clock, and counted FadeInSpan instances. It now mounts the
WordFadeHost that React mounts, feeds it the ranges prop the bridge sends,
and asserts rendered pixels: hidden before a start time, a monotone front
inside and across words, settled frames equal to the baseline, and no frame
callbacks left after settling. Span counts and internal timing state are no
longer asserted.

Choreographer cannot be advanced deterministically from a test and the fade
epoch is the message's wall-clock start time, so the host takes a
WordFadeFrameClock (time plus frame callbacks) as its smallest public seam;
production keeps the Choreographer-backed default.

Fixture text is sized in pixels so every sample stays on one line at any
density; at 420 dpi the 32sp fixture wrapped and the column helper summed
glyphs from the first line.
2026-09-15 03:15:24 +02:00
Mohamed Boudra c3bcad687d fix(app): prevent and recover workspace layout crash loops (#4844)
* fix(app): prevent and recover workspace layout crash loops

* fix(desktop): capture resized browser tabs without hanging

A resident guest can paint fresh pixels after resize while capturePage's
native surface-copy request remains pending. The same failed guest returns
a fresh frame through frame subscription, so waiting for resize or another
animation frame does not correct the failing copy request.

Capture one subscribed frame within the existing five-second budget and
release the subscription on completion or cancellation. Preserve temporary
frame production and its original throttling policy. Remove viewport copy
retries and E2E screenshot polling; assert the resized image dimensions.

Refs #4866. Three full real-Electron browser journeys passed under Xvfb,
including physical clicks and fresh screenshots while the window is hidden.

* test(app): choose Agent before exercising the runtime catalog

The newly merged catalog journey assumed an empty workspace opened an agent draft tab. This branch opens the New launcher, so create the agent draft tab explicitly before checking its composer and model choices.

* fix: settle closed browser captures and validate persisted layout fixtures

Destroying a guest during frame subscription left its screenshot pending until the capture deadline and blocked queued captures. Reject at destruction, remove the listener on every exit, and return browser_tab_closed. The regression proves immediate settlement and queue release.

Reuse the unchanged persisted layout schema from a UI-independent module so the recovery fixture validates storage before editing it.

* test(app): encapsulate Explorer layout setup and cleanup

Keep persisted layout schemas and corrupted-state construction inside the seedCorruptedWorkspaceLayout helper. Use the existing withWorkspace fixture for cleanup in all four Explorer journeys so test bodies follow a straight setup, action and assertion flow.

* test(app): target the selected workspace in layout recovery

The corruption helper selected the first persisted entry, so a multi-workspace journey could leave the intended workspace untouched. Resolve its exact persistence key from the requested workspace ID and fail setup when absent. The failing-first journey now preserves an earlier workspace with an agent draft tab while recovering the selected workspace.
2026-09-15 02:30:27 +02:00
Mohamed Boudra 836f1a9c23 fix(ci): consolidate E2E repairs and wait for Metro readiness (#4858)
* fix(desktop): wait for Metro HTTP readiness before launching Electron

Expo briefly listens while probing port availability. A successful TCP connection can therefore launch Electron before Metro starts and leave it exiting on connection refusal. Require the configured renderer origin to report the running packager status.

* test(ci): consolidate fixture and readiness corrections

Await the connection and UI states each journey owns, isolate Git fixture contents before observation, and hold real Markdown frames at asserted checkpoints. Keep creation completion diagnostics explicit without claiming the original Windows failure is fixed.

* test(desktop): describe Metro readiness through HTTP states

Keep protocol mechanics in the fixture and assert readiness only after a successful packager response. Cover non-success status and unrelated HTTP content without inline synchronization in the test body.
2026-09-14 21:25:38 +02:00
Mohamed Boudra 772ff8d6f3 fix(desktop): preserve Linux sandboxing wherever the host allows it (#4447)
* fix(desktop): preserve Linux sandboxing wherever the host allows it

Chromium checks its Linux sandbox before main.js, while package installation probes run as root and miss AppArmor restrictions on desktop users. Choose the launch flags before exec and always install the SUID alternative for native packages. Test published artifacts as an ordinary user with namespaces both denied and allowed; never repair permissions in the smoke harness.

* fix(desktop): retain the Linux release build baseline

* fix(ci): allow canceled desktop smoke uploads to stop

* test(desktop): verify the installed RPM sandbox under AppArmor

* fix(ci): resolve packaged desktop dependencies before installation

* chore(desktop): keep QA evidence out of the repository
2026-09-11 09:05:26 +02:00
Mohamed Boudra 0110302b6f fix(daemon): keep lifecycle operations on the selected instance (#4575)
* fix(daemon): keep lifecycle operations on the selected instance

Restart reconstructed launch inputs and address fallback could redirect a home-selected shutdown to another daemon. Let supervisors publish bound endpoints, retain their worker restart contract, and share captured local lifecycle ownership between CLI and Desktop.

* test(daemon): verify bounded desktop readiness and cancellable artifacts

* test(cli): verify reload target hints and durable offline pairing

* test(daemon): align lifecycle fixtures with ownership and targeting

* fix(daemon): preserve reachability when status details time out

Provider discovery can outlast a status request after authentication succeeds. Keep local connection facts separate from unavailable details, while explicit remote queries retain their error outcome.

Migrate CLI and packaged Desktop fixtures to persistent managed configuration and explicit targets. Isolate generic Git observation from forge discovery and reuse disposable CLI homes on Windows.

* test(daemon): observe restart through the worker status RPC

The bounded CLI status observation may omit worker details while an authenticated connection remains healthy. Exercise the app-style restart through its full RPC and retain the replacement observation, with a real slow provider probe covering the distinction.
2026-09-11 08:33:22 +02:00
Mohamed Boudra 16d932651c fix(desktop): throttle hidden browsers between screenshots (#4646)
* fix(desktop): throttle hidden browsers between screenshots

Keep Chromium background throttling enabled outside serialized captures. Temporarily enable frame production and wait for fresh paint so hidden-window screenshots remain usable without continuously animating idle guests.

* fix(desktop): share screenshot deadline across paint and capture

* test(desktop): wait for browser fixture readiness before sampling

* test(desktop): exit Electron after screenshot verification

CI completed every screenshot assertion but remained alive after app.quit. Exit the standalone test explicitly and keep its deadline active until process shutdown.

* test(desktop): clean browser profile after Electron exits

Explicit Electron exit skips quit handlers, and removing the profile before shutdown lets Chromium recreate it. The launcher owns the temporary profile and removes it after the child exits.

* test(desktop): reuse browser E2E for hidden screenshot regression

Exercise the running app and browser MCP tools within the existing browser journey. Remove the separate Electron fixtures, npm script, and CI step.
2026-09-11 08:05:45 +02:00
Mohamed Boudra b8e24677e1 chore(release): cut 0.8.0 2026-09-10 12:17:08 +02:00
Mohamed Boudra f7e68e32d1 fix(desktop): restore Linux launcher identity (#4649)
* fix(desktop): restore Linux launcher identity

Electron now derives Linux window identity from the scoped package name before app startup. Pin the existing Paseo.desktop identity in packaged metadata and check the built archive in the packaged-app smoke test.

* test(desktop): declare packaged smoke archive dependency

* fix(desktop): update Nix hash for smoke dependency
2026-09-10 11:42:09 +02:00
Mohamed Boudra 4eab53e24e chore(release): cut 0.8.0-beta.1 2026-09-08 10:19:53 +02:00
Mohamed Boudra e4cd2d1ad0 fix(desktop): preserve the packaged macOS Dock icon (#4389)
The startup override replaced the bundle icon with a flat PNG even in packaged builds. Limit the override to development so macOS retains the bundle icon appearance.
2026-09-06 12:34:17 +02:00
Mohamed Boudra 9e90b86afa Restore reliable macOS desktop updates (#4322)
* fix(desktop): restore macOS app updates

Electron 41's Squirrel helper can leave the update handoff dormant on current macOS, so Paseo quits cleanly but relaunches the old bundle. Electron 44 wakes ShipIt through its XPC service.

Include updater lifecycle events and ShipIt state in app diagnostics so future handoff failures identify the current version, target version, cache paths, and installer output.

* fix(desktop): harden updater diagnostics

Keep each ShipIt file's evidence available when another read fails, carry the service-selected version into updater lifecycle logs, and make clean workspace installs resolve the Electron runtime used by desktop tests.

* refactor(desktop): name updater install options

* refactor(desktop): name updater log payloads

* refactor(desktop): name clipboard payload

* fix(desktop): surface updater lookup failures

* test(desktop): await guest focus transition

* fix(desktop): guard macOS update compatibility

* test(release): validate desktop manifests before publish

* test(desktop): await populated daemon pid file
2026-09-04 22:17:39 +02:00
Mohamed Boudra 35d2457d83 Let plugins provide coding agents (#4314)
Allow trusted local plugins to register complete coding-agent providers through the existing agent lifecycle. Includes direct and ACP implementations, SVG provider icons, custom timeline items, runnable examples, and public authoring documentation.

Closes #3547
Closes #3556
2026-09-04 19:56:57 +02:00
Mohamed Boudra 7178583612 Stop Settings navigation from retaining closed views (#4228)
* fix(desktop): stop retaining closed Settings views

Settings rotations left detached detail panes reachable from the web style registry's strong element sets. Backport the upstream weak cleanup design and cover the real Electron bundle with a forced-GC regression.\n\nKeep the established sibling-route topology; compact Back relies on route-name identity.

* test(app): synchronize Mermaid completion layout assertion

The streaming acceptance test can observe the intentional completion remount between visibility and layout sampling. Consult the existing completion promise around that measurement so the transition is not reported as diagram loss.

* test(app): isolate worktree restart from provider startup
2026-09-02 21:27:30 +02:00
Mohamed Boudra 53d824a320 feat(plugins): split client and server plugin entries (#4206)
* feat(plugins): phase 1 split runtime entries after green gate

The compiler now builds explicit client and server entries, so runtime boundaries are source-owned instead of registration-name filtering. Compiler and runtime tests prove the entry split, directory and suffix boundaries, client Node import errors, migration failure, and client-only operation without a subprocess.

* feat(plugins): phase 2 run client entries after green gate

Explicit client and server contexts replace the mixed plugin context. The app runner owns every client registration and its idempotent removal, including late contributions and composer pills; focused contribution tests prove the removal contract, and all five migrated examples reached running on the isolated worktree daemon.

* feat(cli): phase 3 scaffold runtime split after green gate

The generated project demonstrates the required shared RPC contract, server handler, client surface, and sidebar wiring. The scaffold test and an isolated init, typecheck, install, and running check prove the phase acceptance path.

* docs(plugins): phase 4 publish migration after green gate

Gate: current docs and the paseo-plugin skill describe only explicit client/server entries. The standalone migration guide maps every former registration, and migration-doc.test.ts proves every client add* method remains represented.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Gate: compiler 13, runtime 24, typecheck, lint, and format passed. The dependency fixture proved node_modules/client was incorrectly treated as a plugin boundary.

* fixup! feat(plugins): phase 2 run client entries after green gate

Gate: client runtime and registry tests, typecheck, lint, format

* fixup! docs(plugins): phase 4 publish migration after green gate

Gate: migration docs test, paragraph audit, typecheck, lint, format

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Gate: compiler organization tests, typecheck, lint, targeted format

* fixup! feat(plugins): phase 2 run client entries after green gate

Gate: shared SDK tests, runtime tests, typecheck, lint, targeted format

* fixup! feat(cli): phase 3 scaffold runtime split after green gate

* fixup! docs(plugins): phase 4 publish migration after green gate

* fixup! feat(plugins): phase 1 split runtime entries after green gate

* docs(plugins): rewrite public plugin docs for runtime entries and mobile guardrails

* fixup! feat(cli): phase 3 scaffold runtime split after green gate

The scaffold now keeps DOM globals out of the program and declares only window.open inside client/web.ts. The scaffold test invokes tsc in a fresh process, proves the generated project passes, and proves a stray document access fails.

* fixup! docs(plugins): phase 4 publish migration after green gate

The internal guide and plugin skill now forbid both the DOM lib and triple-slash DOM references. They direct web adapters to declare only the globals used by client/web.ts.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Removing the obsolete Babel parser changes the locked npm dependency graph. The macOS Nix desktop gate reported the new fixed-output hash, which this commit records.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Reject relative imports that escape the plugin root while continuing to skip resolved node_modules internals. The compiler regression test proves the escaped import is rejected and the dependency-internal fixture remains accepted.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Classify absolute imports and reject plugin-authored paths that escape into node_modules. Dependency internals remain exempt based on their importer path. Red-first compiler regressions cover both bypasses, and the existing dependency fixture remains green.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Classify canonical esbuild resolution results so dependency-relative and symlink imports cannot escape client/server boundaries. The two compiler regressions failed before the fix and pass afterward.

* test(ci): synchronize flaky state transitions

Wait for repository watcher registration before emitting buffered ref events, let sidebar order polling retry unlaid-out rows, and wait for the inactive browser parking state before screenshot capture. These changes directly address the three observed CI failures; the affected server, Playwright, and desktop browser tests pass locally.

* test(ci): synchronize Mermaid completion layout assertion

The Playwright streaming acceptance test exposed a completion remount between visibility and layout sampling. Check the existing completion promise around the measurement so that transition is not reported as diagram loss.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Resolve bare package imports before boundary classification so symlinked dependency entries cannot expose server modules to the client bundle. The focused regression proves the bypass and the compiler file passes 20/20.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Allow canonical paths only within a matching linked package root while preserving runtime and containment checks for imports that leave it. The linked-dependency regression fails before the fix and compiler tests pass 21/21 afterward.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Reject matching package manifests that contain the plugin or live inside it, so linked-dependency roots cannot exempt plugin or workspace files. The ancestor-manifest regression fails before the fix and compiler tests pass 22/22 afterward.

* fixup! feat(plugins): phase 1 split runtime entries after green gate

Validate plugin-local lexical boundaries before canonical linked-root exemptions and require remembered-root imports to originate inside that root. The linked-root bypass regression fails before the fix and compiler tests pass 23/23 afterward.
2026-09-02 19:11:45 +02:00
Mohamed Boudra 4aab00f267 fix(desktop): use CLI shim for packaged Linux installs (#4207)
Non-AppImage packages install the Electron executable at a stable path, but invoking it through the CLI symlink cannot restart the daemon. Link the bundled CLI shim for those targets while preserving AppImage links.\n\nFixes #4184
2026-09-02 12:30:28 +02:00
Mohamed Boudra de8535c38b Customize top-level sidebar items (#4203)
* feat(app): customize top-level sidebar items

Let users reorder and hide built-in and plugin sidebar rows from Appearance settings while keeping the preference device-local. Add Search as a first-class row and standardize section explanations as header info tooltips.

* fix(app): preserve consecutive sidebar preference edits

Derive each sidebar change from the latest cached settings and retain unavailable plugin slots while editing the visible list. Keep E2E interaction selectors tied to accessible control names.

* test(app): isolate provider settings unit boundary

Keep the legacy provider-section test focused on provider behavior now that its shared section shell can render the settings info control.
2026-09-02 11:42:39 +02:00
Mohamed Boudra 9400a49af6 chore(release): cut 0.7.2 2026-09-02 02:07:34 +02:00
Mohamed Boudra 3e59adb4dc Add Claude Fable 5.1 to the model catalog (#4179)
* feat(claude): add Fable 5.1 model

Expose the new 1M-context model through the curated Claude catalog and normalize its dated and context-suffixed runtime IDs back to one canonical entry.

* fix(claude): group Fable models in catalog

* chore(release): cut 0.7.1

* fix(nix): refresh 0.7.1 dependency hash

* test(cli): follow package version in plugin scaffold

* test(cli): keep Claude catalog checks extensible
2026-09-01 21:51:51 +02:00
Mohamed Boudra 59661f2bb8 Eliminate recurring cross-platform CI flakes (#4171)
* fix(ci): eliminate recent cross-platform flakes

Synchronize tests on owned state instead of elapsed time or transient identities. Preserve file rename collision safety when platform file IDs are unavailable.

* fix(ci): close follow-up synchronization gaps

Keep fetches in flight through buffered-event classification, scope mock agents to navigation tests, and wait for live workspace hydration before opening agent routes.

* fix(ci): synchronize browser lifecycle boundaries
2026-09-01 19:25:16 +02:00
Mohamed Boudra c56638ea8c chore(release): cut 0.7.0 2026-08-31 19:25:32 +02:00
Mohamed Boudra 95f4eb7577 chore(release): cut 0.7.0-beta.3 2026-08-31 09:52:59 +02:00
Mohamed Boudra fcd81f591e chore(release): carry forward 0.7.0-beta.2 version 2026-08-31 09:46:56 +02:00