Commit Graph
17702 Commits
Author SHA1 Message Date
Neil 3d812eb9bf fix(keybindings): show ⇧ for Shift in Windows/Linux shortcut labels (#35754)
* fix(keybindings): show the ⇧ glyph for Shift on Windows and Linux

Spelled-out Shift made non-mac shortcut labels long (Ctrl+Shift+Backspace).

* test(menu): expect the ⇧ glyph in non-mac app menu hints; align double-tap hint
2026-10-29 00:08:48 -07:00
Neil b6ffc2079b fix(windows): keep Orca's std handles out of every spawned child (#35678)
* fix(windows): keep Orca's std handles out of every spawned child

libuv spawns with bInheritHandles=TRUE, so an inheritable std handle reached
long-lived children such as the WSL holder and the daemon and held a
launcher's pipe open after Orca exited. Clear HANDLE_FLAG_INHERIT on the std
handles at the top of main startup; stdio 'inherit' still works because
libuv hands the child its own inheritable duplicate.

* test(windows): model Orca's Electron main, which keeps std handles inheritable

Plain Node clears its std handles' inherit flag at startup, so a Node child
never reproduced the leak. Electron main opts out of that, which is why Orca
leaks and why the control arm now runs Electron; a plain-Node arm pins the
difference.

* test(windows): say why the std-handle tests need longer timeouts
2026-10-29 00:08:27 -07:00
OrcaWinandOrcaWin 1bfba51e36 fix(i18n): code ephemeral VM provisioning and cleanup failures (#35747)
* fix(i18n): code ephemeral VM provisioning and cleanup failures

* fix(i18n): relocalize recipe-list errors on language change

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-29 07:06:15 +00:00
NeilandClaude 478a008299 feat(wsl): a WSL hosts block can ask for already-moved projects back (#35762)
The cached policy gains an optional movedProjects: keep | bring-back, read
only with a block. An unknown value reads as keep, so it never drops the
block, and older builds ignore the field. The gate records the choice for
the run (wslHostsBringBackMovedProjects) and telemetry reports it as
wsl_hosts_moved_projects. Nothing acts on it yet; bringing projects back
follows.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-29 00:04:54 -07:00
OrcaWinandOrcaWin 4c963268d2 fix(i18n): code Mobile Emulator, PDF export, and diagnostics failures (#35742)
* fix(i18n): code Mobile Emulator, PDF export, and diagnostics failures

* fix(i18n): read emulator error params without Reflect.get

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-29 00:03:35 -07:00
a7f9888d01 feat(wsl): offer the drive-path conversion in project settings, and convert the window's stores (D1c-4d) (#33143)
* feat(wsl): offer the drive-path conversion in project settings, and convert the window's stores (D1c-4d)

* fix(wsl): tell the user why saved state blocked the drive-path conversion

A refusal over saved state now names the cause: a folder with different details under both its WSL
path and its drive path, or a project that also holds a repository that isn't converting. An older
host's refusal without a cause keeps the general message.

* fix(wsl): explain a conversion refused because the distro's /mnt/<letter> is not that Windows drive

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

* test(wsl): give the renderer-rewrite journal fixture the profile the journal now records

* test(wsl): give the e2e conversion journal the profile field the journal now requires

* test(wsl): carry the #33135 startup tests onto the window-store journal: it waits for the window, and finishes on any platform once the gate is open

* test(wsl): off Windows the conversion IPC still reports startup's outcomes, as the journal can finish on any platform

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 23:58:39 -07:00
Hotragn PettuganiandNeil 1279d5f588 fix(cli): never register the bundled launcher as a link to itself (#35371)
* fix(cli): never register the bundled launcher as a link to itself

orca serve registers the CLI at startup. On macOS the installer walks
PATH for an existing orca command, and Orca's own terminals put the
bundled Resources/bin first on PATH. The bundled launcher reads as a
managed Orca launcher, so it was picked as the command slot, moved
aside, and replaced with a symlink to itself. The app's CLI then broke
until reinstalled.

Skip the launcher when scanning PATH, and refuse any symlink install
whose command path is the launcher.

Fixes #33872

* fix(cli): compare the launcher by directory entry, not path spelling

The self-link guards compared path strings, so a PATH entry or
ORCA_CLI_INSTALL_PATH that reached Resources/bin through a symlinked or
differently cased directory still replaced the bundled launcher with a
link to itself. Compare entry and parent-directory identity instead.

Co-authored-by: Hotragn <103170876+Hotragn@users.noreply.github.com>

* fix(cli): keep differently named hard links to the launcher replaceable

* fix(cli): treat a case-variant name as the launcher only when it is an alias, not a second hard link

* refactor(cli): compare the launcher by inode via existing entry snapshots

Replace the speculative isSameDirectoryEntry helper with isSameEntry beside
readEntrySnapshot/hasSameIdentity, and drop the hard-link test that only
pinned the removed behavior.

Co-authored-by: Hotragn <103170876+Hotragn@users.noreply.github.com>

* Revert "refactor(cli): compare the launcher by inode via existing entry snapshots"

This reverts commit f02f8c2a2ba0263fd611170af7c3f76d22e48ea7.

* test(cli): fold the bundled-launcher cases into the macOS command-path suite

* refactor(cli): move the launcher entry check beside the entry snapshots

Reuse readEntrySnapshot/hasSameIdentity for the entries, keep the parent
identity check (a hard link in another dir such as /usr/local/bin must stay
replaceable, which is why f02f8c2a2ba was reverted), and drop the readdir
case-folding branch. Pin the cross-directory hard link instead of the
speculative sibling one.

Co-authored-by: Hotragn <103170876+Hotragn@users.noreply.github.com>

* fix(cli): keep a case-variant hard link beside the launcher replaceable

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-28 23:55:31 -07:00
OrcaWinandOrcaWin 5450afd4db fix(i18n): code AI Vault scan issues and chat auto-name failures for the renderer (#35750)
* fix(i18n): code AI Vault scan issues and chat auto-name failures for the renderer

* fix(i18n): read scan issue params through the wire type

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 23:51:46 -07:00
OrcaWinandOrcaWin 924d757ad6 fix(i18n): code shared validation errors; translate speech model, setup source and project group copy (#35737)
* fix(i18n): code shared validation errors and translate speech model, setup source and project group copy

* fix(i18n): keep detected label for unknown setup import providers

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 23:49:27 -07:00
OrcaWinandOrcaWin f245dc7521 fix(editor): copy local Windows paths with native separators (#35647)
* fix: copy local Windows paths with native separators

Copy Path for a proven-local absolute Windows path now writes backslash
separators, the form native Open/Attach dialogs accept (#26204). Applied at
the clipboard boundary of the editor tab menu and header, File Explorer Copy
Path(s) and its shortcut, source control Copy Path, Monaco Copy Path to Line,
HTML document preview Copy file path, workspace sidebar and activity Copy
Path, skills Copy path, and AI Vault Copy Log Path, each with the owner its
Reveal/Open in action already resolves. Remote, unresolved, web-client, URL,
relative and non-Windows output is unchanged.

* fix: sync Copy Path owner refs after commit

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 23:44:11 -07:00
Neil 0e9ba2167b test(ssh): let Linux finish releasing upload fds before asserting they closed (#35612)
* test(ssh): let Linux finish releasing upload fds before asserting they closed

* test(ssh): pin abort teardown confirmation and reword the fd-release note
2026-10-28 23:37:42 -07:00
OrcaWinandOrcaWin 2fd71477c7 fix(i18n): translate feature wall tour and feature tip copy by id (#35736)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 23:36:33 -07:00
Neil d841f68785 fix(wsl): a holder exiting with code 1 notes its distro may have stopped (E9) (#35738)
The holder's code 1 means its distro stopped. Noting it, independent of bridge activity, closes
the window the bridge exit's burst cooldown or a still-open socket can leave: later background
reads must re-read the running list instead of trusting a cached "running". The holder's process
classification moves to its own module to keep the file under max-lines.
2026-10-28 23:35:26 -07:00
Kelvin Amoaba 5f60520d74 fix(dictation): cancelling dictation frees the speech engine at once (#35730)
* fix(dictation): drop audio on cancel so a re-tap starts at once

A cancelled or abandoned dictation was transcribed anyway, holding the
speech session until the upload or local decode finished.

Fixes #35681

* test(dictation): fake worker answers discard like the real one

* refactor(dictation): one session reset for worker stop and discard

* fix(dictation): exit a worker that cannot open a fresh stream
2026-10-29 06:34:22 +00:00
Neil 277c4a9bf8 perf(orcad): keep the Codex PATH scan off WSL readiness (#35718)
* perf(orcad): keep the Codex PATH scan off WSL readiness

orcad's startup Codex hook lookup and reconcile now wait for readiness to
publish (a launch still releases them at once), and the WSL Windows-copy
check reuses a recent verdict for the same PATH instead of re-statting
every Windows-drive PATH entry on each call.

* refactor(orcad): hand readiness to managed agent hooks through a returned handle
2026-10-28 23:32:17 -07:00
Neil 76f5568d30 fix(editor): highlight CMakeLists.txt with the CMake TextMate grammar (#35735)
Register a cmake Monaco language for CMakeLists.txt backed by Shiki's
CMake grammar, mapping its storage/entity/argument scopes to token
types the stock Monaco themes style. .cmake files stay with plugin
syntax packs.

Fixes #35452
2026-10-28 23:29:21 -07:00
Neil 4049bc77d6 feat(orcad): gate agent launches on readiness and refuse callers that leave while waiting (#35710)
* feat(orcad): gate agent launches on accounts and cancel feature waits for departed callers

requires: may read the parsed params, so terminal.create and
session.tabs.createTerminal wait on accounts only when they start an
agent; plain shells stay ungated. createAgentSession, ensureAgentSession,
agentSession.create, agent.launch (unless it reuses a terminal),
git.generateCommitMessage and the worker-launching orchestration methods
also wait on accounts. The feature wait races the request signal and the
connection close and refuses without running the handler.

* feat(orcad): gate terminal.ensureAgentSession only for explicit launches

An attach to an existing session is restore latency and needs no fresh
account state; only kind 'explicit' may start an agent.

* test: reset orcad feature readiness after every unit test

A startOrcad test that fails partway left latches closed, so later tests
in the same worker could wait 10 s on a gated method.
2026-10-28 23:25:44 -07:00
Neil 682ef86dfe fix(skills): start only the unread distros when Share retries discovery (#35725)
The share RPC's fallback now reads stopped roots only on the distros its first pass left unread, the directory matcher refuses an empty root, and the lowercasing is documented as widen-only.
2026-10-28 23:14:52 -07:00
Neil 571eb885ac fix(menus): let shortcut context menus grow to fit Windows shortcut labels (#35726)
Fixed menu widths clipped long labels like Ctrl+Shift+Backspace on Windows/Linux.
2026-10-28 23:13:36 -07:00
YiGeon(박이건)andNeil ae811d016d fix(github): keep same-repo PRs when origin uses a renamed repository's old URL (#35688)
GitHub serves a renamed repository under its old name, so branch discovery
finds the PR through a stale origin URL, then rejects it because the PR
reports the new head repository name. Accept the PR when the queried
repository is origin itself and GitHub reports it as a same-repository PR,
and share that same-repository check with the cached-number lookup path.

Fixes #35687

Co-authored-by: Neil <neil@stably.ai>
2026-10-28 23:08:42 -07:00
Neil c56d97bf05 perf(wsl): take one activation fence on a same-version connect (#35708)
* perf(wsl): take one activation fence on a same-version connect

A same-version WSL connect fenced twice: the deploy took the activation
fence only to read that the version was already active, released it, and
the wake took it again to start the stopped slot. Now the deploy skips its
fence for that case (WSL-only, when the connect snapshot saw the fence
open), and one wake fence re-reads the record, checks liveness and starts
the slot under its owner guard. Another version active by then, or a busy
fence, runs the full deploy and wake as before. SSH is unchanged.

* fix(wsl): leave an interrupted wake's fence to the wake that can reclaim it

The one-fence wake recorded its own token over an earlier interrupted
wake's, so the full wake could no longer prove that fence its own and the
host stayed fenced until stale recovery. With an interrupted wake on
record, the one-fence path now answers 'fenced' and the full wake runs.
2026-10-28 23:04:20 -07:00
Neil 4296f77d76 fix(wsl): WSL host bridge dials, ready clients and holder re-ensures never boot a stopped distro (E9) (#35707)
* fix(wsl): WSL host bridge dials, ready clients and holder re-ensures never boot a stopped distro (E9)

With a chat open on a WSL host, terminating the distro let the control connection's quick
redial reach the still-open loopback listener, whose per-socket `orca_node_bridge --exec`
spawned wsl.exe and booted it again.
1. The WSL bridge listener admits a dial only on a settled running read (the cached one, under
   the usual lease), refusing a stopped or unverifiable distro. Every wsl.exe launch from the
   WSL host transport notes the distro as launched, so a user act's connect is never refused its
   own bridges by a "stopped" read taken before it started the distro.
2. A bridge the guest ends first notes the distro may have stopped, so the next dial re-reads.
3. A ready runtime client skips the cold reachability check only while the last settled running
   list names the distro.
4. Background holder re-ensures (liveness) re-read the running list right before spawning; a user
   ensure never inherits a background one's refusal. The holder's types move to their own module.
The shared SSH bridge listener and the shared-control reconnect are unchanged.

* fix(wsl): note a possible stop only on the filtered bridge exit, not every guest-first close

A guest-first end is also how one-shot RPC sockets and WebSockets close normally; noting a stop
on each made the next dial wait out the settle and probe, flapped reachability, and fed false
stop notes to the session scanner. The note now rides WslOrcadBridgeActivity's exit signal: the
guest ending the last open bridge, outside the burst cooldown.
2026-10-28 23:02:44 -07:00
Neil b398b91857 fix(worktree): explain how to raise the limit when git worktree add times out (#35720)
A timed-out worktree add surfaced only "git timed out.". Rewrite the
GitCommandTimeoutError message on the add path to state the limit used and
point to ORCA_WORKTREE_ADD_TIMEOUT_MS, set where Git runs (the remote host
for SSH or orca serve workspaces). The advice is dropped once the limit is
already at its maximum. The error's class and timeout field are preserved.

Fixes #35174
2026-10-28 22:57:59 -07:00
OrcaWinandm4air fe1e179cde perf(rpc): reuse required primitive schemas by message (#35072)
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-28 22:52:51 -07:00
Neil 1c33f515de perf(test): reuse Vitest transforms across local runs (#35715)
Enable Vitest's persistent module cache outside CI under node_modules/.cache/orca-vitest-modules,
keyed by the existing CI module-cache policy hash (lockfile, tsconfig/package manifests, runner
scripts) plus the Node version. reclaim-space removes caches unused for 7 days.
2026-10-28 22:47:07 -07:00
Neil de3a87f0cc test(dev): stop run-electron-vite-dev web tests from orphaning fixture processes (#35713)
Track the fake electron-vite CLI pids before the fallible web-build wait, give that wait 20s
on loaded hosts, and have the fake CLI exit once its parent wrapper is gone.
2026-10-28 22:46:43 -07:00
Neil 1529bb49ad refactor(plugins): remove dead plugin code (#35676)
- the command extension registry: workers registered every command into it,
  but nothing ever looked one up (commands run through the service directly)
- PluginListRow, a list row no screen renders anymore
- hasPluginSyntaxFailure, called only by tests
- six exported types nothing imports, and a re-export nothing uses
2026-10-28 22:40:47 -07:00
Neil d91c1aaeea Plugins: binary editors keep unsaved edits across renames, reloads and twin tabs (#35698)
* fix(plugins): keep binary editor edits across renames, reloads and twin tabs

- Renaming a binary file with unsaved plugin-editor edits gave its tab a new
  id, and the bytes store took the old id's disappearance as a close and
  dropped the edit. The bytes now move with the tab as the rename lands.
- Tabs of one file share one dirty flag, so they now share one bytes draft;
  before, saving one tab marked both clean and dropped the other's edit.
- A reload that reads the same bytes keeps the document, so the editor is not
  sent a new version that turns an edit in flight into a conflict.

* fix(plugins): finish a twin-tab bytes save through any tab still open

A tab that closed mid-save left its twin with the old hash, and a twin
adopting a shared draft no longer lets its own older read replace it.

* fix(plugins): hand a closed tab's bytes draft to its open twin; drop stale reads

A twin that had not shown the file yet lost the draft when the tab holding it
closed, and a read that started before an edit and save could replace the
newer saved bytes. Boxes now count edits and saves; a read only lands if none
happened since it started.
2026-10-28 22:40:41 -07:00
Neil b9996755b3 fix(wsl): a missing host gh or glab never boots a stopped default distro (#35709)
Cwd-less gh/glab calls whose host CLI is missing fell back to the default
WSL distro with no running check, so background work (rate limits, auth
status, Settings, task-source probes) booted a stopped distro. The
fallback now asks the background exec gate first: stopped or
unverifiable refuses with the distro-stopped error, running and user
acts fall back as before.
2026-10-28 22:39:49 -07:00
OrcaWinandOrcaWin 300b241fc6 fix(i18n): format renderer dates and numbers in the UI language (#35671)
* fix(i18n): format renderer dates and numbers in the UI language

About 80 renderer sites formatted with the OS locale: Intl constructors
given undefined (or nothing) and argless toLocale*String calls, while
nearby text followed the UI language.

- toLocale*String calls pass getIntlLocale().
- Module-level Intl formatters and collators use new ui-intl-formatters
  helpers, cached per options object and rebuilt on language switches;
  per-call constructors pass getIntlLocale().
- ReleaseBuildPicker's memoized labels re-run on language changes.
- An ast-grep rule (renderer-intl-ui-locale) rejects undefined/omitted
  locales in src/renderer; machine-facing code passes a fixed locale.

* fix(i18n): collators check their own locale support; re-sort memoized lists on language changes; widen the Intl guard

* fix(i18n): name the formatter options type

* fix(i18n): reuse the CSV viewer's existing language subscription

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 22:38:33 -07:00
Neil 0e24367c55 fix(codex): owe a full history scan after launches while the source is in WSL (#35703)
A launch made while the Codex history source is a \\wsl path records no
rollout dates, so after switching back to a plain path the next migration
pass only scanned the old pending dates and missed those sessions. Such a
launch now sets the local backfill marker's full-scan flag (Orca user data,
no 9P access), and the next eligible pass walks all history.

Also checks the resolved source home (setting, else the system home) when
deciding whether the history source is in WSL.
2026-10-28 22:36:13 -07:00
OrcaWinandOrcaWin aa1de424e9 fix(i18n): code PR/MR start-point and stacked PR creation failures (#35705)
* fix(i18n): code GitHub/GitLab operation failures for the renderer

* fix(i18n): show coded failures in comment composers; exhaustive classification switches

* fix(i18n): code PR/MR start-point and stacked PR creation failures

* test: expect coded MR start-point fetch failure

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 22:31:08 -07:00
Neil bb20dafbff Plugins: share the liveness check and list hook across contribution kinds (#35686)
* refactor(plugins): one liveness check and list hook for plugin contributions

Panels, commands, custom editors and new-file types each repeated the same
running/restarting/idle filter, the same plugin tagging, and the same
load-then-memo hook. They now share isLivePlugin, fromPlugin,
keyedPluginContribution and usePluginListDerived.

* perf(plugins): keep the panel-error map's identity when health is unchanged

Every panel load reported its health, and each report rebuilt the map the
sidebar badges subscribe to. Also drops an unchecked cast.
2026-10-28 22:27:33 -07:00
OrcaWinandOrcaWin dbdf3c3708 fix(i18n): native plurals for exact-1 families; drop desktop suffix stripping (#35702)
* perf(i18n): load built-in catalogs once, without copies or a runtime plural fill

Each catalog load parsed the JSON, rebuilt it to fill CLDR plural forms
from _other, then deep-copied and deep-merged it into the store (about
26 ms after parsing per 1.3 MB catalog), and concurrent loads of one
locale fetched it twice.

- sync:localization-catalog now commits the plural forms a locale's
  CLDR rules need beside each _other (fr/es/pt many, tr one, a few
  de/ru), and verify:localization-catalog fails when one is missing.
  Plural forms may include {{count}} where English omits it.
- Renderer and main load built-in catalogs as-is; plugin packs keep the
  runtime fill.
- Renderer catalog requests are shared per locale; a timed-out request
  is dropped so a retry fetches again. Bundles are added with skipCopy.

* fix(i18n): require {{count}} in plural forms when English shows it

* refactor(i18n): share one i18next setup between main and renderer

- createOrcaI18n (src/shared/orca-i18n-instance.ts) owns the backend,
  init options, plugin-pack registration, resource-language resolution
  and the translate wrapper; main and renderer inject only how a
  built-in catalog loads (dynamic import vs. fetch) and extra modules.
- Remove setRendererUiLanguage (tests only); tests load then switch.
- resolveUiLocale maps built-ins by lookup instead of an if-chain, and
  catalog URLs/loaders are direct map lookups.
- Drop <I18nextProvider>: initReactI18next already binds the instance.

* refactor(i18n): native i18next plurals at call sites, plural-aware extraction

Call sites branched on count === 1 and passed literal _one/_other keys,
which resolvePluralFamilyKey stripped again so CLDR could pick the form.
They now call translate('x', otherDefault, { count, defaultValue_one })
and i18next selects the form natively; catalog keys stay x_one/x_other.

- Converted 284 families with an AST codemod plus a few by hand; added
  defaultValue_one to 21 base-key families that relied on en.json.
- Extraction runs with plurals enabled, so both English defaults are
  checked for drift. A key that only fills {{count}} (no defaultValue_one)
  stays singular in en.json.
- verify/sync:localization-catalog expand a native plural call into its
  x_one/x_other English entries.
- Removed 22 base keys that a sibling plural family shadowed in every
  catalog.

* fix(i18n): pick English plural defaults by English rules; require count for plural refs

* refactor(i18n): convert plural sites merged from main; treat key-as-default plural extraction as dynamic

* fix(i18n): keep an undefined pass-through count absent; only dotted key-as-default is dynamic

* refactor(i18n): native plurals for exact-1 families; drop desktop suffix stripping

Twelve families kept special exact-1 copy ("Used the browser") beside an
_other-only family, so CLDR one (ru 21, 31) reused _other wording. They
now call the base key with an English _one translator source, the
exact-1 branch stays a separate key, and ru one forms get the right
noun ("21 раз"). With no suffixed call sites left, desktop translate
no longer strips plural suffixes; mobile keeps resolvePluralFamilyKey.

* test(i18n): update plural call expectations in two renderer tests

* refactor(i18n): convert a plural site merged from main

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 22:26:40 -07:00
OrcaWinandOrcaWin 3b9c1d356d fix(i18n): code GitHub/GitLab operation failures for the renderer (#35689)
* fix(i18n): code GitHub/GitLab operation failures for the renderer

* fix(i18n): show coded failures in comment composers; exhaustive classification switches

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 22:18:41 -07:00
OrcaWinandOrcaWin 3b30779a1f fix(i18n): code Source Control AI settings and generation plan failures (#35697)
* fix(i18n): code AI text-generation failures for commit, PR, and Jira title generation

* fix(i18n): merge duplicate type import

* fix(i18n): code Source Control AI settings and generation plan failures

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 22:15:37 -07:00
Neil 2f3d96f9d2 test(plugins): run the binary custom-editor e2e on Linux (#35638)
In a hidden Linux test window Playwright's mouse click on a button inside the
sandboxed editor frame never reaches it: the frame takes focus, but no
pointerdown or click fires, so the edit was never sent and the tab never went
dirty. Activate the button from the keyboard instead, which still counts as a
real user gesture.
2026-10-28 22:15:17 -07:00
Neil e22e9769b9 fix(orcad): keep terminal models at the daemon's live depth and bound provider snapshot waits (#35679)
* fix(orcad): keep orcad's terminal models at the daemon's live depth

orcad's per-PTY headless terminal model kept 5,000 rows of scrollback, but
orcad has no renderer to reveal from: requested snapshots read the daemon
first and subscribe snapshots read at most 1,000 rows. The model now
mirrors the daemon's live window, from the same resolver the daemon uses.

The execution-context reseed also awaited its provider snapshot with no
bound, so a provider that never answered held every live chunk queued
behind it. It now uses the authoritative snapshot timeout.

Refs #34410
Refs #33870

* fix(orcad): keep provider preference when the reseed snapshot times out

Refs #34410
Refs #33870

* fix(runtime): bound every provider snapshot wait in one place

serializeProviderTerminalBuffer now applies the authoritative snapshot
timeout when a caller passes none, so no reader can wait on a provider that
never answers. The execution-context reseed relies on that bound; when its
capture times out it keeps the PTY provider-preferred and reseeds again once
the capture lands, instead of staying on the provider path.

Refs #34410
Refs #33870

* fix(runtime): reseed once after a late provider snapshot, trust the model on none

awaitProviderTerminalBuffer reports the capture still running when the
bounded wait gives up, instead of callers inferring it from the acquisition
map. The execution-context reseed retries once when that capture lands with
a snapshot, and stops preferring the provider when it lands with none, so a
slow provider can neither loop reseeds nor pin the PTY to the provider path.

Refs #34410
Refs #33870

* fix(runtime): never trust a late-reseed model that found no snapshot

The once-only retry disposes a model that had parsed the live bytes since
the timeout. If its own capture then fails, the replacement holds less than
its predecessor, so the PTY keeps preferring the provider instead of
treating that model as complete.

Refs #34410
Refs #33870
2026-10-28 22:14:33 -07:00
OrcaWinandOrcaWin 938f9ff80f refactor(i18n): native i18next plurals at call sites, plural-aware extraction (#35637)
* perf(i18n): load built-in catalogs once, without copies or a runtime plural fill

Each catalog load parsed the JSON, rebuilt it to fill CLDR plural forms
from _other, then deep-copied and deep-merged it into the store (about
26 ms after parsing per 1.3 MB catalog), and concurrent loads of one
locale fetched it twice.

- sync:localization-catalog now commits the plural forms a locale's
  CLDR rules need beside each _other (fr/es/pt many, tr one, a few
  de/ru), and verify:localization-catalog fails when one is missing.
  Plural forms may include {{count}} where English omits it.
- Renderer and main load built-in catalogs as-is; plugin packs keep the
  runtime fill.
- Renderer catalog requests are shared per locale; a timed-out request
  is dropped so a retry fetches again. Bundles are added with skipCopy.

* fix(i18n): require {{count}} in plural forms when English shows it

* refactor(i18n): share one i18next setup between main and renderer

- createOrcaI18n (src/shared/orca-i18n-instance.ts) owns the backend,
  init options, plugin-pack registration, resource-language resolution
  and the translate wrapper; main and renderer inject only how a
  built-in catalog loads (dynamic import vs. fetch) and extra modules.
- Remove setRendererUiLanguage (tests only); tests load then switch.
- resolveUiLocale maps built-ins by lookup instead of an if-chain, and
  catalog URLs/loaders are direct map lookups.
- Drop <I18nextProvider>: initReactI18next already binds the instance.

* refactor(i18n): native i18next plurals at call sites, plural-aware extraction

Call sites branched on count === 1 and passed literal _one/_other keys,
which resolvePluralFamilyKey stripped again so CLDR could pick the form.
They now call translate('x', otherDefault, { count, defaultValue_one })
and i18next selects the form natively; catalog keys stay x_one/x_other.

- Converted 284 families with an AST codemod plus a few by hand; added
  defaultValue_one to 21 base-key families that relied on en.json.
- Extraction runs with plurals enabled, so both English defaults are
  checked for drift. A key that only fills {{count}} (no defaultValue_one)
  stays singular in en.json.
- verify/sync:localization-catalog expand a native plural call into its
  x_one/x_other English entries.
- Removed 22 base keys that a sibling plural family shadowed in every
  catalog.

* fix(i18n): pick English plural defaults by English rules; require count for plural refs

* refactor(i18n): convert plural sites merged from main; treat key-as-default plural extraction as dynamic

* fix(i18n): keep an undefined pass-through count absent; only dotted key-as-default is dynamic

* test(i18n): update plural call expectations in two renderer tests

* refactor(i18n): convert a plural site merged from main

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 22:11:28 -07:00
OrcaWinandOrcaWin 51b1e0f20c fix(editor): retain combined diff bodies on metadata updates (#35611)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 22:10:14 -07:00
OrcaWinandOrcaWin b4fc981053 fix(i18n): code AI text-generation failures for commit, PR, and Jira title generation (#35669)
* fix(i18n): code AI text-generation failures for commit, PR, and Jira title generation

* fix(i18n): merge duplicate type import

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 22:01:23 -07:00
Neil 46e291b31a fix(codex): never scan a WSL history source over 9P from background lanes (E9) (#35677)
* fix(codex): never read a WSL distro's history over 9P in the per-account and shared-mirror lanes

When codexSessionSourceHome points inside a distro (\\wsl.localhost\…), the
per-account lane used it as a history source and the shared-mirror lane scanned
it, both through 9P from the Windows side, which boots a stopped distro (E9) and
yields links codex resume ignores. Both now leave such a source out; the WSL
lane, which runs only at launch, owns those homes. Ordinary custom sources are
unchanged.

* fix(codex): no system-default history migration scan from a WSL source home

The host system-default session migration (backfill and index heal) also read
resolveHostCodexSessionSourceHome as its sessions root; a \\wsl.localhost
source would be scanned over 9P and boot a stopped distro (E9). Such a source
now makes the migration ineligible, sharing one check with the mirror lane.

* fix(codex): one WSL-source predicate for every history lane, and no backfill prep from it

Every lane that reads the host history source now uses one protected predicate,
isHostHistorySourceInWsl(): the per-account, shared-mirror and ~/.codex lanes,
migration eligibility, backfill invalidation after a managed system-default
launch, and preparing a migration pass, which now returns false without marking
the backfill pending.
2026-10-28 21:59:38 -07:00
OrcaWinandOrcaWin a7ec7d1735 test: remove 12 low-value cases and four suites (#35667)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 21:58:27 -07:00
Neil cf15284120 fix(wsl): Share only starts the stopped distros that hold the chosen skills (#35659)
#34632 let Share read every stopped WSL distro's skill folders. The Share
dialog now sends the chosen skills' folders, and discovery reads only the
stopped roots (and workspace plugin folder) that hold them; every other
stopped root stays unread and unavailable. The skills.share RPC names skills,
not folders, so it scans gated first and reads stopped roots only when a
selector is missing and a folder went unread.
2026-10-28 21:57:25 -07:00
Neil 12bb6aeabe refactor(plugins): share one frame-entry loader between panels and custom editors (#35680)
Panels and custom editors each had their own copy of the read-entry,
newest-read-wins, reload-on-change loop. The panel copy also cleared its
failure dot on the next plugin-list change while it still showed the
suspended message. One hook now loads both; a suspended frame stays suspended
(and reported failed) until its HTML actually changes, and panel health is
derived from the entry state.
2026-10-28 21:56:21 -07:00
Neil a50a44bb8d feat(wsl): bridge sh/dash/ash panes report their folder (OSC 7) at every prompt (#35597)
* feat(wsl): bridge sh/dash/ash panes report their folder (OSC 7) at every prompt

bash, zsh, fish and nushell panes already tell the terminal daemon their
folder; a bridge pane whose login shell is sh, dash or busybox ash did
not, so restores fell back to the starting folder.

dash has no rcfile flag or prompt hook, but an interactive sh reads $ENV
after ~/.profile, and dash 0.5.8+ (plus ash and bash-as-sh) expand $(...)
in PS1. The bridge now points ENV at a new sh/env file in the published
wrapper tree, saving the user's own ENV. That file hands ENV back to the
user's value (so children see theirs), sources it, and inside WSL only
prefixes PS1 with the report. A root containing $, ` or \ is skipped,
since the shell expands ENV's value.

A non-interactive sh -c never reads ENV, and a missing file (Orca
updated away or uninstalled) is silently ignored by dash, ash and bash.

* fix(wsl): sh ENV hook survives quotes in the user's ENV and keeps prompt width

- The user's ENV is expanded through a here-document, so a value with a
  quote, $ or space no longer breaks startup with a syntax error.
- bash-as-sh and busybox ash (fancy prompt) get the report inside \[ \]
  so its invisible bytes don't count toward the prompt width; dash has no
  line editor to miscount and would print \[ literally, so it stays bare.
- Tests: a user ENV of `$HOME/u "q" sp/env`, a user PS1 with $, a quote
  and a backslash, and reports read through the daemon's WSL parser.
- CI: edits to the sh hook and the bridge launch trigger the shell
  contracts job (its prefix list moves to its own module, keeping the
  scope script under its line limit), and the sh and fish WSL pty tests
  leave the unit shards.

* ci: check out the shell-contract prefix module in the change detector

The detector job sparse-checks-out a fixed file list; the new
pr-shell-contract-prefixes.mjs that pr-code-change-scope.mjs imports was
missing from it, so the job failed with ERR_MODULE_NOT_FOUND. A contract
test now walks the scope script's local imports and requires each in
that list.

* ci: list the shell-contract prefix module wherever the scope script's modules are listed

Two config-script suites copy pr-code-change-scope.mjs and its sibling
modules into a fixture repo; without the new pr-shell-contract-prefixes.mjs
the copied script failed to load, and the detector fell back to running
everything. Also lists it with the scope script's other modules in the
mobile workflow trigger and the mobile non-test file set.
2026-10-28 21:56:13 -07:00
OrcaWinandOrcaWin 8e7ec1abaa Prioritize rendered combined diff reads before obsolete queued rows (#35594)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 21:54:56 -07:00
OrcaWinandOrcaWin 662fd6ac35 fix(editor): scope combined diff status invalidation by path (#35570)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 21:54:42 -07:00
OrcaWinandOrcaWin ceb9ca0e3f fix(i18n): code automation run skip and failure reasons (#35666)
* fix(i18n): code automation run skip and failure reasons

* test: expect legacyTwinHosts code on twin-host refusal

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 21:51:53 -07:00
Neil 23a35c2946 test(terminal): run the renderer's OSC 7 agreement cases through xterm (#35661)
* test(terminal): run the renderer's OSC 7 agreement cases through xterm

The renderer's OSC 7 parser only ever sees what xterm's OSC parser hands
its handler, and xterm drops raw C0 bytes first: a raw tab, LF or CR
becomes /tmp/ab, and a raw NUL is dropped rather than refused. The
agreement table now records those rows as a known renderer gap (viaXterm)
and the renderer side runs every case through @xterm/headless.

* test(terminal): pin raw ESC, CAN and SUB, and run the daemon side through its PTY scanner

- A raw ESC ends xterm's OSC at /tmp/a, and the daemon's scanner rejects
  the report (ESC not followed by \), so the pane keeps its folder.
- CAN and SUB cancel the sequence in xterm, so the renderer's handler
  never runs (no-report); the daemon reads the byte as part of the path.
- The daemon side now also runs every case through
  TerminalOscCwdTitleScanner after a prior good report, as PTY traffic
  reaches it, alongside the direct-parse check.
2026-10-28 21:51:12 -07:00