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>
* 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>
* 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.
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>
* 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>
* 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.
* 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
* 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.
* 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
* 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>
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>
* 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
* fix(server): retain archived parent provenance on detach
* fix(server): publish stored child detach updates on parent archive
Keep subscribed clients current when an unloaded child survives its parent archive. Narrow the contributor change to the missing update; archived-parent provenance remains a separate feature.
Co-authored-by: luke <luopeihuan@gmail.com>
* test(server): clean up the detach fixture static directory
Keep the test daemon static directory inside the fixture root so cleanup removes all test-owned directories.
* fix(server): retain runtime settings in stored agent updates
Parent archive publishes stored survivors through the manager. Preserve their saved runtime model and thinking settings so the update matches stored fetches and subscription filters.
---------
Co-authored-by: luke <luopeihuan@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* feat(app): tighten the touch composer toolbar onto the text column
On touch, the toolbar's icon-only glyphs were 20pt muted outlines at a 1.5px
stroke in loosely spaced slots, and the row sat on a flat -6 margin, so the
attach + and the trailing control floated inside the text column. Touch now
places 44pt slots edge to edge, draws icon-only glyphs at 24pt with a 2px
foreground stroke, keeps label-paired glyphs at 20pt, and runs the context
ring at 26pt with a 3px stroke. The row overhangs into the composer padding
by each end control's ink inset (composerToolbarOverhang), so the + ink and
the trailing glyph or send/stop disc sit on the text column's edges.
Pointer sizing is unchanged.
The folded model control now drops its thinking label before the model
label truncates, and the model label truncates on a word boundary
("Ten second…") instead of leaving a space before the ellipsis. Both are
decided from measured widths. The web reports layout widths rounded to whole
pixels, so a tie counts as not fitting.
* fix(app): measure replacement model labels instead of hiding them
The folded model label and TailEllipsisText kept their measuring views mounted
when the label text changed, while discarding the cached widths. A retained
measuring node whose size does not change emits no new layout event on web or
native, so after switching to a model whose label (or a shortened prefix of it)
has the same width, the widths stayed unknown and the label stayed transparent.
Keying each measurer by what it measures remounts it, so every width is
measured again.
Also move the label width sweep into a named helper so the test body states
the expected label order.
* feat(app): space the touch composer toolbar by what it paints
- Lay each control out at its painted width, 20pt apart, with a disc-sized circle that is both its frame and its fully rounded pressed highlight. Pickers are 36pt pills.
- Match the header icons: 20pt glyphs at 1.5px, an 18pt context ring at 2px.
- Always end the row in a disc (voice mode, send or stop) so it does not shift, nested into the composer's corner, with a concentric 26pt corner radius on touch.
- Stop the picker row's clip from flattening the picker highlight's left edge.
- Budget the density ladder on laid-out widths.
* fix(app): disable the stand-in send disc and keep form model fields in their box
- An empty new chat's stand-in Send is disabled until there is content, as in fullscreen.
- A custom model selector trigger, such as the schedule form's field, drops the toolbar slot's margins.
iOS 26 adds a scroll edge effect, a blur at each edge of every scroll view,
and places it in the scroll view's own coordinates without applying
transforms. The keyboard lifts the chat with a translateY, so on iPhones the
blur lands over the readable conversation and stays until the next scroll.
The app draws its own header fade, so the app's ScrollView component class,
which backs every React Native ScrollView, hides all four edge effects.
Co-authored-by: David Greenspan <155814+dgreensp@users.noreply.github.com>
* fix(app): keep unchanged agent fields when an agent update is accepted
* fix(app): share unchanged agent fields at the store write, not in the update policy
* refactor(app): stop resetting the chat timeline when its agent id changes
A mounted timeline only ever shows one agent: every host keys its panel by tab id,
and a tab keeps its id across a target change only when a draft becomes the agent
it created. The per-agent resets inside the timeline therefore never had a different
agent to reset for. They only fired at that create handoff, where they hid and
re-placed a timeline that was already on screen.
Delete them: LegendList's dataKey and the placed agent id, the web viewport key,
the bottom anchor's resetForAgent, the history window's agent id, and the
expansion and path-opener resets in the stream view.
* fix(app): keep one chat timeline across the draft-to-agent handoff
A draft tab and the agent its first prompt creates rendered two different panels:
DraftPanel mounted its own dock and list, and the retarget swapped it for AgentPanel's.
On Android, with the LegendList timeline, the swap blanked the first turn and re-placed it.
Render both from the agent panel. A draft supplies its timeline from the pending create
in the create-flow store (composer/draft/timeline.ts) and its composer in the composer
slot. The retarget then re-renders the same stream view with the agent's timeline, so the
list instance and its measured rows stay mounted. Agent gating is unchanged.
Float the compact header over draft tabs as it already floats over agent tabs. The
header sat in flow above a draft and over an agent, so the list viewport grew 92 dp at
the retarget and the turn moved.
The handoff E2E now fails if the first row disappears, hides, or shifts down while the
draft becomes the agent.
* fix(app): load subagents and tasks only for an agent, not a draft tab
* feat(workspaces): record workspace creation parentage
Retain the caller workspace as informational ancestry for future sidebar nesting, independently of workspace placement and lifecycle.
* fix(app): preserve workspace parentage in replica cache
Under touch sizing the composer's send and stop buttons filled their whole
44pt hit frame with color, so they outweighed every unpainted control in the
toolbar. The trigger keeps the 44pt frame and now paints a 36pt disc
(TOOLBAR_DISC_SIZE) centered inside it. Pointer sizing paints 28 inside 28, so
desktop is unchanged. The voice button's recording fill follows the same rule.
* fix(app): move sidebar footer actions that don't fit behind a caret
Touch sizing makes each footer icon a 44pt target, so five of them plus
the row's padding need 236pt. A docked sidebar can be 200pt and a fold
column about as narrow, so Settings was cut off at the sidebar's edge.
The row now measures itself and keeps as many full-size buttons as fit;
the rest move behind a caret that opens a menu-engine popover. Help and
Usage leave first, Settings and Add project last, and Hosts and Help
open as pages of the overflow menu rather than second popovers.
* fix(app): keep the sidebar footer empty until it has measured
Laying the footer out before its first onLayout put all five buttons in
the row, so a narrow sidebar painted Settings clipped for a few frames
before the caret replaced it. The unmeasured row now places nothing and
holds its height, so the first painted row is already the measured one.
* fix(app): keep the footer overflow menu on a narrow, short screen
The overflow menu asked for 240pt, and edge clamping can move a surface
but not shrink it, so on a 220pt screen its right edge and submenu
arrows hung off the display. Anchored surfaces now cap their width to
the display inside the same edge padding that clamping uses.
The Hosts page lists every host, so the overflow menu is scrollable:
on a short screen the last hosts and Add host were below the fold with
no way to reach them.
Change a worktree workspace's base from the Changes pane. The choice persists and diffs, counts, commits and Git actions follow it.
Supersedes #6872.
Co-authored-by: Tianyu Yang <yangtianyu.cl@bytedance.com>
* fix(app): rest the iPhone composer at the screen's bottom clearance
The composer sat 50pt above the bottom edge on every iPhone: the dock
padded the rectangular home-indicator safe area (34pt) and the composer
stacked another 16pt under it. On iPhone Duo that safe area is also
taller than needed; iOS 27.1's bottom-bar layout guide, which follows
the screen's corners, rests 26pt from the edge.
The native window-layout probe now publishes that clearance, and
WindowLayoutProvider overrides the safe-area context's bottom with it,
so every useSafeAreaInsets() consumer reads one bottom value and null
falls back to the system inset. The docked composer applies
max(bottom, 16) once and keeps 16pt above the keyboard through the
translate view's transform; the gap is not animated as padding because
layout updates through the keyboard transition are expensive on Android
Fabric.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): keep the Import session gap under the wide new workspace composer
The composer's own bottom spacing used to separate it from the Import
session pill on the wide form. With that spacing moved to the dock, the
centered slot padded below the pill instead, so the pill touched the
composer. The pill now owns its gap above.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
---------
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): keep the selected connection when it reconnects during a probe cycle
When the app returns from the background with more than one saved
connection and the selected socket was closed meanwhile, the probe cycle
marks the selected connection unavailable at probe start, while its client
is still reconnecting. The alternative probe lands later, after the
selected client has already reconnected, and finalizing the cycle switched
to the alternative using that stale verdict. That disposed the live client
(rejecting the in-flight timeline catch-up) and reconnected a second time.
The cycle now reconciles the selected connection's verdict with its
client's current state before deciding to fail over. A client that is
still down at that point still fails over as before.
* test(app): cover reconnect with and without a heartbeat RTT
* fix(client): keep active transfers alive during foreground checks
Foreground used an independent three-second probe that closed live connections while a pong waited behind bulk data. Share the heartbeat clock and refresh its outstanding deadline on validated inbound frames, including incomplete chunk assemblies.
* test(client): deliver bulk response before the queued pong
* fix(client): preserve latency deadlines when sharing heartbeat pings
* fix(client): keep heartbeat cadence during active downloads
* fix(sidebar): clear the failed status once the error is read
Reading an errored agent already cleared its error attention, but the
state bucket also mapped lifecycle status "error" to failed. Lifecycle
only leaves "error" when the next turn starts, so the red dot and the
Failed group stuck until the agent was prompted again. The failed bucket
now follows unread error attention, the same way the green dot follows
unread finished attention; lifecycle status stays "error".
Because failed now depends on attention, a failure must replace unread
finished attention, otherwise a reload that fails on an unread agent
would show green.
* fix(sidebar): keep failed subagents red and let read failures be marked unread
The failed bucket now follows unread error attention, which broke three
places that relied on lifecycle "error" alone:
- Subagent track rows and provider-subagent tabs have no read state of
their own, so failed children lost their red mark. They now map the
child's lifecycle directly.
- A read errored workspace became "done" and offered Mark as unread, but
the daemon only accepted idle or closed agents. Marking it unread now
restores error attention.
- A reload that failed again while the lifecycle was already "error"
raised no new attention. A failed reload now raises error attention
directly instead of relying on a lifecycle transition.
Keyboard focus can briefly mount the next card while the previous card closes. Poll uniqueness and visibility together so the assertion tolerates that transition but still rejects persistent duplicates.
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* fix(app): hold the loading screen until first-run setup is decided
A new user saw the empty home, the sidebar and a logo jump before setup
popped in. Startup now keeps the splash while setup may still open over
the empty home, for at most 3 s and only while the setup host is
reachable. Remembered-workspace restore does not wait. Web dialogs now
fade in as they already fade out.
(cherry picked from commit a0a7620b62)
(cherry picked from commit 72d7c0bf26)
* fix(app): fade the dialog card in and land the dim at once
(cherry picked from commit 6ef05705a0)
(cherry picked from commit 71c28b431c)
* fix(app): keep the loading screen up while startup redirects
(cherry picked from commit 0b28d4cbff)
(cherry picked from commit 13cc485e6f)
* test(app): cover a setup host that drops while startup waits
(cherry picked from commit 2a906ef83e)
(cherry picked from commit 7b6e21207a)
* fix(app): type the web-only dialog enter animation style
The CSS animation props are not part of ViewStyle. CI typechecks without the
generated expo-env.d.ts, which declares them, so the cast is required there.
(cherry picked from commit b887d979d2)
* fix(app): keep the setup window mounted when the theme changes
The shell's appearance boundary remounts its dialog hosts whenever a theme
color changes, so picking a theme in setup, or a system light/dark switch,
replaced the open setup window and replayed its fade. Setup draws colors
through CSS variables and themed props, which update in place, and changes
no font size, so it now sits outside that remount.
The dialog enter fade is skipped with reduced motion, and its style is typed
for CI, which typechecks without the generated expo-env.d.ts.
(cherry picked from commit b24c6eacf8)
(cherry picked from commit cb0e9907f5)
* docs(changelog): prepare 0.15.2
(cherry picked from commit 5af1a8ff66)
* chore(release): cut 0.15.2
(cherry picked from commit e896c05b09)
* fix(plugins): install registry npm pins through the configured registry
Registry npm pins carry an npmjs.org tarball URL. Passing that URL to npm made it rewrite the host onto the configured registry and drop any path, so mirrors such as https://mirrors.tencent.com/npm/ returned 404. Install the pinned version instead and identify the reviewed artifact by version and integrity, since a mirror records its own tarball URL.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(plugins): address review on npm artifact comparison
Take an object parameter in isSameNpmArtifact, and clean up the mirror fixture when test setup fails.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(plugins): verify mirror artifacts with a warm npm cache
A shared npm cache lets the mirror regression reuse tarballs from earlier
fixture installations, so successful installs made no tarball requests.
Check the installed artifact's mirror URL, version and integrity instead
of requiring network transfers.
Co-authored-by: finofliu <finofliu@tencent.com>
---------
Co-authored-by: finofliu <finofliu@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
On 0.15.0 the daemon launches the detached terminal worker while it is
being created, and the worker refuses to start unless it can read its own
birth time and command line (Windows PowerShell 5.1 `Get-CimInstance`,
`ps` elsewhere). Any failure of that query used to escape
startWorkerTerminalManager and take the whole daemon down with
"Daemon failed to start (exit 1)", as two Windows machines hit after the
update: one whose PowerShell 5.1 cannot load CimCmdlets, one where
powershell.exe cannot be spawned at all. 0.14.0 never ran that query at
startup.
The launch error is now logged and the daemon starts without a current
worker. The pool already launches one lazily on the first terminal
creation, so that request reports the same error while the machine still
cannot run the query, and terminals come back once it can. The identity
requirement itself is unchanged: a worker without a verified identity is
never written or adopted.
The "reasoning follows Expand thinking (false)" spec clicked a tool-call
group above the viewport. Playwright's scroll-into-view is programmatic,
and the web timeline only leaves sticky-bottom on user input (wheel, key,
touch, scrollbar), so it snapped back to the bottom while the click was
in flight and the group never opened. Wheel the group into view first,
reusing the helper the timeline pagination specs already use for
prompts.
The pkijs lockfile regeneration bumped tiny-async-pool's semver to 5.7.3
and app-builder-lib's @electron/get semver to 6.3.2, both published
2026-11-02 in one batch. Some Cloudflare edges cached the attestation
document for 5.7.3 before the publish attestation attached, so
`npm audit signatures` fails with EMISSINGSIGNATUREKEY depending on the
runner's region. Pin both back to the 2023 releases with per-parent,
range-keyed overrides so global-agent's semver ^7 under @electron/get
is left alone.
* fix(app): scroll web terminal history with a finger swipe
A phone browser claims the vertical drag and drops it, because the canvas covers the scrollable viewport and the page itself does not scroll. Keep the gesture in the touch handler and move one line per cell.
* fix(app): handle terminal finger scrolling once
Keep the vertical gesture in the existing terminal handler so xterm does not also scroll it at document level. Retain only the confirmed propagation correction from the contributor patch and cover the actual phone-sized app with trusted touch input.
Co-authored-by: sino <sino2322@gmail.com>
* fix(app): let xterm own terminal touch input
Remove the duplicate gesture implementation so history scrolls once and terminal applications retain arrow-key and mouse-report input. Cover live application modes with trusted touch gestures and a real raw-stdin process.
---------
Co-authored-by: sino <sino2322@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
* feat(app): swipe open collapsed tablet navigation
Reuse the retained sidebar and mobile panel motion for a partial-width
navigation overlay when the wide dock is hidden. Preserve the dock
preference, project gestures through the sidebar width, and dismiss the
overlay when revealing workspace content.
Reset the animated frame width explicitly across dock transitions and
keep the resize origin on the UI thread.
Validated on Duo portrait and landscape, iPad portrait and landscape,
iPhone full-width navigation, and browser sidebar interactions.
Typecheck, lint, targeted formatting, and 31 relevant tests passed.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): refine touch navigation overlays
Enable navigation swipes only on touch-capable runtimes and reject mouse
and pen input per gesture event. Allow swiping from anywhere in the
workspace instead of requiring an edge start.
Cover the full application window with the backdrop, including safe-area
padding, and remove the wide overlay's inline close button. Preserve
swipe, backdrop, and destination-selection dismissal.
Validated interior swipes on Duo and iPad, mouse exclusion in a browser,
and full-window dimming and dismissal in Duo portrait and landscape.
Typecheck, lint, and targeted formatting passed.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* refactor(app): let the panel store drop the overlay target on dock
Docking the sidebar, or any layout that no longer presents it as an
overlay, now drops the transient agent-list target inside the panel
store. App chrome publishes one left sidebar presentation (overlay,
dock, hidden) instead of an effect in SidebarChrome watching flags.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* refactor(app): answer fine pointer and touch screen from one module
Merge use-fine-pointer and use-has-touch-screen into
hooks/pointer-capabilities, which reads both answers from the media
queries it subscribes to. The touch answer now comes from
(any-pointer: coarse) instead of navigator.maxTouchPoints, which that
query's change events never reported.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): keep the phone web drawer on its edge swipe
Restore the 32px left-edge start for the compact web navigation drawer,
so interior rightward swipes over terminals, code blocks, and diffs stay
with their horizontal scrollers. The wide navigation overlay still opens
from anywhere. The edge rule moves into resolveMobilePanelGestureIntent
so both drawers share it and it has a test.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* refactor(app): one sidebar handler per destination
Settings, host settings, add host, add project, and every navigation
row now use one handler that closes the navigation overlay before it
navigates. Closing is idempotent, so the docked sidebar shares it and
the overlay ? mobile : desktop handler pairs go away.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* refactor(app): resolve the left panel width in one place
MobilePanelsProvider now resolves the left sidebar width (full window
on compact, the saved width clamped to the safe viewport on wide) and
exposes it through the panel runtime. The open and close gestures, the
overlay frame, and the docked sidebar all read that value, so gesture
travel and panel width cannot disagree. MobileGestureWrapper no longer
recomputes it, and the panelWidth/width parameters go away.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* docs: fold the wide navigation overlay into the mobile panel rules
Move the facts the overlay PR appended into the sections that own them:
the destinations intro, store ownership of the agent-list target, the
projection widths, gesture pointer and edge rules, and the sidebar
presentation bullets.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): keep the sidebar pans live across dock and overlay switches
On an Android tablet, opening the dock with the header toggle and collapsing
it again left the touch swipe unable to open the overlay until the app
reloaded. Swipe-to-close from the sidebar also stopped working after the
same toggle cycle.
Both pans took the dock/overlay state through RNGH's `.enabled()`. On
Android, RNGH 2.32 cancels the native handler when `isEnabled` changes and
resets its config on every update. After a false -> true flip the handler
stayed inert. `.enabled()` now carries only the device capability (touch
screen present), which does not change at runtime. The dock/overlay state
moves to a UI-thread derived value that fails the gesture in
onTouchesDown, next to the existing non-touch pointer check.
The wide sidebar's workspace list now always receives the close gesture
ref, as the compact sidebar already does. Toggling it between undefined
and the ref rewired the list's simultaneous handlers on every dock switch,
and that also left the close pan dead after a toggle cycle.
This is a native RNGH handler behavior, so there is no unit repro. It was
verified on the Android fold emulator: toggle open, toggle closed, swipe
opens the overlay, swipe on an empty sidebar strip closes it, no reload.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): keep the workspace full width after collapsing the dock
On an Android tablet, opening the dock with the header toggle and
collapsing it again could leave the workspace at the docked width, with a
blank strip on the right, until the overlay was next opened. Elsa's
original head showed the same.
The sidebar's panel host flips between an in-flow dock and an absolute
overlay. Its width came from a Reanimated style that read the React
`overlay` flag, so the width reached the native view on the UI thread
after React had already committed the new presentation. Layout logs on
the fold emulator show the lag on every toggle-open: the workspace laid
out at width 0 while the docked host still had the overlay's full window
width, then a second pass laid out the 320dp dock. When the late update
was lost on collapse, the workspace kept its dock width.
The host no longer has a width of its own:
- The dock sizes to the inner sidebar view, which already carries the
animated resize width.
- The overlay host is absoluteFill, which covers the whole row including
the safe-area padding.
With no animated layout prop on the host, a presentation switch is one
React commit, and the host and the workspace lay out in the same pass.
Device evidence on the fold emulator:
- Toggle open and collapse cycles: the host and the workspace lay out
together (320/521dp, then 841/841dp).
- A swipe opens the 320dp overlay with a full-window backdrop, and a
swipe on the sidebar closes it.
- Dragging the dock edge still resizes the dock and the workspace
together.
There is no unit repro: the defect is the ordering of Reanimated's
UI-thread layout updates against React commits.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): cancel pending sidebar swipes when docking
An opening swipe still has the agent target until release. Leaving overlay presentation must advance its revision even then, so motion ownership and queued commits reject the obsolete swipe.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): keep mouse selection free of the touch-only pan's capture
On a touch-capable browser the navigation pan registers pointer listeners
over the whole workspace. Gesture Handler's web pointer manager captured
the pressed element before the handler saw the pointer, and captured it
again on every move, so a mouse drag that the pan rejects stayed captured
and the browser's selection stopped at the starting paragraph.
The patched manager now captures after pointer-down, and only a pointer the
handler still tracks. The resize handle and touch swipes keep capturing.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): drop the compact Explorer target when the layout widens
Opening the Explorer on a compact layout selects the `file-explorer` mobile panel target. Widening
the window (unfolding a foldable, rotating a tablet) kept that target, because the store only
heard the left sidebar presentation, which is `overlay` on both compact layouts and wide layouts
with a collapsed dock. The navigation swipe begins only from `agent`, so on the wide layout every
swipe was rejected until something else reset the target.
App chrome now publishes the whole panel layout (compact or wide, plus the left sidebar
presentation) and the store drops any target that layout does not present.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): keep wide overlay sidebar content inside the left safe area
The overlay starts at the window's left edge, over the workspace's
safe-area padding, so its content now takes the left inset itself. The
dock already sits inside app chrome's padding and stays unchanged.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): return an interrupted swipe to its anchor when docking cancels it
Docking mid-swipe bumps the panel revision while keeping the agent
target. The command treated the unchanged target as motion already in
progress and skipped the animation, leaving the dragged position
stranded between anchors. A command that interrupts a drag now always
animates to its target.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): synchronize startup panel selection and gate pending swipes
A child layout effect can publish panel layout before the motion provider subscribes. Establish the current selection when subscribing, then observe later commands. Recheck presentation eligibility before gesture activation so docking also cancels touches that have not started dragging.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* test(app): cover panel subscription through its public contract
Verify initial snapshot delivery, later commands, and unsubscription directly in the existing panel-store suite instead of mounting test-only React components.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* test(app): reopen compact navigation after leaving settings
Destination navigation clears the transient overlay selection. Reopen the compact drawer before asserting its configured footer rather than relying on a stale open target surviving a wide layout.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* test(app): open navigation by keyboard in composer focus checks
Navigation pans intentionally reject mouse drags. Use the keyboard sidebar command to exercise focus and fullscreen Escape behavior without relying on the former mouse swipe helper.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): complete sidebar integration after main rebase
Retain the resize width resolver and format the shared division-width and hybrid touch-sizing integration.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* test(desktop): give split browser panes room for tab rails
After #8740, narrow panes replace tab rails with compact dropdowns. The desktop browser journey counts and selects rail tabs, so widen the real Electron content window before splitting instead of treating missing narrow-pane rails as missing panes.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
---------
Co-authored-by: Elsa <zeyugao@outlook.com>
* fix(app): retain Explorer resize width until acknowledged
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): choose workspace header by measured column width
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): separate iOS composer clipping from its shadow
Keep explicit coverage of the existing keyboard rotation guard.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): choose workspace chrome within each desktop pane
The outer width wrapper added a compact header while the desktop split container kept its full header and pane rails. Each pane now owns the complete choice, replacing its header and rail together.
Each split pane uses its own measured width: below 500 points it shows its own tab dropdown, even if the neighboring pane is wide. Pane width determines usable header space. Keep new-tab keyboard ownership outside the switched chrome and retain positive measurements while hidden.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): reserve readable width for the compact tab popover
Native scroll content does not contribute intrinsic width to the anchored frame. The count badge alone allowed the popover to compress its tab list and footer labels.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): preserve compact pane actions and window clearance
Carry the owning pane into compact launch actions, keep split and restore controls in its menu, and distribute window corners through visible split children. Keep tab rails below header-owned window controls.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): retain window dragging in compact panes
Keep the shared titlebar drag region when pane width replaces full desktop chrome. Cover two compact panes with both sidebars closed through the desktop host adapter.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* test(app): align pane journeys with measured workspace chrome
Give rail-specific split journeys enough column width, assert launched Diff content independently of header shape, and retain pane identity across compact transitions. Expect the compact selector in the half-screen Explorer workflow.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* refactor(app): share pane tab-switcher rendering after header rebase
Keep compact and vertical-bar header actions on the same pane-aware switcher contract while preserving the merged control geometry and script ownership.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): retain Explorer acknowledgements across consecutive drags
Keep earlier released widths as expected allocations while a newer gesture owns the frame, and restore the latest release if that gesture is canceled.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): keep focus mode exit in the compact workspace menu
Carry the workspace focus state into its compact action menu so a narrow header retains an exit without keyboard shortcuts.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* fix(app): distinguish resize rollback from pending acknowledgements
Restore the last released width when repeated widths precede a canceled gesture, and retain ownership when an unchanged old allocation matches a newer release before its predecessors arrive.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
* test(app): allow software WebGL time to finish atlas growth coverage
The 256-batch pixel regression crosses more than 500 render boundaries and exceeds its 30-second total budget under CPU contention. Keep the corpus, every pixel assertion, and the two-second per-frame deadline; allow 60 seconds for the complete workload. Reproduced the timeout with Chromium confined to one CPU and verified two successful constrained runs after the adjustment.
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
---------
Co-authored-by: Elsa Granger <zeyugao@outlook.com>