Commit Graph
29 Commits
Author SHA1 Message Date
Neil ba6599eabb fix(ssh): pair again over SSH when a managed server drops this desktop, instead of reconnecting forever (#30775)
* fix(ssh): say the host dropped this desktop's pairing, not that another Orca holds the port

* fix(ssh): pair again over SSH when our own managed server refuses this desktop's token

* fix(ssh): key re-pairs by the refused revision, cap attempts, retry on explicit connect

* fix(ssh): keep a newer pairing stored mid-repair, count only completed reads, and offer Connect, not an access link, for managed servers

* fix(ssh): judge a re-pair only against the pairing still stored; Check connection retries a managed server's pairing

* fix(ssh): a pane whose managed server refuses this desktop says why and offers Reconnect, instead of spinning

* test(e2e): live failure path for a refused managed pairing

* fix(ssh): a Connect that re-paired answers from the new pairing, not the retired one

* fix(ssh): only an explicit Connect lifts a managed re-pair's hold and cap; say SSH access can pair again

Startup restore reaches ssh:connect with automatic: true and went through the same
ensureTunnel that cleared the hold, so repeated automatic reconnects could bypass the
three-attempt cap. The reset now lives in connectTarget's explicit branch.

The Shared Server Access footnote now says revoking there doesn't stop someone with
SSH access to this computer from pairing again.

* ci(e2e): route the managed pairing-repair spec with the managed-host remote sources

* fix(ssh): the repair gate decides whether a managed tunnel builds, not the build

A refused shared-control open rebuilt the tunnel (SSH execs, forward, handshake, wake)
before the held failure or the attempt cap was consulted, so reopens on a blocked
server rebuilt without limit. buildWithPairingRepair now asks the gate first: a failure
stands for the refused pairing revision for 15 s, or through the window once the cap is
spent, and every caller gets it without reaching the host.

- A connect during an in-flight repair keeps its reset (per-environment reset epoch).
- An unpaired answer fails at once; it is not a moved port, so no re-read or wake.
- A refusal that only rebuilt the tunnel no longer reports "pairing changed".
- WSL and cloud VMs no longer say Orca tried to pair over SSH, and Check connection
  only re-pairs servers managed over SSH (runtimePairingRepairRoute).
- Translate the new strings; route the pane-banner files to the managed-SSH e2e lane.

* fix(ssh): fence a status re-pair follow-up to the same server, and hold a refused repair under its own revision

- getRuntimeEnvironmentStatus followed any pairing change mid-probe, past a caller's
  expectedEnvironmentPairingRevision fence. It now follows only for an unpinned caller and only
  a new pairing of the same SSH-managed server under its pinned host key.
- recordRefusedAfterRepair stood under whatever revision was stored now, so a newer pairing
  stored mid-refusal was blocked untried; it stands under the repaired revision.
- The WSL restart test expects the refused-pairing classification main's WSL path now reaches.

* fix(ssh): a re-pair hands its callers the new credentials, and a failed one judges only the pairing it tried

- ensureOrcadManagedTunnel returns the environment as stored once the tunnel stands. The
  terminal census and a deploy's already-current result use it, so after a re-pair they
  no longer authenticate with the refused token, which read as an unverifiable census and
  refused restarts, updates and stops.
- The shared-control refusal handler blocks the status owner only while the failure stands
  for the pairing stored now (managedOrcadPairingFailureStands). A newer pairing stored
  meanwhile keeps its owner and retries.

* fix(ssh): migrations commit with the pairing the tunnel stands on, and a Connect mid-handshake stays reset

- Conversion binds the environment ensureOrcadManagedTunnel returns, so the destination, the
  server project reads and the result use the re-paired credentials (the migration mark is kept).
- A delta move gets its destination only from connectDestination, which opens the tunnel and
  builds the destination from what it returns; a pre-ensure destination can no longer exist. A
  tunnel that cannot open keeps the delta journaled and the host marked for the next move.
- buildWithPairingRepair takes the reset epoch when it starts, so a refusal after the re-pair
  cannot reinstate a failure that a Connect pressed during the handshake cleared.

* refactor(ssh): one module owns a shared control socket's environment policy, keeping request-connections under its line limit
2026-10-19 22:45:25 -07:00
Brennan Benson 577df19269 fix(wsl): reconnect hosts when a retained connection stops answering (#29005)
* fix(wsl): reconcile server reachability when work needs a host

* fix(wsl): settle a stopped host status after reconnect

* fix(wsl): reuse live transport before reconnecting hosts

* fix(wsl): follow main's tunnel-identity move and keep host service under the line cap

* test(wsl): keep the moved tunnel-identity module's other exports in the connector test mock

* test(wsl): stub the holder's running-distro list in the connector test's gate mock
2026-10-19 19:01:29 -07:00
Neil 699b9a0637 feat(files): guarded file writes that refuse to overwrite a changed file (#30605)
* feat(files): guarded file writes that refuse to overwrite a changed file

Adds an optional disk precondition to file writes: the writer compares the file
on disk with an expected signature inside the write, serialized per file (path
then inode), and refuses with a typed changed/missing result instead of writing.
Remote hosts serve it as files.writeIfUnchanged behind a capability; older
hosts get a client-side compare that fails closed, pinned to the probed runtime
and connection. Undo of a delete and the MCP starter config use it to write only
into a missing or identical file. Groundwork for guarding editor saves.

* fix(files): pin guarded writes on the sending socket; keep refused undo; narrow not-found and pinning

* fix(files): pin a guarded save to one pairing; keep only refused undo steps

* fix(files): ENOENT by code only; resolve symlink chains fully; pin lost-reply recovery to the save's pairing
2026-10-19 01:27:53 -07:00
Neil 85121dd4d0 fix(ssh): no probe stall on the first remote action after a pause (#29240)
* fix(ssh): skip the managed-server probe while the control socket is answering

A remote call after more than 5 s idle first ran a status.get probe on a new one-shot socket (SSH channel, WebSocket upgrade, hello, auth, request). A ready shared control socket that heard from the server in the last 20 s already proves it serves; a kill, reboot, rebind or stop closes that socket, so the probe still runs then.

* fix(ssh): read control-socket answers through a leaf module

orcad-managed-tunnel imported the request-connection cache, which (through the status owner) imports the serving gate back. Tests that mock that cache with an async factory deadlocked importing it. A leaf module now carries the answer source between them.
2026-10-15 19:50:27 -07:00
OrcaWinandm4air 34e2e5f469 refactor(remote): clients connect through a paired-connection handle, not a pairing offer (#27533)
Desktop, CLI, web and runtime transports now take a PairedRuntimeConnection
(endpoint, pinned host key, credential) instead of a PairingOffer. Offers stay on
the pairing input path (codes, QR, links, environment add) and are converted with
pairedRuntimeConnectionFromOffer. The credential is token-only today; request
envelopes still carry the same deviceToken, so the wire is unchanged.

This lets device-key and ticket credentials replace the token later without
touching every connect call site again.

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 14:02:03 -07:00
OrcaWinandm4air f9794d8efc refactor(remote): one paired E2EE handshake core for desktop, CLI, web and mobile clients (#27523)
* refactor(remote): one paired E2EE handshake core for desktop, CLI, web and mobile clients

Every paired client now builds its hello, checks the host's ready, seals its
auth frame and opens host frames through src/shared/paired-e2ee-client-handshake.
Platforms inject their own NaCl/base64 primitives. The v2 client session and its
pure-JS key schedule (@noble/hashes 1.8.0, the version mobile pins) move to
src/shared, and the per-host v2 latch policy is shared with an injected store.

No wire change: frames match the captured legacy and v2 fixtures byte for byte,
and a cross-version test drives the core against the released host and the
released client against this host. The auth step is now async so a device-key
signer can slot in later.

* fix(remote): keep @noble/hashes out of the CLI's unbundled runtime

The packaged CLI loads src/shared output from disk, so a bare @noble/hashes
import failed the packaged-runtime check. The v2 key schedule is now a
primitive: Node clients use the node:crypto schedule (moved to src/shared so
the CLI can reach it), while the browser and phone keep the pure-JS one.

* fix(mobile): re-check the v2 latch before sending a legacy auth frame

The auth step is async, so another socket can latch the host to v2 while a
legacy auth frame is being sealed. Refuse it at send time, as before.

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-11 12:14:51 -07:00
Neil 661372d514 Cancel abandoned file searches and prevent stale results (#25370)
* Bound relay git-grep records and clean up capacity failures

Credits @OrcaWin for the original bounded-record proposal.

* Bound filesystem listing and transfer metadata at the execution host

Apply mobile limits before transport, list Markdown through its semantic producer, and retain complete directory results only within explicit capacity budgets. Stream SFTP directory packets and reject oversized transfer plans before reporting success; preserve narrow older-peer fallbacks and validate streamed response retention.

* Release inactive Markdown candidates and support folder scopes

Keep one current document snapshot per consumer and attach completion candidates to the actual editor model lifetime. Resolve folder workspace roots through existing workspace identities so their previews and completions receive the same authorized listing as worktrees.

* Preserve full runtime inventories under aggregate byte budgets

Leave unqualified inventories complete beyond 20,000 files. Charge retained paths and serialized bytes at local and relay producers, reject oversized full inventories explicitly, and bound old-peer response reassembly; keep caller-specific mobile and explicit result limits.

* Bound SFTP directory handle cleanup waits

Send CLOSE even after cancellation, stop waiting after five seconds without an acknowledgement, and ignore late callbacks. Preserve the original capacity or cancellation error and close handles returned after an aborted OPENDIR.

* Preserve registered root spelling in Markdown document paths

* Reject incomplete SFTP directory cleanup and final cancellation

* Bound pending response bytes before stream ownership arrives

* Preserve bounded directory reads on legacy SSH hosts

* Allow bounded per-chunk padding at response byte boundaries

* Bound response payload retention after stream metadata

* Bound complete legacy Quick Open inventories and cache retention by bytes

* Localize Markdown document listing fallback

* Exercise relay search decoding with real byte streams

* Preserve concrete relay stream fixture types

* test: compare distinct bundled ripgrep platforms on Windows

* Preserve Markdown discovery across Windows and legacy SSH hosts

* Make WSL process termination available to the bounds layer

* Cancel abandoned file searches and preserve search keyboard navigation

* Invalidate searches when workspace roots or owners change

* Cover root replacement and legacy search cancellation together

* Exercise search cancellation through the current desktop access seam

* Keep search ownership and cancellation contracts current

* Share local text-search execution and close canceled subscriptions
2026-10-05 21:46:37 -07:00
Brennan BensonandMerge Sim aabcc57366 fix(runtime): publish remote control outages to host surfaces (#17531)
* fix(runtime): publish remote control diagnostics to renderer

* test(runtime): account for diagnostics bridge listener

* fix(i18n): add runtime connection state labels

* test(runtime): clean up shared control connection

* fix(runtime): fence diagnostics by shared-control capability

* fix(runtime): preserve authoritative transport state

* fix(runtime): preserve diagnostic overlay lifecycle

* fix(runtime): avoid publishing unchanged diagnostics state

---------

Co-authored-by: Merge Sim <sim@local>
2026-08-31 12:25:17 -07:00
Brennan BensonandMerge Sim 91cc834584 fix(remote): preserve standing host reconnect intent (#17067)
* fix(remote): preserve standing host reconnect intent

* chore(lint): merge duplicate imports flagged by the native code-quality audit

* fix(remote): fence stale capability runtime identities

* fix(remote): release capability evidence on host removal

---------

Co-authored-by: Merge Sim <sim@local>
2026-08-30 21:17:06 -07:00
Brennan Benson 4fa3022c47 fix(remote): stop painting a disconnected host as connected (#17050)
* fix(remote): stop painting a disconnected host as connected

A remote host row read "Connected" with a green dot in two states where it
was not connected: a cleanly closed control channel (server restart, host
sleep, network blip leaves lastError null, and the mapping required an error
string before it would say disconnected), and a half-open handshake still in
awaiting_ready/awaiting_authenticated.

lastError/lastClose were also never cleared on a successful reconnect, so a
recovered host kept showing "Connected" beside a stale failure indefinitely.
The SSH lane already clears on success; the shared-control lane did not, which
is why only Remote Server rows showed stale text.

* test(remote): cover stale diagnostics after reconnect
2026-08-28 14:39:20 -07:00
Jinwoo HongandJinwoo-H a9781a4118 STA-4150: client-hosted remote browser (consolidated) (#15448)
Co-authored-by: Jinwoo-H <jinwoo@stably.ai>
2026-08-25 15:36:51 -07:00
Jinwoo Hong fa9b20cb41 feat(skills): reland private bundle sharing safely (#14934) 2026-08-16 13:45:54 -07:00
Jinjing 763b1febeb Revert "feat(skills): add private bundle sharing (#14401)" (#14913)
This reverts commit 757fae28d7.
2026-08-16 10:39:57 -07:00
Jinwoo HongandE2E Test 757fae28d7 feat(skills): add private bundle sharing (#14401)
Co-authored-by: E2E Test <e2e@test.local>
2026-08-16 02:36:18 -07:00
Neil 4882eeb8ac rm git shim: neutralize stale wrappers without a host gate (#14255)
* Revert "fix terminal attribution shim removal edge cases (#14187)"

This reverts 585dd6d3a9. Re-landed in the next commit without the host capability gate. Nothing shipped with it, so no migration constraint.

* rm git shim: neutralize stale wrappers without a host gate

Re-lands the cleanup half of #14187: pass-through tombstones for retained wrapper paths, env/PATH scrubbing at every spawn owner, and the retired setting drop.

Only writes tombstones when the legacy directory already exists, so a clean install no longer has it created. Leaves out the terminal.attribution-removed.v1 capability gate: the tombstone neutralizes each host locally, so refusing terminal create/split against older hosts denied service without adding cleanup.

* rm git shim: surface neutralization failures and fix rollback marker

Readiness review follow-ups: warn on each failed attempt and on give-up (was silent and undiagnosable); write a VERSION marker distinct from the retired shim's '7' so a rolled-back build rewrites its own wrappers; clear a captured ORCA_REAL_* path that no longer exists so the cmd wrapper's where.exe fallback can run; stop a locked temp file masking the real error. Adds retry-exhaustion coverage.

* rm git shim: pin the cmd fallback order and correct the give-up count

Round-2 review follow-ups: string-pin that a stale ORCA_REAL_* is cleared before the where.exe fallback, and count the initial attempt in the give-up warning so it agrees with the per-attempt line.

* rm git shim: keep the split-failure toast

The revert took a toast that #14187 added alongside the gate but which stands on its own: without it a failed remote split only reaches the console and the pane silently never appears. Also pins attempt ordinals in the retry-exhaustion test.
2026-08-13 03:01:45 -07:00
Neil 585dd6d3a9 fix terminal attribution shim removal edge cases (#14187)
* fix(terminal): fully retire attribution shim

* fix(terminal): harden shim tombstone path lookup
2026-08-12 23:22:48 -07:00
Jinwoo Hong 09c8597fb7 perf(orchestration): route federated reads over shared control (#13814) 2026-08-11 16:17:17 -07:00
Brennan Benson e467b3ff7b fix(remote): stabilize shared control and terminal parking (#11656)
* fix(remote): stabilize shared control and terminal parking

* fix(remote): harden parking review edge cases

* fix(terminal): restore parked local floating buffer

* fix(ci): drop superseded paired parking evidence

* fix(terminal): preserve floating park watchers

* fix(ci): include web client in paired e2e artifact

* fix(ci): reuse renderer build for paired e2e
2026-07-31 00:22:53 -07:00
OrcaWinandOrcaWin ab665a3ce7 fix(remote): preserve terminal recovery across control refresh (#11513)
* fix(remote): recover stalled terminal streams

* fix(i18n): localize manual disconnect error

* fix(remote): park paired terminals with host snapshots

* test(remote): mock authoritative resync snapshots

* fix(terminal): defer startup mounts until hydration

* fix(remote): raise paired terminal stream capacity

* fix(remote): harden terminal recovery lifecycle

* fix(remote): preserve calls across control refresh

* test(remote): harden paired recovery oracle

* test(workspace): seed Jira source context

* test(remote): assert raw host terminal identities

* test(terminal): keep restore sentinels atomic

* test(terminal): keep restore sentinel on one row

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-30 03:05:10 -07:00
BingZandOrcaWin b96c2f0582 fix(remote): accelerate terminal recovery on resume/online (#8255)
* fix(remote): accelerate shared-control and pane recovery on resume/online

Narrow #8255 onto current main after #9774: fire pending shared-control
reconnect timers and pane recovery backoffs on system resume and browser
online, without replacing the per-pane recovery state machine or reconnect
banner UX.

* test(remote): cover online and occluded-resume recovery triggers

* fix(remote): centralize recovery acceleration

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-27 01:43:44 -07:00
NeilandOrca 879aad7dd6 oom(foundation): bound shared readers/limits + add BoundedMap primitive (#10299)
* oom(01): A1-shared-readers — reintroduce #10179 subset

Files: 18 applied, 0 deleted (from 6eb70d8370)

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

* oom(02): A2-shared-image-media — reintroduce #10179 subset

Files: 7 applied, 0 deleted (from 6eb70d8370)

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

* oom(03): A3-shared-fs-listing — reintroduce #10179 subset

Files: 21 applied, 0 deleted (from 6eb70d8370)

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

* oom(04): A4-shared-remote-relay — reintroduce #10179 subset

Files: 8 applied, 0 deleted (from 6eb70d8370)

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

* oom(05): A5-shared-misc — reintroduce #10179 subset

Files: 28 applied, 0 deleted (from 6eb70d8370)

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

* oom(06): B-shared-wiring — reintroduce #10179 subset

Files: 81 applied, 0 deleted (from 6eb70d8370)

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-24 21:36:57 -07:00
NeilandOrca aab112933e Revert "fix(memory): bound OOM-prone accumulators (#10179)" (#10255)
Co-authored-by: Orca <help@stably.ai>
2026-07-23 18:35:31 -07:00
Neil 8f40ddf328 fix(memory): bound OOM-prone accumulators (#10179) 2026-07-23 06:22:56 -07:00
OrcaWin 41751dd90d fix(runtime): route HUB-owned SSH worktrees through owning runtime (#9994) 2026-07-22 18:25:05 -07:00
OrcaWinandBrennan Benson 54c1ec5e7a fix(remote-runtime): recover terminals after network loss (#9774)
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-21 15:18:45 -07:00
Jinwoo HongandOrca 6be4e29394 fix(remote): isolate shared control request timeouts (#9016)
Co-authored-by: Orca <help@stably.ai>
2026-07-16 12:21:07 -07:00
Jinwoo HongandOrca 6c988356e2 fix: Remote Orca Server reconnect liveness — recover mirrors, input routing, and creates after tunnel drops (#7827)
Co-authored-by: Orca <help@stably.ai>
2026-07-08 12:08:27 -07:00
Jinwoo HongandOrca e3bf7d8614 feat(mobile): pickers, workspace parity, active-workspace focus, tap-to-open, source-control parity, artifact viewing (#5330)
Co-authored-by: Orca <help@stably.ai>
2026-06-15 22:02:51 -07:00
Jinwoo HongandOrca 719ae00009 Fix remote runtime tab and agent status UX (#5341)
Co-authored-by: Orca <help@stably.ai>
2026-06-14 01:48:26 -07:00