Commit Graph
4039 Commits
Author SHA1 Message Date
Jinjing bc55fe3cb0 Support explicit remote branch reuse across workspaces (#34295)
* Support explicit remote branch reuse across workspaces

- Preserve selected branch identity, tracking, and busy-branch handling
- Add explicit branch-off intent with v2 runtime capability gating

* Style existing workspace action in branch reuse UI

* Preserve explicit branch reuse with versioned host support

- Gate strict reuse on a new request capability while retaining legacy behavior
- Warn when a reused remote branch cannot be configured as the push upstream

* Clarify selected branch checkout options

* Offer existing or new branch choices for worktrees

* Preserve new-branch intent when reusing a branch name

* Adapt branch composer tests and catalog after rebase

* Refresh mobile RPC recordings for branch intent capabilities

* Remove retired Russian localization entries

* Remove retired German and Turkish localization entries

* Add branch selection translations for existing workspaces

* Localize mobile worktree branch selection
2026-10-28 20:48:16 -07:00
OrcaWinandOrcaWin 3094a4ae7b fix(i18n): code Create project failures and keybinding diagnostics (#35626)
* fix(i18n): code Create project failures and keybinding diagnostics

* fix(i18n): localize conflict titles and interpolate create-project copy in one pass

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 20:36:00 -07:00
Kelvin Amoaba 26aa174b3b feat(mobile): show dictated words as you speak and insert them at the cursor (#35502)
* feat(mobile): show dictated words as you speak and insert them at the cursor

Newer desktops already send the text so far with each audio chunk reply;
the phone now shows it: grey in the chat composer or command field where
it will land, and on the live-input status row. The final transcript
replaces the selection the field had when recording started, and is
appended when the field was never focused or changed since.

* refactor(mobile): pass the dictation preview by context and split its hooks out

The preview reaches composers through a context instead of four layers of
props, and the composer's and session's dictation logic move into their
own hooks, which keeps each file under its line cap.

* fix(mobile): keep dictation selections and live words to their own terminal

* test(mobile): keep the composer dictation test typechecked and under the line cap

* fix(mobile): keep the chat field controlled while dictation previews over it

The native composer field swapped its value for rich-text children while
showing heard words, so Android could duplicate or drop typed characters and
selection offsets could drift from the draft. Draw the preview as a hidden-
from-accessibility overlay shared with the web field, and only hide the
field's own text.

* refactor(mobile): build the dictation route context beside its destination

Keeps the session dictation hook under the line cap after rebasing onto main.

* fix(mobile): land dictation where its preview showed after switching chat or terminal

The final words now go to the target captured at start: the chat it began
in (its own draft when another chat is showing) rather than whichever
surface is visible. The command field preview only uses the saved selection
in the terminal it was read from, so it shows the words appended in another
terminal, as they will land.

* fix(mobile): send terminal dictation to the chat that replaced its terminal

Routing only by the captured target dropped the words when a chat tab
replaced the terminal mid-dictation, since no terminal draft was left to
take them.
2026-10-29 03:26:21 +00:00
OrcaWinandOrcaWin c10fbcb0d2 refactor(i18n): share one i18next setup between main and renderer (#35621)
* 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.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 20:21:12 -07:00
OrcaWinandOrcaWin 02db0146c6 perf(chat): order queued turns without repeated transcript scans (#34945)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 20:07:52 -07:00
bfa0dc01ba fix(mobile): preserve long terminal and chat pastes (#31473)
* fix(mobile): preserve long terminal and chat pastes

Agents can keep only the last ~1 KiB read of unframed input (#13332).
Frame long single-line chat messages like multi-line ones, and frame Live
input appends over 1 KiB (an OS paste) with the terminal's paste rule,
including held text flushed before a terminal click.

Co-authored-by: Carlos Baraza Haro <carlos.baraza@ieee.org>

* fix(mobile): end the live input session after a framed paste

Agents such as Codex collapse a framed paste into one token, so later
per-character erases from the field mirror desynced the line. A long
append to a terminal in bracketed paste mode now commits the whole field
in one frame and clears the field, recording it like other session ends
so a late native echo cannot send the paste twice; without that mode the
paste stays unframed and editable. Chat sends measure the sanitized bytes
when deciding to frame.

---------

Co-authored-by: Carlos Baraza Haro <carlos.baraza@ieee.org>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-10-29 03:02:08 +00:00
OrcaWinandOrcaWin 704d3cdd6f perf(i18n): load built-in catalogs once, without copies or a runtime plural fill (#35615)
* 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

* chore(i18n): sync plural forms for catalogs merged from main

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 19:58:12 -07:00
Neil ce44117d91 refactor(shared): colocate native chat cut-turn notices (#35617) 2026-10-28 19:54:34 -07:00
OrcaWinandOrcaWin 3f356edcb7 test: remove 20 low-value cases and ten suites (#35574)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 19:49:33 -07:00
fd8f1f9678 feat(wsl): convert UNC Windows-drive projects to drive paths across a restart (D1c-4c) (#33135)
* feat(wsl): convert UNC Windows-drive projects to drive paths across a restart (D1c-4c)

* fix(wsl): read the conversion's field table from its shared home

* test(wsl): the conversion runs Windows Git only on the drive path, since WSL denies UNC drive folders

WSL 2.7.11 answers EPERM for \\wsl.localhost\<distro>\mnt\c\... from Windows. Pin that the
preflight's Git runs and worktree-link reads use C:\... by making every UNC access throw EPERM.

* fix(wsl): name the cause when a conversion refuses over saved state

A refusal over saved state now says whether a project also holds an unconverted repo, or a drive
key already holds different data (and which key), instead of a bare collision code. The fields are
optional, so the window's copy still reads an older host's refusal. The seeded shape (a saved UNC
worktree with tabs beside the bare drive row startup discovery wrote) now converts.

* test(wsl): startup rolls back only when the drive key holds different user data

* fix(wsl): connect the conversion's pause to a WSL host as a user action, as #34059's mover now requires

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit bfeb4fe3ae7f278ac452e376d426315dbf7b985c)

* fix(wsl): never report a committed conversion as rolled back; record and check its profile; refuse paths off a Windows drive mount

- The journal's post-commit phase write sat inside the rollback catch, so a failure there
  dropped the journal and reported rolled-back for a profile already rewritten. The write now
  follows the commit outside the catch: a failure leaves the journal 'prepared', whose rewrite
  is idempotent, and reports resuming.
- The journal records the profile it rewrites, and startup runs only the active profile's.
- The run reads the distro's Windows-drive mounts and passes them to the scope.

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

* test(wsl): type the journal-failure switch without a cast

* fix(wsl): only this profile's waiting conversion blocks starting another

* test(wsl): compare the two profiles' journals without depending on directory order

* fix(wsl): a torn shell-history meta.json never stalls a conversion, it is written atomically, and only a prepared conversion blocks a new one

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 19:41:20 -07:00
NeilandClaude 8d319166bd fix(wsl): publish the resolved WSL hosts gate state to child processes (kill switch follow-up) (#35543)
* fix(wsl): publish the resolved WSL hosts gate state to child processes

Children inherit ORCA_INTERNAL_WSL_HOSTS=1 even when a remote policy block
closed the gate, and none inherit serve-only. Main now publishes its
resolved state as ORCA_WSL_HOSTS_GATE_STATE at startup, and the shared
serving check in a child trusts it over the internal gate env. A process
that resolves the gate itself ignores an inherited value, so a relaunch
never carries a stale state. ORCA_INTERNAL_WSL_HOSTS is left intact so a
relaunched or updated Orca keeps fetching the policy.

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

* fix(wsl): main ignores an inherited WSL hosts gate state from its first line

A relaunch or the updater's installer inherits the parent's published
ORCA_WSL_HOSTS_GATE_STATE. Main's entry now marks the gate as its own to
resolve before preflight, so even a read before configure falls back to
the internal gate env, and configure always overwrites the value. Tests
pin that a stale inherited closed or open is ignored and overwritten.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 19:38:20 -07:00
OrcaWinandOrcaWin 34ad2218a9 fix(i18n): pick CLDR plural forms for background task and subagent counts (#35605)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 19:36:03 -07:00
OrcaWinandOrcaWin 0144c429b4 perf(i18n): skip unchanged plugin language packs on plugin-list refreshes (#35602)
* perf(i18n): skip unchanged plugin language packs on plugin-list refreshes

Every plugin-list refresh produced a new entries array (even an empty
one), so I18nProvider removed and re-added every pack as a full copy and
ran changeLanguage, whose languageChanged re-rendered the whole tree.

- The plugin content list store reuses structurally unchanged entries
  (and the previous array when nothing changed).
- Pack registration re-adds only packs whose catalog changed, in main and
  renderer; main rebuilds menus only when something changed.
- I18nProvider re-applies the language only when the selected pack
  changes, which keeps forced re-application for real pack updates.

* fix(i18n): match reused plugin entries by id; keep the namespace after pack removal

removeResourceBundle drops the translation namespace that later backend
loads read, so removing one pack while others stay unchanged broke lazy
loading of built-in catalogs.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 19:32:54 -07:00
Neil 005da54a15 fix(wsl): start a bridge fish older than 2.7 without -C instead of failing the pane (#35579)
* fix(wsl): start a bridge fish older than 2.7 without -C instead of failing the pane

fish before 2.7 rejects -C, so the claude function and cwd hook made a
WSL fish pane fail to start on distros such as Ubuntu 16.04. Read the
version first and start those plainly. The cwd-report test now decodes
reports through the daemon's parser and checks fish 4's own reports.

* fix(wsl): read a fish 2.x version from stderr

fish 2.x prints --version to stderr, so the probe read an empty string
and old fish started with -C and failed. Read both streams; the stub now
reports one old version on stderr so the test pins this.
2026-10-28 19:31:54 -07:00
Neil 9dd6f26a6e fix: allow dismissing workspace auto-name failures (#35545)
* fix: allow dismissing workspace auto-name failures

* fix: retain auto-name failures until dismissal is saved

* refactor: use workspace selectors for metadata state updates
2026-10-28 19:28:21 -07:00
Neil ae89e41cad fix(terminal): read nushell's folder reports, which send #, ? and % raw (#35596)
nushell (0.94+, shell_integration.osc7 on by default) percent-encodes
only non-ASCII bytes in its OSC 7 report. The daemon's URL parser cut the
path at a raw `#` or `?` (recording the wrong folder) and rejected a `%`
not followed by two hex digits (dropping the report). The parser now
encodes those three in the path first. A properly encoded URI never has
them there, so every existing sender parses byte-identically, pinned by a
104-case snapshot taken before the change.

One pinned result changes on purpose: a folder named `%ZZ` now parses as
that path instead of being rejected. Non-UTF-8 sequences such as %FF are
still rejected, and control-byte handling is unchanged.

Also pins that the WSL bridge starts nushell as a plain login shell with
its own config, so its own report survives.
2026-10-28 19:19:53 -07:00
NeilandClaude 0976872b9e feat(wsl): let a cached remote policy close the WSL hosts gate (kill switch, remote half 2/2) (#35529)
* feat(wsl): let a cached remote policy close the WSL hosts gate (kill switch 2/2)

A cached remote policy of "block" now closes an internal gate the launch
environment opened, for the whole run. It reuses the closed-gate path that
already exists: with saved hosts or moves the gate becomes serve-only, so
moved projects keep working while nothing new is added or moved. With
nothing saved it is closed. A missing, unreadable or "allow" policy changes
nothing. Main stamps windows so preload agrees, the download runs only on
Windows with the internal gate open, and telemetry reports the block.

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

* feat(wsl): report the applied WSL hosts policy and keep the way back under a block

Telemetry reports the cached policy applied at launch as allow, block or
missing (omitted when the internal gate left it unread). Tests pin that a
block keeps Remove host, the way back and unfinished-move recovery, refuses
new hosts and moves, and that a mid-run download applies only next launch.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 19:16:42 -07:00
Neil 6d21afb09e refactor(wsl): one matcher for the stopped-distro refusal (E9) (#35567)
Two shared matchers recognized the same gate refusal by text:
isWslDistroNotRunningRefusal (beside the message builders) and
isWslDistroRefusalError. The detected-agents refresh now uses the
former and the duplicate module is deleted. Its IPC-prefix test moves
onto the surviving matcher.

The surviving pattern now matches any remedy wording after the sentence,
as the deleted one did, so a refusal phrased differently is still kept
quiet.
2026-10-28 19:07:58 -07:00
OrcaWinandOrcaWin 7802d16fda test: remove 17 low-value cases and seven suites (#35394)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 19:04:19 -07:00
e1b7248f0a feat(wsl): re-key the profile of UNC Windows-drive projects converting to drive paths (D1c-4b) (#33128)
* feat(wsl): re-key the profile of UNC Windows-drive projects converting to drive paths (D1c-4b)

* fix(wsl): keep the conversion's field table out of the main layout-writer census

* fix(wsl): merge the drive-keyed rows startup wrote for a UNC drive project instead of refusing

WSL Git lists a UNC /mnt/c project's folders under their drive paths, so startup discovery writes a
bare C:\-keyed worktree row (and its identity alias) beside the saved UNC one. The re-key then
landed both on one key and every conversion was refused as wsl_drive_unc_key_collision.

Before the re-key, a drive-keyed row holding only discovery bookkeeping is dropped with its alias
and identity row, so the saved row (instance id, name, links) takes the key. Any other two keys
that meet merge when one is empty or both match; only differing user data refuses, and the error
names the key.

* fix(wsl): treat a chosen workspace status as user data, so the conversion never drops it as a discovery row

Discovery writes only the default status. A drive-keyed row with any other status now counts
as the user's, so the conversion refuses with the key instead of dropping it.

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

* fix(wsl): read /mnt/<letter> with the same case rule as the UNC path folding

Only a lowercase /mnt is drvfs, with the drive letter in either case, as
foldWslUncPathCaseInsensitiveParts already says; /MNT is an ordinary Linux folder.

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

* fix(wsl): a refused duplicate spelling no longer blocks the drive folder's one converted project as an overlap

* test(wsl): a project nested under a refused duplicate's folder still converts

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 18:51:54 -07:00
Kelvin Amoaba f640517132 feat(mobile): show chat images that live on the paired desktop (#34462)
* feat(mobile): show chat images that live on the paired desktop

* fix(mobile): read every image type desktop chat shows

* refactor(runtime): share the bounded preview read and chat-image type check

* test: keep the chat-image tests typed and off importActual
2026-10-29 01:27:57 +00:00
Neil 3a68805eee fix(wsl): Settings CLI status, CLI repair, interop health and link checks never boot a stopped distro (E9) (#35412)
* fix(wsl): Settings CLI status, startup CLI repair, interop health and link checks never boot a stopped distro (E9)

- cli:getWslInstallStatus: opening Settings takes a settled running read and shows a refreshable
  "not running" status instead of starting the distro; the refresh button passes wakeWslDistro.
- Startup CLI repair re-reads each listed distro (settled) right before its repair spawns and
  defers it otherwise, so a distro stopping mid fan-out is not restarted.
- Interop health: stop-triggered checks no longer trust the stop event's list; every check takes a
  settled read, and again before the root repair.
- shell:pathsExist (chat link and terminal hover existence) refuses a stopped distro's UNC path
  instead of statting it; callers already retry an unverifiable answer.

* docs(wsl): note the missing-kernel availability probe as the known E9 exception

* fix(wsl): shell:pathsExist refuses only the stopped distro's path, not the whole batch

One UNC path in a stopped distro no longer fails the host paths checked with it; the renderer
treats that path alone as unverifiable and retries it.
2026-10-28 18:24:26 -07:00
NeilandClaude c14c99bb49 feat(wsl-host): a paired phone reaches the desktop's WSL host and never starts its distro (E6) (#33911)
* feat(wsl-host): a paired phone reaches the desktop's WSL host and never starts its distro (E6)

Behind ORCA_INTERNAL_WSL_HOSTS and the Experimental switch. The desktop relay
lists its WSL hosts to phones. Every relayed call (resolve, call, watchChanges,
status.get) passes a never-start check, and a WSL host serves a phone only
while it already runs.

- WslHostPhoneGate holds the phone's view. The call check reuses E9's
  focusReachability, so with no bridge in this process it refuses without a
  running-list read. A host shows as "stopped" only on a confirmed stop
  (liveness's distro-stopped, or a running list read by the check); anything
  else gets a neutral "isn't available" message. Hosts are hidden from phones
  while the Experimental switch is off or a Remove is pending.
- The relay host list sends optional `stoppedOnDesktop` only for a confirmed
  stop. The phone badges those rows "Start on computer"; older peers ignore
  the field.
- The paired-server rows producer carries WSL hosts like any server, so the
  main-computed sidebar feed a phone reads has their rows.
- The paired-phone e2e drives a WSL host project end to end and asserts a
  stopped distro is never started.

* fix(wsl-host): keep the phone's no-start check off non-WSL servers, and a stale stop off a ready host

- status.get runs the no-start check only for a WSL host, through a
  check-only helper. SSH-managed and paired servers reach the status owner
  exactly as on main: no reconnect, no redeploy, and the manual-disconnect,
  coalescing and repair handling stay in place. The preparation option is
  stripped before the probe.
- WslHostPhoneGate drops a 'stopped' read when a ready status arrived while it
  ran (a per-host ready generation), and clears a recorded stop when a check
  finds the host answering.
- A phone asking about a host that is switched off or being removed gets copy
  that says so, not "open one of its projects".

* test(wsl-host): the real no-start helper leaves SSH-managed and paired servers untouched

* fix(wsl-host): tie a phone's confirmed WSL stop to that host's status, and label a host not connected

- A stop a phone's check confirmed is stored with that host's own status
  object and holds only while that status is current. Another host's change no
  longer clears it. Any status of its own (ready, a disable's closed, a failed
  Remove's closed) does. A read never records a stop over a ready status, so a
  read shared with one begun before ready can't. Removed hosts drop their
  entry. The generation map and status listener are gone.
- The relay sends optional `unavailableOnDesktop` for a WSL host this desktop
  isn't connected to and doesn't know is stopped. New phones badge it "Open on
  computer"; older ones still show Connecting.
- The relay list reads the WSL host records once per poll (reachAll).

* refactor(wsl-host): one records read per phone check; note the gate's status identity compare

* test(wsl-host): read the phone's host flags as booleans in the paired-phone e2e

toMatchObject({ stoppedOnDesktop: undefined }) requires the key to be
present, and the desktop omits an unset flag, so the ready poll could never
match. The poll now compares relay plus both flags read as booleans.

* test(wsl-host): expect the neutral refusal once main's fake drops a stopped distro's bridge as closed

Main's fake connector (#34573) now publishes 'closed' when a stopped distro's
bridge drops, not liveness's 'distro-stopped'. So the phone's check reads the
stop under a ready status and doesn't claim it. The phone gets the neutral
copy and unavailableOnDesktop; the confirmed-stop copy stays unit-tested.

* test(wsl-host): the paired-phone e2e expects the confirmed stop again, and checks nothing booted the distro

Main's fake now publishes 'distro-stopped' when it drops a stopped distro's
bridge (#34624), as liveness does, so the phone gets "isn't running" and
stoppedOnDesktop. The spec also checks distros.json still reads 'stopped' at
the end: the fake boots a distro by rewriting it.

* test(wsl-host): the startup reconcile suite stubs the phone gate built beside the service

The reconcile still runs before the connector exists; the suite only lacked the new
setWslHostPhoneGate export on its service-instance mock.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 18:23:02 -07:00
NeilandClaude 5433c70e70 feat(wsl): fetch and cache a remote WSL hosts policy (kill switch, remote half 1/2) (#35528)
* feat(wsl): fetch and cache a remote WSL hosts policy for the kill switch

Adds the client half of a remote switch that can turn WSL hosts off: a
small versioned policy file, downloaded from a fixed release-asset URL per
channel, validated, bounded, and cached in userData with a monotonic
revision. Nothing reads it yet; wiring into the gate follows.

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

* refactor(wsl): expose the cached WSL hosts policy as allow, block or missing

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 18:19:45 -07:00
Neil be78042ab3 refactor(shared): colocate native chat turn row attribution (#35425) 2026-10-28 18:19:15 -07:00
Neil 8c3551bde3 fix(wsl): background Git, GitHub and GitLab reads never start a stopped distro; explicit opens may (E9) (#35410)
* fix(wsl): keep background git and GitHub reads from booting a stopped distro

Git history, diff and status-follower reads, GitHub issue/work-item/check
polls, chat link existence checks, the composer's create-base prefetch,
the worktree-root fan-out and the Settings/Automations orca.yaml previews
now refuse or skip a stopped WSL distro instead of starting it (E9).

* fix(wsl): explicit opens may start a stopped distro; more polls gated (E9)

Folds the parallel G1-G6 work into this branch and splits background
reads from user acts:

- An explicit open runs as a user act for its distro and may boot it:
  showing a diff tab or its Retry, showing Commits or its Refresh,
  opening the checks panel or entering its tab, checks Refresh, a check
  action, and linking or creating a PR. Main takes a wakeWslDistro flag
  on git:diff, git:branchDiff, git:commitDiff, git:history and
  gh:prChecks and runs those reads under runAsWslUserAct.
- Polls, timers, refreshes after Git actions, prefetches and fan-outs
  stay background and are refused while the distro is stopped.
- Also gated: the hosted-review card poll for every provider, the GitLab
  checks-panel poll (background flag), and the composer's open-time
  orca.yaml check (Create re-checks in the foreground).
- Pollers keep the data they already show on a refusal: the issue on a
  card, the checks list, an open diff and loaded submodule children, and
  stop logging it.

* docs(wsl): note that a hosted-review poll's stopped-distro refusal is still logged (E9)

* fix(wsl): Settings re-asks orca.yaml once a stopped distro runs; drop link-check gating (E9)

- Link existence checks (shell:pathsExist / pathExists) are no longer
  gated here: answering "missing" cached chat and terminal links as
  absent and toasted "File not found" on markdown link clicks. The
  pathsExist refusal lives in its own PR, which throws instead.
- Settings no longer sits on "checking" forever: a repo whose distro
  refused the background check is re-asked on window focus, on
  visibility, and after 10 s, until it answers. An unverifiable distro is
  now marked (wsl-distro-unverifiable) and treated as unread too, instead
  of being stored as "no orca.yaml".
- The sidebar issue poll and the Tasks list and count refuse with "Open a
  terminal in it to start it", since their Retry is itself a background
  read; the checks panel keeps "Retry", whose Refresh is a user act.

* fix(wsl): Tasks Refresh, interactive review refresh and manual PR lookups may start a stopped distro (E9)

- Tasks: a Refresh, Retry or search, or Tasks opened on a single repo,
  passes wakeWslDistro through gh:listWorkItems and gh:countWorkItems
  (IPC only; the RPC twins and web routes drop it). A multi-repo open
  stays background. Their refusal says "Retry" again; only the sidebar
  issue poll says "Open a terminal".
- hostedReview:forBranch wakes for an interactive request (the checks
  panel's manual Refresh), so GitLab, Bitbucket, Azure DevOps and Gitea
  reviews with nothing cached can load.
- gh:prForBranch, the renderer's fallback when refreshPRNow is missing,
  is gated like the other card polls; a manual lookup wakes, matching
  refreshPRNow's manual-only rule. The fallback call moves into its own
  module, built from the refresh candidate.

* fix(wsl): only an explicit Refresh starts a stopped distro for review status (E9)

hostedReview:forBranch woke on admissionTier 'interactive', but the
visible-review scheduler sends 'interactive' for the selected card on a
timer, so that poll restarted a stopped distro every interval. The wake
is now an explicit wakeWslDistro flag (desktop IPC only). The card
refresh helper sets it, since every caller follows a user act (the
checks panel's manual Refresh, or a review created, linked or edited),
and the manual Refresh also wakes its pre-refresh git status.
admissionTier is back to priority only.

* fix(wsl): an explicit review Refresh never joins a refused poll (E9)

fetchHostedReviewForBranch let a forced card Refresh join an in-flight
forced scheduler poll, since the join only compared force. On a stopped
WSL distro that poll is refused, so the click got the refusal. The
in-flight entry now records the wake, and a waking request joins only a
waking flight; a poll may still join an explicit Refresh.
2026-10-28 18:16:49 -07:00
Neil 5616230ecd feat(wsl): bridge fish panes report their folder (OSC 7) at every prompt (#35517)
The bridge fish launch's -C now also defines a fish_prompt hook that
sends the same percent-encoded OSC 7 as bash and zsh, only inside WSL,
after the user's config.fish.
2026-10-28 18:10:58 -07:00
Neil 0654de9f6d fix(cli): orca worktree rm reports the delete's outcome, not its acceptance (#35459)
The host answers a worktree delete as soon as it accepts it and lets Git
delete the checkout in the background. Clients that show a "removing"
marker ask it to wait for Git, but the CLI could not, so it printed
`removed: true` before anything was deleted, and a delete Git then refused
(for example a checkout with submodules) failed only in the host's log.
Scripts saw success, and every retry repeated the same silent failure.

`worktree.rm` takes an optional `waitForBackgroundRemoval` flag, and the CLI
sets it, so the command exits with Git's real result or error. It waits as
long as the desktop and web clients do and, if it stops waiting first, says
the delete may still be running and how to check. A reply sent on
acceptance now says `removed: false` alongside `removing: true`, so callers
that still get the early answer are not told the checkout is gone. Older
hosts ignore the flag, and the CLI then says the delete is still running.

Fixes #35135
2026-10-28 18:09:24 -07:00
Neil 459da96fff fix(worktrees): refuse a submodule checkout before stopping its terminals (#35458)
Git refuses `git worktree remove` without --force for any checkout with
submodules, even when nothing is dirty. Orca's dirty check passed such a
clean checkout, so the delete was accepted, its terminals were stopped, and
Git's refusal arrived later in the background, where the CLI and
automation never saw it.

The dirty check now applies Git's own test (submodule repositories under
the worktree's admin directory, or a gitlink checked out with its .git),
which the native Windows path already used, and refuses a clean checkout
that fails it with a message that keeps Git's wording, so every app
version still offers Force Delete with the submodule warning. The same
test now decides when a dirty refusal names submodules. Force is not
applied automatically: it would also delete submodule commits, branches
and stashes that were never pushed.

Refs #35135
2026-10-28 18:09:01 -07:00
Neil 9babef2866 fix(wsl): background preflight and agent detection never boot a stopped WSL distro (E9) (#35411)
* fix(wsl): background preflight and agent detection never boot a stopped WSL distro (E9)

Integration checks (git/gh/glab --version and auth status) and agent detection ran inside the
focused workspace's WSL distro on every mount, poll and status-bar render, and wsl.exe starts a
stopped distro. They now take a settled running read first and refuse (WSL_DISTRO_STOPPED or
WSL_DISTRO_UNVERIFIABLE) when it is not running. Window focus/visibility, retry buttons, agent
refresh buttons, a runtime choice in Settings and first-run onboarding pass wakeWslDistro, which
runs the check as a user act on that distro only. Running distros behave exactly as before.

* fix(wsl): a focus wakes only a focused WSL workspace's distro; end-to-end preflight gate test

A focus with a native workspace active sends no wake. Ports the real running-list preflight case
from #35395 and adds user-act scoping cases.

* test(wsl): model the running gate in the preflight RPC suite and cover a stopped distro

Windows CI runs the real gate, so the WSL detection case must state that the distro runs.

* fix(wsl): an agent refresh click never joins a background refresh it would be refused with

Store the wake flag with each in-flight refresh, as preflight.ts does, so a status-bar or Agents
refresh click during a mount refresh reaches main as a user act. Note why the RPC project branch
stays ungated.

* fix(wsl): a refused background check waits for the distro, and an agent picker wakes it

A preflight or agent detection refused for a stopped WSL distro was final: once the distro came
up, nothing re-ran it, so the tab-bar + menu offered no WSL agents (real-WSL golden). Now a refusal
is deferred until the distro is next confirmed running (deferUntilWslDistroRunning), and main tells
renderers to re-ask. Opening an agent-launch picker (tab-bar +, composer, continuation dialog,
source-control action dialog) whose list was refused retries it as a wake, never joining a
background probe.

* fix(wsl): an open agent picker wakes a refused list once, and never retries real failures

The picker wake fired whenever the list read 'failed', so a woken probe that failed again
(pending -> failed) re-fired it: an endless wsl.exe loop while the picker stayed open, and repeated
probes for native failures too. It now fires at most once per open per target, and only when the
last probe was an E9 refusal (distro not running/unverifiable), matched by the gate's message.

* fix(wsl): forget agent-inventory refusals when the inventory or its project contexts are cleared
2026-10-28 18:01:42 -07:00
Jinjing 5af784a77e fix(preflight): ship worker and preserve CLI liveness 2026-10-28 17:51:53 -07:00
Jinjing 7c364fa757 fix(preflight): preserve path detection compatibility 2026-10-28 17:51:53 -07:00
Pr1p a40db68ed5 fix(preflight): address review on the batched PATH lookup
- Deduplicate candidates per command: 'tool' and 'tool.CMD' may resolve to
  the same on-disk file and both must still be found.
- Probe known candidate names directly when readdir is denied (EACCES/EPERM):
  an execute-only directory cannot be listed, but stat on known names works,
  which is what the per-candidate lookup did.
- Skip the install-dir fallback's PATH re-scan: the batch already proved the
  missed commands absent on the PATH it searched, and that PATH is a superset
  of the fallback's (merged persisted PATH on Windows, the same filtered PATH
  on a WSL host), so the fallback scans install directories only.
2026-10-28 17:51:53 -07:00
NeilandKelvin Amoaba 5470ff907c fix(mobile): stop review notes from disappearing or coming back after deletion (#31699)
* fix(mobile): stop review notes disappearing or coming back after deletion

The phone sent its whole notes list on every add or delete, and the host
stored it as is. A note added on desktop after the phone loaded was wiped
by the phone's next edit, two quick taps could overwrite each other, and a
failed save rolled back to an old snapshot.

worktree.set now takes an optional diffCommentsBase, like linkedItemsBase:
the host applies only the writer's own adds, edits and removals to the
stored notes. The phone sends the list it started from, shows the host's
merged list from the reply, and undoes only its own change on failure.
Older hosts drop the field and replace the list as before; older phones
send no base and get the same.

Co-authored-by: Neil <neil@stably.ai>

* fix(mobile): keep loaded review notes when an early save fails

The first load was discarded once any save started, so a save that failed
before it landed left the host's notes hidden. The load now applies the
phone's changes made during the read onto the host's list instead.

* fix(mobile): send review-note requests one at a time

Two in-flight saves could reach the host in either order, so adopting the
later reply hid a note the earlier save had stored. Session notes now send
one request at a time and reconcile every reply with the phone's changes
made since it was sent, replacing the sequence counters.

* fix(mobile): drop review-note saves queued behind a failed one

A save queued behind one that failed still sent the list built on the
failed change, and an older host stores that list whole. Those saves now
undo their own change instead of sending. Also renames the shared base
type so it no longer shares a name with the renderer's mutation record.

* fix(mobile): show review notes as the host's list plus pending saves

A queued reply was reconciled against the list captured at tap time, so a
note desktop deleted meanwhile came back on the phone. The phone now keeps
the host's last list and its unanswered saves per workspace and shows the
two combined; a reply for another workspace is ignored. The review screen
also shows the host's list after a save, so an edit to a note desktop
deleted no longer looks saved.

* fix(mobile): fail queued review-note requests while disconnected

A notes request queued behind a pending load waited for a reconnect with
no timeout, holding every later save. Notes requests now fail at once when
the connection is down, like terminal input.

* fix(mobile): drop every review-note save built on a failed or dropped one

A save made while a dropped save was still pending carried its change,
so an older host could store it. Each failed or dropped save now moves
the failure count.

* fix(mobile): send each review-note save as the host's list plus its change

Each queued save now builds its payload when it is sent, from the host's
last list plus only its own change. A note added before the first load
no longer replaces the loaded notes on an older host, and a failed save's
change never rides along, so later saves no longer need dropping.
Delivered-note cleanup waits for a reconnect again so those notes are not
sent twice.

---------

Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-10-29 00:40:56 +00:00
Neil 828fe5fea3 fix(wsl-mover): say when a refused undo left the host's terminals asleep (#35449)
* fix(wsl-mover): say when a refused undo left the host's terminals asleep

* fix(wsl-mover): count only running workspaces as asleep, and flag post-sleep refusals
2026-10-28 17:34:55 -07:00
Kelvin Amoaba bbb7f2d695 fix(agent-status): keep Grok background work visible after a cancel (#35495)
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.
2026-10-29 00:34:00 +00:00
OrcaWinandOrcaWin 12a7d4c13a feat(mobile): localization infrastructure and language setting (#35399)
* 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>
2026-10-28 17:14:05 -07:00
Kelvin Amoaba cc3b3fb9a5 fix(agent-status): leave Grok cancels to Grok's own StopCancelled hook (#35417)
Esc never cancels a Grok turn and Ctrl+C can open a stop dialog the user
can decline, so keypress inference marked live Grok turns finished.
2026-10-28 23:22:23 +00:00
Neil 797bbaa64c fix(wsl-mover): say a move waits for a project coming back, not for another move (#35406) 2026-10-28 16:21:09 -07:00
Neil 552691fa0e fix(browser): "Server (streamed)" on a server that cannot stream hosts pages through it (#35381)
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
2026-10-28 16:16:41 -07:00
Brennan Benson 78ad9f4255 fix: keep chat option actions independent of inventory (#31927)
* fix: separate chat option input from inventory observations

* fix: preserve scoped chat choices and publication ownership

* fix: complete scoped catalog and applied control ownership

* test: make catalog fixture dependency boundaries explicit

* test: request catalog discovery explicitly in demand contracts

* fix(chat): preserve option observations and operation boundaries

* fix(chat): finish pending option and catalog lifetimes

* fix(chat): project saved permissions without launch preparation

* fix(chat): correct option rejection and catalog retirement

* Preserve Claude effort validation advice through chat rejection

* test: align refresh mocks and preserve fixture type evidence

* fix: share catalog claims only across equivalent owners

* test: use typed catalog corpus instrumentation

* fix: consolidate structured launch imports

* Validate SDK queue regression envelopes without assertions

* fix: complete catalog runtime and fixture type contracts

* Align native option fixtures with explicit discovery and applied facts

* Keep initial compatibility catalog reads on held memory

* Complete the installed catalog fixture envelope

* Observe learned defaults after asynchronous projection settles

* Consolidate host fixture journal type imports

* test(native-chat): align goal and seed fixtures with applied inputs

* fix(codex): retain applied proof when goal settings skip a write

* test: load rejection wording before timed queue regression

* Fix catalog fixtures and queued option snapshot typing

* Align permission publication fixture with passive option reads

* Separate startup config from held turn permission fixture

* Align input lifecycle fixtures with sparse applied choices

* Simplify scoped catalog refresh ownership and coalesce cache snapshots

* Load catalog persistence real-module fixture during collection

* Compose catalog availability fixtures from valid session records

* Align catalog and startup fixtures with current option contracts

* Fix permission read types and separate catalog fixture preparation

* Align option and restart fixtures with current lifecycle owners

* test(mobile): retain reported model alongside discovery

* test(mobile): recalibrate the measured route script envelope

* test: let the preflight shell-capture mock run its detection callback
2026-10-28 16:08:01 -07:00
Brennan Benson f0d55e7319 Nest chat-UI orchestration workers under the agent that started them (#34601)
* 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
2026-10-28 16:06:38 -07:00
OrcaWinandOrcaWin 3f56b8fdbc test: remove 24 low-value test cases and eight suites (#35147)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 15:42:13 -07:00
Kelvin Amoaba e5b45ac90f fix(mobile): keep dictation recording through a reconnect and keep what was already said (#35338)
* 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
2026-10-28 22:27:21 +00:00
Neil 4881182aad refactor: group native chat image transcript preparation (#35301) 2026-10-28 15:22:44 -07:00
OrcaWinandOrcaWin 95a83ccc6c fix(i18n): native plural rules for plugin packs in main, eager main init (#35403)
- 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>
2026-10-28 15:18:31 -07:00
bfbdcd7e80 feat(wsl): run Git for Windows for a WSL project on a Windows drive (D1c-2) (#33090)
* 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>
2026-10-28 15:15:26 -07:00
Neil 5018241866 feat(plugins): app and terminal themes in one Theme list, painted safely (#30719)
* 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
2026-10-28 15:13:10 -07:00
Jinwoo Hong 93ff4832e4 feat(orchestration): worker-start new worktrees default to a generated name (#35363) 2026-10-28 17:51:08 -04:00
af14a5b50b fix(i18n): localize provider errors and shared/native UI labels (#34680)
* 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>
2026-10-28 14:49:17 -07:00