* refactor(mobile): say whether a host status was readable, and carry its protocol window `useHostStatusGates` settled the same closed gates for a host that answered `status.get` with no capabilities and for one whose status nobody could read: both paths produced an empty capability list and an `ok` verdict. A caller that walls on a missing capability cannot tell those apart, and the mobile web bundle's wall is terminal, so it must never fire for the second. `statusReadable` distinguishes them. `hostProtocolWindow` exposes the two protocol numbers the hook already read for `evaluateCompat`, as the reply's own fields, so the bundle wall can evaluate its own window without a second `status.get`. Both are additive; no existing consumer changes. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): add the hybrid shell flag and the generation path both loaders demand `orca:mobileWebShellEnabled`, default off and unreadable-is-off, in the same shape as the terminal autocomplete flag. `generationDirectoryPath` converts the store's `file://` uri to the absolute path the native shell view requires: both `MobileWebShellGeneration.load` implementations refuse anything without a leading slash, and `expo-file-system` only ever hands out uris. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): the hybrid shell session as a pure reducer Every decision the route makes, as `(session, event) -> (state, effects)`: the capability wall, the lazy sweep and cache read, the manifest check, the cached build-id hit that skips paging, the offline open with no compat check, and the three recovery rules the native shell view's contract states. Pure, so the rules are table tests rather than a simulator run. Two latches sit beside the state because both outlive it: `retriedOnce` spans the delete and refetch that returns to `checking`, and `remountedOnce` spans a `ready` replaced by a `ready` under a new session id. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): the hybrid shell route, dark behind a dev-only flag (OTA phase B, 4/4) Wires the four Phase B and A pieces together and adds no decision of its own. `h/[hostId]/web` sits inside the existing `HostProtocolGate` tree, so the native `desktop-too-old` wall still applies above it. With the flag off — every store build, since the only writer is a `__DEV__` Troubleshoot toggle — the route redirects to `h/[hostId]` and the screen is never constructed, so nothing is fetched, written or swept. The runner owns only the impure edges and checks an epoch before every dispatch, so an unmount, a host change or a retry abandons work in flight and aborts a download that would otherwise hold four of the host's read slots. The native view is keyed on the session id, which is what makes the reducer's remount a rebuilt WebView with every fence reinstalled. A census test pins who touches the flag: the route reads it, the developer row reads and writes it, and the key itself lives in one module. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): type the hoisted test doubles instead of asserting them The changed-code casting gate refuses `as` in new code, and these three were only widening an empty literal. An annotated `vi.hoisted` factory does the same job under a check. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): read a scheduled reconnect as an unreachable host, not a dial in progress Found on a simulator with the paired desktop stopped: the client never settles on `disconnected`. It dials, fails, schedules a retry, and cycles `connecting` -> `reconnecting` -> `connecting` with the delay growing to a minute. Mapping `reconnecting` to "still connecting" left a phone holding a verified cached generation on `checking` forever instead of opening it, which is the one case the offline rule exists for. `connecting` and `handshaking` are the first dial and still wait; everything else is unreachable. The mapping moves next to the reducer it feeds, because it is a decision and the runner is supposed to hold none. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): build the reachability stub instead of asserting it Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): move the shell session vocabulary into its own module Pure move, no behaviour: the states, events, effects and gates the reducer and its runner share now sit beside the reducer rather than inside it, so the transition rules have room to grow under the file's line budget. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): drop a shell effect result whose flow has been superseded Every restart of the flow bumps a number the effects of that run are stamped with, and a result echoes it back: a manifest read still in flight when the socket drops used to reject after the offline path had already opened the cached generation, replacing a displayed workspace with a download failure, and a status refetch arriving mid-check used to run the cache read and the download twice. The gates restart no longer clears the remount latch either; only the retry button does, so a reconnect cannot grant a second remount. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): census the flag across modules, not just src and app The native view tree was outside the scan, so a reader added there would have passed an assertion that reads as exhaustive. Proven by adding one to the shell view module: the census fails. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): let only the state that mounted the view hear the view A native batch reports two failures in a row, and the reducer applied both: document-load-failed started the delete-and-refetch, render-process-gone then made it terminal without a new flow, and the cache read the recovery had already asked for dragged the session back to checking behind a failure screen. A report arriving outside `ready` is from a view that is no longer on screen, so it changes nothing. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): say a host status could not be read instead of spinning on it A transient status.get failure settles the gate unreadable and nothing probes it again, so the route sat on "Checking host" for as long as anyone left it there and Try again re-read the same settled answer. It now says what happened and offers no retry, and a status that does become readable picks the flow back up on its own. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): restart the flow on the verdict that changed, not on every gates object A reconnect cycle rebuilds the gates several times a second with the same answer in them, and each one re-swept the staging tree and flipped an offline screen to a spinner and back. Only a changed verdict restarts now, which is also why the gates effect has to depend on the host id: two hosts whose gates read identically would otherwise leave the second session in `checking`. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): cover what only the shell runner can get wrong Three cancellations had no test: the epoch that stops a result reaching a session that is gone, the unmount cleanup that aborts the download, and the retry that does both before starting over. Each is now red under its own mutant. The download also re-checks the abort before it writes, since an abort landing between the fetch's last read and the commit would otherwise still put a generation on disk for a screen nobody is on. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): write the shell runner's refs after the commit, not during render React can replay or discard a render, so a handle written during one can run effects for a session that never existed. The client and the host cache key stop being refs at all; the effect handle is committed in an effect above every effect that dispatches. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep the hybrid shell flag unreadable outside a development build Development and release share a bundle id, and the iOS data container survives an install-over, so a flag a developer toggled on would follow the store build in and mount the shell on a deep link. The release read never reaches storage. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): drive the route's flag read as each build kind reads it The route test exercises the real preference read, so it has to say which build it is. A store build whose container kept a development toggle redirects. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): let a cache read that lands mid-dial wait for the compat check A connection still being made is not a host that cannot be reached. Opening the cached generation there skips the compat check the landing connection is what makes answerable, so only `unreachable` takes the offline path now. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): move the developer toggle only after its write lands The route reads the flag back from storage, so a switch that moved on the tap let the open button race the value that was being persisted. The switch and the button both stay put until the write settles, and a failed write keeps the previous position. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): say which build kind a test runs as without asserting on globalThis Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): undo a staged generation the abort reached before the commit The commit is the write staging cannot take back: it renames into the active slot and moves the host index. An abort landing while the bytes were being staged now removes the staged tree instead of activating it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): open the cached workspace when the link, not the bundle, cut a read short An RPC rejection can reach the reducer before the reachability change does, so the offline gate never fires and a phone holding a valid generation reads that the workspace could not be downloaded. A read that failed on the link now opens what is on disk, the same path offline takes; a verdict about the bundle, from the host or from the bytes, still fails. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): send a hybrid shell recovery through the same gate a start takes A view failure deleted the host cache and went straight back to the manifest check on whatever gates the ready session happened to be holding. Gates that arrive while a generation is on screen are stored without restarting, so after a reconnect whose status probe failed a ready session carried statusReadable false and an empty capability list, and the recovery's manifest check walled the host as bundle-unavailable: terminal, no retry, about a host that never answered. The gate is now one verdict both entries read, and recovery passes its delete through it, so an unreadable status lands on the status-unreadable message that re-arms when a readable gate arrives, and only a readable refusal still walls. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Orca
中文 · 日本語 · 한국어 · 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
Features
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
Codex
Grok
Cursor
GitHub Copilot
OpenCode
MiMo Code
Amp
OpenClaude
Antigravity
Pi
oh-my-pi
Hermes Agent
Devin
Goose
Auggie
Autohand Code
Charm
Cline
Codebuff
Command Code
Continue
Droid
Kilocode
Kimi
Kiro
Mistral Vibe
Qwen Code
Rovo Dev
+ any CLI agent
Install
Desktop — macOS, Windows, Linux
- Download from onOrca.dev
- Or grab a build directly: macOS Apple Silicon · macOS Intel · Windows (.exe) · Linux AppImage · All builds
- Running
orca serveon a headless Linux server? See the headless Linux server guide.
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.
- iOS: Download on the App Store or join TestFlight
- Android: Download APK 0.0.48 · Install guide
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. Group 8 may be full; if so, scan the Group 9 QR code instead.
-
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.
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.












