Commit Graph
6418 Commits
Author SHA1 Message Date
paseo-bot[bot] ac08077f7a Keep workspace attention in sync after agent runtimes close (#9087)
* fix(server): publish attention clears for unloaded agents

Stored attention clears bypassed agent state events and subscription ownership, leaving connected directory observers stale. Use the existing manager mutation and publish path for unloaded agents, and cover independent filtered subscribers through real daemon requests.

* test(server): run attention regressions in subscription CI

Keep full clear-response coverage alongside independent subscriber checks. Allow asynchronous native deletion classification beyond the default one-second polling deadline.

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-08 06:54:33 +00:00
paseo-bot[bot] 79717e2bd3 test(app): share the settings sidebar scroll journey (#9089)
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-08 06:36:38 +00:00
paseo-bot[bot] 22f50d4dfa fix(app): preserve unsent drafts when closing agent tabs (#9079)
Archiving removes an agent from the active directory without deleting the conversation. Preserve its local composer draft there too, and clear it only on explicit entity removal.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-08 06:10:04 +00:00
Mohamed Boudra 2d7a115614 fix(app): keep the latest turn footer in the chat column on web (#9083)
At widths beyond the chat's max width, the latest turn's footer sat at the
chat's left padding instead of under the message column, while running and
after it ended. Earlier footers were aligned.

#8358 wrapped the web viewport's live auxiliary (the latest turn footer and
pending permissions) in a measuring div with no style. That div is a block
box, so the footer row's alignSelf: center no longer applied. Give it the
same flex column style as the stream rows.

Fixes #9081
2026-11-08 12:48:07 +07:00
Mohamed Boudra 680e6077a8 test(app): keep turn footer timing on one line when the chat has room (#9080)
The completed turn footer wrapped "Worked for 26s" onto two lines at full
chat width in builds before #9007, which sized the duration to whichever
label had more characters. A label with fewer characters can paint wider,
so the duration wrapped inside the narrower timestamp's width. #9007 gives
both labels the full label column; this locks that behaviour with a browser
test using real layout.

The browser test project could not load anything that reaches the toast
context: react-native-web has no ToastAndroid, and native ESM rejects the
named import. A react-native test shim re-exports react-native-web and adds
it. The lucide test stub gains the icons the message module imports.
2026-11-08 12:00:08 +07:00
Mohamed Boudra 835e339516 fix(app): show one hover card at a time while scanning sidebar workspaces (#9071)
* fix(app): keep one hover card open while scanning sidebar workspaces

Sweeping the pointer down the workspace list showed cards about three
frames late, stacked two or three at once, and left some open after the
pointer moved on.

The card was positioned through an async chain (measureInWindow, then
onLayout, then a position effect, then the portal's passive mount), so it
painted at -9999 first. While unplaced, its safe-zone bridge covered
everything above the trigger, so upward pointer moves kept cancelling its
close timer. The previous card also stayed for the close grace period
plus its fade while the next one opened.

Now each overlay layer holds one open card, and opening another trigger
closes it immediately. The card reads its trigger and overlay rects in a
layout effect inside the portal, so the first painted frame is already
placed. The grace timer still covers jitter and the gap onto the card.

* test(app): name the hover scan workspaces so the resting row's card is identified
2026-11-08 11:34:25 +07:00
paseo-bot[bot] 1927d859bc fix(app): identify routed connections in diagnostics (#9058)
The runtime already identifies connections through another host as routed, but both diagnostic fields fell through to unknown. Preserve that distinction in the report.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-08 04:13:10 +00:00
Mohamed Boudra fcde2072ab feat(diff): stream Changes revisions without blocking the socket (#8907)
* feat(protocol): define streamed diff revisions and resolve tag bases

* feat(server): stream shared diff revisions with paced file bodies

* feat(client): observe streamed diffs with atomic revision updates

* feat(app): show the Changes tree while diff bodies stream

* fix(server): retain the legacy structured diff payload budget

* fix(diff): preserve revision ordering and readable files

* fix(diff): select renamed patches and read dangling symlinks

* fix(diff): preserve nested checkouts and ambiguous patch paths

Only regular files and symlinks are readable untracked entries. Prefer patch metadata to ambiguous unquoted headers so paths containing a prefix delimiter keep their hunks.

* fix(diff): preserve BOM text and complete file replacements

Decode each string without consuming its leading BOM. File-type replacements emit multiple patch sections for one listed path, so retain all matching hunks. Assert the optional rename metadata in the existing commit-file journey.

* fix(diff): highlight symlinks from their stored target text

Working tree source context must match the symlink blob emitted by Git rather than the contents reached by following the link. Otherwise highlight runs do not cover the patch line and encoding fails the revision.

* fix(diff): tolerate disappearing highlight sources

Source context is optional after Git has produced the patch. Missing worktree link metadata must preserve the existing hunk-based highlighting fallback instead of failing the revision.

* fix(diff): batch cold reads and refresh logical comparison inputs

Acquire bounded pages of tracked patches lazily after Begin while retaining per-file highlighting and awaited frame delivery. Keep legacy preferred-base selection live across revisions and include submodule Git state in worktree cache identity.

* fix(diff): distinguish gitlink directories from replacements

A raw old gitlink mode also occurs when a submodule becomes a regular file. Query nested Git state only for a current directory, preserving normal file identity for replacements.

* fix(diff): invalidate timestamp-preserving file replacements

Size and modification time do not identify live file contents after a preserving copy. Include filesystem change time and file identity for tracked and untracked reuse, and verify live revisions agree with fresh reads.

* fix(diff): align highlight tokens with normalized patch lines

Git can normalize CRLF worktrees while highlighting reads their original line endings. Match tokens to the emitted patch text and trim only a normalization CR before encoding runs; mismatched source tokens are not attached.

* fix(diff): refresh equivalent worktree paths across platforms

Explicit refreshes must match filesystem-equivalent roots, including Windows path aliases. Use the existing realpath-aware matcher, budget real Git completion waits consistently, and assert optional status metadata in the filesystem commit-file journey.

* fix(diff): validate Git inputs and resolve refresh roots

Reuse now accounts for effective attributes, configuration, and canonical worktree blobs as well as filesystem state. Explicit refresh resolves repository identity like subscription creation, and callers await that resolution before acknowledging the command.

* fix(diff): keep non-Git workspace refresh successful

Diff invalidation has no target repository for an ordinary folder. Treat that expected absence as a no-op while preserving other root-resolution errors.

* fix(diff): observe scoped Git interpretation settings

Classify each metadata root with its worktree and common scopes, retaining both when the directories coincide. Shared attributes affect all checkouts, while config.worktree belongs to the owning checkout. Real subscriptions verify automatic binary and hunk-context refreshes in main and linked worktrees.

* fix(diff): observe interpretation inputs for pinned comparisons

Immutable commit objects still depend on mutable Git configuration and attributes. Declare those metadata inputs independently of ref, index, and worktree mutability, while keeping unrelated endpoint events out of pinned comparisons.

* fix(diff): observe interpretation independently of comparison endpoints

Commit comparisons still read worktree attributes. Watch those inputs independently, checking effective Git interpretation before invalidating fixed contents. Retain paths hidden by whitespace filtering so attribute changes can expose them. Forward binary frames through the branch-loading test interceptor.
2026-11-08 11:00:21 +07:00
Mohamed Boudra 2071bfda7f fix(client): close abandoned browser connections immediately (#9067)
* fix(client): close abandoned browser connections immediately

When the client gave up on a connection (liveness timeout, connect
timeout, reconnect, subscription failure) it closed the WebSocket with
1001. Browsers, Electron and Node's global WebSocket accept only 1000 or
3000-4999 and throw InvalidAccessError otherwise; the throw was
swallowed and the reference dropped, so the physical socket stayed open
and the daemon kept streaming into it until its 45 s lease expired.

The WebSocket adapter is the only code that calls WebSocket.close, so it
now sends 1000 for codes the API rejects and trims reasons to the
123-byte limit. Callers keep their reasons, and Node's `ws` sends the
same frame as a browser, so Node tests now catch this class of bug.

* test(client): drop timing-dependent hello count from abandoned-close test

Handshake retries after close are owned and tested by the relay's handshake-lifetime test; this test only checks that the abandoned socket closes.
2026-11-08 10:00:11 +07:00
paseo-bot[bot] ed4657bdf8 fix(app): avoid false never-online claims in diagnostics (#9054)
A missing runtime timestamp does not establish connection history. Report it as unknown while preserving supplied timestamps.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-08 02:59:58 +00:00
paseo-bot[bot] 71439e51a5 fix(website): submit plugin search text during hydration (#9059)
Read the submitted form value while hydration state is still catching up with text entered before its input listener. Cover the missed input event without relying on startup timing.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-08 00:27:13 +00:00
paseo-bot[bot] d92495487f fix(website): enable the language menu after hydration (#9046)
The server-rendered trigger accepted clicks before its handler existed. Tie its enabled state to hydration and cover delayed scripts so an early click cannot silently disappear.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 21:28:12 +00:00
paseo-bot[bot] 19848899d0 Keep host identity and error details in desktop logs (#9038)
* fix(app): preserve host error details in desktop logs

Bind host context through the existing client logger and emit structured text so desktop console capture retains the error record, including for adopted probe clients.

* fix(app): retain discovered host identity and nested error causes

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 21:19:18 +00:00
paseo-bot[bot] 249c35891b fix(app): load History after a browser reload (#9029)
Consume host connection statuses in History instead of a version-only dependency that the React Compiler removes. Keep saved sessions reachable when the host connects after the screen mounts.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 20:02:59 +00:00
paseo-bot[bot] b7a308ca00 fix(relay): stop handshake retries when the channel closes (#9025)
Cancel handshake work at the channel close transition instead of waiting for a socket acknowledgment from an unreachable peer.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 18:59:08 +00:00
paseo-bot[bot] 063b402d7c test(cli): align worker upgrade checks with protocol 6 (#9033)
The upgrade journey still expected protocol 5 after the worker moved to 6, stopping every main CLI run before adoption coverage. Keep obsolete versions 0 through 4 and exercise newer-worker rejection with version 7.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 18:19:31 +00:00
paseo-bot[bot] c908eddce6 Fill controlled form fields through browser tools (#9022)
* fix(desktop): fill controlled form fields before submission

Write text controls through native DOM setters so page-level value tracking sees the input event as a change. Cover state updates, submission, replacement and clearing through the real browser tool journey.

* refactor(desktop): select native field setters with explicit branches

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 17:52:53 +00:00
8a14d7b2c3 Collect idle Claude runtimes reopened without a turn (#9019)
Reopening a chat lists its commands, which resumes the agent and starts
a Claude process with no prompt. Claude only sends init with a turn, so
the runtime's background-work state stayed unknown and idle cleanup
vetoed it on every sweep: the process stayed resident until the daemon
restarted.

A process that has not been sent a prompt and has not reported init has
run no turn and owns no background work, so it can stop. Older CLIs
that ran a turn without a roster are still kept.

Fixes #9017

Co-authored-by: Brandon Elliott <923787+Brandon168@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 16:54:27 +00:00
paseo-bot[bot] ab8e1e61ff fix(app): keep the composer ready after clicking Send (#9015)
Pointer submission blurs the editor before sending and clears retained focus intent. Restore focus through the existing default send action for mouse-driven layouts, while keeping touch keyboard dismissal intact.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 16:18:07 +00:00
Mohamed Boudra 9131505810 Fill short collapsed chat histories (#8358)
* fix(app): fill short collapsed chat histories

Collapsed whole turns can leave the initial viewport short even when older history exists. Continue paging through measured pages before revealing a fresh chat, while preserving visible history and the reading anchor.

* fix(app): settle padded and stalled chat history

Measure the native history edge using the same content area as viewport filling, and bound child history requests so retry can start after a stalled fetch.

* fix(app): adapt viewport filling to the current timeline

* fix(app): resume history filling after switching tabs
2026-11-07 23:12:08 +07:00
Mohamed Boudra c6f0c24bf6 Make a folded turn's summary line its own chat row (#9009)
* fix(app): make a folded turn's summary line its own chat row

The summary line of a collapsed turn was drawn inside its prompt's row, so the
row gap landed below the summary and the prompt touched it. Turn collapse now
emits a `turn_summary` row between the host and the reply, and one spacing
rule covers every prompt: 16 above whatever answers it (reply, summary row or
tool row). The summary row spaces like the work rows it stands for, 4 above the
reply and flush under a tool host.

This removes the per-row summary lookup, the host revision tracking and the
fragments in the view. A fold reuses a turn's previous summary row object while
its work and open state are unchanged, so mounted rows do not re-render.

* fix(app): keep the turn summary row at its host's timeline position

The chat outline resolves the active prompt from the timeline position of
the row under the reading line. The summary row had none, so the outline lost
its mark while the row sat there. The row now carries its host's position and
is replaced when that position lands.

* fix(app): keep the reader on a folding turn's host, not its summary row

When an older page completes the oldest loaded turn and it folds, the reading
position moves from the hidden rows to the turn's host row, as on main, so
the prompt and its summary row both land in view. The pagination helper now
finds the summary line in the row under the prompt.
2026-11-07 23:00:13 +07:00
paseo-bot[bot] ef7de3d066 test(server): control recursive watcher reconciliation timing (#9010)
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 15:01:32 +00:00
Mohamed Boudra b7b38412a0 Keep turn footer timing within the available chat width (#9007)
* fix(app): keep turn footer timing within the chat width

Let visible timing text determine its height within the available chat width instead of sizing an absolute overlay by character count. Keep actions anchored when duration and timestamp labels swap.

* fix(app): keep footer timestamp hover target stable

* fix(app): reserve both wrapped timing label heights
2026-11-07 21:32:14 +07:00
a48bc3aa6f Keep concurrent Codex discovery from failing during startup (#9008)
* Serialize Codex app-server startup by state directory

* Keep Codex session constructor unchanged

* Type Codex startup failures and inject process launch port

* fix(codex): retain startup diagnostics emitted after exit

Classify failed initialization after stdio closes so the bounded SQLite retry sees the complete stderr. Keep running-session exit handling immediate. The regression delivers the SQLite diagnostic between exit and close.

* test(codex): inject history test process through the public dependency

The history fixture still replaced the old private spawn method, so provider launch preparation tried to find a real CLI in CI. Inject the same process interface as other provider tests and use an invalid command as a guard against accidental real launches.

---------

Co-authored-by: Michael Wu <michaelmwu@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: Ivan Malison <1246619+colonelpanic8@users.noreply.github.com>
2026-11-07 14:03:47 +00:00
Patrick WangandCopilot 28c01ea1ce fix(terminal): preserve mouse encoding when replaying sessions (#9004)
Restore SGR and pixel mouse encoding alongside tracking modes so Copilot CLI scrolling survives switching conversations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-11-07 13:21:25 +00:00
Mohamed Boudra a449271d57 Let plugins provide custom sidebar views across hosts (#8906)
* feat(plugin): expose stable sidebar entity data across hosts

Collections return interned references and preserve their arrays when membership and order stay unchanged. Entity selectors share one hook with identity defaults for full snapshots and shallow defaults for selections. The app caches host projections and shares underlying store and host subscriptions. Browser render counts exercise the real session store and plugin provider without memoized rows. Terminal hooks consume the collection source port; daemon-wide terminal discovery is the separate terminal phase.

* feat(plugin): let users select a sidebar view

An exclusive view replaces the sidebar content while the host retains navigation, callouts, and footer. The device remembers the selection, unavailable views fall back to the default, and throwing views provide recovery. Device-scoped plugin settings persist locally with schema validation and revision conflicts. Web E2E and Android tablet QA cover selection, preserved chrome, disabled-view fallback, and error recovery.

* feat(plugin): add sidebar view examples and ref actions

Keep sidebar layout and gestures inside the plugin while routing entity handles to their owning hosts. Examples cover empty projects, pinned workspaces, task sections, nested agents and device-local label folders.

* feat(plugin): observe terminals across connected hosts

Share host-wide terminal observations across plugin surfaces and gate them on terminalCollection. Keep directory observations compatible, and carry creation time in worker v6 state so v5 terminals can drain with an unknown timestamp.
2026-11-07 18:05:31 +07:00
paseo-bot[bot] aac228e0a8 test(server): separate legacy plugin setup from migration deadline (#8983)
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 09:01:32 +00:00
paseo-bot[bot] 0e40022519 Preserve linked resources when pasting prompts (#8972)
* fix(app): preserve linked resources when pasting prompts

Rich clipboard links carried their destination only in HTML, while the composer let textarea default paste insert the plain label. Convert linked fragments to Markdown at ingestion and replace the selected draft range through the existing editor command.

* fix(app): keep pasted links undoable while drafting offline

Use the browser edit command for converted paste so selection replacement participates in Undo and normal input publication. Keep complete Markdown clipboard text when it already carries the link destinations, and limit connection gating to image uploads.

* fix(app): preserve copied Markdown with normalized links

Copy renders normalized URLs in HTML while retaining original Markdown in plain text. Compare parsed destinations using the same Markdown parser so pasting complete responses keeps tables and formatting intact.

* fix(app): retain external linked tables when pasting prompts

Recovering missing link destinations must retain table rows and columns. Use the existing table converter and keep cell delimiters and line breaks inside their cells.

* test(app): name rich clipboard paste arguments

* fix(app): preserve pasted links around rowless tables

The table converter requires a first row. Convert rowless table content separately so empty markup cannot abort recovery of surrounding destinations.

* test(app): wait for workspace restore before undo teardown

The directory appears before git finishes recreating the worktree. Wait for the host to publish its active workspace before cleanup removes the source repository.

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 07:04:10 +00:00
paseo-bot[bot]andAbdulhakeem Mustapha 751dc82e6c Keep browser evaluation working on strict-policy pages without shadowing page globals (#8973)
* fix(desktop): run browser_evaluate on pages whose CSP omits 'unsafe-eval'

* test(desktop): browser_evaluate on pages that allow and forbid eval

* test(desktop): e2e opens a page under a CSP without 'unsafe-eval' and evaluates on it

* fix(desktop): preserve page globals in browser evaluation

Define the supplied function outside every wrapper binding so strict-policy evaluation keeps the original page scope.

Co-authored-by: Abdulhakeem Mustapha <abdulhakeemmustapha@gmail.com>

---------

Co-authored-by: Abdulhakeem Mustapha <abdulhakeemmustapha@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 06:03:12 +00:00
paseo-bot[bot] daf26892d3 Keep the provider's actual model after unavailable selections (#8957)
* fix(server): keep the model selected by the provider

A live model setter can retain or resolve a different model than the requested ID. Read its effective selection before persisting manager state so an ignored selection cannot overwrite the actual model. Preserve explicit preference resets.

* fix(server): preserve completed model selections

Return the effective selection from the provider mutation so a separate status request cannot reject an already completed switch or leave configured and runtime models inconsistent.

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 04:18:27 +00:00
paseo-bot[bot] c5cf6cbb4c test(server): await shared fetch observation readiness (#8975)
Snapshot refresh completion does not mean both workspaces have joined the repository fetch target. Wait for observation setup before releasing the first fetch, and retain delayed checkout resolution to exercise that ordering.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 03:01:45 +00:00
Abdulhakeem Adetunji Mustapha 78a089a14e fix(desktop): browser_logs reports the HTTP status of each request (#8956)
* fix(desktop): browser_logs reports the HTTP status of each request

* test(desktop): browser_logs reports the status the browser recorded

* test(desktop): e2e checks browser_logs reports a 404 and a 200

* docs(browser): browser_logs also reports the HTTP status
2026-11-07 01:00:56 +00:00
paseo-bot[bot] ec3ae09926 Keep the native model when importing sessions (#8948)
* fix(server): preserve the selected model when importing sessions

Import preparation resolved a new-session catalog default and sent it as an explicit resume override. Keep import selections with the resumed provider and persist its reported model for subsequent reloads.

* fix(codex): allow the first reply after importing a session

Resolve collaboration settings again when default model and effort are applied so imported sessions send a complete first-turn request.

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-07 00:08:25 +00:00
paseo-bot[bot]andSebastian Arena 9f68af0274 Keep uploaded terminal file paths separate (#8946)
* M1: terminal input-sink bridge, upload button in tab-bar slot and key bar, picked-file routing, space-separated pasted paths

* M2: terminal dictation: mic in tab-bar slot and key bar, recording pill and overlay, per-terminal recording lifetime incl. pending mic acquisition, Cmd+D on macOS by keyboard focus, i18n

* M2: review fixes: retry attempt guard, cancellable native pending start, measured terminal slot width, compact-web Cmd+D for terminal dictation

* fix(terminal): separate uploaded file paths when pasting

Keep the remote file-drop regression and its test coverage from the contributor change, without introducing new terminal controls. Each uploaded path needs a trailing separator because adjacent quoted paths are one shell word.

Co-authored-by: Sebastian Arena <sebastian@walkofcode.io>

---------

Co-authored-by: Sebastian Arena <sebastian@walkofcode.io>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-06 18:57:29 +00:00
paseo-bot[bot] 79f2db8d56 test(server): retry locked provider fixture cleanup on Windows (#8942)
Availability probes already await process completion. Allow bounded filesystem cleanup retries for transient executable locks, matching the neighboring Windows launch tests.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-06 18:30:36 +00:00
1efcb31a6c Give agents their own workspace identity through create and resume (#8939)
* fix(server): give agents their workspace id as PASEO_WORKSPACE_ID

Agents got PASEO_AGENT_ID and PASEO_AGENT_CWD but not their workspace id, so an
agent couldn't rename or archive its own workspace, or name it to create_agent,
when several workspaces share its cwd. The launch env now sets
PASEO_WORKSPACE_ID whenever the agent has a workspace, including on resume after
a daemon restart, where the id now comes from the stored record. OpenCode treats
it as a per-session key, so it doesn't force a dedicated server.

Refs #5098

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

* fix(server): keep the workspace when resuming an agent by handle

resume_agent_request matched the stored agent but dropped its workspace id, so
the resumed agent had no PASEO_WORKSPACE_ID and no workspace. Pass the matched
record's workspace id into the resume.

resumeAgentFromPersistence now resolves the workspace once and gives it to both
the launch env and the registered agent, so a caller that omits it no longer
erases the stored value on the next snapshot.

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

* test(server): exercise current daemon creation and permission contracts

The workspace resume regression shares this suite with creation checks that still waited for legacy status frames and permission listeners without an owned subscription. Verify the returned error snapshot and acquire event demand so the complete suite can validate workspace identity changes.

Co-authored-by: Darrell <DT@Terastar.biz>

---------

Co-authored-by: Darrell <DT@Terastar.biz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-06 18:06:59 +00:00
paseo-bot[bot]andluke ea30d2616a Restore Mermaid source toggles and bound inline previews (#8932)
* fix(app): keep Mermaid controls usable in inline previews

* test(app): verify Mermaid controls through user journeys

Keep source-toggle mechanics in diagram helpers and compare preview geometry with its screen and controls instead of requiring exact pixel heights.

Co-authored-by: luke <luopeihuan@gmail.com>

---------

Co-authored-by: luke <luopeihuan@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-06 16:58:28 +00:00
Mohamed Boudra f21699a37a chore(release): reconcile 0.15.3 metadata (#8943) 2026-11-06 23:10:33 +07:00
Mohamed Boudra a9bce9a18d Keep the chat still when an agent's turn ends (#8944)
* Land a turn's reply, completed footer, and tool summary in one frame

The stream handler closed turn liveness as soon as turn_completed arrived,
while the final reply waited in the frame-coalesced reducer queue. For one
commit the turn was finished with no reply: the footer vanished and the
timeline jumped twice, and tool turns folded into their summary later.

The viewed timeline owner now takes stream events whole: a turn boundary
commits the queued content before liveness changes. Running and completed
footers share one fixed content height so the phase switch moves nothing.

* Document the turn-boundary commit and stop racing the re-pin in the turn-open test

The turn-open test scrolled the received message into view with a
programmatic scroll, which leaves the chat following its end. Pressing
the turn summary swaps its icon, and that DOM change re-pins the chat to
the end between pointerdown and pointerup. The summary had moved 14 px,
which left the pointer 2 px inside the button; with the completed footer
now 2 px taller, it moved 16 px and the click landed on the reply below.
The message's fade and Show more are already on screen at the end, so
the test no longer scrolls.
2026-11-06 23:10:29 +07:00
paseo-bot[bot] 73448d483e test(app): scope command-center stability checks to the panel (#8927)
Document-wide layout shifts could fail a stable file-search panel. Count only shifts attributed to the panel or its descendants, and exercise both unrelated and in-panel movement in the existing browser journey.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-06 15:55:09 +00:00
Mohamed BoudraandClaude 161076a853 Show loading and errors in the slash command menu, and stop defaulting to /exit (#8905)
* feat(app): show loading and errors in the composer command menu

Rows are single lines with a kind icon, the matched part of the name, the
argument hint, the description and a source tag. The detail card is gone.

The menu used to stay hidden while commands loaded and then show
"Error: ..." over a list whose options Enter could still pick, so a failed
load plus Enter archived the agent through a hidden /exit. Loading and
error are now status rows with no options; the error row names the reason
and offers Retry. Loading appears after 300ms so a warm cache paints the
results first, as before.

An empty query used to select /exit. Provider commands now come first, so
the bottom row the input selects is a provider command.

useAutocomplete derives the selection during render. When results replace
the loading row, the selection is valid in the same render, so the popover
stays mounted at its anchor instead of remounting.

Claude Code's `__`-prefixed commands (/__remote-workflow) only run in its
own server-launched sessions, so the Claude provider no longer lists them.

* fix(app): never default the command menu to a Paseo action

An arrow-key choice was tied to the query it was made for, so returning to
that query brought it back: arrow to /exit on `/`, type `com`, delete it, and
Enter archived the agent. A choice now clears whenever the query changes.

An empty query still selected /exit when the agent had no provider or plugin
commands, because Paseo's actions were the only entries. With nothing else to
list, an empty query now lists nothing and waits for a search; typing `/ex`
still finds /exit.

* fix(app): sort empty-query commands without toSorted

Hermes has no Array.prototype.toSorted, so typing `/` threw "undefined is
not a function" on iOS and Android and the app showed its error screen.
Browsers and Node have it, which is why web and unit tests passed.

* test(app): cover the stale command choice in Playwright and wait out the loading row on devices

The renderHook test mounted React under JSDOM, which docs/testing.md forbids.
The Playwright test drives the same sequence in a real browser and fails
without the reset in useAutocomplete: Enter chose the stale /exit.

The composer-keyboard harnesses treated a visible popover as a ready list.
The popover now shows a loading row first, and a cold Claude command list
takes several seconds, so both harnesses wait for the rows.

* fix(app): size the command menu loading spinner to the row icons

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-06 22:15:10 +07:00
paseo-bot[bot]anda-sh 14845d3ff3 Keep ACP mode selections valid after config updates (#8912)
* fix(acp): preserve session mode identity across config updates

* test(acp): verify mode identity through public sessions

Exercise initialization, queued notifications, mode selection, and rejection over stdio instead of replacing private connection state.

Co-authored-by: a-sh <846704+a-sh@users.noreply.github.com>

---------

Co-authored-by: a-sh <846704+a-sh@users.noreply.github.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-06 15:10:43 +00:00
Mohamed Boudra 616f5e155c fix(app): show branch loading and fetch errors (#8938)
* fix(app): distinguish branch loading from empty results

Preserve branch query pending and error states through the switcher so first open does not report missing branches before the response. Show request failures with a retry action.

* fix(app): keep branch retry progress and keyboard focus

Verify failed cached refresh and keyboard retry through the real browser and daemon. Keep focus in the footer while Retry is disabled so repeated Enter cannot select a cached branch.

* fix(app): preserve picker interaction when retrying branch loads

Use the existing close-and-reopen retry behavior with clear feedback rather than introducing an action that competes with native list keyboard ownership.
2026-11-06 21:35:20 +07:00
Mohamed Boudra 471db4bdb5 Keep workspace tabs predictable and clear attention on deliberate focus (#8935)
* fix(app): preserve workspace tabs and acknowledge deliberate agent focus

Workspace navigation no longer chooses an attention agent. Preserve repeated focus actions so an already selected chat can be acknowledged, and stop consuming attention on departure.

* fix(app): retain deliberate focus until fresh agent data arrives

Connection readiness permits a pending focus acknowledgement without creating a new focus action. Keep that intent across client changes and wait for the current timeline synchronization generation before consuming it.

* fix(app): preserve reconnect feedback during attention acknowledgement
2026-11-06 20:55:16 +07:00
Mohamed Boudra 68fc16b855 fix(terminal): run terminal workers without process identity queries (#8818)
* fix(terminal): run terminals when process identity cannot be read

On some Windows machines Windows PowerShell cannot run Get-CimInstance, or
cannot be started at all. The terminal worker refused to start without its own
birth time and command line, so those machines had no terminals.

Process identity only authorizes signals. The worker now starts anyway and
records its identity as unavailable, and programs without a birth time. Cleanup
never signals what it cannot identify. It asks such a worker to shut down over
the socket the secret authenticates, and reports survivors as unconfirmed.

So records of crashed workers do not block launches, the process table falls
back to process.kill(pid, 0) when the query fails. When a live pid cannot be
identified, a refused connection on the worker's socket marks it as another
process, because a worker listens until it exits.

* fix(terminal): ask unidentified workers to shut down on every cleanup

Retries of an unconfirmed cleanup and startup cleanup run without a grace
period, which skipped the shutdown request. For a worker whose identity is
unavailable that request is the only way to stop it, so it is now always sent.

A gone program recorded without a birth time is no longer walked through the
process table: the table was unreadable when it started, and the walk only kept
the cleanup unconfirmed.

Also test the socket probe on every platform, restore a test of the daemon
starting when the worker cannot, and keep one pid liveness helper.

* test(terminal): fail the worker launch the same way on every platform

Windows opens a directory sitting at the launcher's log path, so the
worker started there and the #8810 test never saw a failed launch. An
invalid protocol override makes the worker exit before it writes its
record on every platform.

* fix(terminal): identify workers through their recorded sockets

* fix(terminal): authenticate legacy sockets before forced cleanup

* test(terminal): exercise closing records without process queries

* test(terminal): identify stale lifecycle records by their sockets

* refactor(terminal): drop the unreleased unavailable-identity record shape

No release wrote identity: { unavailable }; it existed only between
commits of this change. Records keep reading the identity workers up to
0.15.1 wrote, and nothing consults it.
2026-11-06 20:20:18 +07:00
Mohamed Boudra ae157dbfb6 Even out chat footer spacing and show the Changes base only on worktrees (#8934)
* fix(app): space the turn footer like the rest of the timeline

The footer sat 13px under the last line of text while the text sat about
21px under a tool or thinking row, so the footer looked glued to the
message. The message drops its bottom padding when a footer follows, so the
footer's top margin alone sets that gap; 20 makes the centre-to-centre
distance match the tool-to-text gap.

* fix(app): show the Changes base only on Paseo worktrees

The header rendered "branch -> base" whenever checkout status carried a
baseRef, and the daemon sends one for every git checkout, so plain
checkouts showed "-> main" with no way to change it. Only Paseo worktrees
store a base, so only they show it. The arrow is extra muted so it reads as
a separator, and the base no longer shrinks, so a long branch name truncates
instead of the base.

* fix(app): let a long Changes base truncate instead of overflowing
2026-11-06 20:20:11 +07:00
geoqiao dc51a3b6a9 fix(app): preserve plugin-rendered replies when collapsing turns (#8811)
* fix(app): preserve plugin-rendered replies when collapsing turns

* test(app): exercise plugin reply folding in the browser
2026-11-06 13:12:44 +00:00
77798b2e46 Show and stop agent background work (#6220)
* feat(server): add an in-memory store for agent background work

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

* feat(claude): report background tasks as background work

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

* feat(server): track agent background work in the agent manager

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

* feat(protocol): add agent background work messages

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

* feat(server): serve and push agent background work

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

* feat(client): expose agent background work on agent handles

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

* docs(sdk): document agent background work

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

* test(claude): cover background shells in background work end to end

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

* fix(client): resync background work subscriptions on every snapshot

subscribe() now lists the agent's background work each time the event
observation is established, so the listener starts with the current list
and catches up after a reconnect. A list answered after a newer update
is dropped.

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

* fix(server): answer background work lists without loading dormant agents

Background work lives only in memory, so a stored agent that is not
loaded has none. The list handler now answers [] from storage instead of
resuming the agent, and still rejects unknown, internal, and archived
agents. A new test pins that old apps never receive
agent.background_work.update.

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

* fix(server): read Claude background tasks defensively

The user's claude binary can differ from the SDK typings, so a task's
description is read as unknown and an empty or missing one becomes null.
Drop the monitor kind: Claude Monitor watches report as local_bash, so
kind is "shell" or "other" today. The store now copies only the wire
fields of each input.

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

* test(server): prove internal background work was emitted before checking globals

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

* docs(sdk): describe background work resync and feature gating

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

* test(claude): cover background work reported by a retired query

The daemon stops reading a Claude query it no longer owns, so work that a
retired process still had queued cannot reach the background work list.

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

* fix(client): let background work subscribers handle a failed list read

When the list read that follows each subscription snapshot failed, the
error was only logged and the caller got neither the list nor a failure.
subscribe() now takes an optional onError that receives it; without one
the error is still logged. The read is not retried, since the next
reconnect reads the list again.

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

* feat(agents): show background work as active

Keep background activity visible after a foreground turn ends while preserving independent send and interrupt behavior.

* fix(agents): protect background work subscriptions

Keep internal work out of public updates and restore observation after a subscription failure.

* feat(agents): show Codex and OpenCode background work

Keep provider-owned shell activity visible after a turn ends while leaving foreground turn controls independent.

* feat(agents): stop individual background work

Wait for provider confirmation so stopping one background item leaves sibling work and foreground turn controls independent.

* refactor(agents): name background stop parameters

* test(website): isolate browser tests from live metadata

* test(agents): clarify background stop journeys

* fix(sdk): report background work subscription failures

Report setup failures so subscribers can replace an observation that the host released.

* fix(app): restore background work after reconnect

Wait for the host handshake before selecting the retained client so an offline pane starts observing when the connection returns.

* fix(agents): retain background shells across rewind

Preserve known live shell ownership when conversation history is rewound, and revoke it when the provider runtime exits.

* fix(website): launch test seeder across platforms

Invoke Wrangler through Node so local website test startup does not depend on an executable npm command shim.

* feat(agents): clarify background task details and tracks

Keep launch descriptions and commands distinct so background work remains identifiable, and allow composer tracks to scroll when space is limited.

* fix(agents): preserve active background work during archive

Keep background-running children out of bulk cleanup and warn before an individual archive. Preserve first taps on composer tracks while the keyboard is open.

* fix(codex): retain background terminals across rewind

Keep live terminal inventories attached to their originating threads so a fork does not hide or misroute their stop actions.

* fix(codex): scope background terminal identities to threads

Prevent commands on separate threads with the same provider process ID from sharing a stop target.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-11-06 19:49:49 +07:00
paseo-bot[bot] c8bd4717c9 test(server): wait for Git ref observation before injecting events (#8924)
Zero in-flight fetches also describes observation that has not started. Wait for the first fetch to start and settle, and exercise delayed watcher setup so missing callbacks cannot silently skip the test events.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-11-06 12:25:42 +00:00
Mohamed Boudra 6c1b48d296 Archive a workspace from its sidebar row, with undo (#8904)
* feat(app): archive a workspace from its sidebar row and undo it

Hovering a workspace row on desktop shows an archive button next to the
row menu. Archiving shows a toast with Undo and a Cmd/Ctrl+Z shortcut so a
mis-click costs one keystroke instead of a trip through History.

The daemon serializes archive and undo per workspace across every
connection. An undo that lands while an archive is still tearing down
aborts it at the next checkpoint and restores only the agents that archive
took down; deleting the worktree directory is the point of no return, after
which undo recreates the worktree.

* fix(server,app): close archive and undo races found in review

- An archive request now claims the workspace's archive queue before
  looking up the workspace, so an undo sent right behind it cancels it
  instead of running first and losing to the archive.
- A repeat archive of an already archived workspace no longer wipes the
  agents the earlier archive saved for undo.
- Undo no longer brings back an agent that was archived before the
  workspace archive and only loaded to read its history.
- Each local archive or undo request owns its own intent token, so a
  late response to an earlier request cannot release a newer request's
  hold on the row. An archive canceled by another client's undo puts the
  row back.
- The undo failure toast shows translated copy; the cause goes to the log.

* fix(app): keep a workspace's layout when another client restores it during the undo window

The undo window purged tabs and layout whenever it closed after a
finished archive. If another client restored the workspace before this
client's toast closed, the restored workspace lost its saved
arrangement. The archive now commits only if the workspace is still gone
when the window closes.

* fix(app): show the archived toast's Undo as a button

* fix(app): keep a workspace's agent tabs when it is opened while its undo runs

* fix(app): keep a row whose undo is running when a directory snapshot omits it
2026-11-06 16:16:11 +07:00