Paired phones, web clients and the CLI poll reads nobody is looking at:
history, submodule status, ignored paths, the conflict operation, the
in-flight mutation probe, upstream status and link-existence checks.
Their desktop IPC twins already refuse a stopped distro; these did not, so
a client left open booted the distro again after the user terminated it.
Each now asks the settled running gate first and refuses with
WslDistroStoppedError, which reaches every client version as the existing
runtime_error.
User acts stay ungated: git.status (a viewing client's implicit wake),
diffs and compares (opened views), file reads and directory listing
(taps and browsing), and stat (the download name check).
* fix(mobile): retain renderer-owned browser tabs while unmounted
Adapt the community browser ownership fix from #14792 to the current runtime modules. Preserve renderer epochs and route mobile browser close through its owner.
Co-authored-by: Bing.Z <zzb@gxsmjx.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(mobile): cover browser ownership lifecycle races
* test(runtime): cover browser identity collisions and stale retirement
* fix(mobile): retain browser group membership after renderer reconnect
* fix(mobile): drop renderer browser rows once the desktop window closes
In serve mode the offscreen reconcile no longer checked for a window, so a
closed window's browser rows stayed listed with no page behind them. Also
drop the legacy :headless-merge: ownership fallback, which nothing produces.
* fix(mobile): list a desktop webview's page once in serve mode
The bridge inventory also lists an attached window's webviews. Only the
offscreen reconcile filtered those out, so hydrate added a second row for
the same page, and closing it unregistered the desktop's guest. Filter in
the shared builder instead.
* fix(mobile): list only pages the serve browser backend created
With a desktop window attached in serve mode the bridge inventory also
lists the window's background webviews, which the renderer does not
publish. Those became offscreen rows, and closing one unregistered the
desktop's guest. Ask the backend which pages it owns.
* style(mobile): keep projection layout file under the line cap
* fix(mobile): keep the serve publication epoch when browser pages change
* test(mobile): give browser backend doubles ownsTab and drop removed notifier field
* fix(mobile): republish browser rows when the desktop window closes and dedupe serve rows
* fix(mobile): rebase detached publications only for retained offscreen pages
* fix(mobile): advance snapshot versions when the desktop window closes
* test(mobile): name the offscreen epoch ownership tests after what they cover
---------
Co-authored-by: Bing.Z <zzb@gxsmjx.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
Replace sentence fragments, manual non-family plurals, item(s) copy and
hard-coded English in the right sidebar, GitHub item dialog, PR page,
GitLab item dialog and diff-comment surfaces with whole-sentence keys and
CLDR plural families, and add translations for all built-in locales.
Refs #32207
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(i18n): localize remaining renderer plurals, fragments and hardcoded copy
Convert manual one/many keys and value0 counts to CLDR plural families,
replace fragment concatenations with whole-sentence keys or Trans, and route
hardcoded English labels, errors and compact time units through the UI locale.
Refs #32207
* fix(i18n): re-render memoized agent state dots on language change
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
With "Remote server workspaces" set to "Server (streamed)", a paired server
that has no way to stream pages (an orcad `orca serve`: its browser engine
offers no screencast) could not take the desktop's new tabs. The desktop
fell back to a plain desktop tab the server never heard of: it browsed from
the desktop's network and never appeared in `orca tab list`, so agents could
not see or drive the page the user was on.
The setting is a rendering preference. When the server cannot stream, the
desktop now renders the page itself as a page the server owns (the existing
client-hosted path), so traffic still goes through the server and the tab is
listed and drivable from the server's CLI. Servers that can stream keep
rendering on the server; an explicit server placement is unchanged.
Refs #35156
* fix(wsl): account sync and usage previews never boot a stopped WSL distro (E9)
Four account paths touched WSL distros in the background:
- Every launch mirrored ~/.codex/config.toml into each WSL Codex account home over UNC.
- Inactive Codex usage previews read a WSL home that nobody had checked.
- The startup Claude publish and inactive Claude previews trusted an unsettled
running list.
Any of these booted a stopped distro, or one still stopping. The mirror now skips
WSL homes not seen running after any stop; launch preparation syncs them before
use. The other three use the settled running gate.
* fix(wsl): the Codex mirror trusts only a running list from the last 60 s
WSL's own idle stop is reported by nothing, so an old list must not
vouch for a distro. Drop the inert user-act clause: select and sign-in
do not run as WSL user acts.
* feat(wsl): a WSL bridge shell reports its folder, so restores follow cd
Bridge bash and zsh send no OSC 7, so the terminal daemon's emulator and every checkpoint kept
the spawn folder until a Sleep read the shell's folder in the distro. A terminate, a quit or a
timed-out read reopened the pane at the project root (verify-18 e). The bash rcfile and zsh
wrapper now report the folder at each prompt where it changed, only inside WSL, percent-encoded;
the daemon already reads OSC 7 into a WSL pane's UNC path.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl): re-send the folder report every prompt, and prove the daemon bridge sources it
* fix(wsl): keep the zsh folder report's locals in Orca's namespace; update wrapper snapshots
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(mobile): clear terminal output only after the computer accepts Clear
The phone erased its terminal before sending terminal.clearBuffer and
reported success on any reply, refusal included. Wait for the host's
{ clear: { cleared: true } } reply, fail immediately when disconnected so
the request cannot replay after reconnect, ignore late replies after the
client or terminal changed, and resubscribe a subscribed terminal so its
snapshot (including output that arrived during the request) replaces the
old screen. An unsubscribed tab draws its snapshot when it next subscribes.
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* test(mobile): return the subscribe result from the gesture recording adapter
* fix(mobile): keep accepted clears when the terminal view re-renders
The WebView handle is rebuilt on every render and a relay/direct cutover
bumps the connection generation without changing the host, so both
checks treated accepted clears as stale: the phone neither reloaded the
output nor showed a toast. Only a replaced client is stale now.
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* chore(wsl): keep the Sleep folder and bridge-exit lines in the local trace
A packaged build keeps no console, so #35150's Sleep folder line and the WSL bridge exit verdict
were invisible on a device. Each now also goes to main.trace.ndjson as a span with the line and
its facts as filterable fields.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl): keep session paths and distro names out of the persisted WSL trace
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(mobile): drop voice settings replies older than the latest change
An older save that replies last could put the previous setting back on screen. Ignore mutation replies and errors that a newer change has superseded.
* fix(mobile): keep the Voice screen on the connected desktop
Key the screen by host so a late reply from a desktop the phone switched away from cannot land on the newly connected desktop's settings.
* fix(mobile): re-read Voice settings when an older save succeeds late
The host finishes a model delete only after removing its files, so a newer change can reply first with the model still selected. Dropping the older reply left the deleted model shown as in use; re-read instead.
* fix(mobile): keep Voice model errors and the screen through reconnects
A newer setting change does not replace a failed model select, download or delete, so show those errors again. Key the screen on the last connected desktop so a reconnect of the same desktop no longer remounts it.
* test(mobile): pin Voice model-delete errors after a newer change
* fix(mobile): re-read Voice settings after any failed save
* fix(mobile): re-read Voice settings only after every pending change settles
A read sent while a model delete is still running can return the deleted model after the delete's own reply. Skip reads while a change is pending, and re-read once when all settle if any reply was skipped.
* fix(mobile): keep a failed Voice model action's error on screen
A lone failed select or delete changed nothing on the phone, so it no longer starts a re-read that clears its error. A failed switch still re-reads to undo its optimistic flip.
* fix(mobile): let Voice reads clear only their own load errors
A re-read after a failed or superseded change cleared the change's error before the user could read it. Keep load and change errors apart; re-read after any failure again, which also undoes a failed switch flip.
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* Nest chat-UI orchestration workers under the agent that started them
Main's lineage projection only walked terminal panes, so workers running
in the chat UI never got a parent link and listed flat in the sidebar.
It now also projects chat workers, keyed by their visible chat row, and
resolves a chat creator or coordinator to that chat's visible row
instead of its handle or private mailbox pane key.
* Fix public worker lineage across assignment and row identities
* Complete terminal lineage provenance and bound active session selection
* Repair lineage test runtime boundaries and SQLite counters
* Batch public lineage verification after exact recency selection
* Use checked runtime values in lineage integration tests
* Document the trusted Dispatch SQL row boundary
* Keep lineage integration imports within their test programs
* Load native lineage fixtures before test execution
* test: complete lineage inputs in runtime and sidebar fixtures
* test: seed benchmark facts from active dispatches
* test: replace partial orchestration DB casts with typed fixtures
* test: complete stale-leaf mailbox fixture session input
* fix(test): render worker lineage fixture with production list
The chat mic only got isRecording/isProcessing, so it looked idle while starting; it now shows the live look the terminal mic does. The terminal status row says 'Release mic to stop' in hold mode, both mics expose role and busy state, and one live region announces Starting mic / Listening / Processing.
Fixes#35422
* fix(mobile): preserve file drafts across refresh and reject stale reads
Overlapping preview reads overwrite current content, and refreshed metadata or equivalent route objects revoke dirty editor state. Keep document-owned drafts editable and accept only the current load.
* fix(mobile): prevent duplicate artifact saves before rerender
* refactor(mobile): drop the dead dirty-draft guard and share preview test setup
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* perf(wsl): fold the first libc probe into the WSL host facts exec
The WSL connect's facts exec now also runs the libc probe's first command after its own marker.
resolveOrcadRemoteContext takes it (with the login home) as a known fact, answering that one
probe; the fallbacks still run over the transport if it proves nothing. One fewer serial guest
round trip per connect; SSH passes no known facts, so its probes are unchanged.
* fix(wsl): run the libc fallbacks through the default exec
With a folded first answer, the remaining libc probes used the fenced orcad runner; they now
use the same plain execCommand the default probe does, so they match main exactly.
* fix(mobile): keep dictation recording through a reconnect
The host tied a phone's dictation to its socket, so any reconnect cancelled
it, and the phone ended the recording as soon as the connection dropped.
The host now owns the session by device and waits a grace period for the
phone to come back. The phone keeps recording through the reconnect and
keeps the text transcribed so far if the connection does not return.
Fixes#35211
* fix(mobile): bound dictation reconnect waits and release retired relay waiters
* fix(mobile): resend an unsent dictation finish after a connection move and send the text so far only when it changes
* fix(mobile): hold dictation requests through a suspended relay and bound the start wait
* fix(mobile): recognise a suspended relay client across the page bridge
* refactor(dictation): refresh one abandon timer, report text by version and share the reconnect wait
* fix(mobile): resend dictation text each second and never wait offline for a cancel
- Plugin pack i18next resource IDs become BCP-47 private-use tags on the
pack's base locale (ru-RU-x-orcapk-...), so Intl and i18next pick the
pack's plural rules natively in main and renderer. Drops the renderer
pluralResolver.getRule patch; pack registration is shared. Persisted
uiLanguage and pack ids keep the plugin: form.
- returnEmptyString: false in both instances; main no longer returns the
raw uninterpolated fallback for an empty translation.
- Main i18n initializes at module load (synchronous with in-memory
resources), removing the initialized flag and interpolateFallback.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(mobile): restore lifecycle-safe terminal accessory repeat
The selection hook replaced the established repeat controller with unbounded timers, allowing held keys to cross terminal/client changes. Reintegrate the existing pinned sender, lifecycle cancellation and backpressure.
* test(mobile): stop held terminal input after refusal or disconnect
* test(mobile): give accessory-repeat harnesses the PTY modes and focus hook main now reads
* fix(mobile): stop a held key when its strip unmounts and repeat through the current live-input mode
* fix(mobile): type the accessory repeat sender's input
* refactor(mobile): read app state live in the accessory repeat gate
* test(mobile): pin the key bar's held press to the repeat controller's first send
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* feat(wsl): run Git for Windows for a WSL project on a Windows drive
A project on C:\ whose runtime is WSL ran every Orca Git command through wsl.exe on
/mnt/c: ~300x slower than Windows Git, and blind to Windows-side edits. The project's
WSL runtime preference gains an optional `git: 'windows-host'`. With it, Orca's Git
(and where new worktrees go) is Windows; terminals, agents, accounts and hooks still
resolve to WSL, so nothing else changes. Older builds drop the field and go back to WSL
Git.
The conversion checks first, then the caller saves the returned preference through the
normal project update:
- Git for Windows must exist.
- New worktrees must not be set to go inside the distro.
- No linked worktree may have a Linux-path link. WSL Git made those; Windows Git sees
them as prunable, and Orca's prune after a worktree removal would delete their
metadata.
- Optionally writes core.autocrlf=true into the repo when Windows Git uses it, so WSL
Git in the project's terminals stops seeing CRLF changes. Never core.checkStat.
IPC is refused off Windows and with the internal WSL hosts gate closed.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(wsl): mock the drive-project IPC in the core handler registration test
Co-Authored-By: Claude <noreply@anthropic.com>
* test(wsl): pin WSL Git for a drive project that never chose Windows Git, and record which Git ran
* fix(wsl): base-ref lookups and the remote-identity sweep run Git where the project's runtime does
* fix(wsl): route base-ref and remote-identity reads by runtime only with WSL hosts on
These reads ran host Git on main. Routing them into a WSL-runtime project's
distro is part of D1c, so with the WSL hosts gate closed they run host Git
exactly as before.
(cherry picked from commit 2ed82987dc5b82c3a86ea7e03ce05e9efc3e77fb)
* refactor(ipc): register the filesystem handlers in one call, keeping core registration under its line cap
The optional commit-message resolvers pass straight through, so the call is the same with or
without them. #34059 left this file at the 300-line cap and the WSL drive-project wiring adds to it.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl): skip folder repos in the Windows Git check, and read worktree links with the prune guard's reader
Folder repos run no Git, so rev-parse on them always failed the preview. The link reader and
the WSL-authored test now come from worktree-prune-guard.ts; the copy's startsWith('/') wrongly
refused //wsl.localhost links, which Windows Git writes itself.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(wsl): the remote-identity sweep checks the distro a drive project's Git runs in, so it never boots a stopped one
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* plugins: app and terminal theme contributions
Manifests can contribute themes (public CSS tokens over a light or dark
base) and terminal themes (an ANSI palette), each at most 16 KiB. App
themes must set --background and --foreground; omitted tokens are derived
from them (surfaces copy the background, muted text, borders and accents
mix the foreground into it), and values must be colors (no keywords,
lengths, calc, var or url). Both load through the shared artifact-pack
registry with the plugin's display name and a content revision, reach the
renderer over IPC and are re-validated there. Catalog releases can declare
app-theme and terminal-theme, and marketplaces stop hiding theme listings.
docs/reference/plugin-themes.md documents the format.
* plugins: resolve a selected plugin terminal palette on every host
Selecting a plugin terminal theme saves a copy of its palette in settings
(terminalPluginPalettes). The shared lookup reads it, so main, headless
serve and relays answer terminal color queries and native chat matches the
terminal without the plugin loaded; a malformed snapshot falls back to the
default theme.
* plugins: one Theme list with plugin themes, painted safely
Appearance keeps the System/Dark/Light control until a plugin offers a
theme; then Theme becomes one list with plugin themes after a divider,
labelled with the publisher when names collide. Choosing one stores its
base in theme and remembers the earlier built-in choice, which comes back
while the plugin is off or after it is uninstalled; a plugin update that
changes the base re-syncs theme. It also offers the plugin's terminal
colors in one click.
The renderer paints only values CSS parses as colors, checks every
text/surface pair, ring and border against the effective set (plugin
values over this mode's built-ins) and repeats until nothing changes, so
dropping one pair cannot expose another. The applied theme is cached with
its id and revision, painted before first render, and dropped as soon as
settings show it no longer applies. Main bounds the new settings on every
write and load, and uninstalling a plugin clears its theme choices and
palette copies. Terminal themes appear under Plugins in the picker with the
plugin's name and are passed to lookups instead of read from a store.
* plugins: type the theme settings normalizer input; let the store own its normalization
* fix(plugins): check text contrast on the editor surface and clear terminal overrides when adopting a theme's terminal colors
* fix(plugins): reject theme colors the canvas probe silently ignores
* fix(mobile): scope workspace setup permissions to the responding host
Changing hosts can expose the previous desktop runtime settings and setup approvals. Own published data and setters by host and client.
* fix(mobile): keep an earlier selection's settings callbacks retired when the host is picked again
Ownership compared client and host id, so A -> B -> A made the first A's Save callbacks match again. Each selection now gets its own owner object.
* fix(mobile): own the workspace runtime context by host, not client object
Any server's relay change rebuilds every scoped client, which wiped the same host's settings and approvals.
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
- Editor conflicts: localize conflict-kind labels and hints, and build the
banner, rename note and snapshot time as complete messages instead of
joining English state labels with a translated fragment.
- PR file viewed checkbox: separate Mark/Unmark sentences instead of
injecting raw English verbs into '{{value0}} {{value1}} as viewed'.
- Quick Open ripgrep hint: one sentence with an embedded <code> element and
an interpolated reason instead of three fragments.
- Usage breakdown: session/turn/event counts use CLDR plural keys, so Russian
reads «1 сессия • 21 ответ» instead of «1 ответов».
Refs #34783
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Vyacheslav Scherbinin <14369958+gentslava@users.noreply.github.com>
* fix(i18n): localize provider failures and remaining shared UI copy
Translate Linear/Jira failures at the renderer boundary while preserving
English errors and adding optional stable codes for existing clients.
Localize host status, source-control metadata, discard confirmations,
native dialogs, and catalog gaps across all six shipped locales.
Inventory and reproduction paths contributed in #32207.
Co-authored-by: Seiji Kohara <9543980+seijikohara@users.noreply.github.com>
Co-authored-by: Evgenii <413011+smwbev@users.noreply.github.com>
* test(i18n): assert stable task provider failure codes
* test(i18n): exercise goal notice fallback with an older language pack
* fix(i18n): address provider error and live title review findings
Handle missing remote error text, type provider codes and keep codes intact
through connection tests and optimistic edits. Fix the remaining Linear
load fallback and redundant translation calls, apply Japanese wording
corrections, and refresh native titles on language changes.
Co-authored-by: Seiji Kohara <9543980+seijikohara@users.noreply.github.com>
* fix(i18n): finish provider and shared-copy localization for all locales
- Translate the branch's new keys into de, ru, tr and zh-Hant (added to main
after this branch was cut), with Russian few/many forms.
- Use whole sentences for file deletion failures and the mixed batch notice
instead of inserting a 'Recycle Bin'/'Trash' word, and count keys for the
batch titles.
- Translate the workspace/activity options filter count (was a shared
'N filter(s)' literal) at the renderer call sites.
- Use _one/_other families for MCP server counts, untracked-file discard copy,
resume-chat counts, discard file counts and visible host counts.
- Localize PR/MR card status labels, the unsupported-agent hint, Linear title/
description update failures, the Linear 'showing first N' notice and the
Linear Connect button.
Co-authored-by: Evgenii <413011+smwbev@users.noreply.github.com>
---------
Co-authored-by: Seiji Kohara <9543980+seijikohara@users.noreply.github.com>
Co-authored-by: Evgenii <413011+smwbev@users.noreply.github.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(i18n): re-render memoized translated UI on language and catalog changes
Memoized components and useMemo/module caches that call translate() kept the
previous language after a switch or a plugin language-pack reload, because
react-i18next only re-renders components that subscribe themselves.
Add useUiLanguageRevision() (language + catalog revision via
useSyncExternalStore), call it in the 34 memoized translating components, key
translated useMemo results and module caches on it, and add a ratchet so new
memoized translating components must subscribe.
Fixes the composer pickers reported in #34783.
Co-authored-by: Vyacheslav Scherbinin <14369958+gentslava@users.noreply.github.com>
* fix(i18n): drive the language revision from the resource version
Tests that mock @/i18n/i18n have no i18n instance, so subscribe through
translation-resource-version (now also bumped on languageChanged) and give
useSyncExternalStore a server snapshot for renderToString tests.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Vyacheslav Scherbinin <14369958+gentslava@users.noreply.github.com>
* test(e2e): keys typed into a new remote terminal before its first prompt reach the shell
Add a headless paired-host spec that types into each new remote terminal after
it binds while the host shell's rc file is still running, then requires the
whole command to run. Drop the managed-SSH helper's wait for a prompt before
typing, so those specs assert the same guarantee.
* test(e2e): prove the rc gate held, and that the early-typed command ran exactly once
The tty echo of the typed command must precede the rc file's own prompt, the
command's output must appear exactly once, and the rc wait outlasts the bind
wait. Throw if the host has no isolated HOME.
* test(e2e): drop the early-typed echo-position check; keep the gate and exactly-once proofs
The check that the typed line's tty echo precedes the rc file's prompt failed on
Linux CI 2 of 2 (run 40763469331): the pane began with the prompt and the typed
line followed it, while the command still ran exactly once. macOS echoes it during
the rc. Where the echo lands depends on when the host's line discipline sees the
bytes, which this test cannot pin; it is not evidence of loss. What the test keeps
proving: the keys were typed while the rc file still waited (release file absent),
the rc ran (its own PROMPT appears), and the command ran whole, exactly once.
* test(e2e): name the early-typing test for what it proves
Rename it to say every key typed before the first prompt is delivered exactly
once, and drop the header's claim about how the tty keeps type-ahead.
* Add repeatable Google cookie clearing for browser profiles
* Bind Google cookie clearing to its successful import target
* Repair cookie-clear contract test boundaries and positive flows
* Correct cookie-clear test fixture typing
* test: use checked runtime bindings in browser profile RPC fixture
* test: provide page registry to sidecar forwarding fixture
* fix: use selected browser profile owner for cookie clear menus
* test: provide confirmation context to link materialization fixture
* test: classify cookie clear session channels
* refactor: consolidate browser command property guards
* test: update cookie clear fixture for merged registry API
* feat: move Google sign-in recovery to browser settings
* Freeze Settings Google cookie clearing intent and refresh availability
* Restore baseline browser import disclosure coverage
* Pass the selected browser profile into cookie settings
* Localize Google cookie settings search keywords
* Update cookie restart fixture policy exports
* Keep localized browser search results visible
* fix: make Google cookie clear a red right-aligned setting that explains when to use it
* Explain why Google cookies are handled separately in the clear row caption
* fix(wsl-mover): add-host copy says bridge projects move automatically, in every locale
* fix(wsl-mover): add-host copy scopes the move to this distro and keeps Windows-drive projects here
* test(wsl-mover): sweep every locale's old 'until you move them' wording