Commit Graph
13815 Commits
Author SHA1 Message Date
NeilandClaude 0a1f35e011 refactor(pty): one neutral isWslTerminalSpawn for relay presence and the Codex home (#35316)
The hook relay's bridge-terminal presence borrowed the Codex-home helper; both now call a
neutral isWslTerminalSpawn, so a Codex-only change can't alter relay presence.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 13:33:46 -07:00
OrcaWinandm4air f70c29b6e9 fix(sidebar): prefetch the pressed workspace instead of switching to it (#35300)
* fix(sidebar): prefetch the pressed workspace instead of switching to it

Rows are dragged to reorder them, so a press must not switch. Press activation
from #34594 is removed; a plain mouse press now starts the hover warm mount
immediately, and the click switches as before.

* fix(sidebar): start the press prewarm a task later and skip modified presses

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-28 13:33:04 -07:00
OrcaWinandOrcaWin ade67ab1f2 fix(browser): expire abandoned extension menu snapshots (#35167)
* fix(browser): expire abandoned extension menu snapshots

* Keep menu snapshot expiry cases in a focused host suite

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 13:32:13 -07:00
Pr1pandJinjing 8e9a08b543 fix(preflight): skip probing agents disabled in Settings (#34832)
* fix(preflight): skip probing agents disabled in Settings

Background worktree-startup detection now passes settings.disabledTuiAgents
(which it already held for agent selection) into detectInstalledAgents, so
disabled agents are neither probed nor reported. The preflight:detectAgents
and preflight:refreshAgents handlers keep probing every agent so Settings →
Agents still reports real install status.

Fixes #34635

* test(preflight): cover disabled agent detection forwarding

* fix(preflight): skip disabled agents in background inventory

* fix(preflight): restore native inventory after settings changes

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-10-28 13:32:08 -07:00
Brennan Benson 7c57ad6a55 Reuse equations while chat replies stream (#34819)
* perf(chat): typeset each unchanged formula once instead of on every streamed update

A streaming paragraph or list re-parses on each revealed prefix, and every
complete formula in it went through KaTeX again, as did every formula on a
virtual-row remount. Keep a bounded cache of typeset results keyed by what
the typesetter reads (tag, math classes, formula text), hand each tree its
own copy, and remember failures so invalid formulas still show their source.

* test(chat): import the typeset module type at the top of the math test

* fix(chat): evict the oldest cached formula without a type assertion

* fix(chat): type the readable math fallback without an assertion

* Cache immutable math results with bounded retention
2026-10-28 13:27:53 -07:00
bbd635e1d7 fix(watcher): prune nested ignored dirs on macOS and bound the flush fan-out (#12067)
* fix(watcher): prune nested ignored dirs on macOS and bound the flush fan-out

macOS was the only platform still watching nested node_modules/.git/dist/build
churn: the darwin branch passed the first 8 ignore dirs as plain @parcel/watcher
`ignore` entries, which the daemon resolves to `<root>/<dir>` only, and built
`ignoreGlobs` from the *remainder* of the list. Nested copies were therefore
pruned by neither layer, while Linux/Windows already got the full
component-anchored regex. Build the glob from the full list; the 8 plain paths
stay as-is because FSEventStreamSetExclusionPaths silently drops every exclusion
past its cap.

The resulting storms then hit an unbounded amplifier in the main process:
flushBatch stat'd every coalesced path with Promise.all (up to 5,000 in flight
against a 4-thread libuv pool), and scheduleBatchFlush could start another flush
for the same root while the previous one was still awaiting stats. Bound the
fan-out to the same limit the watcher child already uses and serialize per root,
keeping one flush running plus at most one queued so bursts coalesce instead of
multiplying. A queued flush is delayed, never dropped.

Fixes #11218

Co-authored-by: Orca <help@stably.ai>

* fix(watcher): share one stat budget process-wide and fence flushes on teardown

The per-batch cap multiplied by watched root, so N roots ran 8N stats; extract
the child's limiter and have both consume it. Teardown now fences and drains a
root's flush machinery so a queued flush cannot start, or emit, after close.
Also stop the queued flush from dropping a newer trailing timer's handle.

Co-authored-by: Orca <help@stably.ai>

* fix(filesystem): abort queued metadata when WSL watcher stops

* test(filesystem): satisfy watcher stat casting quality checks

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-10-28 13:24:32 -07:00
Brennan Benson f869c5580c fix(agents): preserve Kanban verdicts and AI Vault monitoring (#34649)
* fix(agents): preserve turn verdicts and monitoring in secondary views

* fix(agents): publish accepted native status changes to dashboard

* fix(agents): suppress equal rebuilt status notifications

* test(agents): explain proxy read forwarding in perf fixture

* test(agents): type production status fixtures completely

* test(agents): stabilize status fixture formatting

* Align web session test mocks with status transactions
2026-10-28 13:11:13 -07:00
Kelvin Amoaba 1f45c87c50 fix(dictation): say dictation is in use elsewhere instead of dictation_already_active on desktop (#35269)
Move the host dictation error map from mobile to src/shared so the desktop start toast and the phone read the same sentences.
2026-10-28 20:10:43 +00:00
Brennan Benson 31e66a9812 Offer Open as Folder before running Git for plain folders (#34660)
* Offer plain folder opening before Git discovery

* Preserve nested repository marker short circuit

* Verify folder handoff parity and ancestor scan bounds

* Format folder handoff verification test

* Use complete settings fixture for folder handoff test
2026-10-28 13:10:00 -07:00
Brennan Benson 7e0eca67c2 Keep saved folder workspaces under groups without a folder (#34652) 2026-10-28 13:09:13 -07:00
Kelvin Amoaba a419c4a3da fix(browser): stop an abandoned orca eval from blocking later commands on its page (#35284)
* fix(browser): free the page queue when an eval's caller disconnects

A local orca eval whose caller gave up (CLI timeout, Ctrl-C) kept the
page's serial command queue until its script settled, so a never-settling
script blocked every later browser command on that page. The unix socket
now forwards its disconnect signal to browser.eval, and the eval stops
waiting on abort; the abandoned script keeps running.

* test(runtime): pin local disconnect forwarding for browser.eval

* refactor(runtime): name the methods that get the local disconnect signal
2026-10-28 20:08:35 +00:00
OrcaWinandOrcaWin be7a6eecc7 perf(quick-open): preserve scans across worktree metadata updates (#34838)
* perf(quick-open): preserve scans across worktree metadata updates

* test(quick-open): collapse duplicate metadata-update case

* fix(quick-open): key exclude request by state instead of render-time ref

React Doctor rejects mutating a ref during render. Hold the nested-worktree
exclude request in state and replace it only when its key changes, so
metadata-only worktree updates keep the same object identity.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 13:06:19 -07:00
OrcaWinandOrcaWin 09bd3b96d4 Avoid host-frame registry scans and retained frame wrappers (#35275)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 13:00:39 -07:00
Brennan Benson 30bb30e25c Long chat replies stream with less stutter: parse each block once (#34821)
* Reduce stuttering while long chat replies stream

* Keep streaming markdown pipeline within lint limits

* Type the streaming parser meter without a cast

* Type the streamed markdown test reveal state

* Preserve markdown render domains when configured syntax cannot reuse discovery
2026-10-28 12:56:11 -07:00
OrcaWinandOrcaWin 58a175f928 perf(files): skip generated churn before Windows tree refreshes (#34859)
* perf(files): ignore generated churn before Windows refreshes

* test(files): trim Windows watch filter tests to behavior-bearing cases

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 12:55:54 -07:00
Jinjing 8eab52c114 fix(native-chat): stop multi-agent Codex chats from bloating the journal (#35226)
A long Codex command in a multi-agent chat could write gigabytes to the
agent-session journal and freeze Orca on the next launch while it replayed
them. One 16 MiB `rg` was saved 661 times in 163 s, 8.3 GB for one tool call.

Two things compounded:

- Every event from any Codex thread force-saved every other thread's
  in-flight stream, bypassing its x1.125 checkpoint cadence. The command's
  own thread wrote nothing else during that span; other sub-agents' events
  drove all 661 saves.
- Since #29982 a running Codex command saved its whole output on each
  revision, though the journal's contract (and ACP/Pi) keep a running call
  to a 16 KiB preview and save it whole only on the closing revision.

Forced flushes now re-save only the event's own thread. flushAll still
writes every other stream's first row first, so nothing is journaled ahead
of the text it follows. The journal enforces the running-preview contract
where item rows are built, so it holds for every provider; completed,
failed and interrupted rows stay whole.

Replaying the incident's shape (an 8 MiB command while another thread is
active) through the real translator and row builder: 354 command rows and
1,370 MiB before, 51 rows and 1.8 MiB after (1 MiB of it the completed
output), with translator time down from 1,043 ms to 47 ms.
2026-10-28 12:55:33 -07:00
Brennan Benson 5f314d05c3 @file mentions and Quick Open keep up with typing in large repositories (#34820)
* perf(quick-open): rescore only the previous matches while a file query is typed

Each keystroke in a native chat @file mention or Quick Open ranked the whole
workspace listing (up to 20,001 paths) on the UI thread and rebuilt a set of
every path. A path that matches a longer term always matches its prefix, so
when the new query only extends the old terms, score just the old matches;
cache the listing's membership set per listing. Results are identical.

* refactor(quick-open): keep separator-variant matching beside the other separator matching

* test: cover mention ranking reuse and inventory replacement

* test: type mention query props for rerenders
2026-10-28 12:55:08 -07:00
OrcaWinandOrcaWin 3ca198857d perf(files): share Windows runtime watches by root (#34858)
* perf(files): share Windows runtime watches by root

* test(files): trim duplicate Windows watch pool cases

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 12:54:57 -07:00
OrcaWinandOrcaWin 6d745ad1b9 perf(explorer): bound deferred filesystem watch events (#34857)
* perf(explorer): bound deferred filesystem watch events

* test(explorer): drop duplicate watch buffer storm and GC retention tests

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 12:54:49 -07:00
OrcaWinandOrcaWin dcc0daf9ba fix(watchers): stop stat admission without listeners (#34870)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 12:51:51 -07:00
OrcaWinandOrcaWin 278b02b27a fix(files): preserve WSL filenames in watcher snapshots (#34855)
* fix(files): preserve WSL filenames in watcher snapshots

* test(files): drop shell-producer WSL snapshot test

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 12:51:45 -07:00
OrcaWinandOrcaWin 4ce1e18930 fix(search): publish capped results without waiting for process exit (#34861)
* fix(search): publish capped content results before process exit

* test(search): drop duplicate queued kill-error variant

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 12:51:40 -07:00
Neil d9aa941f6f fix(ssh): offer Remove from Orca for SSH workspaces this app cannot delete (#35271)
* fix(ssh): offer Remove from Orca for SSH workspaces this app cannot delete

Refs #34677

* fix(ssh): retire delete-remote copy for unserved SSH workspaces

Refs #34677

* fix(ssh): retire delete-remote copy in the German catalog

Refs #34677

* fix(ssh): drop a stale host-removal comment

Refs #34677

* test(ssh): pin that an unserved SSH twin is only forgotten

Refs #34677

* test(ssh): build the unserved-twin store without a cast

Refs #34677

* test(ssh): type the unserved-twin store state

Refs #34677
2026-10-28 12:40:56 -07:00
OrcaWinandOrcaWin d009fab98a fix(browser): abort rejected Web Store downloads (#35158)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 11:37:35 -07:00
OrcaWinandOrcaWin 797e549b54 perf(browser): group extension frame event delivery (#35079)
* perf(browser): group extension frame event delivery

* test(browser): type event dispatch fixture collections

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 11:33:59 -07:00
OrcaWinandOrcaWin 6e7db575d0 fix(speech): abort model requests after pipeline failure (#35154)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 11:31:24 -07:00
OrcaWinandOrcaWin 02b6bde705 Retire extension notification records on native activation (#35139)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 11:29:23 -07:00
Neil 80bbbaff7f refactor: give native chat readability one concrete owner (#35231) 2026-10-28 11:28:03 -07:00
Neil 121eb3bc84 fix(runtime): resolve folder workspaces wherever a command can be given one (#35223)
* fix(runtime): resolve workspaces, not only Git worktrees, wherever a folder id can arrive

The Git-only selector resolver never matches project-group folder
workspaces (no repo row), so worktree show/sleep, terminal list by
workspace key, the active-terminal fallback, scrollback reads, Kitty file
reads, Linear --current, browser commands, plugin context and the session
tab fallbacks failed with selector_not_found for them. Route those
callers through resolveWorkspaceSelector and rename the Git-only resolver
to resolveGitWorktreeSelector so it reads as the repo-backed catalog only.

Refs STA-12562

* test(runtime): follow the workspace resolver rename in host fakes

Refs STA-12562

* test(runtime): keep partial browser host fakes and mail-routing stubs on the renamed resolver

Refs STA-12562

* test(runtime): drop an accidental copy of the folder selector suite

Refs STA-12562

* fix(runtime): keep folder workspaces off the skill worktree fence and the Linear meta backfill

Skill installs fence a worktreeId by repo rows, so a folder id there must
come through folderWorkspaceId, where its own host fence applies. Linear
--current answers a folder's legacy link without writing a worktreeMeta
row keyed by the folder id.

Refs STA-12562
2026-10-28 10:52:37 -07:00
Neil e03057de40 fix(native-chat): name a chat start that failed preparing its launch, with the host error in Details (#35186)
A start that failed on the host while Orca resolved or prepared the launch
(workspace or launch folder, command or account home, ACP prepare or temp
folder) reached the chat only as "Claude couldn't start" with no Details.
The adapters now mark that failure where it happens as the typed reason
launchPreparationFailed, and the host error is shown behind Details. A
reason already named there is kept, and a start withdrawn by Stop or close
keeps today's words. Older clients drop the unknown reason and kind.

Refs #34795
2026-10-28 10:47:58 -07:00
OrcaWinandm4air 40822e2c4e perf(switch): prewarm hovered and neighbor workspaces; coalesce held step keys (#35222)
* fix(terminal): don't count a PTY reattach as workspace activity

A remount (hidden prewarm, background mount) reattaching the tab's own live PTY bumped
lastActivityAt and sortEpoch, re-sorting the sidebar on hover. Also keep store map
identity on that no-op path so subscribers skip re-rendering.

* perf(terminal): prewarm hovered and keyboard-neighbor workspaces before the switch

A row hovered for 120 ms, and the previous/next workspaces in sidebar order (local hosts,
500 ms after a switch settles), take a prewarm lease: hover holds one, neighbors two.
A lease mounts only each group's visible snapshot-restorable terminal plus live tabs,
never wakes sleeping agents, and lays out and warms WebGL in separate idle tasks.
Hover stays laid out until it ends; a neighbor hides again once warm. Held leases are
exempt from ordinary cold parking (the retention budget still applies), individually
parked sibling tabs stay unmounted, a narrowed live mount is handed back on release,
and prewarm is off when hidden-view parking is disabled.

* perf(sidebar): coalesce held workspace-step keys

Auto-repeated Cmd+Shift+Up/Down and list arrows move only the sidebar highlight (and the
listbox's active descendant); the workspace under it switches once the repeat stops, on
keyup or a missed repeat since macOS withholds keyup while Cmd is held. Single presses
still switch synchronously. Keyboard navigation also publishes its sidebar neighbors to
the prewarm lease.

* perf(palette): close Cmd+J without the exit fade after a selection

A selection already moved the user to the new surface; the 150 ms overlay fade only
dimmed it. Escape and outside dismissals keep the fade.

* fix(terminal): undo a prewarm narrowing only while the lease still owns it

Releasing a lease after its workspace was activated (or after another mount replaced the
restriction) stripped the deferred tab ids that activation now owns, so over-cap tabs
stayed on the restoring placeholder and lost parked watchers. The undo now runs only
when the lease's restriction is still installed and the workspace was not activated.
Simulator workspaces also get no restriction on any unrestricted mount, parked included,
so the emulator overlay stays mounted.

* fix(terminal): prewarm never reattaches a session the daemon has not confirmed live

After a daemon loss a parked shell tab keeps its PTY id and snapshot capability, and its
reattach spawns a replacement process. A lease now asks main (pty:hasPty, authoritative
true only) for the revealable tabs it would reattach and admits only confirmed sessions;
unknown or remote-runtime answers decline. Workspaces with a simulator tab never take a
lease, since any restriction unmounts the emulator overlay and its local state.

* fix(terminal): confirm every split leaf and keep parked siblings out after a lease ends

A split tab reconnects each leaf on mount, so prewarm now confirms every resolved leaf
session (re-resolved after the answer; any change fails) instead of only the tab's
first PTY. Releasing a narrowed lease ends its measurement, which starts tab cold
parking's 30 s cool-down with an empty park set; the narrowing now outlasts that
cool-down so individually parked siblings stay unmounted after an abandoned hover.
Prewarm target selection moves to its own file.

* fix(terminal): hold a prewarm narrowing through any newer measurement cool-down

A mount that measures the worktree after the lease ended starts its own tab cold-park
cool-down; lifting the narrowing on the original deadline would remount parked
siblings while parking is still off. The undo now re-waits while the worktree is
measuring or inside its park cool-down. Teardown cancels pending undos after the final
releases, which would otherwise schedule timers past the unmount.

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-28 10:42:08 -07:00
erishandKelvin Amoaba 08ff67af84 fix(dictation): show the no-model toast from Cmd+E, hold mode and the chat mic (#19185)
* fix: forward Cmd+E dictation chord when voice is on but no STT model is set

Both the main-process shortcut router and the renderer's keydown/control
handlers early-returned whenever voice.sttModel was empty, before ever
reaching startDictation() — which is the only place that shows the
"No speech model selected" toast. The result: enabling voice but not
downloading/selecting a model makes Cmd+E (and the mic button) a
complete no-op with zero feedback.

Fix drops the sttModel check from the early-return in all three spots
so the existing toast in startDictation() actually gets a chance to
fire. The enabled=false path is unchanged (still a no-op), and hold
mode is untouched since it already checked voice.enabled and had its
own separate handling.

Added a regression test covering the toggle-mode chord with an empty
sttModel, alongside the existing disabled/hold-mode/toggle-mode cases.

Fixes #18691

* fix(dictation): show the no-model toast from hold mode and the chat mic

The no-model check also sat in the hold-mode gesture and the native chat
mic button, so those still did nothing. Drop it there too, let held keys
replace the toast instead of stacking it, and fold the main-process
regression into the existing dictation chord test.

* fix(dictation): update the no-model toast in place on held-key repeats

* fix(dictation): keep the no-model toast a plain toast for the localization audit

---------

Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-10-28 17:30:44 +00:00
Neil 49695f37b1 refactor: group native chat subagent transcript rules (#35200) 2026-10-28 10:24:47 -07:00
OrcaWinandOrcaWin e7e8bdd2f9 Retire failed extension notification records (#35134)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 10:19:22 -07:00
OrcaWinandOrcaWin 3416bda2ba fix(jira): retire deadline-owned transports when reads settle (#35127)
* fix(jira): retire deadline transport signals after reads settle

* test(jira): drop favourite-filter controls duplicated by existing coverage

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 10:04:18 -07:00
OrcaWinandOrcaWin 4156ad8f69 fix(browser): retire superseded DevTools opens (#35035)
* fix(browser): retire superseded DevTools opens

* fix(browser): keep undocking renderer when a retained old guest closes its dock

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 10:02:52 -07:00
OrcaWinandOrcaWin 55fd5b1b14 Preserve canceled browser mount identity for cleanup (#35131)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 10:01:55 -07:00
Neil 66b2478224 fix(managed-server): let the update candidate name the daemon generations an old host cannot (#34908)
Hosts that predate the census protocol set report a null protocol whenever
terminals span daemon generations, so a forced update stayed refused there
even though the incoming build could reach every daemon. When the census
cannot answer, the candidate's profile preflight now lists the daemon
generations alive under the data root the server serves, passed to it
explicitly, and the planner checks attachability against that list. An
uncounted census still refuses, and any generation the probe cannot rule in
or out leaves the update unverifiable.
2026-10-28 09:58:12 -07:00
OrcaWinandOrcaWin 5232c9ccfd fix(jira): retire binary transports after response consumption (#35123)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 09:48:06 -07:00
Neil 045f8c8266 feat(files): show images and PDFs from the byte-range preview path, local and remote (#35165)
* feat(files): show images and PDFs from the byte-range preview path, local and remote

ImageViewer and PdfViewer read an orca-media:// URL instead of base64 when
the editor opens a file: images load by URL, and pdf.js reads PDFs by
range, so a large PDF no longer arrives whole through files.readPreview or
pages into memory first. The grant mints only for the editor's viewers;
diffs, git blobs, markdown images and the paired web client keep base64.

- Minting an image checks its header with the base64 path's rule, so an
  image proven too large to decode safely never reaches the decoder.
- orca-media gains fetch and CORS privileges, and range responses expose
  Accept-Ranges and Content-Range, so pdf.js can read by range.
- Grants report the file size the viewer used to estimate from base64.

* fix(files): read PDFs by real range, check served images, keep old hosts' previews

- pdf.js fetches ranges only over http(s), so an orca-media URL was read
  whole; a small range transport now hands it bytes=a-b reads from the
  grant, and a failed range fails the preview instead of hanging it.
- A file can change after mint, so the raster header is checked again on
  the bytes each image request serves (the same handle locally).
- A paired host without files.readChunk falls back to files.readPreview
  for images and PDFs, as before; only audio and video need the update.

* fix(files): mint no local document grant for a renderer gone mid-mint

The local image/PDF mint is async now (authorize, open, header check), so
a renderer that closed meanwhile could leave a grant no teardown drops. It
binds the renderer lifetime first and refuses to mint once it's gone, as
the remote mint does.

* fix(files): keep the 50 MB cap for URL images, and probe remote headers incrementally

An <img> decodes the whole file, so image grants keep the base64 path's
50 MB cap; PDFs, now read by range, don't need one. A remote header probe
reads only each wider step's new bytes instead of re-reading from the
start. A replaced image of a different size resets its zoom.

* fix(files): tear down a PDF load whose range failed, and read 256 KiB ranges

A failed range only showed the error and left the pdf.js task, its worker
and pages alive; it now destroys that load first. pdf.js asks for one
readChunk-sized range at a time instead of 64 KiB, and a Range preflight
gets its CORS answer instead of 405.

* fix(files): read PDF ranges over IPC so orca-media needs no fetch or CORS

orca-media had gained supportFetchAPI and corsEnabled, and its responses
sent Access-Control-Allow-Origin: *, so pdf.js could fetch ranges. That let
any default-session frame holding a token read the bytes. pdf.js now reads
a PDF grant's ranges through fs:readPreviewRange, which main answers only
for the renderer that minted the token. The scheme is back to element-load
privileges (standard, secure, stream), responses carry no CORS headers, and
preflights get 405.

Grant bookkeeping moves to preview-grant-registry.ts. An e2e spec proves an
orca-media URL can't be fetched from the app page or read from another
session.

* ci(e2e): route the byte-range preview sources to the viewer specs that exercise them

Changes to the orca-media grant path, the image/PDF viewers and the PDF
range transport now run the PDF reload, rebuild and find specs, the remote
large-file open, media playback and the partition-isolation spec.

* fix(files): stop PDF range reads on abort, abort remote reads with the window, fix the CI route

- A closed or reloaded viewer schedules no further pieces of a merged range,
  and a remote range read stops when its window goes away.
- A URL without a size is an error rather than a fetch the scheme refuses.
- The PR guard job's sparse checkout and its admission test include the new
  route module, and the route now also covers the mint handler, preload
  bridge, editor surfaces, scheme registration and read client.

* fix(files): classify linked previews by their target, keep range reads off stopped WSL distros

A local image or PDF reached through a link is classified by the file it
resolves to, at mint and on every read, and a re-mint after the link is
retargeted gets a fresh grant of the new type. A paired host's document
whose first bytes are another kind defers to files.readPreview, which
classifies the target there. Lazy local reads go through the non-waking
WSL guard, as fs:readFile and fs:readFileChunk do.
2026-10-28 09:31:22 -07:00
OrcaWinandOrcaWin 07a98c2c6d fix(clipboard): preserve copy order across yielding validation (#35124)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 09:30:26 -07:00
Neil 74937b50aa refactor: group native chat transcript retention (#34761) 2026-10-28 09:22:44 -07:00
OrcaWinandm4air 8db8905fac perf(switch): do less work in the workspace-switch frame (#35185)
* perf(i18n): cache option-free translations per language and resource version

A workspace switch re-renders chrome that resolves hundreds of static strings
through i18next (~3 ms per switch). Option-free lookups only change with the
language or the loaded resources, so cache them keyed on both.

* perf(file-search): skip file search state writes that change nothing

Every workspace switch cancels the outgoing workspace's idle search, which wrote
an identical state object and re-notified every store subscriber.

* perf(sidebar): keep unchanged worktree cards and project headers memoized across switches

A plain press replaces the sidebar selection, and the selection-keyed gesture
callbacks plus the shared selectedWorktrees array were passed to every card, so
every card and its menus re-rendered on each workspace switch. Gesture callbacks
now read the latest selection from a ref, only selected rows receive the
selection, and project header actions are memoized.

* test(terminal): bound toast translations from above now that static strings are cached

* perf(terminal): stop re-rendering hidden panes and retained tab strips on workspace switches

Hidden panes re-measured their title-overlay rects on every switch, forcing a
layout and clearing the rects, which re-rendered the pane once on hide and again
on show. Hidden overlays are display:none, so they keep their last rects until
shown. Retained tab strips subscribed to the active repo and worktree for a
Windows-only shell menu, re-rendering every strip; those reads are gated like
the project maps beside them.

* perf(app-shell): let root overlays skip the App render a workspace switch causes

App re-renders on every switch (its chrome layout tracks the active worktree),
and it handed fresh floating-workspace and onboarding objects to the root
overlays, re-rendering every overlay and dialog. Both objects are memoized,
AppRootSurfaces and the floating toggle button are memo components, and the
toggle callback is stable.

* perf(worktrees): switch the active repo and worktree in one store write

Activating a worktree in another project wrote activeRepoId and then the
active worktree separately, so every store subscriber re-ran its selector twice
(~1 ms per switch). The repo change now rides along as a state transition of the
same setActiveWorktree write, as the SSH server-copy activation already does.

* perf(app-shell): stop App re-rendering on workspace switches

App subscribed to the raw active worktree and tab ids through the chrome layout
and three sync hooks, so every switch re-rendered the whole app shell. The
layout now exposes booleans, shortcut dispatch and the titlebar expand button
read the active ids when they fire, and the git-status polling, web session tab
sync and review refresh reporting hooks run in a null gate component under
AppBackgroundServices.

* perf(worktrees): fold visit recency and nav history into the activation write

A switch wrote the active worktree, then the leave/enter recency stamps and the
nav-history entry as separate store writes, each re-running every subscriber's
selector. The stamps (resolved against the post-activation selection, as
markVisited did) and the history entry now ride in the same setActiveWorktree
state transition as the repo follow.

* perf(terminal): present instead of clearing the shared glyph atlas on worktree reveal

The settled reveal reset predates xterm's per-renderer page-layout versioning
(renderers now rebuild their model when a shared atlas page changes), so a
healthy atlas no longer needs re-rasterizing on every worktree switch. DPR
repair and system wake keep the full reset.

* fix(worktrees): keep fresh visit stamps when the activation's history entry is de-duplicated

* fix(app-shell): repaint memoized shell controls on a language change

* test(floating-terminal): stub the language subscription in the toggle button's hook harness

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-28 09:08:11 -07:00
OrcaWinandOrcaWin f81700bd04 fix(browser): retire CDP startup with its debugger owner (#35093)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 09:04:34 -07:00
NeilandClaude 6ddcf1d453 feat(files): save a remote text file larger than one request (#35073)
* feat(files): save a remote text file larger than one request

The editor opened files up to 50 MB from a WSL host or SSH server but refused to save anything
over 720 KB. A staged save sends the content in chunks to a temp the host makes beside the
target, and the commit replaces the target by rename under the same locks and disk
precondition as files.writeIfUnchanged. Bounded per save, connection and host; idle saves are
dropped; a journal lets host start remove temps a crash left. Older hosts keep the refusal.

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

* fix(files): stage large remote saves privately and commit them with the one-request save

Review of #35073: chunks now go to the profile's private staging folder, the commit
re-authorizes the target and writes it in place through writeLocalTextFileWithDiskPrecondition
(same locks, precondition and inode as a one-request save), ops on a save run one at a time so
an abort or commit never races an append, a closed socket's saves are aborted, and host start
clears the staging folder in the background.

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

* fix(files): check a staged save's temp size at commit, and stage per process

Review of #35073: the commit refuses a temp whose size on disk isn't the declared total, and each
process stages in its own subfolder; the start sweep removes only its own folder and those whose
owner has exited, so runtimes sharing a profile never wipe each other's saves.

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

* fix(files): a runtime without staged saves closes a socket cleanly

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 09:03:53 -07:00
OrcaWinandOrcaWin 7efb30d454 fix(skills): route share progress to invoking renderer (#35087)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 08:49:23 -07:00
OrcaWinandOrcaWin bc80bc59fb fix: retire extension iframe event listeners with native frame (#35011)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 08:42:02 -07:00
NeilandClaude 2657567db0 feat(wsl): stop an idle WSL hook relay so its distro can stop (#35173)
* feat(wsl): stop an idle WSL hook relay so its distro can stop

Once no bridge terminal is left in a distro, the hook relay was the only thing keeping the
distro (and the WSL VM's memory) running, for as long as Orca ran. After 5 minutes with no
bridge terminal and no hook traffic the relay now stops, detached first so recovery never
restarts it, and its config is parked; the next terminal or agent in that distro starts it
again. Nothing new starts a relay, so nothing boots a distro.

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

* fix(wsl): a WSL terminal of unreported distro keeps every hook relay

Review of #35173: a WSL shell whose spawn reported no distro (an older daemon) still uses the
default distro's relay, so it now counts as presence for every distro rather than none.

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

* fix(wsl): a hook relay stays while the daemon still runs a bridge session in its distro

Review of #35173: after a relaunch, a hidden worktree's or headless tab's bridge session is live
in the terminal daemon though no pane in this run noted it, and orchestration, automations or
mobile may wake its agent. Before an idle stop the relay now asks the daemon's own session
inventory; a live session there, or an inventory it can't vouch for, keeps the relay.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 08:38:11 -07:00
OrcaWinandm4air 188b4976b0 perf(sidebar): switch workspaces on mouse press instead of click (#34594)
* perf(sidebar): switch workspaces on mouse press instead of click

A plain primary mouse press on a sidebar workspace row now activates it; the click that
ends the same press is skipped. The ~80-120 ms a press is held before release hides the
switch, so the new workspace is usually painted before the button comes up.

Modified presses, touch and pen, nested controls, rows inside a multi-selection, kanban
cards and keyboard-forwarded clicks keep click activation.

* test(sidebar): drop wall-clock cache stub from press activation test

* fix(sidebar): keep press activation off drags that wake, resume or close the board

A press-activated switch now keeps focus in the new terminal: the press's native mousedown
no longer moves it to the workspace list. Presses fall back to click activation when the
target would be woken (sleeping workspace or agent session, slept VM, disconnected SSH),
outside the terminal view, on selectable text, and while the workspace board is open, so a
row dragged onto the board no longer closes it. The skip-the-next-click flag now ends with
its gesture instead of surviving a drag-suppressed click.

* fix(sidebar): keep press activation off hosts a focus could boot

Switching workspaces focuses the row's host: a stopped WSL distro boots on focus and a recipe
VM resumes on activation, both before a press is known to be a drag. A press now activates only
on a proven-ready host (local, an SSH target, a WSL host reporting ready, a connected paired
runtime), resolved from the row's own host so folder workspaces and host twins match; recipe-VM
targets and unknown hosts keep click activation.

* fix(sidebar): activate on press only for local workspaces

Remote hosts can connect, boot or resume when their workspace gains focus, and the
focus subscribers resolve a workspace by bare id, so a twin or an SSH folder row could
still reach a remote host from a press that becomes a drag. Press activation now needs
the row's own host and the id's owning host to both be local; every SSH, WSL, runtime
and VM workspace keeps click activation.

* fix(sidebar): keep click activation while a text field has focus or a terminal would respawn

* fix(sidebar): keep click activation for workspaces without a running terminal; leave focus to the list when the press moved none

* fix(sidebar): keep a multi-selected folder row's press for the group drag

* fix(sidebar): drop the pre-press focus reference when the gesture ends

* fix(sidebar): keep click activation while a menu or dialog is open

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-28 08:36:12 -07:00
OrcaWinandOrcaWin df98fceae1 fix(extensions): release retired user gesture signals (#35076)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-28 08:30:08 -07:00