StopCancelled carries no backgroundTasks, so a cancelled Grok turn settled
done while the shells and subagents it left running kept going. Track
Grok's own task ids per pane and settle the cancelled row on each task's
own end.
* fix(wsl): session history reads never boot a stopped WSL distro (E9)
Transcript and session-history reads touched \\wsl.localhost shares, and ran
`wsl.exe --exec` probes, for any distro the root list named. Those touches are
the OpenCode reader, its setup, HOME and realpath probes, and the session
scanner and search index. Nothing re-checked that the distro still ran. A
`wsl --terminate` mid-scan, or a list taken while the distro was still
stopping, let background work boot it again.
Every UNC transcript touch (stat, readdir, open, access, readFile) and every
scanner `--exec` now asks a settled running read first. That read is no older
than 5 s, because the scanner process hears of no stop by itself. A user act on
the distro still passes.
* fix(wsl): opening a chat starts its stopped distro; scanner hears main's stop notes
A user-opened chat, a history page and an AI Vault resume start the
transcript's stopped WSL distro once, as a user act, so its history
shows; watch and scan reads stay background. Main forwards WSL stop and
wake notes to the session-scanner process so a terminate main saw is
honoured there at once.
* fix(wsl): type the scanner note channel structurally; test the resume wake
Keeps node:child_process out of the new scanner-notes module (the
process-host import check) and adds a test that fails if an AI Vault
resume stops starting the session's WSL distro.
L1: the orcad liveness supervisor's heartbeat/verify reads, its recovery gate, the no-boot
reconnect check and the connector's passive probe now take a settled read of their own distro, so
a list probed while the distro is still stopping cannot admit a holder spawn, identity read or
reconnect that would boot it again.
L2: a Git cancel/timeout's guest kill, the WSL move's process table and each step of the Codex
session bridge chain check the distro first. A confirmed stop counts as the group gone / an empty
table / the end of the chain; an unanswered read proves nothing and spawns nothing.
A slot whose startup qualification marker matches (same artifacts, runtime, libc, kernel and
launch env, last launch reached readiness) now also skips the synchronous node-pty load probe
child (~0.1 s on the WSL lane). The marker is now recorded only when that launch's probe
passed cleanly; schema 2 so earlier markers, which did not promise that, re-qualify once.
Key MermaidViewer's scroll cache with the pane-scoped editorViewStateKey,
matching IpynbViewer, so split panes showing the same .mmd file no longer
overwrite each other's saved diagram position.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Zoom and resize re-renders reuse the same key string instead of rebuilding
and comparing it, which matters for base64-backed images (diff, conflict
review, fallbacks) where the key embeds the whole payload.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Key the colorize effect on joined content and line count instead of the
lines array identity, so re-renders with an equal fresh array (PR comment
code context) no longer re-run monaco.editor.colorize. Check cancellation
before colorizing so excerpts replaced or unmounted while a lazy tokenizer
loads skip the work.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Monaco's CopyPasteController claims paste on the editor container in the
capture phase before Orca's listener, so Ctrl/Cmd+V bypassed the 16 MiB
cap. Listen on the editor shell instead, scoped to this editor's subtree
and text focus, and only reject over-cap pastes; smaller pastes keep
Monaco's atomic native paste. Remove the DOM chunked-paste plumbing
(save deferral, isApplyingLargePasteRef) that could never run.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Prune file/diff read generations only when a tab goes from open to closed,
replacing the append-only known-open-ID set, and fence the restored-owner
migration so a late result cannot discard a newer read's generation.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Upload up to four files concurrently within the existing 2 MiB in-flight budget, sizing each file's window from its size. A per-file failure aborts siblings and surfaces the first error after the SFTP channel closes.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Two clients hold separate grants for one file, and the desktop editor saves through its own guarded path, so concurrent writers could all pass their content check before any of them replaced the file, and the last one silently won. Run the artifact check-to-rename under the same canonical-path/inode lock that guarded editor saves already use; the later writer now sees the earlier one's bytes and is refused.
* feat(mobile): add UI localization infrastructure
Mobile follows the phone's language (or an in-app choice under Settings > Appearance)
across the same 11 languages as desktop, switching live like the theme does.
- i18next with desktop's translate(key, fallback, options) call shape; catalogs in
mobile/src/i18n/locales load lazily, English fallbacks stay inline
- Hermes lacks Intl.PluralRules: load the FormatJS polyfill with the 11 languages' rules
- OTA pages get the shell's language through an optional bridge init field
- iOS permission prompts localized; iOS and Android 13+ per-app language pickers
- verify:mobile-localization and a coverage ratchet run in lint, check:changed and CI
* fix(mobile): mount pages after the newest language request; tighten localization gates
- Wait for the latest page language activation, not a superseded one
- Keep literal fallbacks when a key also has a dynamic one
- Count headerTitle as visible copy; select the mobile check when the runtime catalog generator changes
* feat(mobile): repaint themed components on language changes
Components that read the theme already subscribe for repaint; a language switch
repaints them too, so labels built by helpers during render follow it. RPC
recordings load the real i18next and JSON catalogs.
* fix(mobile): scope the translate() gate to render bodies and memos; prune stale keys on sync
Handlers and effects may call translate() when they run. sync:mobile-localization now
removes keys no code references from every catalog.
* fix(mobile): use node: imports in the locale config plugin; pass uiLocale in bridge hook test
* chore(mobile): refresh the localization coverage baseline
* fix(mobile): follow render callbacks in the translate() gate; keep used plural siblings on prune
Also let anonymous default-exported screens count as components, flag any plural
family a locale started, and mark JSON modules as ES modules in the recording loader.
* fix(mobile): treat async and named local callbacks as handlers in the translate() gate
* chore(mobile): refresh the localization coverage baseline
* chore(mobile): refresh the localization coverage baseline
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(i18n): localize plural counts, sentence fragments and hardcoded copy in editor, chat, skills, stats, onboarding and tips
Refs #32207
* chore(i18n): resync runtime English catalog after merging main
* test(i18n): let mocks interpolate counts and keep real i18n exports
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Handle 'error' on accepted CDP proxy sockets so an invalid frame (bad
UTF-8, bad opcode) closes that client instead of crashing the main
process. Validate parsed command envelopes: null params/sessionId stay
treated as absent, malformed bodies with a numeric id get an
'Invalid CDP request' error reply, and id-less garbage is dropped.
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(i18n): localize sidebar, status bar, tab bar and workspace cleanup copy
Replace fragment-concatenated sentences, separate singular/plural keys and
raw English in the S2 renderer areas (sidebar, status-bar, tab-bar,
tab-group, workspace-cleanup, new-workspace, activity, dashboard,
terminal-pane, cmd-j) with whole-sentence keys and CLDR plural families,
and add translations for all built-in locales.
Refs #32207
* fix(i18n): subscribe DashboardAgentRow to UI language changes
* test: keep real i18n exports in SmartWorkspaceNameField mocks for Trans rows
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
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>
* 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>
* 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
* 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>
* 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
- 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>