Commit Graph
15617 Commits
Author SHA1 Message Date
Neil 41fb3c7a9c fix(mobile): retire host edit navigation after leaving the route (#31692)
Saving an endpoint after the user leaves can pop a second screen. Retire navigation ownership while allowing the endpoint save and client refresh to finish.
2026-10-21 16:41:13 -07:00
Neil 74d11d93d0 fix(mobile): preserve notification changes across delayed focus reads (#31690)
A focus read started before Enable can finish afterward and switch notifications back off. Fence reads against permission and preference mutations.
2026-10-21 16:41:08 -07:00
Neil 154307498b fix(mobile): isolate file review composers by document (#31698)
A reused file reader carries a note composer into another document, and a late submission closes the new composer. Give document state and callbacks a keyed lifetime.
2026-10-21 16:40:15 -07:00
82512a43a3 fix(pairing): normalize base64 consistently across clients (#31465)
Normalize bounded Base64 input consistently across desktop, browser, and mobile pairing paths while preserving lossless legacy padding and trailing-bit spellings. Retain offer validation and authentication behavior.

Adapted from #12027. Compatibility verified across 378 spellings with no previously accepted inputs lost; full PR workflow and aggregate gate passed for a84dfaee35.

Co-authored-by: Henry Su <henrysu4707@gmail.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-21 16:34:37 -07:00
Jinwoo HongandClaude bd0f326218 feat(sidebar): one lifecycle line per workspace in main, with in-progress hints (switch-gated) (#31553)
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip)

The fixes found while the sidebar ran from main's feed, without turning
that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's
stop-gaps come along.

- Main stops counting a closed tab's terminal once it was asked to stop
  and no pane shows it; a failed or refused stop takes the request back,
  and a host out of contact stays unverifiable.
- Main fetches a paired server's rows with the transport's normal bound,
  so a server slower than 5 s still lists.
- A deferred unread raise flips the folder row it was decided for, and a
  read while the owner check is out cancels it.
- Phone: no worktree.ps flash before the feed; a host's last installed
  feed stands in while a fresh read is out; it highlights its own
  last-opened workspace and a fold never scrolls; projects file under
  their project group with the desktop's indent; the collapse spinner
  shows only after 500 ms; "Reopen to update" keeps the rows on screen.
- e2e helpers and journeys for the switch-on suite.

* fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises

- A stop-and-wait that throws takes its stop request back, like one that fails.
- A final stop the legacy relay still owes stays marked, since the relay retries it.
- Pending unread raises are tracked only while one waits, so the map no longer grows.
- The paired-server fallback poll test gives its third fetch a reply and asserts live.

* feat(sidebar): main publishes cached snapshots and announces its own stores; detected, view and live-content domains (inert)

* fix(sidebar): keep links confirmed only by a new git run, no rescan on metadata writes, no phone wait on detected checkouts

* fix(sidebar): rescan the workspace rows when the Windows runtime default changes

* feat(sidebar): one lifecycle queue per workspace in main, with in-progress hints (switch-gated)

Create, delete, terminal.closeAll, terminal.sleep and worktree.sleep now run through one
first-in, first-out queue per host-qualified workspace: strict order, no merging, delete
cancels what has not started and waits for what has, an unconfirmed outcome pauses until
the host's own answer. Hints publish in a new inProgress feed domain (no reader yet); the
creations domain is its projection. A finished create or delete keeps its hint until a scan
started after it applies, in the same revision. Adds the paired-server metadata serializer
(saving domain, no caller yet). Replaces the creation registry.

* fix(sidebar): a create never queues behind the previous occupant's delete

* fix(sidebar): resume a paused workspace by re-observing, never replaying

- A command issued while paused waits behind the pause; when the owner answers again main
  re-reads terminal liveness and the listing, then carries on. A forced delete runs past it.
- Commands keep their arrival order across a workspace lookup; deletes by name key by the
  resolved id, so they wait for that workspace's running sleep.
- A second delete with different options is refused as already in progress.
- Close and delete read lost contact as sleep does.
- A create always frees its own queue entry, even after a retry took over its id.
- Drops the unused opId, and the paired-server write serializer and its feed section,
  which land with their first caller.

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

* fix: restore main's final P2 test fixes a merge reverted

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 19:34:14 -04:00
Jinwoo HongandClaude 98007cb5f5 refactor(sidebar): one shared feed client for the desktop window and the phone (#31524)
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip)

The fixes found while the sidebar ran from main's feed, without turning
that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's
stop-gaps come along.

- Main stops counting a closed tab's terminal once it was asked to stop
  and no pane shows it; a failed or refused stop takes the request back,
  and a host out of contact stays unverifiable.
- Main fetches a paired server's rows with the transport's normal bound,
  so a server slower than 5 s still lists.
- A deferred unread raise flips the folder row it was decided for, and a
  read while the owner check is out cancels it.
- Phone: no worktree.ps flash before the feed; a host's last installed
  feed stands in while a fresh read is out; it highlights its own
  last-opened workspace and a fold never scrolls; projects file under
  their project group with the desktop's indent; the collapse spinner
  shows only after 500 ms; "Reopen to update" keeps the rows on screen.
- e2e helpers and journeys for the switch-on suite.

* fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises

- A stop-and-wait that throws takes its stop request back, like one that fails.
- A final stop the legacy relay still owes stays marked, since the relay retries it.
- Pending unread raises are tracked only while one waits, so the map no longer grows.
- The paired-server fallback poll test gives its third fetch a reply and asserts live.

* refactor(sidebar): read the sidebar feed through one shared client library on desktop and phone

The window and the phone each had their own feed reader. Both now use one reader under
src/shared/sidebar-feed-client: paging, notices, retries and the snapshot it installs. A
snapshot reuses each unchanged entity's parsed object, so one row's change re-parses one row
instead of its whole domain. Adds the gesture and view-report pieces later cuts wire up.
Lists are unchanged on both sides.

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

* refactor(sidebar): drop the feed client's unused pieces until their callers land

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

* test(sidebar): key the snapshot test's rows by host identity, as main's feed types now do

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 19:34:02 -04:00
NeilandChun Chet Ng f6fc6d00f5 fix(mobile): use port 443 for explicit WSS host edits (#31659)
Use port 443 when an explicit WSS host edit omits a port; preserve saved host endpoints, bare-host inheritance and explicit ports.

Adapted from https://github.com/stablyai/orca/pull/31499.

Co-authored-by: Chun Chet Ng <ngchunchet95@gmail.com>
2026-10-21 16:32:07 -07:00
Neil 5f50b95c9c fix: remediate dependency security advisories across workspaces (#31681)
* fix: remediate dependency security advisories across workspaces

* test: verify security patches without ad hoc dependency installs
2026-10-21 16:31:17 -07:00
Jinwoo Hong bc7c15469b docs(readme): update star history chart to 2026-10-21 (#31701) 2026-10-21 19:28:03 -04:00
Neil a57e027cb3 fix(orcad): one update decision for SSH and WSL, with connect, Update and Force intents (#31682)
* fix(wsl-host): apply the SSH update rules to a WSL connect

A WSL connect deployed this app's orcad whenever the active build differed,
so a distro another, newer Orca had activated ended in an unconnectable
deferral, and a build the host had rolled back from was re-activated on
every connect. It now runs the same planManagedOrcadAutoUpdate decision SSH
uses and serves the active build when that plan skips.

* fix(wsl-host): serve the skipped-update incumbent only if it is still active

* fix(orcad): one update decision for SSH and WSL, applied again under the fence

The connect's update plan moves to its own module, and the fenced admission
every first deploy and WSL connect uses now applies it, so a transient
readiness failure or a race cannot reactivate a rolled-back build.

* refactor(wsl-host): adopt the serving build through the SSH first-deploy adoption

* fix(wsl-host): do not retry a candidate rejected earlier this launch

A rejected candidate is restored without recording rolledBackFrom, so WSL
retried it on every connect. SSH's launch-scoped failure memory moves to a
shared helper keyed per host, and WSL feeds it to the shared update decision.
A null adoption now re-reads the record before the deploy, as SSH does.

* fix(wsl-host): keep Force offered when a connect skips this app's build

The WSL connect's update admission moves to its own module, which returns a
forceable hint when it serves the incumbent over a rolled-back or failed build.

* fix(orcad): connect, Update and Force are the three update intents on SSH and WSL

Only a connect applies the skips; an explicit Update retries a skipped or failed
build (clearing the failure memory) behind the terminal census; only Force passes
the census. WSL's Update button now sends the update intent instead of a plain
connect. A connect that records a rejection serves the restored incumbent at once.

* fix(orcad): an explicit Update keeps the newer-host skip on SSH and WSL

Update lifts only a host's own skips (rolled back, failed before); Force lifts
all. SSH's explicit update now applies the same fenced admission, so stable
cannot replace a host a newer Orca activated without Force. A rejection retries
adoption only once the incumbent is back and no journal awaits recovery.

* fix(wsl-host): keep Update and Force offered on a deferred cold connect

A connect the host deferred with nothing serving now publishes its deferral, so
a host stopped after a rollback can still be retried from the UI. SSH's explicit
Update forgets a remembered failure only once it lands, and a restore that could
not be read keeps the rejection as reported.

* fix(wsl-host): offer only Force over a host a newer Orca runs, and keep failure memory until an Update lands

A host-newer refusal gets its own deferral code so the WSL row hides Update,
which can never pass it. An explicit Update no longer clears the launch failure
memory up front; only a landed deploy does, as on SSH.

* refactor(wsl-host): merge the intent type into the existing shared imports
2026-10-21 16:26:35 -07:00
Brennan Benson 10d410455e Repair only the damaged parts of a profile instead of rolling back the whole profile (#30931)
* fix(profile-state): repair only the damaged sections instead of rolling back the whole profile

One profile-state row whose checksum no longer matches made Orca refuse to open the
whole profile, and the only recovery swapped in a whole backup, losing every newer
change in every section.

Startup now checks each section on its own. When only some sections are damaged, the
dialog offers "Repair and Restart": Orca copies the damaged database aside, then replaces
just those sections with the newest backup copy that passes its checks, keeping every
intact section's current value, in one checked write. Headless starts name the damaged
sections and `orca profile state rollback --repair`. Whole-file damage keeps the existing
backup rollback.

Also turns on checkpoint_fullfsync for the profile database: on macOS plain fsync leaves
the drive cache unflushed, which can leave a long row's pages from two versions after a
power loss or forced shutdown.

Tracked in STA-10893.

* test(profile-state): pin the repair dialog's text and buttons

* fix(profile-state): list the repair module for the CLI typecheck; keep section labels out of the layout-writer count

* fix: validate section repair admission before decoding payloads

* fix(profile-state): share backup handles during section repair
2026-10-21 16:22:44 -07:00
Neil 926decd2f1 refactor(jira): group attachment modules with concrete owner imports (#31683) 2026-10-21 16:19:40 -07:00
fcac5cf0f0 refactor(native-chat): one shared parser for the approval envelope (#30848)
The phone and the desktop each parsed the status row's
{ approval: { tool, summary } } envelope with identical validation.
Move that validation and the answer keys into
src/shared/native-chat-approval-envelope.ts; each caller still builds
its own card (phone English labels, desktop translated labels) and
keeps its own precedence and heuristics. No behaviour change.

Co-authored-by: E2E Test <e2e@test.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 19:14:28 -04:00
Neil d22fb0d5b4 refactor(ipc): group Jira handlers with concrete owner imports (#31680) 2026-10-21 15:59:31 -07:00
Jinwoo Hong 7df3ff76d5 fix(sidebar): keep each host's activity, PR lane and retention apart for a same-id workspace (dark) (#31517)
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip)

The fixes found while the sidebar ran from main's feed, without turning
that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's
stop-gaps come along.

- Main stops counting a closed tab's terminal once it was asked to stop
  and no pane shows it; a failed or refused stop takes the request back,
  and a host out of contact stays unverifiable.
- Main fetches a paired server's rows with the transport's normal bound,
  so a server slower than 5 s still lists.
- A deferred unread raise flips the folder row it was decided for, and a
  read while the owner check is out cancels it.
- Phone: no worktree.ps flash before the feed; a host's last installed
  feed stands in while a fresh read is out; it highlights its own
  last-opened workspace and a fold never scrolls; projects file under
  their project group with the desktop's indent; the collapse spinner
  shows only after 500 ms; "Reopen to update" keeps the rows on screen.
- e2e helpers and journeys for the switch-on suite.

* fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises

- A stop-and-wait that throws takes its stop request back, like one that fails.
- A final stop the legacy relay still owes stays marked, since the relay retries it.
- Pending unread raises are tracked only while one waits, so the map no longer grows.
- The paired-server fallback poll test gives its third fetch a reply and asserts live.

* fix(sidebar): key activity, review, retention and repo lookups by host, not bare id

The feed published activity and PR facts by bare workspace id, so a same-id
workspace on two hosts (a paired server and this desktop, or two servers)
shared one host's facts. Activity and review are now keyed by row identity,
retention sweeps per listed row, and the builder resolves each row's repo by
its own host's registration. Bumps the feed semantics version to 3.

Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103

* test(e2e): read a review fact by its row key

Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103

* test(mobile): key fixture rows by row identity

Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103

* fix(sidebar): count and label rows by their own repo registration; keep activity keyed through a failed listing

Host header counts and mixed-host labels read the row's own registration, as
the host filter and row now do, so one render cannot file a row under one host
and count it under another. A failed listing keys activity by the last rows.

Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103

* fix(sidebar): a slower older listing never overwrites a newer one's rows

Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103

* test(sidebar): read the quiet-desktop PR fact by its row key

Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103

* refactor(repos): the renderer re-exports the shared getRepoHostIdentity

Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103
2026-10-21 18:55:25 -04:00
NeilandBrennan Benson 90b86405bc fix(mobile): recover workspace capabilities after transient probe failures (#31480)
Recover workspace capabilities after transient status failures without treating an unknown answer as unsupported. Share probes, invalidate across reconnects, and bound active-sheet retries with backoff and cleanup fencing.

Fixes #16347. Replaces the earlier recovery work in #16850 (already closed).

Credit Brennan Benson (@brennanb2025) for the original report and recovery implementation. Verification includes 105 focused tests, deterministic retry/leak counts, native iOS evidence with disclosed fixture limits, and green CI at a1cf7b54d2.

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-21 15:51:58 -07:00
Jinjing e29c353787 feat(settings): add local search to the release build picker (#31678) 2026-10-21 15:50:28 -07:00
Neil 15b2cd5828 refactor(ipc): group Linear handlers with direct owner imports (#31673)
* refactor(ipc): group Linear handlers with direct owner imports

* chore: keep module organization progress local
2026-10-21 15:41:13 -07:00
Neilandmonsterlag 80b9dca13e fix(mobile): keep terminal wait feedback visible with a draft (#31658)
Keep terminal-wait feedback visible while a draft is present, preserving editing and existing send gating.

Adapted from https://github.com/stablyai/orca/pull/31528.

Co-authored-by: monsterlag <64719697+monsterlag@users.noreply.github.com>
2026-10-21 15:37:59 -07:00
Neil 15246eb4a4 refactor(ssh): the SSH host finder draws its overflow through the host picker's note and returns its bounded list as is (#31631) 2026-10-21 15:33:52 -07:00
NeilandWooseong Kim 2b716f601d fix(mobile): attach Codex images when agent identity is missing (#31370)
Use the shared desktop image-path formatter for unidentified mobile terminals and identified Codex, preserving owned agent identity, bracketed paste, upload routing and acknowledged-input ordering.

Adapted from community PR #30310 by Wooseong Kim (@innocarpe). Fixes #30298.

Validation includes exact-head CI and aggregate gate, native macOS and Windows consumer checks, actual paired Windows uploads with byte-integrity checks, and combined mobile/desktop tests, types, lint and bundle checks.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 15:31:55 -07:00
Jinwoo Hong 7b1d7538c1 fix(relay): release a drained host before telling it to leave (#31572)
* fix(relay): release a drained host before telling it to leave

A zero-grace drain sent `drain` and closed with 4503 in the same tick, then
released the host's control lease. The desktop redials the director as soon
as it reads either, and from asia-east2 the release holds the host's
assignment row for ~1 s, so the redial met it and was refused row_busy.

The release now runs first and the host is told once it lands, bounded at
2 s; a failed or stuck release still closes. Wire frames are unchanged.

* fix(relay): give a drained host's release 3 s and log when it runs out

One host's releases run one after another, so 3 s covers a control lease
plus one open phone connection from asia-east2.

* style(relay): brace the drain release branches
2026-10-21 18:25:34 -04:00
Jinwoo Hong 1b42c4ce5c fix(relay-ops): make each reserve-watch sample one Cloud Logging read (#31619)
* fix(relay-ops): make each reserve-watch sample one Cloud Logging read

The project allows 60 ListLogEntries a minute shared by every watch and tool. Each watch sample
made four to six reads (two director runtime-metrics sub-windows, the ledger summaries, the
seat-behind-row lines, the cell's phone metrics, and since #31484 the 503 request log), so on
10-21 18:39Z twelve watches drew 429s until c21 and c22 tripped unverifiable.

A sample now sends its queries as one OR filter and splits the lines back client-side. The
per-instance 503 split counts each director's own logged /v1/assign 503s from the runtime
metrics it already reads, with the rest of the fleet's count as one unattributed instance,
instead of reading the request log.

* fix(relay-ops): judge unattributed 503s on their 3-minute sum against the whole fleet line

* docs(relay-ops): state that an instance that stops logging trips the unattributed rule
2026-10-21 18:25:30 -04:00
Neil 88aa3f27ce docs: track sequential module organization cleanup (#31670) 2026-10-21 14:54:41 -07:00
NeilandClaude f63e56abd1 Trim unused rules from the editor's same-file check (keep WSL folding for discard holds) (#31669)
* refactor(editor): drop SSH endpoint, path flavor and WSL alias folding from buffer identity

SSH files are keyed by connection target; the guarded save catches a retarget.
\\wsl$ and \\wsl.localhost stay distinct, including for discard holds and the
per-file write lock: main compares WSL writes at write time.

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

* fix(editor): keep WSL alias folding in discard-hold and write-lock keys

A discard via \\wsl$ must hold autosave of a tab opened via \\wsl.localhost:
the local guarded write is not atomic against git. Buffer identity stays
unfolded; only the hold/lock key folds, on the local Windows client.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 14:45:28 -07:00
NeilandShahar Mor 92ef7be3a6 fix(mobile): retain terminal reading position after resume (#31661)
Preserve terminal reading position on foreground and reconnect by reusing the existing scroll-preservation option. Adapted from community PR #23401 by Shahar Mor (@shaharmor), with native iOS before/after proof and 38 focused regression tests.

Co-authored-by: Shahar Mor <shaharmor1@gmail.com>
2026-10-21 14:35:19 -07:00
Neil e728d2ccac refactor(terminal): no catalog-less fail-open to the focused runtime in terminal spawn routing (#31644)
* refactor(terminal): no catalog-less fail-open to the focused runtime in terminal spawn routing

* chore(ratchet): prune the workspace-record read baseline for terminal-worktree-route

* test(terminal): pin that a state with no owner catalogs never spawns on the focused runtime
2026-10-21 14:31:15 -07:00
NeilandJian Zhou a33d596827 fix(mobile): open Create Workspace after source drawer closes
Wait for the source drawer to finish closing before opening the create surface. Preserve task, project, Linear, and Jira selections through the handoff.

Closes #17558.
Adapted from #13147.

Co-authored-by: Jian Zhou <16776397+ZhouJian26@users.noreply.github.com>
2026-10-21 14:31:01 -07:00
NeilandTauri-EPO 2d9da847cb fix(runtime): keep desktop take-back from stale phone input
Invalidate unfinished phone input claims after desktop takeback or disconnect. Recheck presence after delayed layout and preserve fresh phone input.

Closes #23199.
Adapted from #23200.

Co-authored-by: Tauri-EPO <enrico.pin@gmail.com>
2026-10-21 14:30:57 -07:00
Neilandgrboy6770 4a671db940 fix(native-chat): retire ghost queued continuation bubbles
Retire queued continuation echoes when transcript rows represent the sends. Preserve literal backslashes, occurrence boundaries, and image barriers on mobile and desktop.

Closes #15288.
Adapted from #15283.

Co-authored-by: grboy6770 <2000grboy@gmail.com>
2026-10-21 14:30:53 -07:00
NeilandWooseong Kim 75b14606d8 fix(agents): preserve Cursor identity across imported Claude hooks
Preserve Cursor identity across imported Claude hooks and mobile terminal tabs while retaining current tab status, rename, and scroll behavior.

Closes #30543.
Adapted from #30544.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 14:30:48 -07:00
NeilandClaude d6c93963f8 Saves are never cancelled, and Don't Save always keeps discarded text off disk (#31655)
* refactor(editor): one running + one queued save per tab, settle instead of cancel

Saves now run one at a time per tab with at most one queued save behind
them; later requests join the queued save, which reads the latest text
when it runs and writes nothing if the tab is already clean. Writes to
one physical file (edit tab, unstaged diff, combined-diff section) share
one lock. Quiesce no longer cancels saves: it flushes pending text, drops
the autosave timer and waits for the running and queued saves. Autosave
and restart write through the first view whose owner is still current.

Removes save generations, bumpSaveGeneration, the exported
clearAutoSaveTimer, the model-URI queue key and the unbounded save chain.

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

* fix(editor): Don't Save drops edits before draining saves; one lock key

Don't Save now marks the tab clean and clears its draft before waiting
on saves, and a save requested while the tab was dirty writes nothing
if the tab is clean when it reaches the write (Overwrite excepted). A
queued or not-yet-writing save can no longer put discarded text on disk
or recreate a deleted file. A Cmd+S on an already-clean tab still writes,
so a tab whose file was deleted can still recreate it.

Editor saves and combined-diff section saves share one lock key and
skip the lock when the file's host is unknown.

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

* test(editor): type-safe held writes in save queue tests

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

* fix(editor): a discard voids every save asked for before it

Don't Save bumps a per-buffer discard epoch alongside clearing the
draft. A save whose epoch is stale writes nothing, Overwrite included,
and skips the pending-edit flush that could republish the discarded
text. Dirty state alone wasn't enough: a notebook's retained cells or a
remote snapshot can re-dirty the tab after the discard.

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

* fix(editor): stop discarded bytes at the send and hold autosave through Don't Save

A save that passed the discard check could still send discarded content
after a remote host's status/capability round trip or an older host's
compare read. The writer now takes a last-moment check, run just before
the bytes are sent on every path (local, unguarded, guarded, older-host
fallback, retry); a discard in that window aborts the send.

Don't Save's drain no longer flushes pending editor text, and it holds
autosave for the file until the tab is closed, so a view that
republishes the dropped text mid-drain can't schedule a fresh autosave.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 14:22:19 -07:00
NeilandClaude 50f72bd5ae fix(editor): list unsaved editor files before a workspace removal closes them (#31641)
Removing a workspace closed its editor tabs and threw away unsaved text
without a word, including with "Don't ask again" on.

Removal confirms now list the files whose unsaved edits the removal
discards: the tabs the purge itself would close (host twins included),
kept only when no view sharing their model or record stays open. Editable
unstaged diff tabs count; delayed rich Markdown, CSV and notebook text is
flushed first.
- Delete Workspace dialog: the list sits with the git-change warnings.
  While it is non-empty the dialog no longer starts focus on Delete, and
  "Don't ask again" no longer skips the dialog.
- The same list in Remove Project, SSH workspace forget/reconnect-and-
  delete, SSH host removal with its workspaces, nested-worktree deletion
  (rows matched by path containment under the parent) and project-group
  deletion with its contained projects.
- Removals with no dialog whose files stay on disk refuse instead, with
  "Save or close N unsaved files in <name> first": folder-workspace
  delete (card action, context menu, hovered shortcut, through one shared
  helper) and Settings > Remove Project.

Force retries with no list of their own (the failure toast's Force
Delete / Delete Anyway, Space's Force button) re-check at that moment and
refuse the same way. A folder-workspace delete that leaves a same-id
folder on another host closes no tabs, so it is not refused. Removal
confirms touched here render on the security surface.

Removals with no prompt (CLI, automations, paired clients) behave as
before; a follow-up offers their text back.

Known limitation: the list is decided by the tabs a removal closes, not
by paths; a symlink into a deleted folder is not resolved on the host.

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 14:21:45 -07:00
Brennan Benson afcb385c86 ci: install verified fish 4 directly for shell contracts (#31581)
* ci: install fish 4 from its GitHub release when the Launchpad PPA is down

* TEMP: skip the fish PPA to prove the GitHub release fallback (revert)

* Revert "TEMP: skip the fish PPA to prove the GitHub release fallback (revert)"

This reverts commit 58ba0c5074.

* ci: install pinned fish directly from its verified release

* ci: isolate standalone fish from runner resources
2026-10-21 14:05:32 -07:00
Brennan Benson 83f163e09f The default agent can be set to open as a chat or a CLI (#31590)
* feat(settings): the default agent can be set to open as a chat or a CLI

With Chat UI on, Settings > Agents > Default Agent now shows a Chat | CLI
choice for the selected agent. It writes the same per-agent chat switch
that Chat > Providers and the + menu already use, so no new setting is
saved and the two places can't disagree.

* test: type the store stand-in without an assertion

* chore: drop accidental lockfile churn

* fix: clarify default agent launch preference scope
2026-10-21 14:03:50 -07:00
Neil c054e94eff refactor(renderer): name the owner adapters for what they do (resolveOwnerOrLocal) (#31626)
* refactor(renderer): name the owner adapters for what they do: resolveOwnerOrLocal, runtimeEnvironmentIdOrLocalUnlessTwin

The Active Server focus fallback they were named for is gone (#31058, #31119); what is left reads an
id no row places as this app's (a folder's saved-session server, a web client's only server).

* docs(renderer): resolveOwnerOrLocal keeps a named host's miss; baseline note names the setup-terminal read
2026-10-21 13:58:36 -07:00
Neil 554862d671 refactor(orcad): use the shared error and liveness helpers instead of local copies (#31654)
orcad-browser-provider and the pinned runtime materializer each kept a private
errorMessage; the WSL holder record and the secret-key record kept private
error-code checks, and the secret-key record its own kill(0) liveness check.
They now call errorMessage, hasErrorCode and isProcessAlive.
2026-10-21 13:56:21 -07:00
Neil fba3af0b48 docs(ssh): describe the orcad connect path without the removed relay (#31651)
Comments and one log line still said a host falls back to the relay, keeps
the pinned-relay ladder, or shares code with the relay's spawn and install
paths. No relay client exists; an unserved host fails its connect with a
reason, and the legacy bridge only keeps old relay terminals alive.
2026-10-21 13:55:25 -07:00
Neil e7d77e863c refactor(orcad): name the remote install helpers for what they install (#31647)
The SFTP/exec-stdin transfer helpers, their options type and the install
timeout still carried relay names from before orcad; they install the orcad
bundle and the Node runtime. The timeout's derivation from the relay's
native-deps budget is replaced by its value and the reason for it.
2026-10-21 13:54:26 -07:00
NeilandWooseong Kim 0b82f88e9f fix(mobile): dispatch leading terminal scroll without a timer (#31472)
Dispatch the leading terminal scroll immediately and coalesce subsequent movement behind the outstanding request. Preserve queue expiry, limits and ordering.

Closes #30299.
Adapted from #30311.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
2026-10-21 13:42:30 -07:00
65035bc52c fix(mobile): extend the final terminal row background into its remainder (#31474)
Paint the fractional terminal area from the last visible row background. Preserve terminal theme, scrolling and row-color behavior.

Closes #23507.
Adapted from #23508.

Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 13:42:25 -07:00
Jinjing 524fa0039b Preview HTML files by default (#31575)
* Preview HTML files by default across file-opening flows

* Centralize workspace file opening and HTML preview routing

- Preserve source, diff, and host-safety behavior across all entry points
- Reuse previews within the requested editor group and support branch HTML files

* Respect working tree state when opening HTML previews

* Route HTML links through owning workspaces and editors

* Preserve HTML preview state and refresh changed local files

* Resolve HTML preview hosts from workspace ownership

* Update file-opening tests for HTML preview routing
2026-10-21 13:31:50 -07:00
Neil 87ca5dad43 refactor(repo-host): one helper for the hosts registering a repo id (#31634)
Four twin checks (sort stamps, sidebar unread, sort-order notify, worktree-meta owner)
each rebuilt new Set(repos.filter(id).map(getRepoExecutionHostId)). Name it once as
getRepoRegistrationHostIds in shared/repo-host-identity.
2026-10-21 13:31:46 -07:00
NeilandClaude 7511300aa7 fix(ssh): name what blocks an SSH host's move, and offer Move anyway in the terminal pane (#31638)
* fix(ssh): name what refuses an SSH host's move, and offer Move anyway in the terminal pane

The refusal line said only "something on the host stops the move" and hid main's
reason (which already names the blocker and the way out) behind a Details menu,
and the terminal pane banner dropped it entirely. The line now shows that reason,
capped inline with the whole text behind Details, and the pane offers Move anyway
for a refusal saved state alone causes.

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

* fix(ssh): keep a refusal's way out in the pane, and don't race a connect in flight

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-21 13:31:06 -07:00
Neil 8e89202f60 fix(runtime): resolve a repo's SSH host instead of reading raw connectionId (#31630)
Seven runtime decisions read repo.connectionId, so a row naming its SSH host only as
executionHostId 'ssh:<target>' read as local: the missing-worktree sweep killed local PTYs
unfenced, the drift probe ran local git on a remote path, relay recovery skipped its mobile
tabs, and agent launch picked this machine's platform. Route them through
getRepoRoutingConnectionId, drop a dead includeLocalRegistry branch, and shrink the
repo-host-read ratchet from 91 to 81. resolveRepoForConnection keeps the raw read: it
matches the raw id a client sends.
2026-10-21 13:30:24 -07:00
Neil 553a704403 refactor(execution-host): one resolver for the SSH target a repo row may dial (#31623)
getStoredRepoSshConnectionId and seven inline
getSshTargetIdForExecutionHost(getRepoExecutionHostId(repo)) spellings answered the same
question. Name it once in shared (getRepoDialableSshTargetId), build
getRepoRoutingConnectionId on it, and drop the getRepoHostedReviewExecutionHostId re-export
alias in favour of a direct import of getStoredRepoExecutionHostId.
2026-10-21 13:29:42 -07:00
Neil f9aeb34e00 refactor(ssh): import SSH and orcad names from the modules that define them (#31642)
Drops seven pass-through re-exports in the SSH, managed-server, orcad and
runtime-environment modules; each importer now names the defining module.
2026-10-21 13:28:15 -07:00
Neil cd8b289f2f chore(runtime): delete exports nothing reads (#31639)
flushDurableStateOrThrowAsync and six exported types (AgentSessionSpawnTokenScanEvidence,
FreshExplicitAgentStatus, LiveTerminalPty, RemoteStartReceipt, WorkerStartMode(Reason),
WorktreeTeardownStopPty) had no reader anywhere in src, mobile, config or tests.
2026-10-21 13:18:20 -07:00
Neil 7d43ed9024 refactor(orcad): share one unverifiable terminal census (#31633)
Five modules each declared the same all-null census for a daemon that could
not answer; they now use one frozen constant from the shared census module.
2026-10-21 13:16:19 -07:00
+4 ae64a215a3 Add Brazilian Portuguese UI localization (#31627)
* feat(i18n): add Brazilian Portuguese

* feat(i18n): migrate legacy Portuguese plugin preferences once

Co-authored-by: Jackson Florencio <120382485+Jackson-Florencio@users.noreply.github.com>
Co-authored-by: joaoguilhermesimon-collab <245094637+joaoguilhermesimon-collab@users.noreply.github.com>
Co-authored-by: heslanx <167841364+heslanx@users.noreply.github.com>
Co-authored-by: Janderson Fagner <135186779+janderson-fagner@users.noreply.github.com>
Co-authored-by: Carlos <141033701+eusoucarlosmedeiros@users.noreply.github.com>
Co-authored-by: João Messias Lima Pereira <41351492+joaorura@users.noreply.github.com>
Co-authored-by: oliveiraleandro1981 <46854234+oliveiraleandro1981@users.noreply.github.com>
Co-authored-by: hugolrf <12834009+hugolrf@users.noreply.github.com>
Co-authored-by: Jonathan Oliveira <50607380+eojonathan@users.noreply.github.com>
Co-authored-by: Antenor e Filhos <278213917+antenorefilhos@users.noreply.github.com>
Co-authored-by: Daniel Dias <85655597+DeVFirmino@users.noreply.github.com>
Co-authored-by: Sergio Alexandre de Carvalho <65230046+salexcarvalho@users.noreply.github.com>
Co-authored-by: “Melostack” MeloX <229196278+Melostack@users.noreply.github.com>

* rm design doc

* fix(i18n): complete Brazilian Portuguese catalog

---------

Co-authored-by: Jackson Florencio <120382485+Jackson-Florencio@users.noreply.github.com>
Co-authored-by: joaoguilhermesimon-collab <245094637+joaoguilhermesimon-collab@users.noreply.github.com>
Co-authored-by: heslanx <167841364+heslanx@users.noreply.github.com>
Co-authored-by: Janderson Fagner <135186779+janderson-fagner@users.noreply.github.com>
Co-authored-by: Carlos <141033701+eusoucarlosmedeiros@users.noreply.github.com>
Co-authored-by: João Messias Lima Pereira <41351492+joaorura@users.noreply.github.com>
Co-authored-by: oliveiraleandro1981 <46854234+oliveiraleandro1981@users.noreply.github.com>
Co-authored-by: hugolrf <12834009+hugolrf@users.noreply.github.com>
Co-authored-by: Jonathan Oliveira <50607380+eojonathan@users.noreply.github.com>
Co-authored-by: Antenor e Filhos <278213917+antenorefilhos@users.noreply.github.com>
Co-authored-by: Daniel Dias <85655597+DeVFirmino@users.noreply.github.com>
Co-authored-by: Sergio Alexandre de Carvalho <65230046+salexcarvalho@users.noreply.github.com>
Co-authored-by: “Melostack” MeloX <229196278+Melostack@users.noreply.github.com>
2026-10-21 13:14:08 -07:00