* feat(wsl): let an idle WSL distro stop once its orcad idle-exits
The WSL host's holder kept the distro (and the WSL VM) running after Orca quit, even with
nothing running in it. orcad now leaves a release request in ~/.orca when a managed idle stop
retired its terminal daemon; the holder waits for that orcad to exit, claims the request and
ends, so WSL can stop the distro. Live terminals, working agents, enabled automations or a
returning client keep the distro held as before. A host whose distro is stopped at startup
reads as stopped instead of unreachable, and the real-WSL lane gains a phase that proves the
release and records memory.
* fix(wsl): key the holder release on the WSL-host launch marker; name the lane phase
The real-WSL lane showed orcad idle-stopping with its daemon retired while the distro stayed
up: the setsid'd orcad's environment is not guaranteed to carry WSL_DISTRO_NAME, so the
release request was never written. Use ORCA_WSL_ORCAD_HOST, which only WSL-host launches set.
Phase 3 now carries its phase name and path filter, and records what kept a distro running.
* test(wsl): dispose the idle-release lane's guest transports
Each guest command left its exec loop running, a wsl.exe client that kept the distro up after
the holder had already let go (the receipt showed no holder, no orcad, no request left).
* test(wsl): record which wsl.exe clients keep the idle-release lane's distro up
* test(wsl): satisfy the flaky-test ratchet; feed the new idle port in the release test
* test(wsl): snapshot guest processes after launch to find what holds the session
* test(wsl): locate the step that leaves the zombie holding the idle distro
* test(wsl): log the idle-release lane's guest commands with times to place the zombie
* test(wsl): follow the remote runtime client's move
* test(wsl): record when each leftover WSL session init started
* fix(wsl): keep the distro while the user still runs anything, and never trip on the request file
Review of #33084:
- The holder now checks, before claiming a release, that no process of the user runs outside
the holder itself (session plumbing aside: systemd --user, (sd-pam), dbus-daemon). A detached
tmux, nohup job or dev server keeps the distro; it fails toward holding.
- A new holder drops any request left from before it started, so an older orcad's request can
never end a holder a client just asked for.
- orcad touches the request file only for WSL-host launches and never throws over it.
- The real-WSL lane proves a detached job keeps the holder and its request, and the distro
stops once the job ends.
* fix(wsl): a running container keeps the distro, whoever owns its shim
Docker's containerd-shim processes run as root, so the user-only scan missed them and a
release would have stopped running containers. Any containerd-shim process now keeps the hold.
* fix(wsl): read each process status only up to Uid; document what keeps the hold
Delta review of #33084: dash reads a status file a byte at a time, so stop at Uid. Document
that a root default user never releases, that per-user agents hold it, and the hidepid
exception. The lane's pkill no longer matches the shell running it.
* fix(orcad): materialize the app's agent-browser driver into the local slot
Replaces #34111's ORCA_AGENT_BROWSER_DRIVER hand-off, Windows profile copy and
claimed snapshot store. Packaging now references the app's own
resources/agent-browser-<target> from the orcad template manifest
(packagedBrowser, like packagedRipgrep), so the materialized slot carries it and
every local orcad launch finds it beside orcad-server.js. macOS signs the driver
before hashing; Windows reseals it after SignPath. The resolver now also tries
the musl driver name on Linux.
Refs #33846
* build(orcad): let a checkout keep template drivers with ORCAD_OMIT_AGENT_BROWSER=0
Startup evaluated every structured-agent adapter (claude, codex, acp, pi)
even when no chat was opened. The host install already loaded the
registrations with a dynamic import, but structured-chat-cli-fork imported
the same module statically to read sessionHistory, which the lightweight
metadata already exposes.
- structured-chat-cli-fork reads structuredAgentRuntimeMetadata (the same
objects, so behavior is unchanged).
- installOnJournal moves unchanged into structured-agent-session-host-assembly,
which install() loads on first host install; the existing installing slot
already single-flights and retries it.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(wsl): say why a silent WSL guest command failed
A guest command that exits non-zero with empty stderr was reported as
"WSL <distro>: command failed". When wsl.exe itself fails it writes its
error to stdout, so the real cause was dropped. Report stderr, else
wsl.exe's own error, else the exit code, for the Claude account pointer,
OpenCode reader, WSL CLI install and WSL file operations.
Refs STA-12262 (#33917)
* refactor(wsl): share the wsl.exe host-failure exit check
* test(wsl): type the guest failure fixture instead of asserting it
* fix(repos): let a project be removed while its paired server is unreachable
An unreachable server's repo.rm timeout aborted the whole removal. The error toast
now offers 'Remove from this desktop now', which purges this desktop's rows, records
a pending repo.rm (persisted UI state, optional), fences the server's catalog so it
cannot re-add the project, and replays the removal once the server is ready again.
Re-adds and replays for one server are serialized so neither undoes the other.
Fixes STA-9705
* fix(repos): replay owed removals whenever the server is ready, and lift the fence on refusal
Main publishes status only on change, so a record written or seeded after the ready
snapshot now replays at once. A refused replay refreshes the server's catalog, a
retired server drops its records, a user disconnect also offers local removal, and
the field is main-owned so paired clients cannot overwrite it.
* fix(repos): keep owed removals across disconnects and re-pairs; drop them only for forgotten servers
Also picks up records written during an in-flight replay, classifies the new UI field for
WSL moves, and fixes the test's projection shape.
* fix(repos): say 'disconnected' when the user disconnected the server, and test records surviving re-pair
* fix(repos): retry a replay when the server reconnects while it is still failing
* test(repos): narrow the toast action instead of asserting its type
* test(repos): run the toast action's removal directly instead of dispatching its handler
* fix(wsl): close a WSL command channel with stdin EOF before killing wsl.exe
Killing wsl.exe while its guest command still runs can leave the WSL session (an /init with a
wslhost.exe helper) behind, which keeps an idle distro running. A closed channel now ends stdin
first and kills wsl.exe only if the guest has not exited within 2 s.
* fix(wsl): kill WSL-host channels still in their close grace at app quit
Review of #34307: a guest command that ignores stdin EOF would otherwise keep its wsl.exe up
past Orca's exit for the rest of the grace. Quit teardown now kills every channel still in its
grace. The abort test runs on fake timers instead of waiting the grace in real time.
* fix(browser): type text starting with a dash literally instead of as an agent-browser flag
* fix(browser): keep fill scripts on eval --stdin and send well-formed batch text
* fix(browser): leave passthrough multi-command batch replies unwrapped
* fix(orcad): pass the wait process timeout through the run options
* refactor(browser): let caller stdin imply argv mode and share the batch decoder in tests
* test(orcad): decode batch commands in the provider selection fake
* fix(native-chat): keep Claude approval choices across model changes without freezing delivery
A Claude chat saved as "Approve for me" could reopen asking for every approval,
because Orca read the folder's saved model list as launch policy, and a model
change could keep running in a weaker approval mode Claude opened with. The
first fix for this was reverted after it froze the app: restored history left
a turn reading as running, a send was refused against it, and delivery retried
the same send with nothing changed.
- The saved approval choice goes to Claude as-is; the menu offers every mode.
- Orca records the mode Claude actually opened in. A send that picks a new
model reopens Claude first when that mode differs from the one requested,
only when the chat owes no work; Orca mail notices never reopen it.
- A message sent while a Claude turn is running joins that turn on its current
choices; new choices apply at the next new turn. Nothing is refused.
- History catch-up closes every turn it opens, in the thread it opened in.
- At startup, a turn reading as running under the new agent that the agent is
not running is ended as interrupted.
- Delivery goes round again only after a step shows what it changed (a commit,
a different child, a start it joined ending, or a wake); otherwise it waits
for the next change instead of repeating the same step.
Tracked in STA-10953.
* fix(native-chat): preserve execution evidence at dispatch
* Fix delivery iteration ownership and progress contracts
* Correct journal fixture types in delivery regressions
* Separate Claude input choices from error correlation
* feat: drag to reorder queued messages on desktop and phone
* fix: use the queue grip button padding from its primitive
* fix: preserve the queue grip touch width with hit slop
* test: carry queue animation mocks into the chat view harness
* test: complete the shared chat view harness registrations
* test: carry the queue move cross-version wire contract
* Scope queue move feedback to the issuing chat and preserve phone touch width
* Keep queue reorder presentation stable during measurement and pending moves
* Keep measured row handoff tokens constant size
* Avoid rebuilding queue arrival IDs on unchanged renders
* Fix mobile queue regression test fixture boundaries
* Share the queued message leading slot with its reorder grip
* Fix queue regression fixture quality findings
* Check queue icons within the leading slot
* Allow every unsettled queued message to reorder
* Supply stable scope in queued row test mock
The updater kept a reference to the main BrowserWindow after it closed, and
every status/nudge/install-abort send read `webContents` on it. Electron throws
"Object has been destroyed" from that getter, so the hourly background check
produced main-process unhandled rejections from the MacUpdater failure path.
Route all updater IPC through UpdaterState.sendToMainWindow, which drops the
send when the window or its webContents is destroyed. The renderer re-pulls
status via updater:getStatus on mount, so nothing is lost. Updater test window
mocks now come from the shared updaterMainWindow helper.
Fixes#11544
Co-authored-by: Brennan Benson <brennan@stably.ai>
Co-authored-by: Neil <neil@stably.ai>
A page that loads while its browser pane is parked (display:none) never
paints, and Chromium drops input to a document before its first paint.
orca click/hover/keypress/mouse on such a page reported success after a
~5 s mouseMoved stall while the page got no press or key.
Before agent input reaches a webview guest, from the CDP proxy queue or
the bridge's direct pointer and key routes, ask the page whether it has
painted. If not, hold its paint with the existing capture paint hold
until a paint entry appears or a few frames pass, once per document.
Co-authored-by: outof-place <contact@outofplace.space>
* fix(automations): pause a schedule whose agent is stuck on a hook review
Codex opens a blocking 'Hooks need review' dialog when a hook in hooks.json is
new or changed. Only a person can answer it, so every later scheduled run of
an automation hit the same dialog and failed. Pause the schedule on the first
such failure and say how to recover. Also re-enable Orca's own Codex hook on
SSH installs, matching local installs.
Refs #28252#23289
* fix(automations): keep a hook-review-blocked schedule running and say why it fails
Replace the auto-pause with a visible, self-clearing failure: the run error
explains the Codex hook review, the automation and run pages show it above
saved output, and earlier tabs stuck on the same prompt close so only the
newest stays open to answer. Runs succeed again on their own once the hooks
are trusted or removed.
* fix(automations): only close hook-review tabs the run still owns and that are provably unused
- Cleanup resolves the pane through runOwnHandle, so a cold-restored pane holding a new process is left alone.
- Close only on definite 'unused' input history, matching headless run-terminal retention.
- Catch failures from the fire-and-forget cleanup call.
- Show the run page error notice only for dispatch_failed runs, and add the missing en catalog keys.
Condition waits never forwarded --timeout, so agent-browser always stopped at its own 25s default and --timeout 60000 still failed at 25s. orcad's external Chromium run and Electron sidecar socket also capped waits at a fixed 90s. One builder now passes --timeout (explicit or the documented 25000 default) on the local and orcad paths, process kills sit 5s above it, and every forwarding hop waits max(60s, T+30s).
* fix(opencode): run managed OpenCode 2 accounts on a private server
OpenCode 2's background service binds one fixed port per machine, so a
second managed account's service could never start. Launch managed
accounts with --standalone, which picks its own port.
Fixes#33780
* chore(opencode): tighten standalone rewrite comments
* fix(opencode): use a private OpenCode 2 server only when another data dir holds the service port
Users with one managed account keep the shared background service. Orca
adds --standalone only when a live upstream registration
($XDG_STATE_HOME/opencode/service.json) belongs to a different data dir,
plus a short in-process claim for launches that start together.
* fix(opencode): judge resumed accounts by their applied data dir and claim the port only for real launches
Daemon spawns consume the resume request before the private-server check runs, so
derive the launch's own data dir from the applied account env when present. Only
take the shared-port claim when the --standalone rewrite can apply.
When a worker's turn start was not observed, the dispatch receipt claimed
the input was "written and submitted". A swallowed Enter leaves the prompt
in the composer, so say the text and Enter were written, keep the wait bound
and possible causes, and point at `terminal read` before resending.
Co-authored-by: Neil <neil@stably.ai>
* fix(wsl): reap the bash stdio relay's cats before it exits
The relay killed the surviving cat and exited at once. WSL's per-session init never reaps
orphans, so that cat stayed a zombie, its session never ended, and the distro never stopped
even with nothing of Orca's left in it (seen on every real-WSL idle-release run). Wait for both
cats after the kill.
* test(wsl): prove the bash relay waits for the cat it kills
electron-updater sends checks and downloads through its own
"electron-updater" partition, so a proxy configured only in Orca settings
never reached update requests. Apply the same proxy policy (and request
guard) to that partition at updater setup and on settings changes, and
answer its proxy login challenges with the configured credentials.
Co-authored-by: lorenzozanee <195543813+lorenzozanee@users.noreply.github.com>
Packaged Classic builds left the live Dock tile on the 256px icon.png set by
applyAppIcon. Once the queued AppKit custom-icon clear succeeds and no newer
icon request is pending, persistMacDockIcon now calls
app.dock.setIcon(nativeImage.createEmpty()); Electron passes that to AppKit as
nil, so the Dock draws the full-resolution bundle icns. A failed or timed-out
clear keeps the PNG, as before.
Fixes#27138
Co-authored-by: outof-place <contact@outofplace.space>
* fix(mobile): compare branches and show diffs with desktop's base-ref and whitespace settings
Base-ref resolution moves to src/shared and the host publishes the compare-against-upstream
and show-whitespace settings, so the phone's branch compare and diffs match desktop.
* fix(mobile): use the upstream compare policy on the review screen and rebuild diffs when whitespace changes
* fix(mobile): reread Source Control settings on Refresh and restart diffs loading under the old whitespace setting
* test(mobile): cover diffs dropped when desktop's whitespace setting arrives
* refactor(mobile): share one Source Control settings read per host across screens
* fix(wsl): keep a user's GLIBC_TUNABLES and delegate the launch-env wrapper
The orcad launch now sets GLIBC_TUNABLES only when the launching shell has
none, so a value forwarded through WSLENV wins and the claim leaves it in
place. withOwnBuildLaunchEnv delegates to the transport instead of copying
its own properties, so a class-based transport keeps its methods.
* test(wsl): read the delegated transport through its type, not Reflect
withOwnBuildLaunchEnv now returns the transport's own type, so the test
calls the class method directly. The comment no longer claims #private
class fields survive delegation.
Offer confirmed permanent deletion when moving an item to Trash fails.
Preserve authorization and workspace ownership checks, cancellation behavior,
text-snapshot undo, remote compatibility, and all supported translations.
Fixes#33944
Co-authored-by: nori <dosukoicrypto@gmail.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(wsl): background Git and the roots catalog never start a stopped WSL distro
git status ran on the UNC project root 0.45 s after a terminate, next to the
explorer read; either boots the distro. Background Git (status, conflict
operation, upstream status, branch compare, the upstream-ref watch) now asks the
shared running gate first and refuses for a stopped distro, including host
paths whose Git runs through wsl.exe. The authorized-roots rebuild skips a
stopped distro's repo and leaves it dirty, and the detected-worktree scan fails
as unavailable, so rows are kept and the next scan after the distro runs lists it.
* fix(wsl): the active workspace's first status and a wake read's roots rebuild may start WSL
- Drive-path projects whose Git runs through WSL had no wake path: the active
workspace's first git:status after it becomes active now passes
wakeWslDistro, so Source Control starts the distro; later polls stay gated.
- A user act runs in an async scope naming its distro; gated work inside it
(the authorized-roots rebuild an authorization miss triggers, the detected
scan, the Git) may start that distro and no other. External worktrees on a
stopped distro no longer fail a wake read with access denied.
- After a user act wakes a distro, background reads stop trusting the older
'stopped' read and probe again.
* fix(wsl): explicit WSL launches refresh the running read; wake only on tab change; gate the home probe
- Any wsl.exe Git/CLI run (commit, push, fetch) or WSL terminal spawn for a
distro last read as stopped records that it started, so background reads probe
again instead of refusing for up to 60 s.
- The active editor tab wakes only when the active tab changes; effect re-runs
driven by git status or overview views read without waking.
- The authorization roots' WSL home probe no longer runs wsl.exe in a stopped
distro when the home is uncached.
- The settle window no longer imports the orcad liveness module (it pulled main
code into the web typecheck); a test pins it to EXIT_SETTLE_MS.
* fix(wsl): deferred work and running-list listeners never inherit a user act
A user act's async scope could leak into work it triggers indirectly: a drain of
deferred tasks it caused, or listeners of a running-list probe started inside
it. Both now run outside the scope, so only the act's own work may start its
distro.
* fix(wsl): keep shared modules free of main-only gate imports
worktree-logic.ts and wsl-running-distro-cache.ts are also compiled by the web
project, which cannot see main-only modules (AsyncLocalStorage, wsl.exe).
getAllowedRoots now injects the gated home lookup into computeWorkspaceRootWithin,
and the background gate's own cache subscriptions run outside the user-act scope
instead of the cache knowing about it.
* Reuse rendered chat history across readers
* test: cover current journal reads across source changes
* test: align journal assertions with current snapshot reuse
* fix(mobile): keep a renamed tab's name on mobile after the host restarts
After a restart, the reconnecting client restarts each saved tab's
terminal (restored-pane-materialize -> createRuntimeOwnedMobileSessionTerminal),
and the mobile row built from the new terminal carried no customTitle, so
the phone showed the terminal's own title although the saved session still
held the name. publishPtyBackedMobileSessionTerminal kept a name only from
an earlier mobile row, which a restart does not have.
Both builders now resolve the name through one helper: a value already on
the mobile row wins, including a cleared null; only a row with no value
falls back to the tab's saved name.
Fixes#33737
Co-Authored-By: Claude Code <noreply@anthropic.com>
* refactor(mobile): resolve a restarted tab's saved name through getPtyManualTitle
getPtyManualTitle already owns a terminal's manual name (the saved tab's,
else the live rename), so the row builders use it instead of a second
saved-session lookup. A cleared name reads as cleared from the saved
session, so the test now covers that instead of a row-only clear.
* fix(mobile): keep a row's cleared name instead of falling back to the saved one
A null customTitle on a row is a clear: paired desktops need it to drop a
cached name, and a desktop clear lands on the row before the debounced
session write. Only a row with no value falls back to getPtyManualTitle.
* test(mobile): pin that a row's cleared name survives a terminal restart
---------
Co-authored-by: clayphi <193966884+clayphi@users.noreply.github.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
The streamed page's keydown handler forwarded Ctrl/Cmd+V to the host as a
keypress and cancelled the browser's paste event, so the host pasted its
own (usually empty) clipboard. Native paste chords now fall through, and
the frame's paste event inserts the clipboard text with
browser.keyboardInsertText on the pane's input queue, split into chunks
that fit the runtime socket frame.
Co-authored-by: Chris Stephens <nyldn@users.noreply.github.com>
Orca wrote a synthetic "OMP ready" / "OMP - action required" title over
the session title that OMP and Orca's titlebar extension already paint.
OMP's hooks are authoritative and its own rest title reads as idle, so
stop synthesizing terminal titles for OMP, as for OpenCode.
Linear: STA-11986
* fix(browser): let Grab page element pick and track elements inside open shadow roots
Document hit-testing and marker lookups stop at a shadow host, so Grab only
ever picked the host. The overlay now descends through open shadow roots,
and both the overlay and the annotation viewport bridge find the marked
element and listen for scrolls inside them. Closed roots stay opaque.
Co-authored-by: Felipe <5784455+fbetancourtc@users.noreply.github.com>
* fix(browser): track slotted ancestors and stop re-walking shadow roots for a lost target
* fix(browser): walk shadow roots once per selection and count fixed shadow hosts
* fix(browser): restore grab pointer events if a page's shadowRoot throws; type the shadow fixtures
* fix(browser): keep composed-tree walks safe from page-named form controls
---------
Co-authored-by: Felipe <5784455+fbetancourtc@users.noreply.github.com>
* fix(wsl): close a WSL host's loopback listener once its orcad idled out or its distro stopped
The supervisor already stops watching an idle or stopped host and never relaunches it for
background work, but the bridge's loopback listener stayed open, and any dial to it (a status
probe, a stale client) spawned wsl.exe and booted the distro again. Retire the listener in both
branches without publishing 'closed', so the host keeps reading as idle or stopped; the next
user connect binds a new one.
* fix(wsl): a retired listener never proves orcad live; never retire a connect in flight
Review of the listener retirement: with no connection, the port probe answered 'ready', so a
wake verify with no orcad pid to compare read the host as live, published ready and re-held
the distro. It now reads as refused. retireListener also leaves alone a key whose user connect
is in flight, so a trigger mid-connect cannot retire the entry that connect produces.
* fix(wsl): retire the listener on E9's single park path too
E9 A parks every confirmed stop, visible work or not; retire the loopback listener there,
before the deferred re-check, so a dial cannot boot the parked distro.
* fix(wsl): read the park generation before retiring the listener
#33968 pushed terminal-send-method.ts to 301 counted lines against the 300-line rule, failing Lint on main and every PR that merges it. Eight refusal branches spelled out the same zero-byte result; one helper builds it.
* fix(browser): make orca set media --reduced-motion actually apply
agent-browser's set media only looks for the bare tokens dark, light and
reduced-motion and ignores anything else, so the documented reduce value
was dropped and the command still reported success. Map Orca's values
onto those tokens in one builder used by the desktop bridge and the
orcad external Chromium path, and reject unknown values.
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
* fix(browser): reject bad set media values before queuing and list both flags in help
---------
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
* fix(terminal): closing a kept host-loss tab after a relaunch ends its history
A WSL bridge pane lost with its distro keeps its tab and unended history. The
adapter only remembers the loss in memory, so after a relaunch closing that
tab before waking it hit SessionNotFound and left its history directory
behind. The router now ends that history when the id's history is unended and
every daemon, current and legacy, answers that it does not list the id. A
listing or an unanswered daemon keeps the history.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(terminal): end a lost session's history only when every daemon can answer for it
Review of the close-after-relaunch path: the router-only check never ran
when the app has no legacy daemons (the provider is then the bare adapter),
and the router's adapters are not every daemon sharing the history dir. An
older daemon whose socket probe failed at startup gets no adapter, and a
daemon held for a newer protocol has none either; both may still run the id.
The check now lives in the adapter's close. It ends the history only when it
is unended, no daemon is held for a newer protocol, no older protocol's pid
file names a process that may be alive without an adapter to ask, and every
known daemon answers listSessions without the id. A router shares its
adapters as the daemons to ask.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(terminal): decide a lost session's close in one lock, and never connect a daemon to list it
Review: the unlisted-session check already runs inside shutdownWithHistoryLock,
so a spawn queued for the same id waits until the close has decided; a test
now holds the listing open and proves the spawn cannot slip in. listsSession
no longer connects (or respawns) a daemon just to list it: an unconnected
daemon cannot answer, which keeps the history.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(terminal): count an older daemon Orca may not signal as alive; cover slept-tab closes
process.kill(pid, 0) failing with EPERM means the process exists under another
user, so legacyDaemonProcessMayBeAlive now treats it as alive; only a missing
pid file or ESRCH counts as gone. Tests pin that, and pin the wider scope of
the unlisted close: closing a slept tab without waking it ends its unended
history, and keeps it while another daemon lists the session.
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(terminal): move the lost-session listing and check out of session control
daemon-pty-session-control.ts went over max-lines. The daemon listing read and
the unended-history check now live in daemon-unlisted-session.ts beside
mayAnyDaemonRunSession; the adapter keeps only thin calls. No behaviour change.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(runtime): clear Claude's question wait when a phone keystroke answers it
Claude sends no hook after AskUserQuestion is answered; desktop infers it, phone answers left the pane waiting.
* fix(mobile): answer multi-question cards like desktop, with Skip and back-stepping
Mobile required every answer and locked grouped structured questions once tapped; the card rules and structured mapping now live in shared code.
* fix(mobile): announce ask card options as checkboxes or radios
* fix(mobile): answer a grouped question only on the prompt it was picked on
* test(runtime): drop the multi-question case the shared answer rule already covers
* refactor(native-chat): share the typed-answer rules and fold the host answer check into terminal.send
* fix(mobile): send terminal paste, keys and query replies like desktop
* fix(mobile): encode Shift+Enter for the pane's negotiated keyboard modes
The phone's xterm now tracks Kitty flags and modifyOtherKeys with desktop's tracker, seeded from the host snapshot.
* fix(mobile): keep keyboard modes across terminal snapshots
* fix(mobile): adopt snapshot keyboard modes with desktop's rule
* refactor(terminal): share snapshot keyboard-mode adoption and mode mirroring
* fix(mobile): compare terminal modes field by field instead of a dynamic read
* refactor(mobile): share terminal display-mode adoption between snapshot and resize
* fix(mobile): accept only known terminal display modes from the host
* fix(mobile): use desktop's sort, colour, count and agent-row rules in the workspace list
Recent ignores terminal output, repo colours fall back to desktop's neutral,
Home counts skip archived rows, folder workspaces trail each lane, and a stale
agent row on a live terminal reads "No update in …" like desktop.
* fix(mobile): keep Delete disabled on a primary checkout, as desktop does
* fix(mobile): keep the task text on a silent agent row
* fix(mobile): show a silent agent row's time since its last update
* test(mobile): drop agent-row tests that replay the shared verdict
* refactor(mobile): check host PTY liveness once and keep the delete guard on the row flag
* test(mobile): re-record the home catalog goldens for the list's active count
* fix(wsl): background file reads and watches never start a stopped WSL distro
Any Windows touch of a \\wsl.localhost path boots a stopped distro. After a
terminate, the dying explorer watcher's overflow refresh ran realpath on the
project root 0.23 s later and booted the distro again. Background reads, stats
and watch installs now ask a fresh running list first (shared, with a 5 s lease
and a stop signal from the watcher exit and the hook relay link) and refuse when
the distro is stopped. Opening the workspace and Retry still start it. A stopped
watch waits for the distro and resyncs the explorer when it runs again.
* fix(wsl): trust a confirmed running read for 60 s without a stop signal
* fix(wsl): user acts wake; unverifiable is not stopped; settle after a stop
- Opening a workspace, expanding a folder, opening a path (explorer, quick-open,
links), the active editor tab's load and every Retry pass wakeWslDistro on
readDir, readFile, stat and readFileChunk. Background reloads, restored-tab
scans and watchers stay gated.
- A failed running-list read is 'unverifiable', not 'stopped': the gate re-reads
once past the failure backoff (once per failure window) and otherwise refuses
with 'WSL didn't answer', never touching the path.
- After any stop signal, a running read that started within EXIT_SETTLE_MS of it
never counts, in isWslDistroRunning itself, so neither the watcher's own
overflow refresh nor the hook relay's restart check can read a stale listing.
- A confirmed read that sees a distro stop records a stop signal, resetting the
60 s lease.
* test: user-opened stats now carry the WSL wake flag
* fix(mobile): list Source Control changes with desktop's sections, sort and row rules
Conflicts get their own section in the user's group order, submodule rows lose stage and
discard like desktop, paths sort with the shared collator, and review notes go out as desktop formats them.
* fix(mobile): read the group order the host now publishes and keep submodule rows read-only
The host's paired settings projection now carries sourceControlGroupOrder, the
normalized status projection keeps submodule metadata, and conflict rows reuse the
shared conflict-kind labels.
* refactor(mobile): share Source Control grouping and row rules with desktop
Desktop and mobile group rows through one shared helper, mobile's unstage button
follows desktop's rule, and the group order resets when the host changes.
* fix(window): repaint a never-shown background window on Windows so CDP screenshots return
Windows composites nothing for an idle window that was never shown, so
Page.captureScreenshot under ORCA_BACKGROUND_LAUNCH=1 waited forever once the
status-bar usage chip stopped animating (#33799).
* fix(window): pump hidden-window frames from the renderer on Windows background launches
Main-process webContents.invalidate() did not make a never-shown Windows window
produce a frame CDP can capture (verified on Windows 11). A one-frame root
background flip from the preload does, so main stamps a preload flag for
ORCA_BACKGROUND_LAUNCH=1 on win32 and tells the preload to stop on first show.
* fix(window): stop the hidden-frame pump only on main's show signal, including after reloads
A never-shown Windows window can report visible after a desktop change, so the
preload no longer stops on visibilitychange; main re-sends the stop after any
reload once the window has been shown.
* fix(window): register the frame-pump stop with on('show') so every window double supports it
createMainWindow suites simulate win32 under the runner's ORCA_BACKGROUND_LAUNCH=1,
so they install the pump stop; their window doubles implement on() but not once().
* chore(terminal): log the WSL bridge exit verdict and how long the probe took
A bridge pane's exit is host loss only if the running list, read after the
exit, lacks its distro. Orca's own background reads can reboot the distro
within seconds, and nothing recorded what the probe answered, so a run could
not tell host loss from a raced reboot or a slow wsl.exe. The check now
returns lost, running or unavailable, and the adapter logs it with the probe
latency. Behaviour is unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
* chore(terminal): put the probe's doc comment on checkWslBridgeDistro
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(mobile): ask before running orca.yaml default-tab commands from the phone
The host's repo.hooks reply now carries desktop's setup config, so the phone
gates the Run/Skip choice and the trust prompt on defaultTabs commands too.
* fix(setup): keep the local script in run-both setup config without orca.yaml commands
* test(setup): drop the shared duplicate-host trust test desktop already covers
* refactor(setup): derive host setup trust from the shared setup config
* fix(mobile): count every host's repo rows before honouring repo-wide setup trust
* fix(mobile): show desktop's PR lanes and failed/interrupted status dots on the phone
The host builds worktree.ps linkedPR with the desktop's PR-lane cache reader
and sends the desktop sidebar dot as an optional sidebarStatus field.
* fix(mobile): keep uncached PR badges and let finished turns show through working titles
* fix(mobile): suppress agent panes' permission and parent titles in the host status dot
* test(runtime): expect sidebarStatus on the compact ps summary
* test(runtime): keep only host-specific sidebar status cases
* refactor(mobile): keep host title suppression and the uncached PR lane in one place each
* test(wire): pass the terminal title evidence map to new host builds
* fix(runtime): read each workspace's PR through its own host's repo