Neil 2ee507d744 fix(ssh): move Windows file writes off PowerShell 5.1 stdin onto sftp (#18596)
* fix(ssh): move Windows file writes off PowerShell 5.1 stdin onto sftp

#16432 was fixed by chunking writes to 32KB, on the belief that a
`DefaultShell=cmd.exe` host caps one stdin at roughly 50KB. Re-measured on
Windows 11 26200.9168 / OpenSSH_for_Windows_10.0p2, that premise is wrong in
both directions, and the chunking does not fix the hang.

The real constraint: a read on Windows PowerShell 5.1's redirected-stdin handle
over a non-pty ssh exec can die permanently when it finds the stream momentarily
empty, taking both the remaining data and the EOF with it. It is probabilistic
per such read — not a size threshold, and not certain on the first one. Measured
by swapping the copy loop for a counting reader:

  a 1.5s gap before any byte    -> 0 bytes received, 6 of 6
  1 byte, 1.5s gap, then 32767  -> exactly 1 byte
  32768, 1.5s gap, then 32768   -> exactly 32768
  a continuous 2MB              -> 167936 / 270336 / 372736

Those three 2MB figures are one payload run three times under the same
conditions, which is what rules out a threshold. Independently reproduced by a
second harness where one 1.9MB counted read completed through 39 reads and
another died after 11.

A payload that fits one burst usually presents only one read that can find the
stream empty, which is why 32KB mostly works — and it still failed 15 times in
120 under load, and 1 in 40 on a quiet host. Neither rate survives the 62 execs
a 1.9MB file needs: even 2.5% compounds to about four uploads in five failing.
No chunk size helps, because the defect is per blocking read, not per byte.
Three controls on the same host, same DefaultShell, rule out both a size limit
and cmd.exe: `findstr` took 2,016,000 bytes through one exec's stdin, sftp moved
1.9MB 5/5, and PowerShell 7 took 2MB in one exec.

Windows writes now go over the sftp subsystem, whose batch script is read by
the *local* client, so no remote process reads a pipe at all. PowerShell 7 is
the fallback where sftp is unavailable, and Windows PowerShell 5.1 is last,
still bounded, and now reports the host limitation and its remedy instead of a
bare timeout.

Measured on the same host, through this code: 1.9MB x20 all succeeded,
hash-verified, median 315ms, against 0/6 before. 32KB x120 zero hangs, against
15/120.

Also:
- Stage under a unique name per attempt. An abandoned write leaves a remote
  process that may still hold the staging file, and losing contact is not
  evidence it died (docs/reference/ssh-execution-boundary.md), so a retry must
  not reuse a name its predecessor may own. Sweep is best-effort and never
  treated as proof of anything.
- Create upload directories over sftp too; the JSON mkdir batch rode the same
  defective read.
- Cover makeWindowsWriteFileCommand and the publish command against the
  8000-char budget, which F11 flagged as untested.

* fix(ssh): replace the staged Windows write atomically, and translate ssh -l

Three review findings, all on the failure path that the success-path
measurements say nothing about.

CodeRabbit, Critical: the publish deleted the destination before moving the
staged file onto it, so a failed move destroyed the user's existing file and
left a window where a reader saw no file at all. That is worse than the
truncated partial the staging discipline exists to prevent. Now File.Replace
(Win32 ReplaceFile, atomic), falling back to a plain Move only when the
destination is absent — and that race is safe, because a destination appearing
in between makes Move throw with the staged file preserved. The exclusive
branch already had it right: Move throwing on an existing destination is the
exclusive contract. Append stays non-atomic and now says why.

buildSshArgs can emit '-l <username>' for a config alias no Host block claims,
and the translator threw on it. isSftpUnavailableError read that throw as 'this
host cannot do sftp', so those hosts fell back to the defective PowerShell 5.1
path and had the refusal cached against them for 30 minutes, silently. '-l' now
maps to '-o User=', with a test for the exact argument shape buildSshArgs
produces in that case.

CodeRabbit, minor: two assertions passed on an absent observation — an
unmatched regex yields '' and every() is true of an empty list. Both now assert
the positive form first, and the same audit was applied to the three other
some()/every() assertions in the file. The temp-file test now asserts mode 0600
rather than only that the file is cleaned up.

* fix(ssh): keep a path sftp cannot spell from becoming a verdict about the host

Audit of isSftpUnavailableError, prompted by the '-l' gap having the same
shape: a per-operation condition being written into a per-host cache that
holds for 30 minutes.

It had a second instance, and this one was mine. UnsupportedSftpPathError was
classified as 'this host cannot do sftp', but it is thrown for a UNC or
relative destination and for any path sftp's batch lexer cannot quote --
including a *local* filename containing a newline, which POSIX clients allow.
One such file would have routed every later Windows write to that host down
the defective PowerShell 5.1 path for the rest of the cache window.

The host verdict is now only the errors that really are host-scoped: a refused
subsystem, a client that will not start, and an untranslatable argument list.
A path refusal falls back for that one write and leaves the cache alone, in
both the file-write and directory-creation paths.

Revert-tested. Removing the operation-scoped catch fails all three new tests,
whether or not the predicate is also widened. Widening the predicate alone
does not fail them, correctly: with the catch in place the predicate no longer
gates that path, so keeping it narrow is defence-in-depth rather than the live
mechanism.

Flag audit at the same time: -F, -o, -T, -S, -p, -i, -J, -l and -- are now the
complete set buildSshArgs can emit, and all are handled.

* fix(ssh): make the atomic publish actually run, and unroll the mkdir batch

Two runtime defects that only a real host could surface. Both were invisible
to unit tests that assert the shape of the generated command string, because
both are PowerShell rejecting an argument at execution time.

File.Replace was passed a bare $null for destinationBackupFileName. PowerShell
coerces $null to an empty string when binding a .NET string parameter, and
Replace rejects that with 'The path is not of a legal form' -- so every
create-mode publish failed. The Critical fix was inert as shipped. Now
[NullString]::Value, which is the construct that exists for this.

Measured on awin, same staging-file lock, opposite outcomes:

  old publish  rc=1  destination MISSING          <- prior contents destroyed
  new publish  rc=1  destination PRESENT, sha 7f06b7e0... unchanged
  control, destination present, no lock  rc=0  replaced exactly
  control, destination absent, no lock   rc=0  Move fallback created it

End-to-end through the real uploader afterwards: 1.9MB x15 all hashes exact,
median 303ms; overwrite of an existing destination exact both times.

Separately, the PowerShell mkdir fallback could not create a tree of more than
one directory. '@($json | ConvertFrom-Json)' wraps the parsed array in another
array, so the loop variable binds to the whole thing and [string] of it is the
paths joined by spaces. It only ever worked for a one-element batch, where
stringifying a single-element array happens to yield the element -- which is
why no existing test caught it. Pre-existing on main; fixed here because this
PR puts that command on the fallback tier and claims the ladder works.
Both tiers now verified live against a three-directory tree.
2026-09-04 01:22:09 -07:00
2026-09-03 17:32:59 -07:00
2026-05-04 20:42:03 -07:00
2026-03-16 22:27:51 -07:00
2026-03-28 10:19:14 -07:00

Orca Orca

GitHub stars Total downloads across all releases License: MIT Join the Orca Discord Follow Orca on X Supported platforms: macOS, Windows, and Linux

中文 · 日本語 · 한국어 · Español · Français · Português

The AI Orchestrator for 100x builders.
Run Codex, ClaudeCode, OpenCode or Pi side-by-side — each in its own worktree, tracked in one place.

Download Orca

Orca desktop app running agents in parallel worktrees, with the Orca mobile companion app in the corner

Features

Mobile Companion

Monitor and steer your agents from your phone — get notified when an agent finishes and send follow-ups from anywhere.

iOS App Store · TestFlight · Android APK 0.0.47 · Docs →

Orca desktop with the mobile companion app

Parallel Worktrees

Fan one prompt across five agents, each in its own isolated git worktree — compare the results and merge the winner.

Docs →

Parallel worktree orchestration

Terminal Splits

Ghostty-class terminals with WebGL rendering, infinite splits, and scrollback that survives restarts.

Docs →

Terminal splits

Design Mode

Click any UI element in a real Chromium window to send its HTML, CSS, and a cropped screenshot straight into your agent's prompt.

Docs →

Embedded browser and Design Mode

GitHub & Linear, Native

Browse PRs, issues, and project boards in-app — open a worktree from any task and review without a context switch.

Docs →

GitHub and Linear task workflows in Orca

SSH Worktrees

Run agents on a beefy remote box with full file editing, git, and terminals — auto-reconnect and port forwarding included.

Docs →

Remote worktrees over SSH

Annotate AI Diffs

Drop comments on any diff line and ship them back to the agent — review, edit, and commit without leaving Orca.

Docs →

Annotate AI-generated diffs

Drag Files to Agents

VS Code's editor with autosave everywhere — drag files or images straight into an agent prompt.

Docs →

Drag files and images into an agent prompt

Orca CLI

Agents drive Orca too — script every workflow with orca worktree create, snapshot, click, and fill.

Docs →

Script Orca from the CLI

Also in the box:

  • Quick open — Search across worktrees, files, agents, commands, and repo context without leaving your flow.
  • Account switcher & usage tracking — See Claude and Codex usage and rate-limit resets, and hot-swap accounts without re-logging in.
  • Rich repo previews — Preview Markdown, images, PDFs, and repo docs in the workspace.
  • Computer Use — Let agents operate desktop apps and visible UI when a workflow needs real interaction.
  • Notifications and unread state — Know when an agent finishes or needs attention, then mark threads unread to come back later.
  • And many, many more — we ship daily, so this list is perpetually behind. The changelog is the real feature list.

Supported Agents

Works with any CLI agent — if it runs in a terminal, it runs in Orca.

Claude Code logo Claude Code   Codex logo Codex   Grok logo Grok   Cursor logo Cursor   GitHub Copilot logo GitHub Copilot   OpenCode logo OpenCode   MiMo Code logo MiMo Code   Amp logo Amp   OpenClaude logo OpenClaude   Antigravity logo Antigravity   Pi logo Pi   oh-my-pi logo oh-my-pi   Hermes Agent logo Hermes Agent   Devin logo Devin   Goose logo Goose   Auggie logo Auggie   Autohand Code logo Autohand Code   Charm logo Charm   Cline logo Cline   Codebuff logo Codebuff   Command Code logo Command Code   Continue logo Continue   Droid logo Droid   Kilocode logo Kilocode   Kimi logo Kimi   Kiro logo Kiro   Mistral Vibe logo Mistral Vibe   Qwen Code logo Qwen Code   Rovo Dev logo Rovo Dev   + any CLI agent


Install

Desktop — macOS, Windows, Linux

Or via a package manager:

# macOS (Homebrew)
brew install --cask stablyai/orca/orca

# Arch Linux (AUR) — or stably-orca-git to build from source
yay -S stably-orca-bin

Mobile Companion — iOS, Android

Pair with your desktop app to monitor and steer your agents from your phone.


Community & Support

  • Discord: Join the community on Discord.

  • Twitter / X: Follow @orca_build for updates and announcements.

  • WeChat: Scan to join the Orca community WeChat group 8.

    WeChat group 8 QR code for the Orca community
  • Feedback & Ideas: We ship fast. Missing something? Request a new feature.

  • Privacy: See the privacy & telemetry docs for what anonymous usage data Orca collects and how to opt out.

  • Show Support: Star this repo to follow along with our daily ships.


Developing

Want to contribute or run locally? See our CONTRIBUTING.md guide.

The relay that pairs the mobile app with a desktop host is also in this repository under cloud/, with a separate pnpm workspace and setup guide.

Orca contributors

GitHub star history chart for stablyai/orca

Signed Builds

Windows code signing sponored/provided by SignPath.io, certificate by SignPath Foundation.

License

Orca is free and open source under the MIT License.

S
Description
Orca is the ADE for working with a fleet of parallel agents. Run any coding agent with your own subscription. Available on desktop, mobile and remote runtime.
Readme MIT
1.4 GiB
Languages
TypeScript 95.2%
JavaScript 4.1%
Swift 0.2%
CSS 0.1%
HCL 0.1%