One entry point (and preview/start/cancel/listUnfinished IPC for D1b-9) plans the move, gates
terminals on the hook server's agent status, the close-tab process probe, running automation runs
and live dispatches, pauses and proves exit, stages, sends blobs, commits only from
blobs-transferred (#29825), and finishes on the desktop. Any failure resets to zero or goes forward
when the host committed. Restore folders and the automations to turn back on are kept in the
journal; the host turns those automations back on after commit, retried until it confirms.
A real-PTY test pauses daemon shells, and runs on a real distro in the WSL orcad lane.
A committed WSL move now copies each in-app browser profile's cookies once into
the WSL host's stable route partition (owner decision 3a), merging without
overwriting what the host jar holds and never touching the source. Where that
is infeasible (no deployment link so no stable partition, the destination is
not a WSL host or is gone, or the partition name is held by another identity)
the host starts fresh and the move journal records why (3b). Registered as the
ledgered browser-cookies rewrite store; it runs only once browser sessions and
an app window exist. WSL_MOVE_DESKTOP_REWRITE_REGISTRY_COMPLETE stays false.
Settled local worker dispatches and their worker-terminal rows in a moved
project take the WSL host's worktree id, in one ledgered, idempotent store.
Chat workers, live rows, federated attachments and JSON receipts stay as
written; automations keep their unchanged local copy (the scheduler only
advances it). Per-table dispositions for orchestration.db.
Co-Authored-By: Claude <noreply@anthropic.com>
Registers the renderer-side desktop rewrite stores quick-open-history, file-view-preferences and notebook-environments. The renderer copies each moved workspace's entry to its WSL host key, marks it applied in the same storage, and records the ledger entry; main requests a storage flush first and accepts only the main window. Committed moves are re-listed for re-application at every startup. Catalog prunes keep filters, hook trust and setup-script dismissals of moved repos.
Registers the main-side desktop rewrite stores ui-routing, session-globals and host-session-seed. Adds a +2 main layout-writer baseline line for wsl-move-main-rewrite-stores.ts: these are one-shot, ledgered move rewrites, and no LayoutCommand can set focus or seed a runtime host's partition.
Collects the scoped projects, their groups and every dormant-state kind through the
local-WSL export scope, rewrites UNC paths to the distro's, and returns blockers for
anything the collectors can't carry instead of dropping it. Automations move disabled,
as the wire requires, and come back as a list to re-enable on the host after commit.
Co-Authored-By: Claude <noreply@anthropic.com>
A local-WSL scope lives on host local and owns only its projects, so host-wide
matches stay an SSH whole-host rule. SSH bytes are pinned by the collector golden.
Co-Authored-By: Claude <noreply@anthropic.com>
fix(mobile): match workspace header safe-area surface
Adapted from community PR #30531 with native iOS simulator before/after validation.
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
* test(claude): real-claude suite passes on any signed-in machine and current model aliases
Model ids come from the CLI's own model list instead of dated ids, tests that need a SessionStart hook or an effort bring their own project settings, and the supervised-stop test launches and reads its transcript under one Claude home, as acquisition does.
* test(claude): one real-claude flow for the model, effort and permission pickers and the context meter
Drives the live CLI through the production send path (model list, set model, apply effort, read settings), checks the turn's context report, and relaunches into a new permission mode the way Orca changes it. CI only meets these requests through stand-ins.
* test(claude): accept the ran options a send now reports
Since #30561 a Claude send resolves with the options it ran under; the stop test only needs the send admitted, and the options flow checks the ran options are exactly the per-message pick.
* test(claude): an alias pick's dispatch report may omit the model the CLI resolved
* fix(orcad): refuse new terminals between an update's last census and the stop
The last census before an SSH or WSL update stops the server now closes the
server's terminal admission first. Every spawn lease (acquireWorktreeTerminalSpawn,
which both spawn paths hold across the provider spawn) passes the fence, the
census waits for leases taken before the close, and an update that leaves the
incumbent serving reopens it. A reattach to a session the daemon still runs is
still admitted.
* fix(orcad): fence-exempt reattaches spawn attach-only; reopen admission when no stop was sent
* fix(orcad): stop nothing once the recount's admission fence may have lapsed
* fix(orcad-migration): a sidebar view preference never blocks moving an SSH host to its server
* fix(orcad-migration): keep repo filters on the desktop, keep later acknowledgements; e2e repro
* feat(terminal): the window's store shows main's pane start status (layout-owner build)
A status-only frame changes one store entry and no pane tree, so the pane's view stays mounted.
* refactor(terminal): keep the frame sync within its line budget
* feat(terminal): every pane main starts shows 'starting' from before it is written (layout-owner build)
* feat(terminal): a pane main is starting waits and attaches; a failed start shows why (layout-owner build)
Fixes owner-visible row 15: a failed agent or setup start no longer opens a plain shell.
* test(terminal): iterate store listeners directly
* fix(terminal): an exit that reports no code keeps its pane, as lost contact (layout-owner build)
* test(terminal): a pane main started attaches to main's terminal at its own grid
* test(terminal): detach census tests assert main's move and frame with the switch on
* test(terminal): exited-pane tests assert main's close with the switch on
These 8 have failed switch-on since #31130 moved the exit close to main.
* feat(terminal): main writes and starts every split of a pane it holds; the window attaches (layout-owner build)
The window's split, a startup split with its command, and a CLI split all go through main's
terminal.split; main's frame mounts the pane, with or without a window intent. Deletes the
window's split wait and withdraw, the split appear intents, and the queued-split skip and cancel.
The e2e helper uses the real split. A startup split leaves focus on its source pane.
* fix(terminal): the attach-only wait leaves sleeping, resuming and held panes to their restore path
* fix(terminal): a split pane attaches to the terminal main bound it to; a taken pane id is refused
Also restores the proof that a split closed while it starts gets one answer and leaves no
pane or start status behind.
* fix(terminal): a window split main cannot start still shows its pane, failed, with why
* fix(terminal): check a taken pane id first, keep the start's own error, clear a gone pane's status
Three test stores gain the pane-start field the window's store always has.
* test(terminal): the admission stub answers whether its pane is still written; a closed pane's failed start clears
* refactor(terminal): the rebound workspaces' bindings show through one call in the bindings module
* test(terminal): a pane main wrote and the user closed mid-start drops its failed status
* fix(terminal): a split main starts is listed to phones and session.tabs without a window's report
* fix(terminal): a reconnect shows its pane starting and why it failed; a lost owner race shows no failure; a split no one asked to see keeps the phones' tab
* fix(terminal): a kept create another owner won shows no failure; the window attaches to the winner
forSave captures the workspace's route (and its pairing pin) once; every read, stat,
status.get and write of that save uses it, so a workspace that moves between local and
a paired server mid-save never sends the write to the other host unpinned.
Scoped Session History built its candidate list by spreading each
discovery's files into push(), which throws RangeError once one root holds
more files than V8 accepts as call arguments. Append item by item instead,
and do the same for the bucket-layout candidates (unbounded on unlimited
scans) and the search index's unchecked-paths hand-off.
Tracked in STA-11092.
The served session page (react-native-web) reads flex: 0 as flex-basis 0%,
so the title block collapsed to zero width beside the tab strip. flex: -1
is natural width capped at 160, shrinking only when the row overflows, on
both web and native.
* fix(terminal): start an agent's terminal at its first real size instead of after a 12-frame wait
The startup grid settle waited for the grid to change and then hold for 2 frames;
a grid that was right from the start always paid the 12-frame maximum (~90 ms at
120 Hz, ~200 ms at 60 Hz, seconds when frames are throttled, since it also cleared
the 250 ms fallback timer). Its only case, the setup split, was retired in #29819.
Command panes now connect like plain panes: on the first frame with a real
measurement, or the 250 ms fallback timer.
* fix(terminal): bound startup by completed fit and deadline
* test(terminal): brace startup contract control flow
* fix(terminal): release pending startup when window hides
* fix(runtime): a phone reads and saves Markdown tabs on a headless server
With no window to relay through, markdown.readTab and markdown.saveTab answered
renderer_unavailable. Main now reads the tab's file (or its saved draft) from disk and
saves with a version compare plus the disk write precondition, in the shapes the
window's editor relay returns. With a window attached the relay is unchanged.
* Headless Markdown save: keep the draft's disk baseline, honour read-only tabs, re-find the record to clear
* Use the disk baseline only for a draft
* ci: run the phone Markdown spec's orcad case in the orca serve mode-switch job
* Headless Markdown: answer from disk only; a window's saved draft stays its view state
Clearing a saved draft wrote workspace layout from main, which no layout command carries.
Main now never shows or edits a draft; the desktop's restore check reports a disk change.
* Phone Markdown: one path through main and the disk, window or not
A paired server's workspace is read and saved on that server with files.read /
files.writeIfUnchanged, so it answers from its own disk and keeps the precondition.
The window relay (ui:mobileMarkdownRequest and the renderer bridge) is removed.
* e2e: a phone saves a Markdown tab the desktop holds unsaved edits in
The phone reads the disk with the tab marked dirty; its save lands on disk, and the
desktop keeps its draft and shows the changed-on-disk banner.
* Group the Markdown disk host's imports
* Drop an unneeded store cast from the dirty-readers test
* Phone Markdown: refuse a save on a server too old to check the disk; cover the untitled tab
A server before #30605 has no files.writeIfUnchanged, so the save is refused with the
existing update-required message rather than written unchecked.
* Phone Markdown: refuse a file an SSH host holds outside the workspace; pin the untitled answer
* Phone Markdown: untitled tabs stay read-only; older servers save like the desktop editor; a closed-source preview still reads
- The desktop lists an untitled document's tab with isUntitled (optional, additive); main answers
it read-only with unsupported_untitled and refuses its save before touching the disk.
- A server without the files.write.disk-precondition capability gets the desktop editor's guarded
save: compare from here, then a plain files.write, or a conflict. Every call of a save is pinned
to the pairing it probed.
- A preview whose source tab was closed has no saved record; it reads its own file, read-only.
* e2e: read the untitled tab's answer without a cast
* Phone Markdown: pin a save's pairing before its first read
A save captures the server's pairing before it reads, and every call of the save, the read
included, is pinned to it, so a re-pair between the read and the write is refused.
* Test: with no window, a phone saves an existing untitled.md and never creates a deleted one
---------
Co-authored-by: E2E Test <e2e@test.local>
* test(layout): downgrade probe for the layout-owner session format, and the touched-records patch's guard
Downgrade probe (ledger L8): a checked-in golden of the session main writes in a layout-owner
build (layout commands through the Store plus a window save), matched byte for byte; a
switch-off Store hands it on unchanged and a switch-off window restores its tabs, panes, groups,
editor file and browser tab. Regenerate with ORCA_UPDATE_LAYOUT_OWNER_GOLDEN=1.
L104: for each writer of the touched-records patch (layout.apply, the cross-partition move, the
window save), the patch equals a full save on every partition the load leaves alone, and differs
only on records the load normalizes otherwise. Two concrete legacy cases are pinned, and a
switch-off window restores each differently after a full save, so the patch stays.
* test(layout): type the guard's partition ids and locate the golden without import.meta
* test(layout): pin every legacy fixture's patch difference, assert restored panes and group selection, keep the golden deterministic
* fix(orcad): voice dictation on headless orca serve hosts
Mobile dictation against an orca serve host answered speech_unavailable
since serve moved to orcad: orcad never installed the speech service
factories, and the model manager and download imported Electron.
The model download now takes a host request opener (Chromium net on the
desktop, Node https on orcad, with redirects mapped to the same event),
the STT worker ships in the orcad slot, and orca serve hands orcad the
installed app's on-device recognizer. A host without a recognizer (an
SSH or WSL managed slot) lists only cloud models and sends a phone that
picked an on-device model to the setup sheet. Mobile names the fix when
an older server still has no speech at all.
The parity test now requires both roots to install speech.
Reported-by: delorean2020 (@delorean2020)
* fix(orcad): retry Node download drops, stage the recognizer on Windows, review fixes
- Node https errors (ECONNRESET, ETIMEDOUT, ...) are retryable, so an
interrupted orcad model download resumes (Codex P2).
- On Windows orca serve hands orcad a per-version copy of the recognizer
in the profile, so a loaded addon never locks the app's files against
an update (Opus).
- Drop a recognizer candidate path that could never match; one SAFETY
helper per test file instead of repeated casts; comments.
* fix(orcad): cloud speech is ready only with a key this host can decrypt; prune superseded recognizer copies
Codex round 2: a desktop-sealed OpenAI key exists on disk but orcad's
secret store cannot open it, so the catalog reported cloud models ready
and dictation failed at the end. orcad's model manager now proves the
key decrypts. The desktop keeps its existence check (no keychain prompt).
* fix(orcad): never prune staged recognizers before the profile lock; copy linked packages as real files
Codex round 3. ref-oss check: versioned copies are cleaned only by the
process that owns them, so the pre-lock prune is dropped rather than
patched.
* fix(orcad): remove a partial recognizer copy when staging fails (Codex round 4)
* fix(speech): never hand out another store's ciphertext as the OpenAI key (Codex round 5)
* refactor(orcad): no recognizer copy on Windows; Windows orcad keeps cloud dictation only
Design check after review round 5: every remaining finding came from the
Windows copy that dodges addon file locks. Dropping it shrinks the
surface to a path lookup; Linux and macOS serve keep on-device models.
* fix(speech): name the fix when an old server refuses a mic tap; always set the recognizer env; retry ENOTFOUND (final Opus review)
* fix(managed-server): probe install completeness relative to the slot dir
CI: shipping stt-worker.js added one more absolute slot path to the
install-complete probe, pushing it further past the exec-size budget.
Testing each artifact relative to the slot (inside a subshell cd) cuts
the probe from ~12.2 KB to ~3.6 KB, so it drops off the over-budget
list instead of raising a ceiling.
* fix(mobile): pin host.platform's read to a platform type, independent of which zod copy resolves
With mobile's own dependencies installed (which CI does for any PR that
touches mobile/), the e2e program resolves two zod copies, and the
salvaged platform arm from src/shared inferred as {}. The schema now
narrows through an explicit platform guard.
* fix(mobile): keep the host.platform read line intact for its RPC mutant; narrow in a second step
Mobile verify: the recorded-RPC mutant anchors on the read line, which
the previous guard rewrote, so the mutant could not apply.
* refactor(cli): app-executable resolution in its own module; drop the platform guard now zod is aligned
launch.ts went one line past max-lines with main; resolveAppRoot,
resolveForegroundOrcaExecutable, getExecutableAppArgs and
stripElectronRunAsNode move to orca-app-executable.ts (re-exported, so
callers are unchanged). With mobile and root on one zod (#31260), the
host.platform schema no longer needs the extra narrowing step.
* refactor(cli): import app-executable helpers from their module; no re-exports from launch.ts
* fix(chat): separate applied Claude choices from provider reports
* fix(chat): preserve Max effort without settings readback
* test(chat): retain Max effort confirmation contract
* test(chat): a literal model change no longer pre-flights the catalog
* fix(chat): record observed execution independently of accepted choices
* fix(chat): fence model reports at prompt input before recording
* test(chat): inspect Fast evidence without unsafe outcome access
* test(chat): honor input preparation in turn fixtures
* test(claude): accept dispatch metadata after stopping a turn
* fix: retire deferred chat options at input preparation