* refactor(orchestration): resolve a session caller at the dispatch entry and bind it by actor WIP: entry resolver on both dispatchers, caller identity through run scope, actor-keyed Run binding and unbind, actor writes on bind/create/assign, and actor-aware mail ownership exclusions. * test(orchestration): pin actor-keyed Run binding, stale-actor precedence and actor mail ownership Keeps the non-session dispatch path synchronous so terminal and session-tab streams reach their handler without an extra async hop. * feat(orchestration): resolve session callers before params parse and pin every verb on both routes A session caller need not name itself in a param that requires a caller: the entry binds the declared caller to the session before the schema runs. Session refusal codes pass through the RPC error map, and DB row reads added here carry their SAFETY rationale. * test(orchestration): pin the SSH check's pane through the caller-identity lookup * test(orchestration): pin a Run-less session's direct check and receipt binding without a caller param Drops the actor clause from self-dispatch detection: a creator and assignee can only share an actor when they already share a handle or pane. * test(orchestration): pin a session's own Run for plain and group sends and the assignee-only mail sweep * test(orchestration): name the party-naming field population for its role * fix(orchestration): clear a worker actor an older binary's unbind leaves, and refuse a worker without its identity An older binary unbinds a structured worker's Run by clearing handle and pane, which leaves the actor looking like a handle-less chat binding. The every-open repair clears that shape for actors recorded as structured workers only, and the resolver refuses a worker session whose worker identity is gone, so this binary never writes the shape itself. * refactor(orchestration): read a Run's coordinator actor through its generation, and give a party's addresses one owner The coordinator actor now counts only at the consumer generation it was written at, so a Run binding matches a session by that rule alone. It replaces two mechanisms for the same fact: the rule that an actor beside a handle it did not bind with never matches, and the open-time repair that cleared a structured worker's actor an older binary's unbind left. Every write of an older binary that rebinds or unbinds bumps the generation, so both shapes stop counting by themselves, including a chat's actor after a rebind then an unbind, which neither old mechanism caught. createRun and the same-coordinator actor correction write the generation in the statement that writes the actor. The resolver still refuses a structured worker whose worker identity is gone; its predicate moves next to the worker identity lookup. addressSpellingsOf is the one owner of the addresses a party is reachable at (a structured worker's handle and session actor). createRun, bindRun, the coordinator unbind and the declared-caller check use it instead of hand-built sets, and each has a test at both of a worker's addresses. * fix(orchestration): say a released session is not running, and scope the pane-key credential claim to requests without a session A released lease is evicted, not ended: a user turn resumes the session, so the refusal now says it is not running right now instead of that it has ended. The worker pane-key comment claimed the random leaf is what stops anyone who learns a session id from acting as the worker. On the same-host socket route the session id now names the worker with no token by design; the pane key still matters where a request names no session (a PTY agent's, or the paired-client route, which refuses session ids). * test(orchestration): pin the same-coordinator actor correction's generation write on a row an older binary wrote * refactor(orchestration): resolve session callers by the bare Orca session id, typed apart from its address Carries the Orca session id rename into caller resolution, Run binding and Dispatch creation. The caller identity holds the bare `orcaSessionId`; the `session:<id>` spelling is derived by formatOrcaSessionAddress wherever mail needs it. `OrcaSessionId` and `OrcaSessionAddress` are distinct branded strings. Only isOrcaSessionId and parseOrcaSessionAddress produce an id, and only formatOrcaSessionAddress produces an address, so comparing the two is a type error. The Orca session id columns on the row types carry the id type. Every reader that compares a stored id with a mail address now compares like with like: the active-Dispatch ownership check formats the stored id (orcaSessionAddressSql), and the stray-mail sweep, the creator nesting lookup and the recorded-worker check bind a parsed or typed bare id. The Run-mailbox ownership check keeps its existing handle comparison beside the session one. * fix(orchestration): accept a worker's ask to every address its coordinator is reachable at A worker's preamble names its coordinator as `session:<id>` when the coordinator is a structured session, but ask only accepted `run:<id>` or the coordinator's terminal handle. A chat coordinator has no handle, and a coordinating structured worker has two addresses, so ask --to the session address was refused as dispatch_run_mismatch. The check now takes the Run's current coordinator addresses from addressSpellingsOf(runCoordinatorKey(run)). * refactor(orchestration): require every caller-identity entry point to be handed the resolved session The resolved session parameter was optional on resolveRunScope, resolveOrchestrationCaller, orchestrationCallerIdentity, resolveDispatchCreator and resolveDispatchCallerWorktreeId, so a method that forgot to pass it would compile and silently treat a chat's session address as a terminal handle. It is now required and typed `OrchestrationSessionCaller | undefined`, so leaving it out is a type error. Every call site already passed it; no behavior changes. * fix(orchestration): deliver mail sent to a session address to the mailbox that session reads A send to session:<id> was resolved like a terminal handle: no live pane, so a chat's current Run was missed (two Runs read as ambiguous), a Run-less chat was refused though it reads its direct mailbox, and a structured worker's session address never reached its Dispatch. Resolve a worker's session address as its handle, a chat's by its bound Run, and fall back to the chat's durable direct mailbox while it runs on this host. * refactor(orchestration): resolve every party through one resolver with one mailbox address A structured worker is reachable at its handle and at its session address, and a chat only at its session address. Callers and targets were each compared or resolved at their own site, some against one spelling and some against every spelling, and the sites that did neither refused or misrouted. Add orchestration-party: resolveOrchestrationParty (and resolveOrcaSessionParty for a bare id) is now the only place an address becomes a party. The session caller, the declared-caller check, ask's target and inbox's filter all resolve through it, so caller and recipient resolution cannot disagree, including on a recorded worker whose identity this host lost. Every session-to-party step passes through canonicalOrcaSessionId, the seam later lineage canonicalization plugs into. mailboxAddressOf replaces addressSpellingsOf: a party has one mailbox address (a worker's handle today), and Run binding remembers and reroutes only that. A request with no session id that declares a session address as its caller now gets the party it names: a worker's handle, as if it had named it, or a session_caller_chat_not_declarable refusal for a chat, which is identified only by the session id its own environment sends. * fix(orchestration): route dispatch and mail targets through the party they name dispatch --to a worker's session address stored that address as the assignee handle, so the worker, which reads by its handle, never saw the Dispatch. The assignee now resolves through the party resolver: a worker's either spelling assigns its handle and records its Orca session id. A chat cannot receive a dispatch yet, so dispatch --to a chat is refused with session_chat_not_dispatchable before any row is written. Recipient routing resolves the party once and, for any session-backed party, finds its Run by its durable binding rather than by a live pane. A structured worker that coordinates a child Run while assigned in its parent got mail at its parent Dispatch mailbox whenever its session was evicted, which it never reads while bound to the child Run. Terminal handles keep the same live-pane lookup. * refactor(orchestration): delete the SQL that matched a worker's second spelling With every caller and target resolved to one mailbox address, no writer stores mail under a structured worker's session address: sends resolve it to the handle, a declared session caller is rewritten to the handle or refused, replies answer a stored canonical sender, and questions, answers, escalations, federation and legacy mail write run:, dispatch: or handle addresses (legacy rows are legacy_direct, which these queries never read). The branches that matched that spelling were unreachable and are removed: - the session-address OR in activeDispatchOwnsAddressSql, back to the assignee handle alone; - the session branches in routeForeignDirectMessagesToOwnedMailboxes and findActiveDispatchForDirectMessageOwner, which return to the base-branch form. Tests that inserted such rows directly now pin the behaviour through the verbs: both spellings of a worker land in the one mailbox it reads. * test(orchestration): pin one canonical address per party across every caller and target - every target param in ORCHESTRATION_TARGET_PARAM, for a worker's handle and session address and a chat's session address: send, ask, dispatch (a chat refused with no row) and inbox; - a declared session-address caller on a request with no session id: a worker acts as its handle, a chat is refused; - a worker that coordinates a child Run while assigned in its parent gets mail in the child Run with no live pane; - no mail writer stores to_handle or from_handle as a worker's session address; - the caller a session id resolves to and the recipient its address resolves to agree in every party state, and every session-to-party step goes through canonicalOrcaSessionId. * refactor(orchestration): drop the session caller's chat-to-terminal-view handoff remnants The structured-chat terminal handoff is gone from the base branch: a session is owned natively only. - the lease rule no longer speaks of either owner or a handoff keeping identity; - an in-progress owner change (new-owner-proving, recovering, manual-recovery, which the base branch still produces) is refused as changing owners, not as switching between chat and terminal view; - the native to terminal-view to native identity test is deleted, and the terminal-evidence test no longer describes that evidence as a terminal view's. * fix(orchestration): read a declared caller param by typed access, not Reflect.get
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
Muse
ZCode
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.50 · 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 10.
-
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.










