Agent-readable docs
Orca Documentation
Reference for the Orca Electron IDE and `orca` CLI: worktree orchestration, multi-agent terminals, runtime RPC, browser/computer automation, SSH remotes, and git-provider integrations. For builders automating Orca or contributing to stablyai/orca.
Pages
- OverviewWhat Orca exposes (desktop app, runtime RPC, bundled CLI), primary entry points, BYOC agent model, and the shortest path from install to a running worktree with an agent terminal.
- InstallationPlatform packages (onOrca.dev, Homebrew, AUR), Node 24/pnpm prerequisites for source builds, CLI registration, and mobile companion app install links.
- QuickstartFirst successful session: open Orca, add a repo, create a worktree, launch a CLI agent terminal, and verify with `orca status` and `orca worktree current`.
- Worktrees and reposOrca-managed git worktrees, repo registration, lineage/parent links, sparse checkout, workspace statuses, and CLI/RPC create/list/remove semantics.
- Terminals and agentsPTY lifecycle, `TuiAgent` catalog and detection order, pane keys, agent status hooks, terminal env injection (`ORCA_*`), and when to use terminal CLI vs orchestration messaging.
- Runtime environmentsHeadless `orca serve`, runtime metadata/RPC transport, pairing codes, saved environments, and targeting a remote runtime from the CLI with `--environment` or `--pairing-code`.
- Repository hooks`orca.yaml` setup/archive scripts, issue-command defaults, hook command-source policies, trust prompts, and CLI `--run-hooks` behavior on create/remove.
- CLI scripting workflowEnd-to-end agent automation with `orca open`, `orca status`, repo/worktree discovery, terminal read/send/wait, file open helpers, and worktree comment updates at checkpoints.
- Agent orchestrationMulti-agent coordination: messaging (`send`/`check`/`reply`), task DAGs, dispatch/inject, coordinator `run`, decision gates, and group addresses—requires experimental feature and running runtime.
- SSH remote worktreesSSH targets, relay grace periods, remote repo registration, remote PTY leases, agent-hook forwarding over SSH, and CLI selector constraints on paired runtimes.
- Scheduled automationsCreate and run cron/RRULE automations via CLI: triggers, provider agents, workspace modes, session reuse, run history, and SSH execution targets.
- Browser automationAutomate Orca's embedded browser: accessibility snapshots, interaction commands, tabs/profiles, capture/intercept/storage, and `orca exec` passthrough to agent-browser.
Complete Markdown
# Orca Documentation
> Reference for the Orca Electron IDE and `orca` CLI: worktree orchestration, multi-agent terminals, runtime RPC, browser/computer automation, SSH remotes, and git-provider integrations. For builders automating Orca or contributing to stablyai/orca.
## Context Links
- [Agent index](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/llms.txt)
- [Human interactive docs](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c)
- [GitHub repository](https://github.com/stablyai/orca)
## Repository Metadata
- Repository: stablyai/orca
- Generated: 2026-06-01T20:12:52.786Z
- Updated: 2026-06-01T20:13:17.164Z
- Runtime: Grok CLI
- Format: Documentation
- Pages: 22
## Page Index
- 01. [Overview](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/01-overview.md) - What Orca exposes (desktop app, runtime RPC, bundled CLI), primary entry points, BYOC agent model, and the shortest path from install to a running worktree with an agent terminal.
- 02. [Installation](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/02-installation.md) - Platform packages (onOrca.dev, Homebrew, AUR), Node 24/pnpm prerequisites for source builds, CLI registration, and mobile companion app install links.
- 03. [Quickstart](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/03-quickstart.md) - First successful session: open Orca, add a repo, create a worktree, launch a CLI agent terminal, and verify with `orca status` and `orca worktree current`.
- 04. [Worktrees and repos](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/04-worktrees-and-repos.md) - Orca-managed git worktrees, repo registration, lineage/parent links, sparse checkout, workspace statuses, and CLI/RPC create/list/remove semantics.
- 05. [Terminals and agents](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/05-terminals-and-agents.md) - PTY lifecycle, `TuiAgent` catalog and detection order, pane keys, agent status hooks, terminal env injection (`ORCA_*`), and when to use terminal CLI vs orchestration messaging.
- 06. [Runtime environments](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/06-runtime-environments.md) - Headless `orca serve`, runtime metadata/RPC transport, pairing codes, saved environments, and targeting a remote runtime from the CLI with `--environment` or `--pairing-code`.
- 07. [Repository hooks](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/07-repository-hooks.md) - `orca.yaml` setup/archive scripts, issue-command defaults, hook command-source policies, trust prompts, and CLI `--run-hooks` behavior on create/remove.
- 08. [CLI scripting workflow](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/08-cli-scripting-workflow.md) - End-to-end agent automation with `orca open`, `orca status`, repo/worktree discovery, terminal read/send/wait, file open helpers, and worktree comment updates at checkpoints.
- 09. [Agent orchestration](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/09-agent-orchestration.md) - Multi-agent coordination: messaging (`send`/`check`/`reply`), task DAGs, dispatch/inject, coordinator `run`, decision gates, and group addresses—requires experimental feature and running runtime.
- 10. [SSH remote worktrees](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/10-ssh-remote-worktrees.md) - SSH targets, relay grace periods, remote repo registration, remote PTY leases, agent-hook forwarding over SSH, and CLI selector constraints on paired runtimes.
- 11. [Scheduled automations](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/11-scheduled-automations.md) - Create and run cron/RRULE automations via CLI: triggers, provider agents, workspace modes, session reuse, run history, and SSH execution targets.
- 12. [Browser automation](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/12-browser-automation.md) - Automate Orca's embedded browser: accessibility snapshots, interaction commands, tabs/profiles, capture/intercept/storage, and `orca exec` passthrough to agent-browser.
- 13. [Source control integrations](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/13-source-control-integrations.md) - Built-in review flows for GitHub, GitLab, and Linear: PR/MR/issue linking on worktrees, hosted review creation, diff annotation, and provider-specific IPC surfaces.
- 14. [CLI core reference](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/14-cli-core-reference.md) - Flags and signatures for `open`, `serve`, `status`, `repo`, `worktree`, `terminal`, `file`, `environment`, and `agent hooks` command groups.
- 15. [CLI browser and computer reference](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/15-cli-browser-and-computer-reference.md) - Browser automation commands (navigation, interaction, cookies, storage, capture) and `computer` desktop accessibility commands with capability flags.
- 16. [Selectors and JSON output](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/16-selectors-and-json-output.md) - Global CLI flags (`--json`, `--help`, `--pairing-code`, `--environment`), worktree/repo selector grammar (`id:`, `path:`, `branch:`, `active`), JSON error shapes, and remote-runtime resolution rules.
- 17. [Settings reference](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/17-settings-reference.md) - `GlobalSettings` fields that control workspaces, terminals, agents, themes, notifications, and integration toggles—persisted by the main-process store.
- 18. [Develop and build](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/18-develop-and-build.md) - Local dev (`pnpm dev`), typecheck targets, native runtime rebuilds, electron-vite build graph, relay/computer native artifacts, and platform release builds.
- 19. [Testing](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/19-testing.md) - Unit tests (Vitest), Playwright Electron E2E, computer-use smoke scripts, and SSH-localhost E2E env flags documented in test helpers.
- 20. [Contributing](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/20-contributing.md) - Contribution workflow, cross-platform/SSH/agent constraints from AGENTS.md, PR expectations, and maintainer release cutting via GitHub Actions.
- 21. [Troubleshooting](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/21-troubleshooting.md) - Common failure modes: runtime not reachable, selector ambiguity on remote CLI, worktree delete preflight, terminal/orchestration timeouts, and platform-specific native dependency issues.
- 22. [Telemetry and privacy](https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/22-telemetry-and-privacy.md) - Consent gating, event schemas in `telemetry-events.ts`, dashboard-ready boundaries in repo docs, and what usage data Orca collects.
## Source File Index
- `.env.e2e`
- `.github/CONTRIBUTING.md`
- `.github/workflows/release-cut.yml`
- `AGENTS.md`
- `components.json`
- `config/electron-builder.config.cjs`
- `config/scripts/computer-use-smoke.mjs`
- `config/scripts/ensure-native-runtime.mjs`
- `config/scripts/install-dev-cli.mjs`
- `config/scripts/orca-dev`
- `config/scripts/run-electron-vite-build.mjs`
- `config/scripts/run-electron-vite-dev.mjs`
- `config/vitest.config.ts`
- `docs/automations-navigation-stack.md`
- `docs/reference/telemetry-availability.md`
- `docs/STYLEGUIDE.md`
- `docs/worktree-delete-preflight.md`
- `orca.yaml`
- `package.json`
- `README.md`
- `skills/computer-use/SKILL.md`
- `skills/orca-cli/SKILL.md`
- `skills/orchestration/SKILL.md`
- `src/cli/args.ts`
- `src/cli/dispatch.ts`
- `src/cli/format.ts`
- `src/cli/handlers/automations.ts`
- `src/cli/handlers/browser-capture.ts`
- `src/cli/handlers/browser-interact.ts`
- `src/cli/handlers/browser-tab.ts`
- `src/cli/handlers/computer.ts`
- `src/cli/handlers/core.ts`
- `src/cli/handlers/orchestration.ts`
- `src/cli/handlers/worktree.ts`
- `src/cli/help.ts`
- `src/cli/index.test.ts`
- `src/cli/index.ts`
- `src/cli/runtime-client.ts`
- `src/cli/selectors.ts`
- `src/cli/specs/agent-hooks.ts`
- `src/cli/specs/automations.ts`
- `src/cli/specs/browser-advanced.ts`
- `src/cli/specs/browser-basic.ts`
- `src/cli/specs/computer.ts`
- `src/cli/specs/core.ts`
- `src/cli/specs/environment.ts`
- `src/cli/specs/file.ts`
- `src/cli/specs/orchestration.ts`
- `src/main/agent-hooks/server.ts`
- `src/main/automations/service.ts`
- `src/main/browser/agent-browser-bridge.ts`
- `src/main/browser/browser-manager.ts`
- `src/main/git/worktree.ts`
- `src/main/index.ts`
- `src/main/ipc/pty.ts`
- `src/main/ipc/runtime-environments.ts`
- `src/main/persistence/index.ts`
- `src/main/runtime/orca-runtime.ts`
- `src/main/runtime/orchestration/db.ts`
- `src/main/runtime/orchestration/formatter.ts`
- `src/main/runtime/runtime-metadata.ts`
- `src/main/runtime/runtime-rpc.ts`
- `src/main/runtime/worktree-teardown.ts`
- `src/main/telemetry/client.ts`
- `src/main/telemetry/consent.ts`
- `src/main/text-generation/pull-request-context.ts`
- `src/preload/api-types.ts`
- `src/preload/gitlab.ts`
- `src/preload/index.ts`
- `src/preload/runtime-environment-subscriptions.ts`
- `src/renderer/src/assets/main.css`
- `src/renderer/src/components/settings/RepositoryHooksSection.tsx`
- `src/renderer/src/lib/ensure-hooks-confirmed.ts`
- `src/renderer/src/main.tsx`
- `src/shared/agent-detection.ts`
- `src/shared/automations-types.ts`
- `src/shared/hosted-review.ts`
- `src/shared/runtime-bootstrap.ts`
- `src/shared/runtime-rpc-envelope.ts`
- `src/shared/setup-runner-command.ts`
- `src/shared/ssh-types.ts`
- `src/shared/telemetry-events.ts`
- `src/shared/tui-agent-config.ts`
- `src/shared/tui-agent-selection.ts`
- `src/shared/tui-agent-startup.ts`
- `src/shared/types.ts`
- `src/shared/work-items.ts`
- `tests/e2e/helpers/orca-app.ts`
- `tests/e2e/ssh-localhost.spec.ts`
- `tests/playwright.config.ts`
- `vite.web.config.ts`
---
## 01. Overview
> What Orca exposes (desktop app, runtime RPC, bundled CLI), primary entry points, BYOC agent model, and the shortest path from install to a running worktree with an agent terminal.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/01-overview.md
- Generated: 2026-06-01T20:05:13.356Z
### Source Files
- `README.md`
- `package.json`
- `src/main/index.ts`
- `src/preload/index.ts`
- `src/renderer/src/main.tsx`
- `src/cli/index.ts`
- `AGENTS.md`
---
title: "Overview"
description: "What Orca exposes (desktop app, runtime RPC, bundled CLI), primary entry points, BYOC agent model, and the shortest path from install to a running worktree with an agent terminal."
---
Orca ships as an Electron desktop application (`package.json` → `main: ./out/main/index.js`), a long-lived **runtime** (`OrcaRuntimeService`) exposed over **runtime RPC** (`OrcaRuntimeRpcServer`), and a bundled **`orca` CLI** (`bin.orca` → `./out/cli/index.js`). The renderer talks to main through a preload `contextBridge` (`window.electron`, `window.api`); automation and scripting talk to the same runtime state through RPC using bootstrap metadata in `orca-runtime.json` under the app user-data directory.
## Three surfaces
| Surface | Role | Primary entry |
| --- | --- | --- |
| **Desktop app** | UI for repos, worktrees, terminals, review, browser, settings | `src/main/index.ts` → `createMainWindow()` / `src/renderer/src/main.tsx` |
| **Runtime + RPC** | Authoritative graph of worktrees, tabs, PTYs, git, orchestration DB | `OrcaRuntimeService` + `OrcaRuntimeRpcServer` started from `src/main/index.ts` |
| **CLI** | Scripting, headless serve, remote pairing | `src/cli/index.ts` → `RuntimeClient` + `COMMAND_SPECS` |
Desktop and headless modes share one runtime implementation. `orca serve` starts RPC without opening a window; the packaged app starts RPC in parallel with the first `BrowserWindow`.
```mermaid
flowchart TB
subgraph desktop["Desktop (Electron)"]
R["renderer — React App"]
P["preload — contextBridge api"]
M["main — IPC + services"]
R --> P --> M
end
subgraph runtime["Runtime layer"]
ORS["OrcaRuntimeService"]
RPC["OrcaRuntimeRpcServer"]
M --> ORS
RPC --> ORS
end
subgraph clients["Clients"]
CLI["orca CLI — RuntimeClient"]
MOB["mobile / web pairing"]
end
CLI --> RPC
MOB --> RPC
MD["orca-runtime.json metadata"] --> CLI
```
## Build and process layout
`electron-vite` compiles three targets from `electron.vite.config.ts`:
- **main** — `src/main/index.ts` plus sidecars (`daemon-entry`, `computer-sidecar`, `stt-worker`, agent-hook controls)
- **preload** — `src/preload/index.ts` (IPC bridge)
- **renderer** — `src/renderer/src/main.tsx` (React 19 + Vite)
The CLI is built separately (`pnpm build:cli` → `out/cli/`). Release artifacts bundle the desktop app and register the `orca` binary; local development can use `orca-dev` (`package.json` → `bin.orca-dev`).
<Note>
Engine requirement for source builds: **Node 24** (`package.json` → `engines.node`). Package manager: **pnpm 10**.
</Note>
## Runtime RPC and discovery
When the runtime starts, `OrcaRuntimeRpcServer` writes **`orca-runtime.json`** (see `getRuntimeMetadataPath` in `src/shared/runtime-bootstrap.ts`) containing:
- `runtimeId`, `pid`, `authToken`
- `transports[]` — typically Unix socket or Windows named pipe **and** WebSocket (default port **6768**, overridable in dev/E2E/serve)
The CLI `RuntimeClient` reads that file, authenticates, and dispatches RPC methods implemented under `src/main/runtime/rpc/`. Global CLI flags (`src/cli/args.ts`): `--json`, `--help`, `--pairing-code`, `--environment`.
| CLI area | Examples | Notes |
| --- | --- | --- |
| Lifecycle | `open`, `serve`, `status` | `serve` runs foreground headless runtime; `open` launches app and waits for reachability |
| Repos / worktrees | `repo add`, `worktree create`, `worktree current` | Worktree selectors: `id:`, `path:`, `branch:`, `active` |
| Terminals | `terminal list`, `read`, `send`, `wait`, `create` | Targets live PTYs in the runtime graph |
| Remote | `--environment`, `--pairing-code` | Resolved before RPC except for `environment`, `serve`, `agent` command trees |
`orca status --json` reports `app.running`, `runtime.reachable`, and `graph.state` (`CliStatusResult` in `src/shared/runtime-types.ts`).
## BYOC agent model
Orca does **not** require an Orca account or hosted model API. The README positions the product as **bring your own** CLI agent subscription (Claude Code, Codex, Grok, etc.). Implementation details:
1. **Catalog** — `TuiAgent` union in `src/shared/types.ts` (claude, codex, grok, cursor, …). Each id has launch/detect metadata in `TUI_AGENT_CONFIG` (`src/shared/tui-agent-config.ts`).
2. **Detection** — `detectInstalledAgents()` probes `TUI_AGENT_CONFIG[*].detectCmd` on PATH (`src/main/ipc/preflight.ts`). Auto-pick order when no default is set: `TUI_AGENT_AUTO_PICK_ORDER` in `src/shared/tui-agent-selection.ts`.
3. **Overrides** — `GlobalSettings.agentCmdOverrides` replaces the launch command per agent id; workspace creation can use `agentId: 'custom'` with a user command template.
4. **Terminal context** — Spawned PTYs receive `ORCA_PANE_KEY`, `ORCA_TAB_ID`, and `ORCA_WORKTREE_ID` so agent hooks and scripts can attribute activity to a pane/worktree without inferring from cwd alone.
Any CLI agent on PATH can be used; the catalog is for detection, trust presets, prompt injection, and UI—not an allowlist.
## Shortest path: install → worktree → agent terminal
<Steps>
<Step title="Install Orca">
Download from [onOrca.dev](https://onOrca.dev) or install via Homebrew (`brew install --cask stablyai/orca/orca`) / AUR packages listed in the README. Ensure your chosen agent CLI is installed and authenticated with its vendor (e.g. `claude`, `codex`, `grok`).
</Step>
<Step title="Open Orca and register a repo">
Launch the desktop app. Add a repository (folder with a git root). Orca persists repos and worktree metadata in its store (`Store` in main process).
</Step>
<Step title="Create a worktree workspace">
Create a new worktree from the UI (or `orca worktree create --repo <selector> --name <name> --json`). Orca creates an isolated git worktree and wires it into the workspace graph. Optional `orca.yaml` setup hooks may run in a first terminal depending on repo policy.
</Step>
<Step title="Start an agent terminal">
Open a terminal tab in that worktree and pick an agent (or accept auto-detected default). Orca spawns the agent TUI via `buildAgentStartupPlan` / PTY spawn with agent-specific env and trust presets.
</Step>
<Step title="Verify from the shell">
From the worktree directory:
```bash
orca status --json
orca worktree current --json
```
Expect `runtime.reachable: true` and `worktree current` resolving to a `path:` selector for the enclosing worktree.
</Step>
</Steps>
<Tip>
For automation without the UI, use `orca worktree create` then `orca terminal create --command "<agent-cmd>"` in the new worktree, or drive an existing pane with `terminal read` / `send` / `wait`. Agent-to-agent messaging uses a separate orchestration surface (experimental)—see `/agent-orchestration`.
</Tip>
## Desktop IPC vs CLI RPC
| Concern | Desktop path | CLI / headless path |
| --- | --- | --- |
| UI state, settings, file picks | `window.api` IPC (`src/preload/index.ts`) | N/A (use RPC + JSON flags) |
| Worktrees, terminals, git | IPC handlers → `OrcaRuntimeService` | `RuntimeClient` RPC |
| PTY I/O | Renderer xterm + main/daemon PTY provider | `terminal read` / `send` / streaming RPC |
| Headless server | — | `orca serve` registers headless PTY runtime, publishes empty graph, prints pairing endpoint |
In desktop mode, a **daemon PTY provider** (`initDaemonPtyProvider`) can route spawns after first window startup; `orca serve` skips the desktop daemon and uses `registerHeadlessPtyRuntime` instead.
## What else lives in the repo
High-level capabilities referenced elsewhere in this wiki (not separate product binaries):
- **SSH remote worktrees** — relay/SSH PTY path with remote agent detection
- **Source control** — GitHub, GitLab, Linear review flows via main-process clients and preload APIs
- **Browser automation** — embedded browser + `agent-browser` bridge; CLI browser command groups
- **Scheduled automations** — `AutomationService` + CLI `automation` specs
- **Computer use** — macOS sidecar (`computer-sidecar` entry) for accessibility automation
Cross-cutting constraints for contributors: `AGENTS.md` (cross-platform shortcuts, SSH-aware changes, Git provider neutrality, worktree-safe editing).
## Related pages
<CardGroup>
<Card title="Installation" href="/installation">
Platform packages, Node/pnpm prerequisites, CLI registration, mobile companion links.
</Card>
<Card title="Quickstart" href="/quickstart">
First session walkthrough with UI and verification commands.
</Card>
<Card title="Worktrees and repos" href="/worktrees-and-repos">
Registration, lineage, selectors, and CLI create/list/remove semantics.
</Card>
<Card title="Terminals and agents" href="/terminals-and-agents">
PTY lifecycle, TuiAgent catalog, ORCA_* env, pane keys, agent hooks.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
Headless serve, pairing codes, remote `--environment` targeting.
</Card>
<Card title="CLI scripting workflow" href="/cli-scripting-workflow">
End-to-end automation with open, status, terminal I/O, and checkpoints.
</Card>
</CardGroup>
---
## 02. Installation
> Platform packages (onOrca.dev, Homebrew, AUR), Node 24/pnpm prerequisites for source builds, CLI registration, and mobile companion app install links.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/02-installation.md
- Generated: 2026-06-01T20:04:55.667Z
### Source Files
- `README.md`
- `package.json`
- `config/electron-builder.config.cjs`
- `config/scripts/install-dev-cli.mjs`
- `config/scripts/orca-dev`
- `.github/CONTRIBUTING.md`
---
title: "Installation"
description: "Platform packages (onOrca.dev, Homebrew, AUR), Node 24/pnpm prerequisites for source builds, CLI registration, and mobile companion app install links."
---
Orca ships as a packaged Electron desktop app (`com.stablyai.orca`) with a bundled `orca` shell command that runs the compiled CLI via `ELECTRON_RUN_AS_NODE` against the app’s own Electron binary—no separate Node/npm CLI install for end users. Desktop installers come from [onOrca.dev](https://onOrca.dev), [GitHub Releases](https://github.com/stablyai/orca/releases/latest), Homebrew, or the Arch AUR; contributors build from source with **Node 24** and **pnpm 10.24.0**.
## Desktop packages
| Channel | Command or URL | Artifact / notes |
| --- | --- | --- |
| Website | [onOrca.dev](https://onOrca.dev) | Primary download for macOS, Windows, and Linux |
| GitHub Releases | `https://github.com/stablyai/orca/releases/latest` | Same desktop builds as the site |
| Homebrew (macOS) | `brew install --cask stablyai/orca/orca` | Tap `stablyai/homebrew-orca`; cask installs `Orca.app` |
| AUR (Arch) | `yay -S stably-orca-bin` | Precompiled binary package |
| AUR (Arch, source) | `yay -S stably-orca-git` | Builds from GitHub source |
Stable desktop releases publish artifacts including `orca-macos-arm64.dmg`, `orca-macos-x64.dmg`, `Orca-<version>-arm64-mac.zip`, `orca-windows-setup.exe`, `orca-linux.AppImage`, and `orca-ide_<version>_amd64.deb`. The Homebrew cask tracks GA desktop tags only (not `mobile-v*` or `-rc.*` releases) and sets `auto_updates true` so in-app `electron-updater` is not competing with `brew upgrade`.
<Tabs>
<Tab title="macOS">
```bash
brew install --cask stablyai/orca/orca
```
The cask requires macOS Big Sur or newer, installs `Orca.app`, and documents zap paths for `~/.orca` and standard Electron user-data locations.
</Tab>
<Tab title="Linux">
Download **AppImage** or **`.deb`** from Releases. Packaged Linux uses executable name `orca-ide` and Debian package name `orca-ide` so the install does not collide with Ubuntu’s GNOME **Orca** screen reader (`/usr/bin/orca`).
The `.deb` declares runtime dependencies: `python3`, `python3-gi`, `gir1.2-atspi-2.0`, `at-spi2-core`, `xdotool`, `xclip` (computer-use support).
</Tab>
<Tab title="Windows">
Download `orca-windows-setup.exe` from Releases. NSIS creates a desktop shortcut; the packaged app executable is `Orca.exe`.
</Tab>
</Tabs>
<Note>
Bring your own CLI agent subscriptions (Claude Code, Codex, Grok, etc.). Orca does not require an Orca account to run the desktop app.
</Note>
## Build from source
Source development targets the repo toolchain pinned in `package.json`:
| Requirement | Value |
| --- | --- |
| Node.js | `24` (`engines.node`) |
| Package manager | `pnpm@10.24.0` (`packageManager`) |
<Steps>
<Step title="Install dependencies">
```bash
pnpm install
```
`postinstall` runs `config/scripts/rebuild-native-deps.mjs` to rebuild native modules (`better-sqlite3`, `node-pty`, etc.) for Electron. On Windows, optional `cpu-features` is skipped when the MSVC toolchain is absent; `ssh2` falls back to pure JS.
</Step>
<Step title="Run the app">
```bash
pnpm dev
```
`pnpm dev` ensures the Electron native runtime, then starts electron-vite dev.
</Step>
<Step title="Optional: compile the CLI">
```bash
pnpm run build:cli
```
Compiles `src/cli` to `out/cli/index.js` and runs `config/scripts/install-dev-cli.mjs`, which symlinks `orca-dev` into `/usr/local/bin` on macOS/Linux when permissions allow.
</Step>
</Steps>
Full build graphs (`pnpm build`, platform release targets, native relay/computer artifacts) live on the develop-and-build page. CI and release workflows read Node version from `package.json` via `node-version-file`.
<Warning>
`pnpm install` on Windows may require Visual C++ build tools for some native modules. If install fails on `cpu-features`, the repo’s rebuild script already documents the Windows skip path; other node-gyp failures usually mean installing the desktop C++ workload.
</Warning>
## CLI registration (`orca` on PATH)
Packaged Orca exposes a **Shell command** toggle under **Settings → Orca CLI**. Registration is implemented in `CliInstaller` and uses platform-specific launchers shipped under `resources/{darwin,linux,win32}/bin/`, copied into the app bundle as `resources/bin/orca` (or `orca.cmd` on Windows).
```text
Terminal: orca …
│
▼
Registered command (symlink or .cmd wrapper)
│
▼
resources/bin/orca → Electron binary + app.asar.unpacked/out/cli/index.js
│
└── ELECTRON_RUN_AS_NODE=1
```
| Platform | Default command path | Method |
| --- | --- | --- |
| macOS | `/usr/local/bin/orca` | Symlink to bundled launcher; may prompt for admin via `osascript` if `/usr/local/bin` is not writable |
| Linux | `~/.local/bin/orca` | Symlink (user-scoped; avoids needing a global privileged bin dir) |
| Windows | `%LOCALAPPDATA%\Programs\Orca\bin\orca.cmd` | Wrapper forwarder; installer adds that directory to the **user** `Path` |
The launcher clears `NODE_OPTIONS` / `NODE_REPL_EXTERNAL_MODULE` (stashing them in `ORCA_NODE_*`) and invokes:
`ELECTRON_RUN_AS_NODE=1 <Electron> <app.asar.unpacked/out/cli/index.js> "$@"`
<ParamField body="ORCA_CLI_INSTALL_PATH" type="string">
Override the install target path for CLI registration (used by tests and advanced setups).
</ParamField>
Install states returned to the UI include `installed`, `not_installed`, `stale`, `conflict`, and `unsupported`. Orca refuses to replace a non-Orca binary at the command path (`conflict`).
<Tip>
On Windows with WSL, **Settings → Orca CLI** also offers **WSL shell command** registration so `orca` works inside your default WSL distro, separate from the host `orca.cmd` registration.
</Tip>
### Development CLI (`orca-dev`)
For local repo work, use the dev wrapper after `pnpm run build:cli`:
| Item | Detail |
| --- | --- |
| Binary | `orca-dev` (`package.json` `bin.orca-dev` → `config/scripts/orca-dev`) |
| Entry | `out/cli/index.js` (must exist; build first) |
| User data | Separate from production: `orca-dev` under Application Support / XDG config / `%APPDATA%` |
| Override | `ORCA_DEV_USER_DATA_PATH` |
| Global symlink | `install-dev-cli.mjs` → `/usr/local/bin/orca-dev` (or manual `sudo ln -s`) |
`orca-dev` sets `ORCA_APP_EXECUTABLE` to the repo’s Electron binary when available so CLI commands that open the app target the dev build.
### Verify CLI registration
```bash
orca --help
orca status
```
If the command is missing, open **Settings → Orca CLI**, enable **Shell command**, and confirm the status line shows the registered path. On macOS permission errors, follow the sudo hint from `install-dev-cli.mjs` or approve the elevated symlink prompt.
## Mobile companion app
The mobile app pairs to a running Orca desktop runtime; enable **Settings → Mobile** (`experimentalMobile`) on desktop for QR pairing.
| Platform | Install link |
| --- | --- |
| iOS | [App Store — Orca IDE](https://apps.apple.com/us/app/orca-ide/id6766130217) |
| Android | [GitHub release `mobile-v0.0.9`](https://github.com/stablyai/orca/releases/tag/mobile-v0.0.9) (APK on the mobile release tag; Google Play noted as coming in settings copy) |
Mobile builds use their own release tags (`mobile-v*`), separate from desktop `v*` tags. Protocol compatibility is versioned in `src/shared/protocol-version.ts`; outdated mobile clients may be blocked with `mobile-too-old` when the desktop runtime requires a newer build.
## What gets installed where
```text
Packaged desktop (example paths)
├── macOS: Orca.app (CLI launcher → Contents/Resources/bin/orca)
├── Linux: orca-ide binary + resources/bin/orca
├── Windows: Orca.exe + resources/bin/orca.cmd
└── User data: ~/.orca, Electron app userData (worktrees, settings)
Dev checkout
├── node_modules/ + out/cli/index.js after build:cli
└── orca-dev userData (isolated from production Orca)
```
## Related pages
<CardGroup>
<Card title="Quickstart" href="/quickstart">
First session after install: repo, worktree, agent terminal, and `orca status`.
</Card>
<Card title="Develop and build" href="/develop-and-build">
`pnpm dev`, typecheck, native rebuilds, and platform release builds.
</Card>
<Card title="CLI core reference" href="/cli-core-reference">
`open`, `status`, `worktree`, `terminal`, and other command groups after PATH registration.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
Headless `orca serve`, pairing codes, and remote `--environment` targeting.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Runtime reachability, CLI selector issues, and native dependency failures.
</Card>
</CardGroup>
---
## 03. Quickstart
> First successful session: open Orca, add a repo, create a worktree, launch a CLI agent terminal, and verify with `orca status` and `orca worktree current`.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/03-quickstart.md
- Generated: 2026-06-01T20:05:06.155Z
### Source Files
- `README.md`
- `src/cli/specs/core.ts`
- `src/cli/handlers/core.ts`
- `src/shared/tui-agent-startup.ts`
- `src/shared/tui-agent-selection.ts`
- `skills/orca-cli/SKILL.md`
---
title: "Quickstart"
description: "First successful session: open Orca, add a repo, create a worktree, launch a CLI agent terminal, and verify with `orca status` and `orca worktree current`."
---
Orca’s first-session path wires the Electron desktop app to a local **runtime RPC** (`status.get`, `repo.add`, `worktree.create`, terminal PTY spawn) and exposes the same control plane through the **`orca` CLI** (`package.json` registers `orca` → `out/cli/index.js`). A successful quickstart ends with `runtime.reachable: true` from `orca status` and a resolvable enclosing worktree from `orca worktree current` inside the new workspace directory.
## Prerequisites
| Requirement | Notes |
| --- | --- |
| Orca desktop app | Install from [onOrca.dev](https://onOrca.dev), GitHub Releases, Homebrew (`brew install --cask stablyai/orca/orca`), or AUR packages listed in the README. |
| `orca` on `PATH` | Shipped with the app; verify with `command -v orca`. |
| A git repository | Orca registers repos by filesystem path (`orca repo add --path`). Non-git folders are supported as `folder` repos but follow a different first-terminal path. |
| At least one CLI agent | Orca is BYOC: no Orca login. Install any supported agent binary (Claude Code, Codex, Grok, OpenCode, etc.) on `PATH`. |
<Note>
Orca does not require a specific model provider. Detection walks installed agent binaries and falls back through `TUI_AGENT_AUTO_PICK_ORDER` when no default is set.
</Note>
## Session architecture
```mermaid
sequenceDiagram
participant User
participant CLI as orca CLI
participant App as Orca desktop
participant Runtime as Orca runtime RPC
participant PTY as PTY / agent shell
User->>App: Launch Orca (or orca open)
CLI->>Runtime: status.get
Runtime-->>CLI: graphStatus ready
User->>App: Add repo / create worktree
App->>Runtime: repo.add / worktree.create
Runtime->>PTY: spawn (ORCA_WORKTREE_ID, ORCA_TERMINAL_HANDLE, …)
User->>CLI: orca status / orca worktree current
CLI->>Runtime: status.get / worktree.show
```
## Step-by-step first session
<Steps>
<Step title="Open Orca and confirm the runtime">
Launch the Orca desktop app from your install, or start it from a shell:
```bash
orca open
```
`orca open` checks runtime reachability first; if the runtime is already up it returns immediately. Otherwise it launches the app (macOS uses `open` on the `.app` bundle when packaged) and polls every 250ms for up to **15 seconds** until `runtime.reachable` is true.
Verify readiness:
```bash
orca status
```
<RequestExample>
```bash
orca status --json
```
</RequestExample>
<ResponseExample>
```json
{
"ok": true,
"result": {
"app": { "running": true, "pid": 12345 },
"runtime": {
"state": "ready",
"reachable": true,
"runtimeId": "runtime-…"
},
"graph": { "state": "ready" }
}
}
```
</ResponseExample>
Human-readable `orca status` prints the same fields as lines (`appRunning`, `runtimeState`, `runtimeReachable`, `runtimeId`, `graphState`). In non-JSON mode, `orca status` sets **exit code 1** when `runtime.reachable` is false.
</Step>
<Step title="Register a repository">
In the desktop UI, add a project through the sidebar **Add repo** flow (`AddRepoDialog`), pointing at your git checkout’s root directory.
Equivalent CLI registration (paths resolve against the invoking shell’s cwd unless you pass an absolute path):
```bash
orca repo add --path /absolute/path/to/your-repo --json
orca repo list --json
```
Copy the returned `repo.id` for the next step (`id:<repoId>` selector).
</Step>
<Step title="Create an Orca-managed worktree">
In the UI, create a new workspace/worktree for the registered repo (for example via the worktree palette or add-worktree dialog). Orca creates a dedicated git worktree path and, by default, seeds a **first terminal** without switching the active workspace unless you opt in.
CLI equivalent:
```bash
orca worktree create \
--repo id:<repoId> \
--name my-first-task \
--comment "quickstart seed" \
--json
```
| Flag | Effect |
| --- | --- |
| `--activate` | Reveal the new workspace in the desktop UI (also implied by `--run-hooks`). |
| `--run-hooks` | Force `orca.yaml` setup hooks and reveal the worktree so hooks can run in the first terminal. |
| `--parent-worktree active` | Record lineage from the caller’s current Orca worktree. |
| `--no-parent` | Create an independent workspace with no parent link. |
<Info>
`orca worktree create` from the CLI spawns the initial PTY in the background (`activate: false`) and pre-allocates a terminal handle. The desktop UI path can additionally set `createdWithAgent` and launch your default or auto-detected agent via `activateAndRevealWorktree`.
</Info>
</Step>
<Step title="Launch a CLI agent terminal">
Pick any agent you have installed. The desktop uses `settings.defaultTuiAgent` when set; otherwise `pickTuiAgent` walks `TUI_AGENT_AUTO_PICK_ORDER` (`claude`, `codex`, `grok`, `copex`, …) against `detectInstalledAgents()`.
From the CLI, create a focused agent tab in the new worktree (replace the command with your agent’s launch binary from `TUI_AGENT_CONFIG`, e.g. `claude`, `codex`, `grok`):
```bash
cd /path/to/orca/worktree/checkout
orca terminal create \
--worktree active \
--title "Agent" \
--command "claude" \
--focus \
--json
```
| Behavior | Detail |
| --- | --- |
| Default targeting | Omit `--worktree` to use the active worktree for the current directory. |
| Focus | Without `--focus`, the tab is created without stealing UI focus when possible. |
| Agent commands | Bare agent binaries (`claude`, `codex`, …) route through Orca’s renderer-backed terminal path for correct geometry. |
| Env injection | Spawned shells receive `ORCA_WORKTREE_ID`, `ORCA_TAB_ID`, `ORCA_PANE_KEY`, and `ORCA_TERMINAL_HANDLE` (pre-allocated `term_*` handle for CLI control). |
List live terminals:
```bash
orca terminal list --worktree active --json
```
</Step>
<Step title="Verify from the worktree directory">
`cd` into the Orca-managed worktree path (printed by `orca worktree list` or `worktree create` JSON). Then confirm Orca recognizes the directory and the runtime is still healthy:
```bash
orca status --json
orca worktree current --json
```
`worktree current` resolves the shell’s cwd to the **longest enclosing** Orca-managed worktree path, then returns the full worktree record via `worktree.show` (same shape as `worktree show --worktree id:<id>`).
<Warning>
On a **remote paired runtime**, `active` / `current` cwd shortcuts are rejected. Pass an explicit server-side selector (`id:`, `path:`, `branch:`, `issue:`) instead.
</Warning>
Optional human-readable check inside the worktree:
```bash
orca worktree current
# prints key: value lines for id, path, branch, comment, parentWorktreeId, …
```
</Step>
</Steps>
## `orca status` field reference
| Field | Ready signal | Other common values |
| --- | --- | --- |
| `app.running` | `true` | `false` when Orca has never started |
| `runtime.reachable` | `true` | `false` during bootstrap or after crash |
| `runtime.state` | `ready` | `not_running`, `starting`, `graph_not_ready`, `stale_bootstrap` |
| `graph.state` | `ready` | `not_running`, `starting`, `unavailable` |
<Tip>
If metadata exists but the socket is dead, `runtime.state` is `stale_bootstrap`—restart the Orca app, then rerun `orca open` and `orca status`.
</Tip>
## Worktree selectors used in verification
| Selector | Resolves to |
| --- | --- |
| `active` / `current` | Enclosing Orca worktree for shell `cwd` (local CLI only) |
| `id:<worktreeId>` | Stable runtime id (preferred after `worktree current`) |
| `path:<absolute-path>` | Worktree checkout path |
| `branch:<name>` | Branch name (ambiguous if duplicated) |
| `issue:<number>` | Linked issue number |
## CLI-only quickstart (scripted)
For automation or headless agents, the same session collapses to:
```bash
command -v orca
orca open --json
orca status --json
orca repo add --path /abs/repo --json
# capture repo.id from JSON
orca worktree create --repo id:<repoId> --name task-1 --json
cd "$(jq -r '.result.worktree.path' <<<"$WT_JSON")"
orca terminal create --command "codex" --focus --json
orca status --json
orca worktree current --json
```
Use `--json` on every call when parsing output programmatically.
## Troubleshooting
| Symptom | Likely cause | Mitigation |
| --- | --- | --- |
| `runtime_open_timeout` | App did not become reachable within 15s | Start Orca manually; retry `orca status` |
| Exit code 1 from `orca status` | `runtime.reachable` is false | `orca open`; wait for `graph.state: ready` |
| `graph_not_ready` / `unavailable` | Renderer graph not synced | Focus Orca window; wait and recheck |
| `No Orca-managed worktree contains the current directory` | Shell cwd is outside any worktree | `cd` into the worktree path or pass `--worktree id:…` |
| Empty agent terminal | No agent on `PATH` and `defaultTuiAgent` is `blank` | Install an agent binary or set a default in Orca settings |
| `selector_ambiguous` on `path:` | Duplicate repo registrations share a path | Prefer `id:` from `worktree list` |
## What you have after quickstart
- A **registered repo** known to Orca’s runtime store.
- An **Orca-managed worktree** with its own checkout path and metadata (`comment`, optional `parentWorktreeId`, linked issue fields).
- At least one **live PTY** running your CLI agent, addressable as `term_*` via `orca terminal read/send/wait`.
- Confirmed **runtime RPC** (`orca status`) and **cwd resolution** (`orca worktree current`) for later scripting.
Update the worktree comment at meaningful checkpoints:
```bash
orca worktree set --worktree active --comment "reproduced issue; implementing fix" --json
```
## Related pages
<CardGroup>
<Card title="Overview" href="/overview">
Desktop app, runtime RPC, bundled CLI, BYOC agents, and install-to-worktree flow.
</Card>
<Card title="Installation" href="/installation">
Platform packages, prerequisites, and CLI registration.
</Card>
<Card title="Worktrees and repos" href="/worktrees-and-repos">
Repo registration, worktree create/list/remove, lineage, and selectors.
</Card>
<Card title="Terminals and agents" href="/terminals-and-agents">
PTY lifecycle, agent catalog, `ORCA_*` env vars, and terminal CLI vs orchestration.
</Card>
<Card title="CLI scripting workflow" href="/cli-scripting-workflow">
End-to-end automation with `orca open`, terminals, and worktree comments.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Runtime reachability, selector ambiguity, and terminal timeouts.
</Card>
</CardGroup>
---
## 04. Worktrees and repos
> Orca-managed git worktrees, repo registration, lineage/parent links, sparse checkout, workspace statuses, and CLI/RPC create/list/remove semantics.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/04-worktrees-and-repos.md
- Generated: 2026-06-01T20:05:28.307Z
### Source Files
- `src/shared/types.ts`
- `src/cli/specs/core.ts`
- `src/cli/handlers/worktree.ts`
- `src/main/runtime/orca-runtime.ts`
- `src/main/git/worktree.ts`
- `docs/worktree-delete-preflight.md`
- `orca.yaml`
---
title: "Worktrees and repos"
description: "Orca-managed git worktrees, repo registration, lineage/parent links, sparse checkout, workspace statuses, and CLI/RPC create/list/remove semantics."
---
Orca registers git repositories and folder projects in persistent state (`orca-data.json`), maps each linked git checkout to a stable worktree id (`<repoId>::<absolute-path>`), and drives create/list/remove through `OrcaRuntimeService` RPC methods that the `orca` CLI calls. Git operations live in `src/main/git/worktree.ts`; user-authored fields and parent/child lineage live in `worktreeMeta` and `worktreeLineageById`.
## Architecture
```mermaid
flowchart TB
subgraph clients [Clients]
CLI["orca CLI"]
Desktop["Electron renderer IPC"]
end
subgraph runtime [Runtime]
RPC["runtime-rpc worktree.* / repo.*"]
ORS["OrcaRuntimeService"]
end
subgraph persist [Persistence]
DATA["orca-data.json"]
META["worktreeMeta"]
LINE["worktreeLineageById"]
end
subgraph git [Git]
WT["git worktree add/remove/list"]
SC["git sparse-checkout"]
end
CLI --> RPC
Desktop --> ORS
RPC --> ORS
ORS --> DATA
ORS --> META
ORS --> LINE
ORS --> WT
ORS --> SC
```
| Layer | Responsibility |
| --- | --- |
| `Repo` / `Worktree` types | Canonical shapes in `src/shared/types.ts` |
| `orca-data.json` | `repos`, `worktreeMeta`, `worktreeLineageById`, `sparsePresetsByRepo`, `settings.workspaceStatuses` |
| `OrcaRuntimeService` | Selector resolution, visibility, lineage, create/remove ordering, SSH branches |
| `src/main/git/worktree.ts` | Porcelain listing, `addWorktree` / `addSparseWorktree`, `removeWorktree` |
| CLI (`src/cli/handlers/`) | Thin RPC wrappers for scripting agents |
Desktop surfaces also use IPC handlers (`worktrees:create`, `worktrees:list`, `worktrees:remove`, …) that share the same main-process logic as the runtime.
## Repo registration
A registered **repo** is a `Repo` record: `id`, `path`, `displayName`, `badgeColor`, `addedAt`, optional `kind`, and optional `connectionId` for SSH-backed remotes.
<ParamField body="kind" type="'git' | 'folder'">
`git` (default): requires a valid git repository at `path`. `folder`: non-git project root; worktrees are synthetic workspace instances over the same directory.
</ParamField>
<ParamField body="worktreeBaseRef" type="string | undefined">
Per-repo default base ref for new worktrees when create args omit `baseBranch`. Set via `orca repo set-base-ref` or `repo.setBaseRef` RPC.
</ParamField>
<ParamField body="externalWorktreeVisibility" type="'hide' | 'show'">
Controls whether git worktrees Orca did not create appear in the sidebar. New git repos default to `hide`; legacy repos may still default to `show`.
</ParamField>
<ParamField body="connectionId" type="string | null">
When set, repo git/filesystem operations delegate to SSH providers; local preflight/PTY teardown paths are skipped.
</ParamField>
### CLI: repo commands
| Command | RPC | Notes |
| --- | --- | --- |
| `orca repo list` | `repo.list` | All registered repos |
| `orca repo add --path <path>` | `repo.add` | Path must be absolute on the runtime host; remote CLI rejects relative paths |
| `orca repo show --repo <selector>` | `repo.show` | One repo |
| `orca repo set-base-ref --repo <selector> --ref <ref>` | `repo.setBaseRef` | Persists `worktreeBaseRef` |
| `orca repo search-refs --repo <selector> --query <text>` | `repo.searchRefs` | Branch/tag search for base picker |
Additional RPC-only repo operations include `repo.create`, `repo.clone`, `repo.update`, `repo.rm`, `repo.sparsePresets`, `repo.saveSparsePreset`, and hook helpers (`repo.hooks`, `repo.hooksCheck`, …).
### Repo selectors
Runtime resolves repos with optional prefixes:
| Form | Match |
| --- | --- |
| `id:<uuid>` | `Repo.id` |
| `path:<absolute>` | `Repo.path` (normalized) |
| `name:<displayName>` | `Repo.displayName` |
| bare value | Tries id, then path, then display name |
Ambiguous matches throw `selector_ambiguous`; none throw `repo_not_found`.
## Worktree identity and git shape
### Stable id
Worktree ids use `WORKTREE_ID_SEPARATOR` (`::`):
```text
<repoId>::<absolute-worktree-path>
```
Folder projects can add a session suffix: `<repoId>::<path>::workspace:<uuid>` for multiple workspace instances on one directory. Filesystem callers strip the suffix via `splitWorktreeIdForFilesystem`.
### Git-level vs app-level
| Type | Fields | Source |
| --- | --- | --- |
| `GitWorktreeInfo` | `path`, `head`, `branch`, `isBare`, `isSparse?`, `isMainWorktree` | `git worktree list --porcelain` (+ sparse detection) |
| `Worktree` | Metadata + git fields | `mergeWorktree(repoId, gitInfo, WorktreeMeta)` |
| `RuntimeWorktreeRecord` | Above + `parentWorktreeId`, `childWorktreeIds`, `lineage`, `git` | Runtime resolution cache |
`isMainWorktree` is true for the first porcelain block (primary checkout). Linked worktrees from `git worktree add` are `false`.
### Ownership and visibility
`classifyWorktreeOwnership` labels each detected checkout:
| `WorktreeOwnership` | Meaning |
| --- | --- |
| `orca-managed` | Strong Orca metadata (`orcaCreatedAt`, …) or path under configured Orca workspace layouts |
| `external` | Git worktree outside Orca layout roots |
| `unknown-legacy` | Under an Orca root but without strong metadata |
`worktree.list` returns only **visible** managed worktrees (respects `externalWorktreeVisibility`). `worktree.detectedList` returns the full scan for one repo with `ownership`, `visible`, `authoritative`, and `source` (`git` | `metadata-fallback` | `session-fallback`).
## Lineage and parent links
Parent/child relationships are stored in `worktreeLineageById` keyed by child worktree id. Each `WorktreeLineage` record includes:
- `worktreeId`, `worktreeInstanceId`, `parentWorktreeId`, `parentWorktreeInstanceId`
- `origin`: `orchestration` | `cli` | `manual`
- `capture`: `source` + `confidence` (`explicit` | `inferred`)
- Optional orchestration fields: `orchestrationRunId`, `taskId`, `coordinatorHandle`, `createdByTerminalHandle`
**Instance ids** on `WorktreeMeta` prevent stale lineage after a path is reused for a different checkout.
### Create-time resolution
When creating via CLI/RPC, lineage input can include:
| Input | Effect |
| --- | --- |
| `--no-parent` / `noParent: true` | No lineage record |
| `--parent-worktree <selector>` | Explicit parent (`explicit-cli-flag`) |
| Default (CLI) | Infers parent from `ORCA_TERMINAL_HANDLE` terminal, else cwd enclosing worktree (`cwd-context`) |
| `orchestrationContext` (RPC) | Parent from orchestration dispatch |
Conflicting `--parent-worktree` and `--no-parent` are rejected. Multiple inferred parents with different ids yield warning `LINEAGE_PARENT_CONTEXT_CONFLICT` and no lineage. Cycle detection walks the parent chain and rejects self-parents.
Resolved worktrees attach lineage only when both child and parent instance ids match stored lineage (`attachLineageToResolvedWorktrees`). Missing git checkouts trigger `pruneLineageForMissingRepoWorktrees`.
### CLI lineage output
`orca worktree create` prints stderr summaries: `parent: <id> (explicit from …)` or `parent: none`, plus lineage warnings when not using `--json`.
`orca worktree set` can update parent via `--parent-worktree` or clear with `--no-parent`.
RPC: `worktree.lineageList` returns all lineage records.
## Sparse checkout
Sparse worktrees use cone mode:
1. `addWorktree` (or checkout existing branch)
2. `git sparse-checkout init --cone`
3. `git sparse-checkout set -- <directories>`
4. `git checkout <branch>`
On failure after partial create, `addSparseWorktree` force-removes the worktree and may surface a cleanup-failed message.
**Metadata:** `WorktreeMeta.sparseDirectories`, `sparseBaseRef`, `sparsePresetId` (cleared when the worktree is no longer sparse on refresh).
**Presets:** `SparsePreset` per repo in `sparsePresetsByRepo`. RPC: `repo.sparsePresets`, `repo.saveSparsePreset`. Create accepts `sparseCheckout: { directories, presetId? }` on `worktree.create` (desktop/mobile RPC); the bundled CLI `worktree create` spec does not expose sparse flags yet.
Directories must be repo-relative paths.
## Workspace statuses
Board-style statuses are user-defined `WorkspaceStatusDefinition` entries in global settings (`id`, `label`, optional `color`, `icon`). Defaults include `todo`, `in-progress`, `in-review`, `completed` (max 12 statuses, labels up to 32 chars).
Per-worktree assignment: `Worktree.workspaceStatus` / `WorktreeMeta.workspaceStatus`, set at create (`workspaceStatus` on `worktree.create`) or via `worktree.set`.
Statuses are orthogonal to git state; they organize the sidebar/board only.
## Create lifecycle
```mermaid
sequenceDiagram
participant C as Client
participant R as OrcaRuntimeService
participant G as git/worktree.ts
participant S as Store
C->>R: worktree.create
R->>R: resolveRepoSelector
R->>R: resolveLineageForWorktreeCreate
R->>G: addWorktree / addSparseWorktree
R->>S: setWorktreeMeta + setWorktreeLineage
R->>R: createTerminal (default)
R-->>C: CreateWorktreeResult
```
<Steps>
<Step title="Resolve repo and base ref">
`baseBranch` → `repo.worktreeBaseRef` → `getDefaultBaseRef(repo.path)`. If none, error asks for explicit `--base-branch`.
</Step>
<Step title="Branch and path">
Sanitize name, resolve branch (optional `branchNameOverride`), detect conflicts with existing local/remote branches or open PRs, compute path via `computeWorktreePath` (`settings.workspaceDir`, `nestWorktrees`).
</Step>
<Step title="Git worktree add">
Local: `addWorktree` or `addSparseWorktree`. SSH: `createManagedRemoteWorktree`. Folder: metadata-only instance id.
</Step>
<Step title="Persist metadata">
Stamp `orcaCreatedAt`, `orcaCreationSource` (`cli` | `runtime` | `desktop` | `ssh`), optional links (`linkedIssue`, `linkedPR`, GitLab slots, `pushTarget`, `workspaceStatus`), lineage.
</Step>
<Step title="Terminal and activation">
Creates first terminal by default. `--activate` or `--run-hooks` reveals workspace in the app; `--run-hooks` also runs `orca.yaml` setup per repo policy.
</Step>
</Steps>
<Note>
`orca worktree create` does not switch the active desktop workspace unless `--activate` or `--run-hooks` is passed. Setup hooks follow `RepoHookSettings.setupRunPolicy`; use `--run-hooks` to force when policy would skip.
</Note>
### Create result fields
`CreateWorktreeResult` / `RuntimeWorktreeCreateResult` include:
- `worktree` (with lineage attachment on list/show)
- `lineage`, `warnings` (lineage warnings)
- `warning` (hook/terminal failures)
- `setup` (`WorktreeSetupLaunch` when setup script runs)
- `initialBaseStatus`, `localBaseRefRefresh` (desktop IPC paths)
## Remove lifecycle
`worktree.rm` / `removeManagedWorktree` share destructive semantics:
| Phase | Local git repo | SSH repo |
| --- | --- | --- |
| Registration check | `findRegisteredDeletableWorktree` | Provider `listWorktrees` |
| Archive hook | Optional with `--run-hooks`; skipped by default on RPC with warning | Provider-owned |
| Preflight | `assertWorktreeCleanForRemoval` (non-force) before PTY kill | N/A (provider delete) |
| PTY teardown | `killAllProcessesForWorktree` after clean preflight | Not local PTYs |
| Git remove | `removeWorktree` + prune; optional branch delete unless `preserveBranchOnDelete` | Provider `removeWorktree` |
| Metadata | `removeWorktreeMeta`, lineage prune, terminal history dir | Same |
<Warning>
Non-force delete runs `git status --porcelain` preflight **before** killing terminals. Dirty/untracked worktrees fail without tearing down PTYs. Force delete skips preflight and may kill PTYs first (IPC path). Orphan paths (`is not a working tree`, missing dir) skip PTY kill and follow existing prune/metadata cleanup.
</Warning>
Concurrent deletes on the same worktree id share one in-flight promise; conflicting `force`/`run-hooks` options throw `Worktree deletion already in progress`.
Unregistered paths require `--force` and pass safety checks (`canSafelyRemoveOrphanedWorktreeDirectory`).
## List, show, and orchestration summary
| Command / RPC | Behavior |
| --- | --- |
| `orca worktree list` / `worktree.list` | Visible resolved worktrees; optional `--repo`; default limit 200 with `totalCount` / `truncated` |
| `worktree.detectedList` | Full repo scan + ownership (RPC/desktop) |
| `orca worktree show` / `worktree.show` | One record by selector |
| `orca worktree current` | Resolves cwd to enclosing worktree → `id:<id>` (local only for cwd shortcuts) |
| `orca worktree ps` / `worktree.ps` | Cross-worktree terminal/agent summary (`RuntimeWorktreePsSummary`, status `active` \| `working` \| `permission` \| `done` \| `inactive`) |
| `worktree.sleep` / `worktree.activate` | RPC workspace focus helpers |
## Worktree selectors
| Form | Match |
| --- | --- |
| `id:<worktreeId>` | Exact id |
| `path:<absolute>` | Filesystem path (first match if duplicate registration) |
| `branch:<name>` | Branch selector match |
| `issue:<number>` | `linkedIssue` |
| `active` / `current` | **Local CLI only:** cwd → enclosing worktree → `id:…` |
| bare value | id, path, or branch |
Remote/paired runtimes: `active`/`current` and cwd-derived `path:` from the client machine are invalid; use server-absolute `path:` or `id:`.
`orca worktree current` lists up to 10,000 worktrees and picks the longest matching path prefix.
## RPC method inventory (worktree / repo)
**Repo:** `repo.list`, `repo.add`, `repo.show`, `repo.update`, `repo.setBaseRef`, `repo.baseRefDefault`, `repo.searchRefs`, `repo.sparsePresets`, `repo.saveSparsePreset`, `repo.create`, `repo.clone`, `repo.rm`, `repo.reorder`, hook/issue helpers.
**Worktree:** `worktree.list`, `worktree.detectedList`, `worktree.show`, `worktree.create`, `worktree.set`, `worktree.rm`, `worktree.ps`, `worktree.lineageList`, `worktree.sleep`, `worktree.activate`, `worktree.persistSortOrder`, `worktree.resolvePrBase`, `worktree.resolveMrBase`.
Method schemas live under `src/main/runtime/rpc/methods/`.
## Persistence snapshot
```text
orca-data.json
├── repos[]
├── worktreeMeta{ "<repoId>::<path>": WorktreeMeta }
├── worktreeLineageById{ "<childId>": WorktreeLineage }
├── sparsePresetsByRepo{ "<repoId>": SparsePreset[] }
└── settings.workspaceStatuses[]
```
User-facing hook scripts come from repo-root `orca.yaml` (`scripts.setup`, `scripts.archive`) merged with per-repo `RepoHookSettings`; see Repository hooks for trust and `--run-hooks` policy.
## Verification
```bash
orca status
orca repo list --json
orca worktree list --repo id:<repoId> --json
orca worktree current --json
orca worktree create --repo id:<repoId> --name feature-x --parent-worktree active --json
orca worktree rm --worktree id:<worktreeId>
```
Expect `parent:` stderr lineage on create (non-JSON), `removed: true` on successful rm, and `selector_not_found` when cwd is outside any managed worktree.
## Related pages
<CardGroup>
<Card title="Quickstart" href="/quickstart">
First repo, worktree, and `orca worktree current` verification.
</Card>
<Card title="CLI core reference" href="/cli-core-reference">
Full flag lists for `repo` and `worktree` command groups.
</Card>
<Card title="Selectors and JSON output" href="/selectors-json-output">
Selector grammar, `--json` shapes, and remote-runtime resolution rules.
</Card>
<Card title="Repository hooks" href="/repository-hooks">
`orca.yaml` setup/archive scripts and `--run-hooks` on create/remove.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
`connectionId` repos, remote worktree CRUD, and CLI selector constraints.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Delete preflight failures, selector ambiguity, and runtime reachability.
</Card>
</CardGroup>
---
## 05. Terminals and agents
> PTY lifecycle, `TuiAgent` catalog and detection order, pane keys, agent status hooks, terminal env injection (`ORCA_*`), and when to use terminal CLI vs orchestration messaging.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/05-terminals-and-agents.md
- Generated: 2026-06-01T20:06:37.986Z
### Source Files
- `src/shared/types.ts`
- `src/shared/tui-agent-config.ts`
- `src/shared/agent-detection.ts`
- `src/main/ipc/pty.ts`
- `src/main/agent-hooks/server.ts`
- `src/cli/specs/agent-hooks.ts`
- `skills/orca-cli/SKILL.md`
---
title: "Terminals and agents"
description: "PTY lifecycle, `TuiAgent` catalog and detection order, pane keys, agent status hooks, terminal env injection (`ORCA_*`), and when to use terminal CLI vs orchestration messaging."
---
Orca hosts interactive terminals through a main-process PTY layer that routes local `node-pty` sessions and SSH relay leases, injects per-pane `ORCA_*` environment variables at spawn, and pairs each session with optional loopback agent-status hooks. Launchable coding agents are identified by the `TuiAgent` union and `TUI_AGENT_CONFIG` catalog; PATH detection and automatic fallback order are separate concerns.
## PTY lifecycle
PTY operations flow through a provider registry: `connectionId === null` uses the local provider (optionally backed by the Orca daemon); non-null IDs use per-connection SSH providers. Each spawned PTY records ownership, geometry, last-input time, and bidirectional `ptyId` ↔ `paneKey` maps so teardown can clear hook caches and serializer state.
```mermaid
stateDiagram-v2
[*] --> Spawning: pty:spawn
Spawning --> Live: provider.spawn OK
Live --> Live: write / resize / data batch
Live --> Teardown: pty:kill or process exit
Teardown --> [*]: clearProviderPtyState
note right of Teardown
Unregisters paneKey maps,
hook/OpenCode/Pi overlays,
migration flags, serializers
end note
```
On exit or kill, `clearProviderPtyState` runs a single cleanup path: OpenCode and Pi overlay state, agent-hook pane caches, advertised-URL bindings, Claude live-PTY gates, renderer-serializer flags, and `paneKeyTeardownListeners` (for example cursor-agent synthesized title loops). SSH connection loss calls the same cleanup via `clearPtyOwnershipForConnection`.
Daemon-backed spawns use a stable `sessionId` (minted from worktree context) so Pi/Codex overlay directories survive daemon reconnects. Renderer scrollback cooperation uses generation tokens on `declarePendingPaneSerializer` / `settlePaneSerializer` so a remounted pane is not overwritten by an older PTY’s teardown.
| IPC / concern | Role |
| --- | --- |
| `pty:spawn` | Create session; validate `paneKey`; call `buildPtyHostEnv` on local/daemon host path |
| `pty:kill` | Terminate; `finishPtyShutdown` drops ownership |
| `pty:write` / resize | Routed via `getProviderForPty` |
| `pty:declarePendingPaneSerializer` | Renderer claims scrollback before spawn |
| Provider `onExit` | Mirrors kill cleanup for local exits |
<Warning>
`buildPtyHostEnv` must not run for SSH remote shells: hook server coordinates, attribution shims, and host filesystem paths are local-only. Remote pane identity env vars are forwarded only when `isRemoteAgentHooksEnabled()` is true.
</Warning>
## `TuiAgent` catalog
`TuiAgent` is the closed union of agents Orca can launch from the new-workspace flow and default-agent settings. Per-agent metadata lives in `TUI_AGENT_CONFIG`:
| Field | Purpose |
| --- | --- |
| `detectCmd` | Binary checked on PATH during preflight |
| `launchCmd` | Command line passed to the PTY (may include flags, e.g. `cursor-agent`, `hermes --tui`) |
| `expectedProcess` | Foreground process name for agent-vs-shell detection |
| `promptInjectionMode` | How initial prompts are delivered (`argv`, `flag-prompt`, `stdin-after-start`, etc.) |
| `draftPromptFlag` / `draftPromptEnvVar` | Native draft seeding (`--prefill`, `ORCA_PI_PREFILL`, `ORCA_OMP_PREFILL`) |
| `preflightTrust` | Pre-write trust artifacts so first-run menus do not swallow pasted drafts |
| `draftPasteReadySignal` | Agent-specific readiness before bracketed paste |
The catalog currently includes Claude, Codex, Copilot, Grok, OpenCode, Pi, OMP, Gemini, Antigravity, Aider, Cursor, Droid, Hermes, and additional BYOC CLIs—each mapped to concrete binaries (for example `kiro` → `kiro-cli`, `aug` → `auggie`).
## Detection and auto-pick order
**PATH detection** runs in preflight (`detectInstalledAgents`): for every `TUI_AGENT_CONFIG` entry, Orca checks whether `detectCmd` resolves with `which` / `where` (or inside a WSL distro when configured). Detection is parallel and returns the list of installed agent ids; it does not impose UI ordering.
**Auto-pick fallback** when the user leaves the default at “auto” (`null`) or picks an agent that is not installed uses `TUI_AGENT_AUTO_PICK_ORDER`:
1. Honor explicit user preference if that agent is detected.
2. Otherwise walk the ordered list and return the first detected agent.
3. `'blank'` yields no agent.
Order (highest priority first): `claude`, `codex`, `grok`, `copilot`, `opencode`, `pi`, `omp`, `gemini`, `antigravity`, then the remaining catalog entries through `openclaw`.
`refreshShellPathAndDetectAgents` re-hydrates PATH from the login shell before re-running detection—useful after installing a new CLI without restarting Orca. Remote repos call `preflight.detectAgents` over SSH with the same command list.
<Tip>
Renderer state caches detection per session via `ensureDetectedAgents` / `refreshDetectedAgents`. Remote SSH hosts keep a separate `remoteDetectedAgentIds` map keyed by `connectionId`.
</Tip>
## Pane keys
A **pane key** is `${tabId}:${stableLeafUuid}` where `stableLeafId` is a durable terminal-layout leaf UUID. It replaces legacy `${tabId}:${numericPaneId}` keys for hook routing and retained dashboard rows.
| Env var | Set when | Used for |
| --- | --- | --- |
| `ORCA_PANE_KEY` | Spawn validates tab + leaf against layout | Hook POST bodies, agent scripts, status routing |
| `ORCA_TAB_ID` | Matching `tabId` on spawn | Hook payloads, relay revival |
| `ORCA_WORKTREE_ID` | Worktree known at spawn | Worktree-scoped status and CLI context |
Spawn logic only keeps these variables when the incoming `ORCA_PANE_KEY` parses and matches `tabId` / `leafId`; otherwise they are stripped so hooks cannot attribute events to the wrong pane. Legacy numeric pane keys are migrated to UUID keys when metadata proves the mapping; unsupported combinations are recorded for diagnostics.
`makePaneKey(tabId, leafId)` and `parsePaneKey` are the canonical helpers; `getPtyIdForPaneKey` resolves live PTYs from the main-process map.
## Agent status: hooks and title signals
Orca uses two complementary signals:
### Explicit status (hooks)
When `agentStatusHooksEnabled` is not `false` (default on), Orca starts a loopback HTTP hook server and injects runtime coordinates into each PTY. Managed installers cover: `claude`, `codex`, `gemini`, `antigravity`, `cursor`, `droid`, `command-code`, `grok`, `copilot`, `hermes`. Pi/OpenCode use overlay config dirs rather than separate HTTP targets in that list.
Hook-reported states: `working`, `blocked`, `waiting`, `done`. Payloads include prompt previews, tool name/input, assistant message snippets, and optional orchestration context (`taskId`, `dispatchId`, parent handles).
| Hook runtime env | Meaning |
| --- | --- |
| `ORCA_AGENT_HOOK_PORT` | Loopback port |
| `ORCA_AGENT_HOOK_TOKEN` | Bearer token for `X-Orca-Agent-Hook-Token` |
| `ORCA_AGENT_HOOK_ENV` | Environment label (`dev` / `prod`) |
| `ORCA_AGENT_HOOK_VERSION` | Protocol version (`1`) |
| `ORCA_AGENT_HOOK_ENDPOINT` | Optional on-disk endpoint file path |
CLI surface:
```bash
orca agent hooks status [--json]
orca agent hooks on [--json]
orca agent hooks off [--json]
```
`off` removes managed hook entries locally; `on` re-enables installation. Status entries persist in `userData/agent-hooks/last-status.json` (7-day hydrate cap) so dashboard rows can survive restarts. Stale explicit status decays after `AGENT_STATUS_STALE_AFTER_MS` (30 minutes).
### Title-based signals (OSC / heuristics)
OSC title parsing from PTY output drives sidebar badges, unread notifications, and `terminal wait --for tui-idle`. States are coarser: `working`, `permission`, `idle`. This path is intentionally narrower than the full `TuiAgent` list (substring false positives are avoided for short names like `amp`).
`createAgentStatusTracker` fires `onBecameIdle` on working→idle transitions— the trigger for unread-style attention. Gemini and Pi titles are normalized to stable labels to avoid renderer churn.
<Note>
Dashboard-grade status comes from hooks; title inference still drives several UX paths (TUI idle wait, Claude prompt-cache timer scoping, smart attention).
</Note>
## Terminal environment injection (`ORCA_*`)
`buildPtyHostEnv` is the single source of truth for host-local injections before the provider wraps `TERM`, `LANG`, and shell startup.
| Variable group | When set | Purpose |
| --- | --- | --- |
| Agent hooks | Hooks enabled | Loopback server + strip inherited stale hook env |
| `ORCA_OPENCODE_*` / `OPENCODE_CONFIG_DIR` | Hooks enabled | Per-PTY OpenCode config overlay |
| `ORCA_PI_*` / `ORCA_OMP_*` / `PI_CODING_AGENT_DIR` | Hooks enabled | Pi/OMP agent-dir overlays and status extension |
| `ORCA_CODEX_HOME` / `CODEX_HOME` | Codex account selected | Scoped Codex auth home |
| `ORCA_USER_DATA_PATH` + dev `PATH` prepend | Dev unpackaged | CLI calls hit the dev runtime |
| `ORCA_ENABLE_GIT_ATTRIBUTION` + shims | Setting on | Git/gh attribution in Orca PTYs only |
| `ORCA_TERMINAL_HANDLE` | Runtime present | Orchestration self-addressing |
| Draft prefill | Draft launch | `ORCA_PI_PREFILL`, `ORCA_OMP_PREFILL` |
Attribution and hook keys are deleted when disabled so nested Orca terminals do not inherit another session’s overlays.
## Terminal CLI vs orchestration messaging
Both surfaces talk to the running Orca runtime, but they target different recipients.
| Use | Surface | Why |
| --- | --- | --- |
| Shell commands, builds, tests | `orca terminal send` | Writes to the PTY byte stream; safe for non-agent processes |
| Read output, wait for exit or TUI idle | `orca terminal read` / `wait` | Observes PTY transcript and OSC-derived idle |
| Create/split/list terminals | `orca terminal create` / `split` / `list` | Structural terminal management |
| Message another **agent** terminal | `orca orchestration send` / `check` / `reply` | Structured agent-to-agent mailboxes; uses `ORCA_TERMINAL_HANDLE` |
| Dispatch tasks with preamble | `orca orchestration dispatch --inject` | Requires recognized agent CLI in target terminal |
| Multi-agent coordinator loop | `orca orchestration run` | Experimental; needs runtime + feature flag |
<Warning>
Do not use `orca terminal send` to hand off work to Claude Code, Codex, Gemini, or other agent TUIs when the goal is agent coordination—use orchestration messaging. Terminal send injects raw keystrokes and races with interactive UIs. Orchestration `--inject` also requires an agent process in the target pane; for a bare shell, send the prompt with `terminal send` instead.
</Warning>
Orca CLI guidance: **writes** go to non-agent terminals; **reads and waits** are valid on any terminal, including agents. Orchestration owns nudges, replies, task DAGs, and worker completion messages.
### Typical automation patterns
<Steps>
<Step title="Observe a shell or build">
```bash
orca terminal list --worktree active --json
orca terminal read --terminal <handle> --json
orca terminal wait --terminal <handle> --for exit --timeout-ms 60000 --json
```
</Step>
<Step title="Wait for an agent turn to finish">
```bash
orca terminal wait --terminal <handle> --for tui-idle --timeout-ms 120000 --json
```
Uses OSC title transitions (`working` → `idle`). Unsupported CLIs may time out—always set `--timeout-ms`.
</Step>
<Step title="Coordinate agents">
```bash
orca orchestration send --to <handle> --subject "Review API changes" --body "Focus on auth middleware" --json
orca orchestration check --terminal <handle> --unread --wait --timeout-ms 30000 --json
```
Requires experimental orchestration enabled in Settings.
</Step>
</Steps>
Handles are runtime-scoped; after reload, re-list terminals if you see `terminal_handle_stale`.
## Verification
| Check | Command / signal |
| --- | --- |
| Runtime up | `orca status --json` |
| Installed agents | Settings → Agents refresh, or RPC `preflight.detectAgents` |
| Pane key in PTY | `printf '%s\n' "$ORCA_PANE_KEY"` inside the pane |
| Hooks enabled | `orca agent hooks status --json` |
| Hook reachability | `ORCA_AGENT_HOOK_PORT` set; POST `/hook/<target>` with token header |
| TUI idle | `orca terminal wait --for tui-idle` after agent boot |
## Related pages
<CardGroup>
<Card title="CLI scripting workflow" href="/cli-scripting-workflow">
End-to-end automation with terminal read/send/wait and worktree comments.
</Card>
<Card title="Agent orchestration" href="/agent-orchestration">
Messaging, task DAGs, dispatch, and coordinator `run`.
</Card>
<Card title="CLI core reference" href="/cli-core-reference">
Full `terminal` and `agent hooks` command signatures.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
Remote PTY leases and hook forwarding constraints.
</Card>
<Card title="Settings reference" href="/settings-reference">
`agentStatusHooksEnabled`, terminal defaults, and experimental flags.
</Card>
</CardGroup>
---
## 06. Runtime environments
> Headless `orca serve`, runtime metadata/RPC transport, pairing codes, saved environments, and targeting a remote runtime from the CLI with `--environment` or `--pairing-code`.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/06-runtime-environments.md
- Generated: 2026-06-01T20:06:28.555Z
### Source Files
- `src/cli/specs/core.ts`
- `src/cli/specs/environment.ts`
- `src/main/runtime/runtime-rpc.ts`
- `src/main/runtime/runtime-metadata.ts`
- `src/cli/runtime-client.ts`
- `src/shared/runtime-bootstrap.ts`
- `src/preload/runtime-environment-subscriptions.ts`
---
title: "Runtime environments"
description: "Headless `orca serve`, runtime metadata/RPC transport, pairing codes, saved environments, and targeting a remote runtime from the CLI with `--environment` or `--pairing-code`."
---
An Orca **runtime** is the main-process control plane that exposes JSON RPC over a local Unix socket or Windows named pipe, and optionally over WebSocket for remote clients. The bundled `orca` CLI discovers a **local** runtime through `orca-runtime.json` in user data; **remote** commands use a pairing offer (`orca://pair#…`) or a saved environment entry in `orca-environments.json`, then connect over WebSocket with per-device tokens and application-layer E2EE.
## Runtime roles
| Surface | Process | Local transport | Remote transport | Typical client |
| --- | --- | --- | --- | --- |
| Desktop app | Electron main + renderer | Unix socket / named pipe | WebSocket (optional, for pairing) | Renderer, in-app terminals |
| Headless `orca serve` | Electron main, no window | Same as desktop | WebSocket always enabled | CLI, mobile, web client |
| CLI against saved env | Any machine with `orca` | Reads store only for credentials | WebSocket to saved endpoint | `orca … --environment <name>` |
Headless serve skips the desktop PTY daemon and agent-hook loopback server; it registers a headless PTY runtime and publishes an empty window graph so `status.get` reports a ready server without a renderer.
## Bootstrap metadata (`orca-runtime.json`)
When `OrcaRuntimeRpcServer` starts, it writes a single metadata file under user data (default paths mirror Electron: macOS `~/Library/Application Support/orca`, Windows `%APPDATA%/orca`, Linux `$XDG_CONFIG_HOME/orca` or `~/.config/orca`). Override with `ORCA_USER_DATA_PATH` so CLI and app target the same instance (for example `orca-dev` during `pnpm dev`).
| Field | Type | Role |
| --- | --- | --- |
| `runtimeId` | string | Identity checked on in-flight local RPC responses |
| `pid` | number | Owning process; used for stale detection and socket sweep |
| `transports` | array | One or more of `unix`, `named-pipe`, `websocket` |
| `authToken` | string | Shared secret for **local** Unix/named-pipe RPC only |
| `startedAt` | number | Epoch ms at runtime start |
Local CLI RPCs read this file, pick the first `unix` or `named-pipe` transport, and send newline-delimited JSON:
```json
{"id":"<uuid>","authToken":"<from metadata>","method":"<rpc.method>","params":{...}}
```
<Note>
Metadata is written with mode `0o600` (via `writeSecureJsonFile`). On shutdown, metadata is **not** deleted by default so parallel restarts do not erase a sibling process’s bootstrap; `clearRuntimeMetadataIfOwned` only removes the file when `pid` and `runtimeId` still match the quitting process.
</Note>
### Local socket naming
| Platform | `kind` | Endpoint pattern |
| --- | --- | --- |
| macOS / Linux | `unix` | `{userData}/o-{pid}-{suffix}.sock` |
| Windows | `named-pipe` | `\\.\pipe\orca-{pid}-{suffix}` |
Orphan `o-*.sock` files whose PID no longer exists are swept at RPC server startup.
## RPC transports and security
```mermaid
flowchart TB
subgraph clients [Clients]
CLI_LOCAL[orca CLI local]
CLI_REMOTE[orca CLI remote]
DESKTOP[Desktop renderer]
MOBILE[Mobile app]
end
subgraph runtime [Orca main process]
RPC[OrcaRuntimeRpcServer]
DISP[RpcDispatcher]
META[(orca-runtime.json)]
DEV[DeviceRegistry]
end
CLI_LOCAL -->|authToken + unix/pipe| RPC
CLI_REMOTE -->|deviceToken + E2EE WS| RPC
DESKTOP -->|authToken + unix/pipe| RPC
MOBILE -->|deviceToken + E2EE WS allowlist| RPC
RPC --> DISP
RPC --> META
RPC --> DEV
```
| Transport | Auth | Encryption | Connection model |
| --- | --- | --- | --- |
| Unix / named pipe | `authToken` in each request | None (same-user machine) | One-shot request/response per connection |
| WebSocket | Per-device `deviceToken` after E2EE handshake | Curve25519 ECDH + NaCl secretbox | Streaming dispatch; mobile methods restricted to allowlist |
WebSocket is supplementary: if bind fails (port in use), the runtime continues with Unix socket only and logs the failure. Default listen port is **6768**; the first dev instance pins **6769** when not in E2E mode.
Long-poll RPCs (`terminal.wait`, `orchestration.check` with `wait: true`) emit `{"_keepalive":true}` frames every 10s and are capped at 16 concurrent slots server-side (`runtime_busy` when full). The CLI extends socket timeouts for those methods using `params.timeoutMs + 10s` grace.
## Headless `orca serve`
`orca serve` spawns the Orca executable with `--serve` (foreground, Ctrl+C to stop). It does **not** use `--environment` or `--pairing-code`; those flags are ignored for `serve` and `environment` subcommands.
<ParamField body="--port" type="string">
Maps to `--serve-port` on the child. Integer 0–65535. Default WebSocket port is 6768 unless overridden or dev-pinned.
</ParamField>
<ParamField body="--pairing-address" type="string">
Advertised host for pairing URLs and web client links. Accepts a hostname/IP, `host:port`, bracketed IPv6, or full `ws://` / `wss://` URL. Without it, pairing endpoints use `127.0.0.1` on the bound port.
</ParamField>
<ParamField body="--mobile-pairing" type="boolean">
Creates a **mobile**-scoped device and prints a terminal QR when available. Mutually exclusive with `--no-pairing`.
</ParamField>
<ParamField body="--no-pairing" type="boolean">
Starts RPC without emitting a pairing offer.
</ParamField>
<ParamField body="--json" type="boolean">
Emits a single `orca_server_ready` JSON object instead of human-readable lines.
</ParamField>
### Readiness output
Human mode prints the WebSocket endpoint, optional web client URL (pairing data in URL **fragment** to avoid proxy logs), and `Pairing URL: orca://pair#…`.
JSON mode (`--json` or `--serve-json`):
```json
{
"type": "orca_server_ready",
"runtimeId": "<id>",
"endpoint": "ws://0.0.0.0:6768",
"pairing": {
"url": "orca://pair#...",
"endpoint": "ws://100.64.1.20:6768",
"deviceId": "<uuid>",
"webClientUrl": "http://.../web-index.html#pairing=...",
"scope": "runtime",
"qr": null
}
}
```
`pairing` is `null` when `--no-pairing` is set or WebSocket/pairing prerequisites are unavailable.
<Steps>
<Step title="Start a headless runtime on a reachable address">
```bash
orca serve --pairing-address 100.64.1.20 --json
```
Copy `pairing.url` from the JSON payload (or the printed `Pairing URL` line).
</Step>
<Step title="Save the environment on your workstation">
```bash
orca environment add --name work-server --pairing-code 'orca://pair#...'
```
</Step>
<Step title="Run commands against the remote runtime">
```bash
orca worktree list --environment work-server --json
```
</Step>
</Steps>
<Warning>
`orca serve` requires `ORCA_APP_EXECUTABLE` (or running inside the packaged Electron CLI with `ELECTRON_RUN_AS_NODE=1`). Set the executable path when invoking serve from a plain Node install.
</Warning>
## Pairing codes
A pairing offer is version **2** (`PAIRING_OFFER_VERSION`) with:
| Field | Meaning |
| --- | --- |
| `endpoint` | WebSocket URL clients connect to |
| `deviceToken` | Pending device token registered in `DeviceRegistry` |
| `publicKeyB64` | Server Curve25519 public key for E2EE handshake |
Encoded form:
```
orca://pair#<base64url(JSON)>
```
`parsePairingCode` also accepts the bare base64url payload (paste-friendly). `orca environment add` and `--pairing-code` reject invalid codes with `invalid_argument`.
`createPairingOffer` on the server:
- Resolves the public WebSocket endpoint (honors `--pairing-address` / `--serve-pairing-address`).
- Creates or rotates a pending device (`scope`: `runtime` for CLI/desktop pairing, `mobile` for mobile QR flow).
- Optionally builds `webClientUrl` when the bundled web client exists (`out/web/web-index.html`).
## Saved environments
Persisted at `{userData}/orca-environments.json` (secure JSON, schema version `1`).
| Field | Notes |
| --- | --- |
| `id` | UUID; preferred selector when names collide |
| `name` | Unique display name at add time |
| `endpoints[]` | At least one `websocket` endpoint with `deviceToken` and `publicKeyB64` |
| `preferredEndpointId` | Used to rebuild a `PairingOffer` |
| `runtimeId` | Last seen remote runtime id (updated on successful RPC) |
| `lastUsedAt` | Updated when CLI or desktop marks usage |
CLI commands (always local store I/O — no remote RPC for the subcommand itself):
| Command | Purpose |
| --- | --- |
| `orca environment add --name <n> --pairing-code <code>` | Parse offer, append environment |
| `orca environment list` | List saved environments (secrets redacted in output) |
| `orca environment show --environment <selector>` | Show one (`id` or unique `name`) |
| `orca environment rm --environment <selector>` | Remove; desktop also closes cached WS connections |
`environment list` / `show` redact `deviceToken` and `publicKeyB64` from printed JSON; the on-disk file retains them for reconnect.
## Targeting a remote runtime from the CLI
Global flags on most command groups (from `GLOBAL_FLAGS`):
<ParamField body="--pairing-code" type="string" required={false}>
One-shot remote session using an `orca://pair#…` URL or bare payload. Mutually exclusive with `--environment`.
</ParamField>
<ParamField body="--environment" type="string" required={false}>
Selector for a saved environment (`id` or unique `name`). Resolves to the preferred endpoint’s pairing offer.
</ParamField>
Equivalent environment variables (used when flags omitted): `ORCA_PAIRING_CODE` or `ORCA_REMOTE_PAIRING`, and `ORCA_ENVIRONMENT`.
`RuntimeClient` behavior when remote:
1. Opens WebSocket to `offer.endpoint`, completes E2EE handshake, sends RPC with `deviceToken`.
2. Before non-`status.get` calls, runs protocol compatibility check (`RUNTIME_PROTOCOL_VERSION` 3, min compatible server 2).
3. On success with `--environment`, updates `lastUsedAt` and `runtimeId` in the store.
Commands that **ignore** remote selection (always local user data / local spawn): `environment *`, `serve`, and `agent` (per `shouldIgnoreRemoteSelection`).
<Tip>
For remote worktrees, use server-side selectors (`id:`, `branch:`, `issue:`, `path:<absolute-on-server>`). Local shortcuts such as `path:` for the current working directory are rejected against a remote runtime.
</Tip>
### Example invocations
```bash
# Ephemeral remote session
orca status --pairing-code 'orca://pair#eyJ2IjoyLCJl...' --json
# Named environment
orca terminal list --environment work-server --json
# Cannot combine
orca repo list --pairing-code '...' --environment work-server # invalid_argument
```
## Protocol compatibility
Remote CLI calls require overlapping protocol versions reported by `status.get`:
| Constant | Value |
| --- | --- |
| `RUNTIME_PROTOCOL_VERSION` | 3 |
| `MIN_COMPATIBLE_RUNTIME_SERVER_VERSION` | 2 |
Mismatch surfaces as `incompatible_runtime` before other RPCs run. Capability `runtime.environments.v1` is advertised for environment-aware clients.
## Desktop and web clients
The renderer uses `runtimeEnvironments` IPC (`runtimeEnvironments:list`, `addFromPairingCode`, `call`, `subscribe`, …) to manage the same `orca-environments.json` store and proxy RPC to remote servers. Preload subscribes to `runtimeEnvironments:subscriptionEvent` **before** `invoke('runtimeEnvironments:subscribe')` so streaming frames are not missed.
Web and mobile clients consume pairing URLs from serve output or in-app pairing flows; mobile WebSocket RPCs are limited to `MOBILE_RPC_METHOD_ALLOWLIST` in `runtime-rpc.ts`.
## Operational notes
| Symptom | Likely cause | Mitigation |
| --- | --- | --- |
| `runtime_unavailable` / metadata read error | App not running, wrong `ORCA_USER_DATA_PATH`, or incomplete metadata | Start Orca or `orca serve`; align user data path |
| `stale_bootstrap` in `orca status` | Metadata exists but RPC dead | Restart runtime; check PID |
| `Invalid remote pairing code` | Malformed `--pairing-code` | Copy full `orca://pair#…` from serve output |
| `Use either --pairing-code or --environment` | Both flags set | Pick one targeting mode |
| `Environment name "…" is ambiguous` | Duplicate names in store | Use environment `id` from `environment list` |
| `incompatible_runtime` | CLI/runtime protocol skew | Upgrade client or server together |
| `runtime_busy` on long waits | Long-poll capacity (16) exhausted | Retry with backoff |
| WebSocket `endpoint: null` in serve JSON | WS failed to bind | Free port 6768/6769 or pass `--port` |
```text
userData/
orca-runtime.json # local discovery: socket + authToken
orca-environments.json # saved remote targets (CLI + desktop)
o-<pid>-<suffix>.sock # Unix RPC (ephemeral per process)
```
## Related pages
<CardGroup>
<Card title="CLI core reference" href="/cli-core-reference">
`serve`, `status`, `open`, and global flags including `--environment` and `--pairing-code`.
</Card>
<Card title="Selectors and JSON output" href="/selectors-json-output">
Remote selector rules, `--json` envelopes, and error shapes.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
SSH execution targets and CLI constraints on paired runtimes.
</Card>
<Card title="Quickstart" href="/quickstart">
Local install path: open Orca, worktree, and `orca status` without remote targeting.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Runtime reachability, selector ambiguity, and native dependency failures.
</Card>
</CardGroup>
---
## 07. Repository hooks
> `orca.yaml` setup/archive scripts, issue-command defaults, hook command-source policies, trust prompts, and CLI `--run-hooks` behavior on create/remove.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/07-repository-hooks.md
- Generated: 2026-06-01T20:06:47.695Z
### Source Files
- `orca.yaml`
- `src/shared/types.ts`
- `src/renderer/src/components/settings/RepositoryHooksSection.tsx`
- `src/renderer/src/lib/ensure-hooks-confirmed.ts`
- `src/cli/specs/core.ts`
- `src/shared/setup-runner-command.ts`
---
title: "Repository hooks"
description: "`orca.yaml` setup/archive scripts, issue-command defaults, hook command-source policies, trust prompts, and CLI `--run-hooks` behavior on create/remove."
---
Repository hooks are repo-scoped automation defined in committed `orca.yaml`, optional per-machine overrides in Orca settings and `.orca/issue-command`, and trust-gated execution in the desktop app. Setup runs after a worktree is created; archive runs before a worktree is removed or archived. The `orca` CLI can create and delete worktrees without the renderer, but shared `orca.yaml` scripts only run when you pass `--run-hooks` or when repository policy and `setupDecision` allow setup on create.
## `orca.yaml` shape
Orca reads `orca.yaml` at the repository root (and from the created worktree path when resolving setup for a new checkout). The parser supports only `scripts` and `issueCommand`; other top-level keys may trigger an “unrecognized keys / update Orca” warning in settings while still leaving hook scripts unreadable.
| Key | When it runs | Notes |
| --- | --- | --- |
| `scripts.setup` | After worktree creation | Multiline block scalar (`\|`) or inline shell string |
| `scripts.archive` | Before worktree removal | Same formats as setup |
| `issueCommand` | When a workspace launches with linked issue automation | Template string; not a shell hook |
Example (from the Orca repo itself):
```yaml
scripts:
setup: |
node config/scripts/run-internal-dev-setup.mjs
pnpm install
```
Settings UI also documents a fuller template including `archive` and `issueCommand`:
```yaml
scripts:
setup: |
pnpm worktree:setup
archive: |
echo "Cleaning up before archive"
issueCommand: |
Complete {{artifact_url}}
```
<Note>
If `orca.yaml` exists but none of `scripts.setup`, `scripts.archive`, or `issueCommand` parse successfully, Orca treats the file as missing for hook purposes. Binary `orca.yaml` files are ignored.
</Note>
## Lifecycle
```mermaid
sequenceDiagram
participant User as User / CLI
participant UI as Desktop renderer
participant RT as Runtime / main
participant Git as Git worktree
participant Hook as Hook runner
User->>UI: Create worktree (composer / UI)
UI->>UI: ensureHooksConfirmed(setup)
UI->>RT: worktree.create(setupDecision)
RT->>Git: git worktree add
alt setup allowed and trusted
RT->>RT: createSetupRunnerScript
RT->>User: setup launch in first terminal
end
User->>RT: worktree rm (CLI, no --run-hooks)
RT->>RT: skip archive unless runHooks
User->>RT: worktree rm --run-hooks
RT->>Hook: runHook(archive)
Hook->>Git: teardown while path exists
RT->>Git: git worktree remove
```
| Phase | Desktop default | CLI / headless runtime |
| --- | --- | --- |
| Setup on create | Follows `setupRunPolicy` + user `setupDecision` after trust prompt | Skipped unless `--run-hooks` (forces `setupDecision: run`) or explicit `setupDecision` |
| Archive on remove | Runs after trust prompt unless user skips | Skipped unless `--run-hooks` |
Archive hooks run while the worktree directory still exists so teardown scripts can read files and env vars. Failed archive hooks are logged; removal can still proceed depending on force/clean preflight.
## Effective commands: shared vs local
Two layers combine:
1. **Shared** — `orca.yaml` in the repo (committed).
2. **Local** — per-repo `hookSettings.scripts` stored in Orca settings (not in git).
`commandSourcePolicy` on `RepoHookSettings` controls merging:
| Policy | Setup / archive behavior |
| --- | --- |
| `shared-only` | Use `orca.yaml` only; ignore local script fields |
| `local-only` | Use settings scripts only; ignore `orca.yaml` for execution |
| `run-both` | Run shared script, then local (newline-separated) |
If `commandSourcePolicy` is unset and a local setup or archive script is non-empty, effective policy defaults to **`local-only`**. Otherwise it defaults to **`shared-only`**. Legacy persisted value `shared-first` normalizes to `shared-only`.
Setup and archive can resolve different effective strings when local scripts differ per kind.
## Setup run policy and create-time decisions
`setupRunPolicy` (default **`run-by-default`**) applies when `setupDecision` is `inherit`:
| `setupRunPolicy` | Behavior |
| --- | --- |
| `ask` | Composer must collect an explicit run/skip choice before create |
| `run-by-default` | Setup runs unless user chooses skip |
| `skip-by-default` | Setup skipped unless user chooses run |
`setupDecision` on create:
| Value | Meaning |
| --- | --- |
| `inherit` | Use `setupRunPolicy` |
| `run` | Force setup |
| `skip` | Force skip (also used when user declines trust) |
CLI `--run-hooks` on `worktree create` sets the effective decision to **`run`** and sets **`activate`** so the new worktree is revealed and the setup runner can attach to the first terminal—matching desktop runner behavior instead of a hidden background shell.
## Setup runner and environment
When setup runs with a visible Orca window, main writes a runner under the worktree git dir (for example `.git/orca/setup-runner.sh` or `.cmd` on native Windows) and returns a `WorktreeSetupLaunch` with `runnerScriptPath` and env vars. The renderer launches `buildSetupRunnerCommand(runnerScriptPath)` (bash on POSIX, `cmd.exe /c` on Windows, WSL UNC paths translated for cross-boundary runs).
Hook and runner environments inject:
| Variable | Role |
| --- | --- |
| `ORCA_ROOT_PATH` | Registered repo root |
| `ORCA_WORKTREE_PATH` | New worktree path |
| `ORCA_WORKSPACE_NAME` | Worktree directory basename |
| `CONDUCTOR_ROOT_PATH` / `GHOSTX_ROOT_PATH` | Compatibility aliases for Conductor-style scripts |
Posix runners use `set -e`; Windows batch runners wrap each line with `call` and exit on non-zero. Direct `runHook` execution (headless path) uses a **2 minute** timeout and the platform shell (`/bin/bash` or `cmd.exe`), with a dedicated WSL `wsl.exe` path when the worktree is on WSL.
## Issue command defaults
Issue automation is separate from setup/archive shell hooks.
| Source | Path | Trust |
| --- | --- | --- |
| Shared | `orca.yaml` `issueCommand` | Requires trust prompt (content hash) |
| Local override | `{repoRoot}/.orca/issue-command` | User-owned; no `orca.yaml` trust prompt |
| UI default | Built-in template when nothing configured | `Complete {{artifact_url}}` |
Resolution order for the effective command: **local file first**, then shared `orca.yaml`. Writing an empty override deletes `.orca/issue-command` and restores the shared default. First write creates `.orca/` and appends `.orca` to `.gitignore` when possible.
Template variables at launch:
| Placeholder | Substituted with |
| --- | --- |
| `{{artifact_url}}` | Linked work item URL |
| `{{issue}}` | Linked issue number (legacy) |
Long commands use an `issue-command-runner` script under the worktree git dir, same pattern as setup, to avoid PTY line-wrapping on paste.
## Trust prompts and persistence
Before shared `orca.yaml` content runs from the UI, `ensureHooksConfirmed` checks:
1. **`trustedOrcaHooks[repoId].all`** — “Always trust” for the repo.
2. **Command source** — `local-only` skips shared script inspection for setup/archive; local `issueCommand` skips trust.
3. **Content hash** — SHA-256 of trimmed script text; mismatch reopens the modal (“changed since you last approved”).
4. **Empty script** — No prompt; returns `run`.
The modal (`confirm-orca-yaml-hooks`) offers **Don’t run**, **Run hooks**, and **Always trust `orca.yaml` in {repo}**. Per-script approval stores `{ contentHash, approvedAt }` under `setup`, `archive`, or `issueCommand` in persisted UI state.
<Warning>
If hook inspection RPC fails, trust fails closed and the hook is skipped (`skip`).
</Warning>
Trust prompts are serialized so overlapping create/remove actions cannot replace the active modal callback.
## Repository settings UI
**Settings → Repository → Hooks** (`RepositoryHooksSection`) exposes:
- YAML presence state (loaded, missing, invalid, unrecognized keys / update available)
- Local setup/archive script editors and env var reference
- **Setup run policy** (`ask` / run by default / skip by default)
- **Script source** (`orca.yaml` only / local only / run both)
- Issue command override editor (backed by `repo.issueCommandRead` / `repo.issueCommandWrite`)
- Import candidates from other tools (`repo.setupScriptImports` — Conductor, Superset, Codex environment, Cmux, etc.)
Runtime RPC mirrors desktop checks: `repo.hooks`, `repo.hooksCheck`, `repo.issueCommandRead`, `repo.issueCommandWrite`.
## CLI `--run-hooks`
| Command | Flag | Effect |
| --- | --- | --- |
| `orca worktree create` | `--run-hooks` | Force setup; imply `--activate`; may print warning if setup still skipped |
| `orca worktree rm` | `--run-hooks` | Run archive hook before git removal |
Without `--run-hooks`, runtime RPC returns a warning string (also printed to stderr in text mode):
- `orca.yaml setup hook skipped for <path>; pass --run-hooks to run it.`
- `orca.yaml archive hook skipped for <path>; pass --run-hooks to run it.`
JSON output includes the same `warning` field on the result object.
Desktop `worktree rm` maps trust **skip** to `skipArchive` / `runHooks: false` on the runtime call. Desktop create does not use `--run-hooks`; it uses `setupDecision` and trust instead.
## SSH and remote runtimes
On SSH-connected repos, `orca.yaml` is read through the remote filesystem provider. Archive/setup execution uses remote non-interactive exec with the same env var semantics where applicable. Trust hashing for shared setup can use remote file content. Local-only policy still prevents trusting or running shared scripts you intentionally overrode in settings.
## Verification
<Steps>
<Step title="Confirm shared hooks are visible">
In Orca, open repository settings and confirm the `orca.yaml` card shows **Using `orca.yaml`** (or add the file from the example template).
</Step>
<Step title="Create with CLI hooks">
```bash
orca worktree create --repo path:/path/to/repo --name hook-test --run-hooks --json
```
Expect no setup `warning` in JSON when `scripts.setup` exists and policy allows run.
</Step>
<Step title="Remove with archive hook">
```bash
orca worktree rm --worktree path:/path/to/repo/.git/worktrees/hook-test --run-hooks --json
```
Archive runs before removal when `scripts.archive` is defined.
</Step>
<Step title="Trust gate in UI">
Change `scripts.setup` in `orca.yaml`, create a worktree from the composer, and confirm the trust dialog shows the new script hash before execution.
</Step>
</Steps>
## Related pages
<CardGroup>
<Card title="Worktrees and repos" href="/worktrees-and-repos">
Git worktree registration, create/remove semantics, and how Orca paths relate to hooks.
</Card>
<Card title="CLI core reference" href="/cli-core-reference">
Full `worktree create` and `worktree rm` flags including `--run-hooks` and `--activate`.
</Card>
<Card title="Terminals and agents" href="/terminals-and-agents">
How setup and issue-command runners attach to the first terminal and agent startup.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
Remote `orca.yaml` reads and hook execution over SSH.
</Card>
<Card title="Settings reference" href="/settings-reference">
Where `RepoHookSettings` and global UI trust state persist.
</Card>
</CardGroup>
---
## 08. CLI scripting workflow
> End-to-end agent automation with `orca open`, `orca status`, repo/worktree discovery, terminal read/send/wait, file open helpers, and worktree comment updates at checkpoints.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/08-cli-scripting-workflow.md
- Generated: 2026-06-01T20:06:53.240Z
### Source Files
- `src/cli/index.ts`
- `src/cli/dispatch.ts`
- `src/cli/runtime-client.ts`
- `src/cli/specs/core.ts`
- `src/cli/specs/file.ts`
- `src/cli/help.ts`
- `skills/orca-cli/SKILL.md`
---
title: "CLI scripting workflow"
description: "End-to-end agent automation with `orca open`, `orca status`, repo/worktree discovery, terminal read/send/wait, file open helpers, and worktree comment updates at checkpoints."
---
The `orca` command-line interface is a Node entrypoint (`src/cli/index.ts`) that parses argv, validates command specs before any runtime lookup, constructs a `RuntimeClient`, and dispatches to thin handlers that call Orca runtime RPC methods over local metadata transport or a paired remote WebSocket. Scripting workflows treat Orca as the source of truth for repos, worktrees, terminals, and editor tabs—use `--json` for machine-driven loops and selectors plus runtime-issued terminal handles for repeated live I/O.
## Preconditions
| Requirement | Verification |
| --- | --- |
| Public CLI on `PATH` | `command -v orca` (macOS/Windows); Linux installs expose `orca` via `resources/linux/bin/orca` while the desktop binary is `orca-ide` to avoid the GNOME Orca screen reader |
| Running runtime | `orca status --json` → `result.runtime.reachable: true` |
| Machine output | Pass `--json` on every scripted call |
<Note>
On Linux, substitute `orca-ide` only when referring to the desktop app binary. The scripting command name remains `orca` for the packaged launcher symlink.
</Note>
Remote runtimes accept `--pairing-code` or `--environment` (or `ORCA_PAIRING_CODE` / `ORCA_ENVIRONMENT`). `active` and `current` worktree selectors resolve from the client machine `cwd` and are invalid against a remote runtime—use explicit server-side selectors such as `id:<id>`, `branch:<branch>`, `issue:<number>`, or `path:<absolute-server-path>`.
## Control-plane flow
```mermaid
sequenceDiagram
participant Agent as Script / agent
participant CLI as src/cli/index.ts
participant RC as RuntimeClient
participant RT as Orca runtime RPC
Agent->>CLI: orca <command> [--json]
CLI->>CLI: validateCommandAndFlags
CLI->>RC: new RuntimeClient(pairing, environment)
alt Local desktop runtime
RC->>RT: sendRequest(metadata, method, params)
else Remote paired runtime
RC->>RT: sendWebSocketRequest(pairing, method, params)
end
RT-->>RC: RuntimeRpcSuccess / failure
RC-->>CLI: result
CLI-->>Agent: human text or JSON (prepareCliJsonResult)
```
Syntax and flag errors are reported before runtime contact so typos do not surface as false “Orca is not running” messages.
## Startup and health
### `orca open`
Launches Orca when the runtime is unreachable: `open` calls `RuntimeClient.openOrca()`, which reads status, invokes `launchOrcaApp()` if needed, then polls `getCliStatus()` every 250 ms until `runtime.reachable` or a 15 s timeout (`runtime_open_timeout`). Override launch with `ORCA_OPEN_COMMAND` or `ORCA_APP_EXECUTABLE`.
<RequestExample>
```bash
orca open --json
```
</RequestExample>
### `orca status`
Returns app PID, runtime state, reachability, runtime id, and graph state (`CliStatusResult`). In human mode, `status` sets exit code `1` when `runtime.reachable` is false. JSON mode always prints the structured payload; callers inspect `result.runtime.reachable`.
<RequestExample>
```bash
orca status --json
```
</RequestExample>
<Warning>
When the runtime is down, human-mode errors append `Run 'orca open' first.` JSON failures use `RuntimeRpcFailure` or local `RuntimeClientError` shapes from `reportCliError`.
</Warning>
Headless automation without a desktop window uses `orca serve` (foreground runtime server). Pairing and saved environments are covered on the runtime environments page.
## Discovery: repos and worktrees
Use selectors for identity and handles for live terminals.
### Repo selectors
| Form | Example |
| --- | --- |
| `id:<repoId>` | `id:repo-abc` |
| `name:<name>` | `name:orca` |
| `path:<absolute-path>` | `path:/Users/me/projects/orca` |
```bash
orca repo list --json
orca repo show --repo id:<repoId> --json
orca repo add --path /abs/repo --json
```
### Worktree selectors
| Form | Resolves |
| --- | --- |
| `id:<worktreeId>` | Stable runtime id (preferred after `worktree current`) |
| `path:<absolute-path>` | Filesystem path |
| `branch:<branch-name>` | Git branch |
| `issue:<number>` | Linked issue number |
| `active` / `current` | Enclosing Orca-managed worktree from shell `cwd` (local only) |
```bash
orca worktree ps --json # compact cross-worktree summary
orca worktree list --json
orca worktree current --json # cwd → id:… via worktree.list + path match
orca worktree show --worktree branch:feature-x --json
```
`worktree ps` rows include repo, branch, live terminal count, PTY attachment, unread flag, path, and optional preview—useful as the first map when many workspaces exist.
### Create and metadata
```bash
orca worktree create \
--repo id:<repoId> \
--name my-task \
--issue 123 \
--comment "seed" \
--json
orca worktree set --worktree active --comment "reproduced auth failure; running integration tests" --json
```
`worktree create` infers parent lineage from `ORCA_TERMINAL_HANDLE`, caller `cwd`, or `--parent-worktree`. Use `--no-parent` for independent work. `--activate` reveals the workspace in the UI; `--run-hooks` forces setup hooks and implies activation.
<Tip>
Treat `orca worktree set --worktree active --comment "…" --json` as a best-effort checkpoint whenever work inside an Orca-managed tree reaches a meaningful state (repro found, fix landed, blocked on review). Skip trivial command noise; write short status snapshots.
</Tip>
## Terminal scripting loop
Terminals are runtime-scoped. After Orca reloads, handles may return `terminal_handle_stale`—re-list and adopt a fresh handle.
### Resolve a handle
```bash
orca terminal list --worktree id:<worktreeId> --json
# → terminals[].handle, preview, connected, worktreePath
```
Omit `--terminal` to target the **active** terminal in the current (or `--worktree`) scope via `terminal.resolveActive`.
### Read → act → wait → read
<Steps>
<Step title="Baseline transcript">
```bash
orca terminal read --terminal term_abc --json
```
Default read returns a bounded tail plus cursor fields: `nextCursor`, `oldestCursor`, `latestCursor`, `limited`, `truncated`.
</Step>
<Step title="Send input (non-agent shells)">
```bash
orca terminal send --terminal term_abc --text "pnpm test" --enter --json
```
`--interrupt` sends interrupt-style input when supported. Use `terminal send` for shell/build commands—not for agent-to-agent messaging (see orchestration).
</Step>
<Step title="Wait for completion">
```bash
orca terminal wait --terminal term_abc --for exit --timeout-ms 600000 --json
```
`--for tui-idle` waits for recognized agent TUIs (OSC title working→idle). Unsatisfied waits set exit code `1` even in JSON mode. Client RPC timeout extends to `timeoutMs + 5000` (minimum five-minute cap) so long waits are not cut off at the default 60 s transport limit.
</Step>
<Step title="Page retained output">
After a limited tail, page from `oldestCursor`:
```bash
orca terminal read --terminal term_abc --cursor 0 --limit 1000 --json
```
Continue with `nextCursor` while `limited` is true and `nextCursor !== latestCursor`. If `truncated` is true, older lines left the retained buffer.
</Step>
</Steps>
### Create and boot agent terminals
```bash
orca terminal create --worktree active --title "Agent" --command "claude" --json
orca terminal wait --terminal term_new --for tui-idle --timeout-ms 120000 --json
```
Local interactive agent commands (`codex`, `claude`, etc.) may use the renderer-backed terminal path for correct geometry. `terminal create` stays in the background unless `--focus` is passed. Remote `terminal create` requires an explicit `--worktree` because client `cwd` does not identify a server worktree.
## File helpers
Paths are relative to the selected worktree. Without `--worktree`, local commands infer the enclosing worktree from `cwd`; remote commands require `--worktree`.
```bash
orca file open src/App.tsx --json
orca file diff src/App.tsx --staged --json
orca file open-changed --mode diff --worktree active --json
```
`file open-changed` loads `git.status` for the worktree, then opens targets per `--mode`:
| Mode | Behavior |
| --- | --- |
| `diff` (default) | Opens unstaged/staged diffs; skips unresolved conflicts without a single diff target |
| `edit` | Opens modified/untracked files; skips deleted paths |
| `both` | Edit plus diff where applicable |
## Example end-to-end script
```bash
#!/usr/bin/env bash
set -euo pipefail
command -v orca >/dev/null
orca open --json >/dev/null
orca status --json | jq -e '.result.runtime.reachable' >/dev/null
WT=$(orca worktree current --json | jq -r '.result.worktree.id')
orca worktree set --worktree "id:${WT}" --comment "script: starting test run" --json >/dev/null
HANDLE=$(orca terminal list --worktree "id:${WT}" --json \
| jq -r '.result.terminals[0].handle')
CURSOR=$(orca terminal read --terminal "$HANDLE" --json | jq -r '.result.terminal.nextCursor')
orca terminal send --terminal "$HANDLE" --text "pnpm test" --enter --json
orca terminal wait --terminal "$HANDLE" --for exit --timeout-ms 900000 --json
orca terminal read --terminal "$HANDLE" --cursor "$CURSOR" --limit 2000 --json \
| jq -r '.result.terminal.tail[]'
orca worktree set --worktree "id:${WT}" --comment "script: tests finished" --json
```
## Global flags and JSON shape
<ParamField body="json" type="boolean">
Emit `RuntimeRpcSuccess` JSON (`ok`, `result`, `_meta`) via `prepareCliJsonResult`. Failures print a failure object when `ok: false`.
</ParamField>
<ParamField body="pairing-code" type="string">
Connect to a remote runtime with an `orca://pair#…` code. Mutually exclusive with `--environment`. Suppressed for `environment` and `serve` command groups.
</ParamField>
<ParamField body="environment" type="string">
Connect using a saved environment id or name from `orca environment list`.
</ParamField>
Human `status` output fields: `appRunning`, `pid`, `runtimeState`, `runtimeReachable`, `runtimeId`, `graphState`.
## Agent vs orchestration boundary
| Surface | Use for |
| --- | --- |
| `orca terminal read` / `wait` | Observe any Orca-managed terminal, including agent TUIs |
| `orca terminal send` | Shell commands, builds, prompts typed into **non-agent** PTYs |
| `orca orchestration send` / `check` / `reply` | Structured agent-to-agent messages, task DAGs, coordinator loops (experimental; requires runtime) |
Reading and waiting stay in the terminal CLI; messaging another coding agent belongs in orchestration.
## Common failure modes
| Code / signal | Meaning | Recovery |
| --- | --- | --- |
| `runtime_unavailable` | No reachable runtime | `orca open`, or start `orca serve` / desktop app |
| `runtime_open_timeout` | Launch poll exceeded 15 s | Start Orca manually; check `ORCA_OPEN_COMMAND` |
| `selector_not_found` | No worktree contains `cwd` | `orca worktree list`; create or `cd` into a managed tree |
| `terminal_handle_stale` | Handle invalid after reload | `orca terminal list --json` and re-select |
| `wait.satisfied: false` | Timeout or idle not detected | Increase `--timeout-ms`; re-read transcript |
| Exit code `1` on `status` | Human mode, runtime down | Use `--json` and branch on `reachable` |
<Info>
Flag validation runs in `index.ts` before `RuntimeClient` construction. Unknown commands fail with `invalid_argument` from dispatch, not from the runtime.
</Info>
## Command map (scripting-focused)
| Group | Commands |
| --- | --- |
| Startup | `open`, `status`, `serve` |
| Repo | `repo list`, `add`, `show`, `set-base-ref`, `search-refs` |
| Worktree | `worktree list`, `show`, `current`, `create`, `set`, `rm`, `ps` |
| Terminal | `terminal list`, `show`, `read`, `send`, `wait`, `stop`, `create`, `split`, `switch`, `close`, `rename` |
| File | `file open`, `diff`, `open-changed` |
Handler registration lives in `src/cli/dispatch.ts` (`CORE_HANDLERS`, `WORKTREE_HANDLERS`, `TERMINAL_HANDLERS`, `FILE_HANDLERS`). Command grammar and examples are defined in `src/cli/specs/core.ts`, `src/cli/specs/file.ts`, and `src/cli/help.ts`. Agent-oriented conventions are summarized in `skills/orca-cli/SKILL.md`.
## Related pages
<CardGroup>
<Card title="CLI core reference" href="/cli-core-reference">
Flags and signatures for `open`, `status`, `repo`, `worktree`, `terminal`, and `file` command groups.
</Card>
<Card title="Selectors and JSON output" href="/selectors-json-output">
Selector grammar, `--json` shapes, and remote-runtime resolution rules.
</Card>
<Card title="Terminals and agents" href="/terminals-and-agents">
PTY lifecycle, agent detection, `ORCA_*` env injection, and when to prefer orchestration messaging.
</Card>
<Card title="Worktrees and repos" href="/worktrees-and-repos">
Worktree create/remove semantics, lineage, sparse checkout, and workspace statuses.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
Headless `orca serve`, pairing codes, and `--environment` targeting.
</Card>
<Card title="Agent orchestration" href="/agent-orchestration">
Multi-agent `send` / `check` / `reply`, task DAGs, and coordinator `run`.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Runtime reachability, stale handles, wait timeouts, and remote selector constraints.
</Card>
</CardGroup>
---
## 09. Agent orchestration
> Multi-agent coordination: messaging (`send`/`check`/`reply`), task DAGs, dispatch/inject, coordinator `run`, decision gates, and group addresses—requires experimental feature and running runtime.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/09-agent-orchestration.md
- Generated: 2026-06-01T20:08:29.654Z
### Source Files
- `src/cli/specs/orchestration.ts`
- `src/cli/handlers/orchestration.ts`
- `src/main/runtime/orchestration/db.ts`
- `src/main/runtime/orchestration/formatter.ts`
- `skills/orchestration/SKILL.md`
- `src/cli/help.ts`
---
title: "Agent orchestration"
description: "Multi-agent coordination: messaging (`send`/`check`/`reply`), task DAGs, dispatch/inject, coordinator `run`, decision gates, and group addresses—requires experimental feature and running runtime."
---
Orca’s `orca orchestration` command group talks to the running desktop or headless runtime over RPC. The runtime persists mail, tasks, dispatch contexts, decision gates, and coordinator runs in SQLite (`orchestration.db` under Electron `userData`), injects unread mail into agent terminals on TUI idle, and runs an optional background coordinator loop. Enable **Agent Orchestration** in **Settings → Orchestration** (`orca.orchestration.enabled` in `localStorage`) and keep Orca running (`orca status --json` with `runtime: true`) before using these commands.
## Architecture
```mermaid
flowchart TB
subgraph cli["CLI handlers"]
OC[orca orchestration *]
end
subgraph rpc["Runtime RPC (`orchestration.*` methods)"]
SEND[send / check / reply / inbox / ask]
TASK[taskCreate / taskList / taskUpdate]
DISP[dispatch / dispatchShow]
RUN[run / runStop]
GATE[gateCreate / gateResolve / gateList]
RESET[reset]
end
subgraph store["SQLite (`OrchestrationDb`)"]
MSG[(messages)]
TSK[(tasks)]
CTX[(dispatch_contexts)]
GAT[(decision_gates)]
CRN[(coordinator_runs)]
end
subgraph runtime["Orca runtime"]
PTY[PTY inject on idle]
COORD[Coordinator loop]
end
OC --> rpc
SEND --> MSG
TASK --> TSK
DISP --> CTX
DISP --> PTY
RUN --> COORD
COORD --> TSK
COORD --> CTX
COORD --> MSG
GATE --> GAT
RESET --> store
PTY --> MSG
```
| Layer | Responsibility |
| --- | --- |
| CLI | Parses flags, resolves `--from` / `--terminal` via `ORCA_TERMINAL_HANDLE` or active terminal selector, calls RPC |
| RPC (`orchestration.*`) | Validation, group fan-out, long-poll `check --wait`, dispatch preamble build/inject |
| `OrchestrationDb` | WAL SQLite schema, DAG promotion, dispatch circuit breaker, gate blocking |
| `Coordinator` | Poll loop: read coordinator inbox, dispatch ready tasks, stale-heartbeat warnings |
| PTY path | Push-on-idle delivery of undelivered banners (`formatMessagesForInjection`) |
<Note>
Orchestration is for **agent-to-agent** coordination. Use `orca terminal` (see [CLI scripting workflow](/cli-scripting-workflow)) for shell I/O, `orca worktree` for git workspaces, and `orca orchestration` only when messaging tasks between agent terminals.
</Note>
## Prerequisites
<Steps>
<Step title="Runtime reachable">
Confirm the Orca app or `orca serve` runtime is up: `orca status --json` should report a ready runtime.
</Step>
<Step title="Enable orchestration">
In **Settings → Orchestration**, turn on **Agent Orchestration**. Optionally install the bundled `orchestration` agent skill from the same pane so workers know the CLI contract.
</Step>
<Step title="CLI on PATH">
Register the CLI from **Settings → Browser → Enable Orca CLI** (or use `orca-dev` when developing against a dev user-data directory).
</Step>
<Step title="Agent terminals">
Create terminals with a recognized agent CLI (`claude`, `codex`, etc.) before `dispatch --inject`. Each Orca-managed PTY receives `ORCA_TERMINAL_HANDLE` for default `--from` / `--terminal` resolution.
</Step>
</Steps>
## Terminal handle resolution
Handlers resolve the sending or checking terminal in this order:
1. Explicit `--from` or `--terminal`
2. `ORCA_TERMINAL_HANDLE` in the process environment (injected at PTY spawn)
3. Active terminal for the current worktree via CLI selectors
`orchestration ask` extends the RPC client timeout to `timeoutMs + 5000` so the default 60s transport limit does not abort a long question wait.
## Messaging
Inter-agent mail is stored per recipient with independent `read` and `delivered_at` tracking. Unread rows returned by `check` (default) are marked read; push-on-idle injection uses `delivered_at` so the same message is not re-injected on every idle transition.
### Commands
| Command | Purpose |
| --- | --- |
| `orca orchestration send` | Point-to-point or group fan-out |
| `orca orchestration check` | Read inbox for a handle; optional inject, wait, type filter |
| `orca orchestration reply` | Reply in-thread to a message id |
| `orca orchestration inbox` | Global or per-handle history (`--full` includes body/payload) |
| `orca orchestration ask` | Send `decision_gate`, block until reply (not in `orca help` summary yet) |
```bash
orca orchestration send --to <handle|@group> --subject "<text>" \
[--from <handle>] [--body <text>] [--type <type>] [--priority <level>] \
[--thread-id <id>] [--payload <json>] [--json]
orca orchestration check [--terminal <handle>] [--unread | --all] \
[--types <type,...>] [--inject] [--wait] [--timeout-ms <n>] [--json]
orca orchestration reply --id <msg_id> --body "<text>" [--from <handle>] [--json]
```
### Message types and priority
| `type` values | Typical use |
| --- | --- |
| `status` | General updates (default) |
| `dispatch` | Work assignment metadata |
| `worker_done` | Structured task completion |
| `merge_ready` | Branch ready to merge |
| `escalation` | Blocker before completion |
| `handoff` | Pass work to another agent |
| `decision_gate` | Human or coordinator decision |
| `heartbeat` | Liveness during long tasks |
Priority: `normal`, `high`, `urgent` (banner tags `[HIGH]` / `[URGENT]` when injected).
### `check` behavior
| Flag | Behavior |
| --- | --- |
| *(default)* | Unread only; marks returned rows read |
| `--all` | All messages for handle; does not mark read |
| `--inject` | Adds `formatted` banner text for prompt injection |
| `--wait` | Blocks until a matching message or timeout (default inner wait **2 minutes** when `--timeout-ms` omitted) |
| `--types` | Comma-separated filter (e.g. `worker_done,escalation`) |
With `--wait`, the CLI emits JSON heartbeat objects on **stderr** every 15s (`_heartbeat: true`) so parent processes know the subprocess is alive. Filter with `jq 'select(._heartbeat|not)'` when merging streams.
### Group addresses
Addresses starting with `@` fan out to one `messages` row per recipient, sharing `thread_id`, with per-recipient read state. Resolution happens at send time in the runtime group resolver.
| Address | Recipients |
| --- | --- |
| `@all` | All terminal handles except sender |
| `@idle` | Handles whose agent TUI status is `idle` |
| `@claude`, `@codex`, `@opencode`, `@gemini` | Handles whose title contains the agent name (case-insensitive group token) |
| `@worktree:<id>` | Handles in the given worktree, except sender |
Empty resolution throws `No recipients resolved for group address`. `orchestration ask` **rejects** group addresses; use `send --type decision_gate` for fan-out questions.
### `ask`
```bash
orca orchestration ask --to <handle> --question "<text>" \
[--options <csv>] [--timeout-ms <n>] [--from <handle>] [--json]
```
Default wait **600000 ms** (10 minutes). On timeout, exit code is **1**. With `--json`, stdout is a **bare** JSON object (`answer`, `messageId`, `threadId`, `timedOut`) — not the usual RPC envelope — so `jq -r .answer` works directly.
## Task DAG
Tasks live in `tasks` with a JSON `deps` array of task ids. Tasks without deps start as `ready`; tasks with deps start as `pending` until every dependency is `completed`, then promotion runs synchronously inside `updateTaskStatus`.
### Task statuses
```mermaid
stateDiagram-v2
[*] --> pending: deps non-empty
[*] --> ready: no deps
pending --> ready: all deps completed
ready --> dispatched: dispatch
dispatched --> completed: worker_done / task-update
dispatched --> ready: dispatch failed (< 3 failures)
dispatched --> failed: circuit_broken
ready --> blocked: gate-create
blocked --> ready: gate-resolve
ready --> failed: task-update / circuit
```
| Status | Meaning |
| --- | --- |
| `pending` | Waiting on dependencies |
| `ready` | Dispatchable |
| `dispatched` | Active dispatch context |
| `completed` | Done; promotes dependents |
| `failed` | Terminal failure (including circuit breaker) |
| `blocked` | Held by a pending decision gate |
### Commands
```bash
orca orchestration task-create --spec "<text>" \
[--deps '["task_a","task_b"]'] [--parent <task_id>] [--json]
orca orchestration task-list [--status <status>] [--ready] [--json]
orca orchestration task-update --id <task_id> --status <status> \
[--result <json>] [--json]
```
`task-list` joins active dispatch metadata (`assignee_handle`, `dispatch_id`) for `dispatched` rows. `task-create` records `ORCA_TERMINAL_HANDLE` as `created_by_terminal_handle` when set (used for worktree lineage).
## Dispatch and preamble
Dispatch uses a **sling** pattern: the `tasks` row stays canonical; `dispatch_contexts` track each assignment, failures, and heartbeats. A terminal may hold only one active dispatch (`pending` or `dispatched`).
```bash
orca orchestration dispatch --task <task_id> --to <handle> \
[--from <handle>] [--inject] [--dry-run] [--return-preamble] [--json]
orca orchestration dispatch-show --task <task_id> [--preamble] [--from <handle>] [--json]
```
| Flag | Behavior |
| --- | --- |
| `--inject` | Sends built preamble + task spec into the PTY (`enter: true`). Requires a recognized agent in the target terminal |
| `--dry-run` | Returns preamble only; no state change; `--to` optional |
| `--return-preamble` | Includes preamble text in the RPC result for auditing |
The dispatch preamble documents required `worker_done` and periodic `heartbeat` payloads (5-minute cadence; coordinator stale warning at ~10 minutes without heartbeat). In dev user-data paths, preamble uses `orca-dev` instead of `orca`.
**Circuit breaker:** after **3** consecutive dispatch failures for a task, the context becomes `circuit_broken` and the task moves to `failed`. Failed dispatches otherwise return the task to `ready` for retry.
**Stale base guard (coordinator):** when `orchestration run --worktree` is set, dispatch may skip tasks whose worktree is more than **20** commits behind tracking branch unless the task spec includes a final line `allow-stale-base: true` (stripped from the worker-facing spec).
## Decision gates
Gates block a task and complete its active dispatch until resolved.
```bash
orca orchestration gate-create --task <task_id> --question "<text>" \
[--options '["A","B"]'] [--json]
orca orchestration gate-resolve --id <gate_id> --resolution "<text>" [--json]
orca orchestration gate-list [--task <task_id>] [--status <status>] [--json]
```
| Gate status | Meaning |
| --- | --- |
| `pending` | Awaiting resolution |
| `resolved` | `gate-resolve` applied; task set back to `ready` |
| `timeout` | Timed out (DB supports; coordinator does not auto-resolve) |
Workers can also send `--type decision_gate` messages; the coordinator creates gates from structured payloads.
## Coordinator loop
```bash
orca orchestration run --spec "<text>" \
[--from <handle>] [--poll-interval-ms <n>] [--max-concurrent <n>] \
[--worktree <selector>] [--json]
orca orchestration run-stop [--json]
```
| Behavior | Detail |
| --- | --- |
| Concurrency | Default **4** simultaneous `dispatched` tasks (`--max-concurrent`) |
| Poll interval | Default **2000 ms** (`--poll-interval-ms` on run record) |
| Single flier | Second `run` throws if a coordinator run is already `running` |
| Return | `run` returns immediately with `runId`; loop runs in the background |
| Task creation | **Does not** decompose `--spec` into tasks; create tasks with `task-create` first |
| Stop | `run-stop` signals the in-process `Coordinator` to stop; may mark run `failed` if tasks remain incomplete |
Coordinator phases (in-memory): `decomposing` → `dispatching` → `monitoring` → `merging` → `done`. Progress is observed via `task-list`, coordinator logs, and message traffic—not a blocking RPC on `run`.
```mermaid
sequenceDiagram
participant C as Coordinator terminal
participant R as Runtime RPC
participant W as Worker terminal
C->>R: task-create (DAG)
C->>R: run (background)
R->>W: dispatch --inject (preamble)
W->>R: send --type worker_done
R->>C: check / coordinator inbox
R->>R: promote dependent tasks to ready
```
## Reset
```bash
orca orchestration reset [--all] [--tasks] [--messages] [--json]
```
If no scope flag is passed, **`--all`** is assumed (messages, tasks, dispatches, gates, coordinator runs). `--tasks` clears orchestration state but keeps messages.
## JSON and errors
Pass `--json` on any orchestration verb for structured RPC results (except `ask`, noted above). Invalid task status values are rejected client-side with an enum list before RPC. Common runtime errors include unknown task/message ids, dispatch to busy terminal, `inject` without agent, and `Coordinator already running`.
Remote runtimes follow the same RPC names; use global `--environment` / `--pairing-code` as documented on [Selectors and JSON output](/selectors-json-output).
## Verification
```bash
orca status --json
orca terminal list --json
orca orchestration task-create --spec "Smoke task" --json
orca orchestration task-list --ready --json
orca orchestration send --to <peer_handle> --subject "ping" --json
orca orchestration check --wait --timeout-ms 5000 --json
```
Expect `runtime: true`, stable terminal handles for the session, and message/task ids prefixed with `msg_`, `task_`, `ctx_`, `gate_`, `run_`.
## Related pages
<CardGroup>
<Card title="Terminals and agents" href="/terminals-and-agents">
PTY handles, `ORCA_*` env, TUI idle detection, and when to use terminal CLI vs orchestration mail.
</Card>
<Card title="CLI scripting workflow" href="/cli-scripting-workflow">
`orca terminal` read/send/wait for shell automation alongside orchestration messaging.
</Card>
<Card title="CLI core reference" href="/cli-core-reference">
Global flags, selectors, and command groups including orchestration entry points.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
Headless `orca serve` and targeting remote runtimes from the CLI.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Runtime unreachable, orchestration timeouts, and stale terminal handles after restart.
</Card>
</CardGroup>
---
## 10. SSH remote worktrees
> SSH targets, relay grace periods, remote repo registration, remote PTY leases, agent-hook forwarding over SSH, and CLI selector constraints on paired runtimes.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/10-ssh-remote-worktrees.md
- Generated: 2026-06-01T20:08:08.886Z
### Source Files
- `src/shared/ssh-types.ts`
- `src/shared/types.ts`
- `tests/e2e/ssh-localhost.spec.ts`
- `src/cli/selectors.ts`
- `src/main/ipc/runtime-environments.ts`
- `AGENTS.md`
---
title: "SSH remote worktrees"
description: "SSH targets, relay grace periods, remote repo registration, remote PTY leases, agent-hook forwarding over SSH, and CLI selector constraints on paired runtimes."
---
Orca’s desktop app connects to remote hosts over SSH, deploys a versioned **relay** process on the server, and routes git, filesystem, and PTY operations through that relay. Remote repositories are tagged with a `connectionId` (the SSH target id); terminals run on the remote machine while the UI stays local. A separate **paired runtime** path (`orca` CLI with `--environment` or `--pairing-code`) talks to `orca serve` over WebSocket and imposes additional worktree selector rules—distinct from SSH wiring but documented here because the same CLI surfaces both modes.
```mermaid
flowchart LR
subgraph local["Local (Electron main)"]
UI["Renderer / store"]
IPC["ipc/ssh + repos:addRemote"]
Session["SshRelaySession"]
Hooks["agentHookServer.ingestRemote"]
end
subgraph ssh["SSH transport"]
Conn["SshConnection"]
Mux["SshChannelMultiplexer"]
end
subgraph remote["Remote host"]
Relay["orca relay"]
PTY["Remote PTYs + git/fs"]
end
UI --> IPC --> Session
Session --> Conn --> Mux --> Relay
Relay --> PTY
Relay -->|agent.hook notification| Mux --> Hooks --> UI
```
## SSH targets
An SSH target is a persisted `SshTarget` record (settings UI and `window.api.ssh.*`). Required fields are `id`, `label`, `host`, `port`, and `username`. Optional fields mirror OpenSSH config resolution:
| Field | Role |
| --- | --- |
| `configHost` | Host alias for `ssh -G` lookup (defaults to `label` on load if missing) |
| `identityFile` / `identityAgent` / `identitiesOnly` | Key-based auth |
| `proxyCommand` / `jumpHost` | ProxyJump / ProxyCommand |
| `relayGracePeriodSeconds` | Relay shutdown delay after disconnect (see below) |
| `lastRequiredPassphrase` | Whether the last connect needed a credential prompt (startup reconnect partitioning) |
| `portForwards` | Saved local→remote forwards restored on connect |
Preload IPC covers target CRUD, connect/disconnect, relay reset, port forwards, remote directory browse, and credential prompts (`ssh:addTarget`, `ssh:connect`, `ssh:disconnect`, `ssh:resetRelay`, `ssh:terminateSessions`, etc.).
<Note>
SSH targets are **not** runtime environments. Pairing a CLI to `orca serve` uses saved environments and pairing codes (`runtimeEnvironments:*`), documented on the runtime environments page.
</Note>
## Connection and relay lifecycle
Connect serializes per target (`connectInFlight`) so two concurrent `ssh:connect` calls cannot spawn duplicate relay sessions.
| `SshConnectionStatus` | Meaning |
| --- | --- |
| `connecting` | SSH handshake in progress |
| `auth-failed` | Authentication failed |
| `deploying-relay` | Relay binary deploy / attach |
| `connected` | SSH up and relay ready |
| `reconnecting` | Relay channel lost; bounded backoff retry |
| `reconnection-failed` | Backoff exhausted |
| `disconnected` / `error` | Idle or terminal failure |
`SshRelaySession.establish()` deploys the relay, verifies it with `session.resolveHome`, registers providers (PTY, filesystem, git), installs remote agent hooks when enabled, reattaches known PTYs, then sends `relay.configureGraceTime`. `detach()` on `ssh:disconnect` tears down local providers but **does not** kill remote PTYs—the relay grace timer keeps them alive. `dispose()` / explicit terminate paths mark leases `terminated` and stop remote processes.
Relay channel loss while SSH stays up triggers exponential backoff (base 500 ms, max 15 s, up to six attempts) before surfacing `reconnection-failed`. Version mismatch is terminal (no backoff).
Runtime RPC exposes `ssh.getState` and `ssh.connect` for headless callers that share the same registered handlers.
## Relay grace periods
Grace time controls how long the relay keeps remote PTYs (and workspace state) after Orca disconnects.
| Constant | Value |
| --- | --- |
| `DEFAULT_SSH_RELAY_GRACE_PERIOD_SECONDS` | 10 800 (3 hours) |
| `MIN_SSH_RELAY_GRACE_PERIOD_SECONDS` | 60 |
| `MAX_SSH_RELAY_GRACE_PERIOD_SECONDS` | 604 800 (7 days) |
| `SSH_RELAY_CONFIGURE_GRACE_TIME_METHOD` | `relay.configureGraceTime` |
Behavior:
- **Unset** on a target → default 3 hours at configure time.
- **`0`** → relay stays up until manual reset (“keep alive until reset” in settings). Host sleep calls `prepareForHostSleep()` and pushes grace `0` immediately.
- **Other values** → clamped to \[60, 604 800\] before notify (except `0`).
Settings validate the same bounds. Legacy persisted fields `remoteWorkspaceSyncGracePeriodSeconds` migrate into `relayGracePeriodSeconds` on load.
## Remote repo registration
`repos:addRemote` registers a repository on a **connected** SSH target:
<ParamField body="connectionId" type="string" required>
SSH target id returned from `ssh:addTarget` / `ssh:connect`.
</ParamField>
<ParamField body="remotePath" type="string" required>
Absolute path on the remote host, or `~` / `~/…` (resolved via `session.resolveHome` on the relay).
</ParamField>
<ParamField body="displayName" type="string">
Optional display name; for `~/` without a name, the SSH target label is used.
</ParamField>
<ParamField body="kind" type="'git' | 'folder'">
Default `git`. Non-git paths return an error unless `kind: 'folder'` (same UX as local add-repo).
</ParamField>
The handler requires `getSshGitProvider(connectionId)`. Duplicate `(connectionId, path)` returns the existing repo. Successful registration sets `Repo.connectionId` and notifies the relay with `session.registerRoot` so filesystem ACLs scope writes to that workspace root.
Worktrees for remote repos are listed and managed like local ones, but git/fs/terminal operations dispatch through SSH providers keyed by `connectionId`.
## Remote PTY identity and leases
Relay-local PTY ids (for example `pty-1`) are not globally unique. The app encodes them as:
```text
ssh:{encodeURIComponent(connectionId)}@@{relayPtyId}
```
Helpers `toAppSshPtyId`, `toRelaySshPtyId`, and `parseAppSshPtyId` translate between app-facing and relay-facing ids.
**`SshRemotePtyLease`** records are persisted in store state (`sshRemotePtyLeases`) per target:
| Field | Purpose |
| --- | --- |
| `targetId`, `ptyId` | Target-local relay id (storage normalizes app ids back to relay ids) |
| `worktreeId`, `tabId`, `leafId` | UI binding for reattach |
| `state` | `attached` \| `detached` \| `terminated` \| `expired` |
| `createdAt`, `updatedAt`, `lastAttachedAt`, `lastDetachedAt` | Timestamps |
Lifecycle highlights:
- **Disconnect** → leases → `detached`; remote PTYs survive in the relay.
- **Reattach** (`reattachKnownPtys`) → merges in-memory ownership with durable leases inside the grace window; success → `attached`, missing PTY → `expired` and synthetic `pty:exit`.
- **PTY exit** → `terminated`.
- **`ssh:terminateSessions`** → kills remote PTYs; may require reconnect first (`SSH_TERMINATE_RECONNECT_REQUIRED`) when disconnect left preserved sessions.
Remote terminals receive Orca env vars including `ORCA_PANE_KEY` (`tabId:leafId`), `ORCA_TAB_ID`, `ORCA_WORKTREE_ID`, and agent-hook variables when hooks are enabled.
## Agent-hook forwarding over SSH
Remote agent status is on by default. Set `ORCA_FEATURE_REMOTE_AGENT_HOOKS=0` (or empty) to opt out.
<Steps>
<Step title="Relay and main process wire-up">
On connect, `SshRelaySession` installs managed hook configs on the remote home (SFTP), ships OpenCode/Pi plugin sources via `agent_hook.installPlugins`, and subscribes to mux notifications.
</Step>
<Step title="PTY → relay → Orca">
Agents POST to `http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/<source>` on the **remote** loopback. The relay normalizes payloads and sends JSON-RPC notification `agent.hook` with `connectionId: null` on the wire.
</Step>
<Step title="Trust boundary ingest">
`agentHookServer.ingestRemote()` re-validates the payload and stamps the real `connectionId` from the SSH target id. Renderer agent status and `agentStatus.onSet` events include `connectionId` and `worktreeId` for remote panes.
</Step>
<Step title="Reconnect replay">
After wiring, Orca calls `agent_hook.requestReplay` so cached per-`paneKey` payloads survive channel gaps.
</Step>
</Steps>
Injected PTY environment (when hooks are active) includes `ORCA_AGENT_HOOK_PORT`, `ORCA_AGENT_HOOK_TOKEN`, `ORCA_AGENT_HOOK_ENV`, `ORCA_AGENT_HOOK_VERSION`, plus pane/worktree keys. Plugin overlays use `OPENCODE_CONFIG_DIR` and `PI_CODING_AGENT_DIR` paths materialized on the remote.
<Warning>
Treat remote hook POST bodies as untrusted: `ingestRemote` re-runs canonical normalizers at the SSH boundary before updating main-process state.
</Warning>
## Port forwards and detected ports
`SavedPortForward` on the target restores forwards after reconnect. `PortForwardEntry` ties forwards to `connectionId` and can carry `advertisedUrl` / `advertisedProtocol` when a remote PTY prints dev-server URLs; the main process rewrites them to local forwarded ports for the embedded browser.
## CLI selector constraints on paired runtimes
When the CLI sets `RuntimeClient.isRemote` (via `ORCA_PAIRING_CODE`, `ORCA_REMOTE_PAIRING`, or `ORCA_ENVIRONMENT`), the **client machine’s cwd is not the server’s filesystem**. Selectors and defaults change accordingly:
| Surface | Local behavior | Remote (`isRemote`) constraint |
| --- | --- | --- |
| `active` / `current` worktree | Resolves enclosing worktree from cwd | **Rejected** — use `id:`, `branch:`, `issue:`, or `path:` with **server-absolute** paths |
| `terminal create` | Can infer worktree from cwd | Requires `--worktree` |
| `file` commands | Can default worktree from cwd | Requires explicit `--worktree` |
| `repo add --path` | Resolves relative to cwd | Path must be absolute on the remote server |
| Browser/computer targets | Auto-resolve worktree from cwd | Omit `--worktree` to use server-side focus, or pass explicit server selectors |
Automations can also target `{ type: 'ssh', connectionId }` for external scheduler integrations—separate from CLI pairing but sharing the same SSH target ids.
<Info>
Desktop SSH worktrees do not require CLI remote pairing. A local `orca` CLI against your laptop runtime still uses `active`/`current`; only WebSocket-paired invocations hit the remote selector rules.
</Info>
## Verification
Localhost SSH E2E is gated and documents expected signals:
| Variable | Purpose |
| --- | --- |
| `ORCA_E2E_SSH_LOCALHOST=1` | Enable `tests/e2e/ssh-localhost.spec.ts` |
| `ORCA_E2E_SSH_HOST`, `ORCA_E2E_SSH_PORT`, `ORCA_E2E_SSH_USER` | Target host |
| `ORCA_E2E_SSH_CONFIG_HOST`, `ORCA_E2E_SSH_IDENTITY_FILE` | OpenSSH alias / key |
| `ORCA_FEATURE_REMOTE_AGENT_HOOKS` | Unset or non-`0` for hook/pane-key tests |
The test connects with `relayGracePeriodSeconds: 1`, registers a remote repo, verifies terminal markers, `ORCA_PANE_KEY` / hook env, Codex hook → renderer status, and Ctrl+C / Escape interrupt semantics.
## Related pages
<CardGroup>
<Card title="Worktrees and repos" href="/worktrees-and-repos">
Repo registration, worktree create/list/remove, and `connectionId` on `Repo`.
</Card>
<Card title="Terminals and agents" href="/terminals-and-agents">
PTY lifecycle, `TuiAgent` detection, `ORCA_*` env, and terminal CLI vs orchestration.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
`orca serve`, pairing codes, and `--environment` targeting (CLI remote mode).
</Card>
<Card title="Selectors and JSON output" href="/selectors-json-output">
Selector grammar, `--json`, and remote-runtime resolution errors.
</Card>
<Card title="Scheduled automations" href="/scheduled-automations">
Cron automations with `ssh` execution targets.
</Card>
<Card title="Testing" href="/testing">
Vitest, Playwright E2E, and SSH localhost env flags.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Runtime reachability, selector ambiguity, and SSH relay failures.
</Card>
</CardGroup>
---
## 11. Scheduled automations
> Create and run cron/RRULE automations via CLI: triggers, provider agents, workspace modes, session reuse, run history, and SSH execution targets.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/11-scheduled-automations.md
- Generated: 2026-06-01T20:08:16.920Z
### Source Files
- `src/cli/specs/automations.ts`
- `src/cli/handlers/automations.ts`
- `src/shared/automations-types.ts`
- `src/main/automations/service.ts`
- `docs/automations-navigation-stack.md`
- `skills/orca-cli/SKILL.md`
---
title: "Scheduled automations"
description: "Create and run cron/RRULE automations via CLI: triggers, provider agents, workspace modes, session reuse, run history, and SSH execution targets."
---
Orca persists scheduled automations in the main-process store and exposes them through the `orca automations` command group, which calls runtime RPC methods (`automation.list`, `automation.create`, `automation.runNow`, and related). A background `AutomationService` evaluates due schedules every 60 seconds and asks the renderer to dispatch agent terminal sessions; runs are recorded with statuses, optional output snapshots, and provider usage when available.
```mermaid
sequenceDiagram
participant CLI as orca automations
participant RPC as Orca runtime RPC
participant Store as persistence Store
participant Svc as AutomationService
participant UI as Renderer dispatch
CLI->>RPC: automation.create / edit / run
RPC->>Store: createAutomation / updateAutomation
Svc->>Store: listAutomations / createAutomationRun
Svc->>UI: automations:dispatchRequested
UI->>UI: SSH connect / worktree / agent terminal
UI->>RPC: automations:markDispatchResult
RPC->>Store: updateAutomationRun
```
## Prerequisites
<Note>
On Linux, the public CLI is `orca-ide` (not `orca`) to avoid conflicting with GNOME Orca. Substitute `orca-ide` everywhere this page says `orca`.
</Note>
- A running Orca editor or headless runtime reachable by the CLI (`orca status --json`).
- For scheduled firing (not just CRUD): an Orca window with a ready renderer. Without it, due runs are recorded as `skipped_unavailable`.
- Automations are persisted in Orca state; do not edit automation storage files directly.
## Command surface
| Command | Purpose |
| --- | --- |
| `orca automations list [--json]` | List all automations |
| `orca automations show <id> [--json]` | Show one automation |
| `orca automations create ...` | Create a scheduled automation |
| `orca automations edit <id> ...` | Patch fields on an automation |
| `orca automations remove <id> [--json]` | Delete automation and its run history |
| `orca automations run <id> [--json]` | Trigger a manual run now |
| `orca automations runs [--id <automation-id>] [--json]` | List run history |
All commands accept global CLI flags such as `--json`, `--environment`, and `--pairing-code` when targeting a remote runtime. See [Selectors and JSON output](/selectors-json-output).
### Create and edit flags
<ParamField body="--name" type="string" required>
Display name for the automation.
</ParamField>
<ParamField body="--prompt" type="string" required>
Text submitted to the agent on each run (create only).
</ParamField>
<ParamField body="--provider" type="string" required>
Agent id (`TuiAgent`) to launch — for example `codex`, `claude`, `gemini`. Must be a known provider or the CLI returns `Unknown provider`.
</ParamField>
<ParamField body="--trigger" type="string" required>
Schedule preset, 5-field cron, or RRULE string. Alias: `--schedule` (mutually exclusive with `--trigger`).
</ParamField>
<ParamField body="--repo" type="selector">
Repo selector (`id:`, `path:`, etc.). Mutually exclusive with `--workspace`.
</ParamField>
<ParamField body="--workspace" type="selector">
Worktree selector (`id:`, `path:`, `branch:`, `active`, `current`). Mutually exclusive with `--repo`.
</ParamField>
<ParamField body="--workspace-mode" type="string">
`existing` or `new-per-run` / `new_per_run`. Default: `existing` when `--workspace` resolves; otherwise `new_per_run`.
</ParamField>
<ParamField body="--base-branch" type="string">
Base branch for `new_per_run` automations only.
</ParamField>
<ParamField body="--time" type="string">
`HH:MM` (24-hour). Default `09:00`. Only with preset triggers `daily`, `weekdays`, or `weekly` — not `hourly` or custom cron/RRULE.
</ParamField>
<ParamField body="--day" type="integer">
Day of week `0`–`6` (Sunday = `0`). Only with preset `weekly`.
</ParamField>
<ParamField body="--timezone" type="string">
IANA timezone string stored on the record. Defaults to the runtime host timezone when omitted at create.
</ParamField>
<ParamField body="--enabled / --disabled" type="boolean">
Toggle whether the scheduler fires the automation. Flags take no value.
</ParamField>
<ParamField body="--missed-run-grace-minutes" type="positive integer">
Minutes after a scheduled time Orca may still run a missed occurrence. Default **720** (12 hours).
</ParamField>
<ParamField body="--reuse-session / --fresh-session" type="boolean">
Control session reuse for `existing` workspace automations. Mutually exclusive. Flags take no value.
</ParamField>
## Schedule triggers
The CLI normalizes presets into RRULE strings stored on the automation. Custom schedules pass through as either a **5-field cron** expression or an **RRULE** string (detected by presence of `=`).
| `--trigger` value | Stored / behavior |
| --- | --- |
| `hourly` | `FREQ=HOURLY;BYMINUTE=<minute>` (default minute `0`) |
| `daily` | `FREQ=DAILY;BYHOUR=<h>;BYMINUTE=<m>` |
| `weekdays` | `FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=...;BYMINUTE=...` |
| `weekly` | `FREQ=WEEKLY;BYDAY=<day>;BYHOUR=...;BYMINUTE=...` (default day Monday / `1`) |
| `"0 9 * * 1-5"` | Five-field cron (minute hour day-of-month month day-of-week) |
| `FREQ=...;BYHOUR=...` | RRULE |
<Warning>
`manual` triggers are rejected. For on-demand runs only, create with `--disabled` and use `orca automations run <id>`.
</Warning>
Occurrence math (`nextRunAt`, missed-run detection) lives in `automation-schedules.ts` and uses the **host local calendar** (`Date` setHours / day scans). The stored `timezone` field is metadata today; do not assume IANA timezone conversion drives firing unless product behavior changes.
**Hourly minute:** use a cron trigger such as `"30 * * * *"` instead of `--time` on the hourly preset.
## Workspace modes
| Mode | CLI | Per-run behavior |
| --- | --- | --- |
| `existing` | `--workspace <selector>` or default enclosing worktree from `cwd` | Runs in the bound worktree (`workspaceId`). |
| `new_per_run` | `--repo <selector>` (or repo implied from workspace) | Renderer creates a new worktree per run (`auto-<slug>-<timestamp>` naming). |
**Default target resolution (create):**
1. If `--repo` or `--workspace` is set, use that (not both).
2. Else on a **local** CLI client, resolve the enclosing Orca worktree from `cwd` as `existing`.
3. Else (remote CLI with no explicit target), omit target and the runtime may require `--repo` or `--workspace`.
**Edit constraints:**
- Switching an `existing` automation to a different repo requires `--workspace-mode new-per-run`.
- `reuseSession` is cleared when retargeting to `new_per_run`.
## Provider agents
`--provider` maps to `agentId` and must be a valid `TuiAgent` (Claude Code, Codex, Gemini CLI, Pi, Grok CLI, and the rest of Orca's agent catalog). Automations launch agents through the same background terminal path as other Orca agent sessions.
<Info>
Token/cost usage is collected after **completed** runs for **local** `claude` and `codex` automations from local usage logs. SSH automations report `remote_usage_unavailable`; other agents may report `provider_unsupported`.
</Info>
## Session reuse
For `workspaceMode: existing` only:
- `--reuse-session`: later runs try to submit the prompt into the previous run's live terminal when the agent pane is still `done` and the tab still exists.
- `--fresh-session`: disables reuse on edit.
- If reuse cannot find a live session, dispatch falls back to a fresh background agent session.
Runtime enforces: `reuseSession: true` on create/update is rejected unless `workspaceMode` is `existing`.
## Run lifecycle and history
Each firing creates an `AutomationRun` with `trigger` `scheduled` or `manual` (`orca automations run`).
| Status | Meaning |
| --- | --- |
| `pending` | Run record created |
| `dispatching` | Dispatch requested; waiting on renderer |
| `dispatched` | Agent terminal launched; work in progress |
| `completed` | Finished successfully |
| `dispatch_failed` | Launch or agent exit with error |
| `skipped_missed` | Scheduled time passed beyond grace window while Orca was unavailable |
| `skipped_unavailable` | No Orca window, missing repo/workspace, or SSH connect failure |
| `skipped_needs_interactive_auth` | SSH target needs interactive passphrase |
`orca automations remove` deletes the automation **and all run history**. `orca automations runs` returns runs newest-first; filter with `--id`.
Run records retain `workspaceDisplayName` after workspace deletion, store optional `outputSnapshot` (plain text), and attach `usage` when collection succeeds.
## Scheduler and missed runs
- `AutomationService` ticks every **60 seconds** (`DEFAULT_TICK_MS`).
- Policy: `missedRunPolicy: run_once_within_grace` with configurable `missedRunGraceMinutes` (default **720**).
- When `now - scheduledFor > grace`, the run is marked `skipped_missed` and `nextRunAt` advances.
- Duplicate runs for the same `(automationId, scheduledFor)` are deduplicated in the store.
<Check>
Use `--disabled` when creating automations during setup or tests so nothing fires before review. Enable later with `orca automations edit <id> --enabled`.
</Check>
## SSH execution targets
When the automation's repo is registered with an SSH `connectionId`, persistence sets:
- `executionTargetType: ssh`
- `executionTargetId: <connectionId>`
- `schedulerOwner: ssh_bridge`
Before dispatch, the renderer checks SSH state: interactive passphrase prompts yield `skipped_needs_interactive_auth`; connection failures yield `skipped_unavailable`. SSH automations only run while Orca can reach the host.
<Warning>
Do not assume local usage/cost metrics for SSH automations; usage collection is skipped with `remote_usage_unavailable`.
</Warning>
## Examples
<Steps>
<Step title="Daily review in the current worktree">
From inside an Orca-managed worktree:
```bash
orca automations create \
--name "Daily review" \
--trigger daily \
--time 09:00 \
--prompt "Review open changes and summarize risk" \
--provider codex \
--json
```
Verify:
```bash
orca automations list --json
orca automations show <automationId> --json
```
</Step>
<Step title="Weekday cron on a repo (new worktree per run)">
```bash
orca automations create \
--name "Weekday triage" \
--trigger "0 9 * * 1-5" \
--prompt "Triage open issues" \
--provider claude \
--repo path:/abs/path/to/repo \
--workspace-mode new-per-run \
--base-branch main \
--json
```
</Step>
<Step title="Reuse agent session in an existing workspace">
```bash
orca automations create \
--name "Inbox digest" \
--trigger hourly \
--prompt "Summarize unread notifications" \
--provider codex \
--workspace active \
--reuse-session \
--json
```
</Step>
<Step title="Manual run and inspect history">
```bash
orca automations run <automationId> --json
orca automations runs --id <automationId> --json
```
</Step>
</Steps>
### JSON shape (automation)
<ResponseField name="automation" type="object">
Core fields returned by create/show/edit/list: `id`, `name`, `prompt`, `agentId`, `projectId`, `workspaceMode`, `workspaceId`, `baseBranch`, `reuseSession`, `rrule`, `dtstart`, `enabled`, `nextRunAt`, `lastRunAt`, `missedRunGraceMinutes`, `executionTargetType`, `executionTargetId`, `schedulerOwner`, `timezone`.
</ResponseField>
## RPC methods (runtime)
| Method | Role |
| --- | --- |
| `automation.list` | List automations |
| `automation.show` | Fetch one by id |
| `automation.create` | Resolve repo/workspace, validate schedule, persist |
| `automation.update` | Partial update (`updates` object) |
| `automation.delete` | Remove automation and runs |
| `automation.runNow` | Manual run via `AutomationService` |
| `automation.runs` | List runs (optional `automationId`) |
The CLI never bypasses these methods for native Orca automations.
## Troubleshooting
| Symptom | Likely cause |
| --- | --- |
| `Missing required --trigger` | Create without `--trigger` / `--schedule` |
| `Use either --trigger or --schedule, not both` | Both flags set |
| `Manual-only automations are not supported yet` | `--trigger manual` |
| `Unknown provider` | Invalid `--provider` |
| `Use either --repo or --workspace, not both` | Conflicting targets |
| `Automation requires --repo or --workspace` | Remote CLI or cwd outside any Orca worktree without explicit target |
| `Session reuse requires an existing workspace target` | `--reuse-session` with `new_per_run` |
| Runs stay `skipped_unavailable` | Orca closed, renderer not ready, or missing project/workspace |
| Runs `skipped_needs_interactive_auth` | SSH host needs interactive credentials |
| Runs `skipped_missed` | Machine offline longer than grace window |
| No token/cost on run | SSH target, non-claude/codex agent, or incomplete run |
## Related pages
<CardGroup>
<Card title="CLI scripting workflow" href="/cli-scripting-workflow">
Terminal read/send/wait and worktree comments alongside automations.
</Card>
<Card title="Terminals and agents" href="/terminals-and-agents">
Provider catalog, agent terminals, and `TuiAgent` detection.
</Card>
<Card title="Worktrees and repos" href="/worktrees-and-repos">
Repo registration, worktree selectors, and `new_per_run` workspace creation.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
SSH targets, connection state, and remote repo constraints.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
Target a remote Orca runtime with `--environment` or `--pairing-code`.
</Card>
<Card title="CLI core reference" href="/cli-core-reference">
Full flag inventory for `orca` command groups.
</Card>
</CardGroup>
---
## 12. Browser automation
> Automate Orca's embedded browser: accessibility snapshots, interaction commands, tabs/profiles, capture/intercept/storage, and `orca exec` passthrough to agent-browser.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/12-browser-automation.md
- Generated: 2026-06-01T20:08:03.973Z
### Source Files
- `src/cli/specs/browser-basic.ts`
- `src/cli/specs/browser-advanced.ts`
- `src/main/browser/browser-manager.ts`
- `src/main/browser/agent-browser-bridge.ts`
- `src/cli/handlers/browser-interact.ts`
- `src/cli/handlers/browser-tab.ts`
- `package.json`
---
title: "Browser automation"
description: "Automate Orca's embedded browser: accessibility snapshots, interaction commands, tabs/profiles, capture/intercept/storage, and `orca exec` passthrough to agent-browser."
---
The `orca` CLI drives Orca's embedded Electron `<webview>` guests through runtime RPC methods (`browser.*`). Each open browser page gets a per-tab `agent-browser` session backed by a localhost CDP WebSocket proxy (`CdpWsProxy`) that exposes only that guest to automation, while `BrowserManager` owns guest registration, navigation policy, and paintability leases for hidden tabs.
## Architecture
```mermaid
sequenceDiagram
participant CLI as orca CLI
participant RPC as Runtime RPC
participant RT as OrcaRuntimeBrowser
participant Bridge as AgentBrowserBridge
participant Proxy as CdpWsProxy
participant AB as agent-browser binary
participant Guest as webview guest
CLI->>RPC: browser.snapshot / browser.click / ...
RPC->>RT: browserSnapshot(params)
RT->>Bridge: snapshot(worktreeId, browserPageId)
Bridge->>Bridge: enqueue per orca-tab-{pageId}
Bridge->>Proxy: ensureSession + --cdp port
Bridge->>AB: exec --session orca-tab-{id} snapshot --json
AB->>Proxy: CDP over ws://127.0.0.1:PORT
Proxy->>Guest: debugger commands
AB-->>Bridge: JSON stdout
Bridge-->>CLI: BrowserSnapshotResult
```
| Layer | Responsibility |
| --- | --- |
| CLI handlers (`src/cli/handlers/browser-*.ts`) | Parse flags, resolve worktree/page target, call RPC, format human or `--json` output |
| `OrcaRuntimeBrowser` | Resolve worktree/page to live `webContents`, tab create/close/profile IPC |
| `AgentBrowserBridge` | Per-tab command queue, session lifecycle, `agent-browser` subprocess execution |
| `CdpWsProxy` | Local HTTP `/json` + WebSocket CDP bridge scoped to one guest |
| `BrowserManager` | Guest registration maps, anti-detection injection, `acquireAutomationVisibility`, downloads/popups |
<Note>
Browser automation requires a running Orca desktop app (or paired runtime) with at least one registered browser page. Commands do not launch a headless browser on their own.
</Note>
## Prerequisites
<Steps>
<Step title="Start Orca and open a browser tab">
Create or focus a worktree, open the browser pane, and load a page (or run `orca tab create --url <url>`).
</Step>
<Step title="Verify connectivity">
From the worktree directory (or with explicit selectors):
```bash
orca status
orca tab list --json
```
</Step>
<Step title="Prefer JSON for agents">
Pass `--json` on every command so output is structured and stable for scripting.
</Step>
</Steps>
On Linux, the public command may be `orca-ide` instead of `orca` (same RPC surface).
## Snapshot–interact loop
Automation centers on accessibility snapshots that assign short element refs (`@e1`, `@e3`, …). Interact by ref, then re-snapshot after navigation or DOM changes.
```bash
orca goto --url https://example.com --json
orca snapshot --json
orca click --element @e3 --json
orca snapshot --json
```
<ParamField body="snapshot" type="BrowserSnapshotResult">
Returns `browserPageId`, `url`, `title`, a text `snapshot` (accessibility tree), and `refs: { ref, role, name }[]`.
</ParamField>
| Ref rule | Behavior |
| --- | --- |
| Assigned at snapshot time | Only refs from the latest snapshot on that tab are valid |
| Tab scope | Refs from one `browserPageId` are invalid on another tab |
| Navigation | Full navigation invalidates refs → `browser_stale_ref` |
| Tab switch | `orca tab switch` changes active tab and invalidates refs on the new target until re-snapshot |
## Command targeting
### Worktree scope
By default, browser commands see only tabs belonging to the **current worktree** (resolved from cwd). Tab indices in `orca tab list` and `orca tab switch --index` are relative to that filtered list.
<ParamField body="--worktree" type="selector">
`active` / `current` (cwd worktree), `id:<uuid>`, `path:<dir>`, `branch:<name>`, or `all` for cross-worktree tab listing.
</ParamField>
```bash
orca snapshot --json # current worktree, active tab
orca snapshot --worktree all --json # all worktrees (tab list only)
orca tab list --worktree id:wt-abc --json
```
If no live tab exists in scope, RPC returns `browser_no_tab`.
### Stable page identity
For concurrent CLI processes or scripts, pin a tab with `--page <browserPageId>` from `orca tab list --json` instead of relying on ambient active-tab state.
```bash
orca tab list --json
orca snapshot --page page-7f2a --json
orca click --page page-7f2a --element @e2 --json
```
When `--page` is set, `--worktree` is optional validation/scoping; without `--page`, commands use the active tab in the resolved worktree.
## Navigation and observation
| Command | RPC method | Notes |
| --- | --- | --- |
| `orca goto --url <url>` | `browser.goto` | 60s CLI RPC timeout (network idle) |
| `orca back` / `forward` / `reload` | `browser.back` / `browser.forward` / `browser.reload` | `reload` uses Electron `webContents.reload()` to avoid process-swap session loss |
| `orca snapshot` | `browser.snapshot` | Fresh refs; bypasses stale-ref guard |
| `orca screenshot [--format png\|jpeg]` | `browser.screenshot` | Viewport; base64 in JSON |
| `orca full-screenshot` | `browser.fullPageScreenshot` | Full page via direct CDP capture |
| `orca pdf` | `browser.pdf` | `webContents.printToPDF()` (not agent-browser CDP) |
| `orca eval --expression <js>` | `browser.eval` | Page-context JavaScript |
| `orca wait …` | `browser.wait` | See wait modes below |
### Wait modes
```bash
orca wait --timeout 1000 --json
orca wait --selector ".results" [--state hidden] [--timeout 30000] --json
orca wait --text "Dashboard" --timeout 30000 --json
orca wait --url "/dashboard" --timeout 30000 --json
orca wait --load networkidle --timeout 60000 --json
orca wait --fn "document.querySelector('.ok')" --timeout 30000 --json
```
`browser.wait` uses a 60s default CLI RPC timeout. Condition waits pass a bridge-level timeout (`timeout + 1s` grace) so missing selectors surface as `browser_timeout` instead of hanging until the generic RPC deadline.
## Interaction commands
Refs are passed as `--element <ref>` (e.g. `@e5`).
| Command | RPC | Purpose |
| --- | --- | --- |
| `click`, `dblclick`, `hover` | `browser.click`, `browser.dblclick`, `browser.hover` | Pointer actions |
| `fill --value` | `browser.fill` | Clear + set value (bridge uses focus + JS setter for webview compatibility) |
| `type --input` | `browser.type` | Type at current focus |
| `select --value` | `browser.select` | Dropdown |
| `check` / `uncheck` | `browser.check` | Checkbox/radio |
| `scroll --direction up\|down [--amount]` | `browser.scroll` | Viewport scroll |
| `scrollintoview --element` | `browser.scrollIntoView` | Element into view |
| `focus`, `clear`, `select-all` | `browser.focus`, `browser.clear`, `browser.selectAll` | Focus and text selection |
| `keypress --key` | `browser.keypress` | Named keys (Enter, Tab, ArrowDown, …) |
| `drag --from --to` | `browser.drag` | Drag between refs |
| `upload --files path,…` | `browser.upload` | File input |
| `get --what` / `is --what` | `browser.get`, `browser.is` | Property and state queries |
| `find --locator --value --action` | `browser.find` | Semantic locators (role, text, label, …) |
| `inserttext --text` | `browser.keyboardInsertText` | Text without key events |
| `mouse move\|down\|up\|wheel` | `browser.mouseMove`, etc. | Low-level mouse; `mouse click` uses direct CDP on the guest |
| `download`, `highlight` | `browser.download`, `browser.highlight` | File download trigger, visual highlight |
## Tab and profile management
### Tabs
| Command | RPC | Behavior |
| --- | --- | --- |
| `orca tab list [--show-profile]` | `browser.tabList` | Lists tabs; `tab list` is side-effect free (does not change active tab) |
| `orca tab current` | `browser.tabCurrent` | Active tab for worktree scope |
| `orca tab show --page <id>` | `browser.tabShow` | Metadata for one page |
| `orca tab switch (--index n \| --page id) [--focus]` | `browser.tabSwitch` | Updates per-worktree active tab; `--focus` surfaces browser UI when already on that worktree |
| `orca tab create [--url] [--profile]` | `browser.tabCreate` | 60s RPC timeout |
| `orca tab close [--index]` | `browser.tabClose` | Destroys agent-browser session for that page |
`AgentBrowserBridge` names sessions `orca-tab-{browserPageId}`, serializes commands per session, and destroys sessions on tab close or Chromium process swap (intercept patterns may be restored on next init).
### Session profiles
Profiles isolate cookies/storage partitions for browser tabs.
```bash
orca tab profile list --json
orca tab profile create --label "Staging" --scope isolated --json
orca tab profile set --page <id> --profile <profileId> --json
orca tab profile show --page <id> --json
orca tab profile use-default --page <id> --json
orca tab profile clone --profile <id> [--page <id>] --json
orca tab profile delete --profile <id> --json
```
<ParamField body="--scope" type="isolated | imported">
`isolated` (default) creates a fresh partition; `imported` is for profiles that import browser state (registry may reject invalid scopes).
</ParamField>
## Cookies, storage, and emulation
| Area | CLI examples | RPC prefix |
| --- | --- | --- |
| Cookies | `cookie get`, `cookie set --name --value`, `cookie delete --name` | `browser.cookie*` via agent-browser `cookies` |
| Viewport | `viewport --width --height [--scale] [--mobile]` | `browser.setViewport` (CDP `Emulation.setDeviceMetricsOverride`; `--mobile` applied in bridge) |
| Geolocation | `geolocation --latitude --longitude` | `browser.setGeolocation` |
| Device / network | `set device`, `set offline`, `set headers`, `set credentials`, `set media` | `browser.set*` |
| Storage | `storage local get\|set\|clear`, `storage session …` | `browser.storage.local.*`, `browser.storage.session.*` |
| Clipboard | `clipboard read`, `clipboard write --text` | `browser.clipboard*` |
| Dialogs | `dialog accept [--text]`, `dialog dismiss` | `browser.dialog*` |
## Capture, intercept, and network tooling
```bash
orca capture start --json
orca capture stop --json
orca console [--limit n] --json
orca network [--limit n] --json
```
| Command | Implementation note |
| --- | --- |
| `intercept enable [--patterns glob,...]` | Maps to agent-browser `network route <pattern>`; first pattern used; state restored after session recreate |
| `intercept disable` | `network unroute` |
| `intercept list` | `network requests` (paused/intercepted listing) |
<Warning>
Per-request `intercept continue` / `intercept block` are **not** implemented yet. agent-browser currently supports URL-pattern routing only, not per-request decisions.
</Warning>
`capture start` / `stop` wrap agent-browser HAR capture (`network har start|stop`). Console and network log commands read captured buffers.
## `orca exec` passthrough
`orca exec --command "<agent-browser subcommand>"` forwards arbitrary agent-browser syntax through `browser.exec`. Orca injects `--session` and `--cdp` itself; user-supplied `--cdp` and `--session` flags are stripped to prevent target hijacking.
```bash
orca exec --command 'get text @e1' --json
orca exec --command 'find role button click --name "Submit"' --json
orca exec --command 'frame main' --json
```
Use `exec` for keyboard helpers (`keyboard type`, `keydown`/`keyup`), frame switching, semantic `find`, and agent-browser features without a dedicated top-level `orca` subcommand. Prefer first-class `orca click`, `orca fill`, etc. when available—they share the same queue and error translation.
## Hidden tabs and screenshots
Inactive browser panes are not painted in the renderer (`display:none`). Before most commands, `BrowserManager.acquireAutomationVisibility` acquires a renderer lease so the target guest can paint **without** switching the user's visible Orca tab. Screenshot paths additionally serialize captures globally and wait ~300–500ms after the lease for compositor settle.
Background throttling is disabled on guests so CDP screenshots work when Orca is not the focused app.
## Error codes
| Code | Typical cause | Recovery |
| --- | --- | --- |
| `browser_no_tab` | No registered/live tab in worktree | `tab create` or open browser UI |
| `browser_tab_not_found` | Bad `--page` or index out of range | `tab list --json` |
| `browser_tab_closed` | Tab closed while command queued/running | Re-create tab |
| `browser_stale_ref` | Ref from old snapshot/navigation | `snapshot` again |
| `browser_timeout` | Condition wait exceeded | Increase `--timeout`, fix selector |
| `browser_error` | Generic agent-browser/CDP failure | Read message; retry after `reload` |
Stale-ref detection maps agent-browser messages matching `unknown ref`, `ref not found`, etc. to `browser_stale_ref`.
<RequestExample>
```bash
orca snapshot --json
```
</RequestExample>
<ResponseExample>
```json
{
"browserPageId": "page-abc",
"url": "https://example.com/",
"title": "Example",
"snapshot": "- button \"More\" [ref=@e1]\n ...",
"refs": [{ "ref": "@e1", "role": "button", "name": "More" }]
}
```
</ResponseExample>
## Agent workflow checklist
1. `orca tab list --json` — discover `browserPageId` values when multiple tabs or agents run in parallel.
2. `orca snapshot --page <id> --json` — capture refs after every navigation or major UI change.
3. Interact with `--page` pinned; avoid bare active-tab assumptions in concurrent scripts.
4. Prefer `orca wait --text` / `--url` / `--load` over blind `wait --timeout`.
5. On `browser_stale_ref`, re-snapshot; on `browser_no_tab`, create or switch tabs explicitly.
6. Use `orca exec` only for agent-browser features without a dedicated Orca wrapper.
<Info>
The bundled `agent-browser` package (see `package.json`, currently `~0.24.1`) ships a platform-specific native binary under `resources/` in production builds or `node_modules/agent-browser/bin/` in dev. Bridge command timeout is 90s per subprocess call; three consecutive timeouts destroy and recreate the session.
</Info>
## Related pages
<CardGroup>
<Card title="CLI browser and computer reference" href="/cli-browser-computer-reference">
Full command signatures for browser and `computer` automation.
</Card>
<Card title="CLI scripting workflow" href="/cli-scripting-workflow">
End-to-end agent automation with terminals, worktrees, and checkpoints.
</Card>
<Card title="Selectors and JSON output" href="/selectors-json-output">
`--worktree`, `--page`, `--json`, and selector grammar.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
Target a remote Orca runtime with `--environment` or `--pairing-code`.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Runtime reachability, selector ambiguity, and timeout failures.
</Card>
</CardGroup>
---
## 13. Source control integrations
> Built-in review flows for GitHub, GitLab, and Linear: PR/MR/issue linking on worktrees, hosted review creation, diff annotation, and provider-specific IPC surfaces.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/13-source-control-integrations.md
- Generated: 2026-06-01T20:09:26.439Z
### Source Files
- `src/preload/api-types.ts`
- `src/preload/gitlab.ts`
- `src/shared/hosted-review.ts`
- `src/shared/work-items.ts`
- `src/main/text-generation/pull-request-context.ts`
- `src/shared/types.ts`
- `README.md`
---
title: "Source control integrations"
description: "Built-in review flows for GitHub, GitLab, and Linear: PR/MR/issue linking on worktrees, hosted review creation, diff annotation, and provider-specific IPC surfaces."
---
Orca wires GitHub (`gh`), GitLab (`glab`), and Linear (API key) through Electron preload namespaces (`window.api.gh`, `window.api.gl`, `window.api.linear`, `window.api.hostedReview`), main-process IPC handlers under `src/main/ipc/`, and shared types in `src/shared/`. Worktrees persist provider links and local diff notes in `Worktree` / `WorktreeMeta`; hosted review creation and branch status share a provider-neutral layer in `src/shared/hosted-review.ts` and `src/main/source-control/`.
## Architecture
```mermaid
flowchart TB
subgraph renderer["Renderer"]
Tasks["Tasks / PR pages / diff UI"]
Store["Zustand: worktrees, hosted-review cache"]
end
subgraph preload["Preload"]
gh["api.gh"]
gl["api.gl"]
linear["api.linear"]
hr["api.hostedReview"]
end
subgraph main["Main process"]
IPCgh["ipc/github.ts → gh:*"]
IPCgl["ipc/gitlab.ts → gitlab:*"]
IPClin["ipc/linear.ts → linear:*"]
IPChr["ipc/hosted-review.ts"]
GHcli["github/client (gh CLI)"]
GLcli["gitlab/client (glab CLI)"]
LINcli["linear/client (GraphQL)"]
HR["source-control/hosted-review*"]
end
Tasks --> Store
Store --> gh & gl & linear & hr
gh --> IPCgh --> GHcli
gl --> IPCgl --> GLcli
linear --> IPClin --> LINcli
hr --> IPChr --> HR
HR --> GHcli & GLcli
```
GitLab bindings live in a dedicated preload module (`src/preload/gitlab.ts`) composed into `api.gl` so the large central preload file does not churn on every `gitlab:*` channel change. Type contracts for renderer and preload stay synchronized in `src/preload/api-types.ts`.
## Task providers and settings
Three first-class task providers are defined in `src/shared/task-providers.ts`:
| Provider | Availability gate | Backend |
| --- | --- | --- |
| `github` | Always available | `gh` CLI + registered repo path |
| `gitlab` | `gitlabInstalled` (CLI present) | `glab` CLI |
| `linear` | `linearConnected` (API key stored) | Linear GraphQL client |
<ParamField body="defaultTaskSource" type="TaskProvider">
Last-used Tasks tab (`github` \| `gitlab` \| `linear`). Normalized so a hidden default is re-added to `visibleTaskProviders`.
</ParamField>
<ParamField body="visibleTaskProviders" type="TaskProvider[]">
Providers shown in Tasks chrome and sidebar shortcuts. At least one must remain visible.
</ParamField>
<ParamField body="defaultLinearTeamSelection" type="string[] | null">
`null` = sticky-all teams; a string array freezes the subset (invalid IDs dropped on load).
</ParamField>
<ParamField body="githubProjects" type="GitHubProjectSettings">
Pinned/recent GitHub Projects (V2) and per-project view selection.
</ParamField>
<ParamField body="gitlabProjects" type="GitLabProjectSettings">
Pinned/recent GitLab project paths for cross-project navigation.
</ParamField>
<ParamField body="enableGitHubAttribution" type="boolean">
When branch prefix mode uses git username, can incorporate GitHub identity for naming.
</ParamField>
<ParamField body="markdownReviewToolsEnabled" type="boolean">
Shows local markdown review note controls and the review panel (separate from hosted PR threads).
</ParamField>
## Worktree linking
Each `Worktree` stores parallel link slots so a repo with both GitHub and GitLab remotes stays unambiguous:
| Field | Provider | Type |
| --- | --- | --- |
| `linkedPR` | GitHub | `number \| null` |
| `linkedIssue` | GitHub | `number \| null` |
| `linkedGitLabMR` | GitLab | `number \| null` (optional on older records) |
| `linkedGitLabIssue` | GitLab | `number \| null` |
| `linkedLinearIssue` | Linear | `string \| null` (issue id) |
`CreateWorktreeArgs` accepts the same fields at creation time. Workspace cards can surface `issue`, `linear-issue`, `pr`, and `comment` via `WorktreeCardProperty` (`src/shared/worktree-card-properties.ts`).
Branch-scoped hosted review lookup passes linked numbers into `HostedReviewForBranchArgs` (`linkedGitHubPR`, `linkedGitLabMR`, plus optional Bitbucket/Azure DevOps/Gitea slots). GitHub also supports `fallbackGitHubPR` when no explicit link exists but a cached PR number is known.
<Warning>
Cross-repo GitHub work items for SSH-only remotes require a GitHub remote; otherwise list/detail calls fail with `GitHub work items require a GitHub remote for SSH repositories` (`src/shared/work-items.ts`).
</Warning>
## Hosted review (PR / MR)
`HostedReviewProvider` covers `github`, `gitlab`, `bitbucket`, `azure-devops`, `gitea`, and `unsupported`. Provider detection for a worktree follows the publishing remote: GitLab project slug first, then GitHub repo slug, then other hosts (`detectHostedReviewProvider` in `hosted-review-creation.ts`).
### IPC and runtime RPC
| Surface | Methods |
| --- | --- |
| Preload `hostedReview` | `forBranch`, `getCreationEligibility`, `create` |
| IPC channels | `hostedReview:forBranch`, `hostedReview:getCreationEligibility`, `hostedReview:create` |
| Runtime RPC | `hostedReview.forBranch`, `hostedReview.getCreationEligibility`, `hostedReview.create` |
Handlers assert the repo is registered in the Orca store and, for create/eligibility, that `worktreePath` belongs to that repo (including SSH remote path normalization).
### `HostedReviewInfo`
Normalized review card fields: `provider`, `number`, `title`, `state` (`open` \| `closed` \| `merged` \| `draft`), `url`, `status` (check rollup), `updatedAt`, `mergeable`, optional `headSha` and `conflictSummary`.
### Creation eligibility and errors
`getHostedReviewCreationEligibility` returns `canCreate`, `blockedReason`, `nextAction`, optional `defaultBaseRef` / `head` / `title` / `body`, and an `existingReview` summary.
| `blockedReason` | Typical `nextAction` |
| --- | --- |
| `dirty` | `commit` |
| `needs_push` | `push` |
| `needs_sync` | `sync` |
| `auth_required` | `authenticate` |
| `existing_review` | `open_existing_review` |
| `unsupported_provider` | — |
`createHostedReview` error codes include `auth_required`, `already_exists`, `validation`, `push_failed`, `timeout`, and `unknown_completion`. Main-process preflight re-checks branch dirtiness and upstream at submit time so stale renderer eligibility cannot open a review on the wrong head.
GitHub creation uses `gh`; GitLab uses the GitLab client layer. SSH repos route git operations through the connection’s SSH git provider when `connectionId` is set.
### Review queue classification
`src/shared/hosted-review-queue.ts` classifies summaries into queues (`mine`, `requested`, `agent`, `teammate`, `needs-response`, `ready-to-merge`) using viewer login, requested reviewers, bot heuristics, unresolved thread counts, and check status.
## GitHub (`api.gh`)
Main-process handlers register as `gh:*` in `src/main/ipc/github.ts`. Calls go through `src/main/github/client` (GitHub CLI semaphore, `gh api` / `gh pr`).
Representative preload surface (see `api-types.ts` for full signatures):
| Area | Key IPC methods |
| --- | --- |
| Identity / repo | `gh:viewer`, `gh:repoSlug` |
| PR by branch | `gh:prForBranch` (supports `linkedPRNumber`, `fallbackPRNumber`) |
| Refresh | `gh:refreshPRNow`, `gh:enqueuePRRefresh`, `gh:reportVisiblePRRefreshCandidates` |
| Work items | `gh:workItem`, `gh:workItemDetails`, `gh:listWorkItems`, `gh:listIssues`, `gh:createIssue` |
| PR review | `gh:prComments`, `gh:addPRReviewComment`, `gh:addPRReviewCommentReply`, `gh:resolveReviewThread`, `gh:setPRFileViewed` |
| PR lifecycle | `gh:mergePR`, `gh:updatePRState`, `gh:updatePRTitle`, `gh:rerunPRChecks` |
| Projects V2 | `gh:listAccessibleProjects`, `gh:getProjectViewTable`, `gh:updateProjectItemField`, slug-based issue/PR mutations |
| Diagnostics | `gh:rateLimit`, `gh:diagnoseAuth` |
After local mutations, `gh.onWorkItemMutated` broadcasts invalidation to all renderer windows (except the originator) so SWR caches stay coherent.
`GitHubPRReviewCommentInput` requires `repoPath`, `prNumber`, `commitId`, `path`, `line`, and `body` (optional `startLine` for multi-line). File contents for the PR Files tab use `gh:prFileContents` with head/base SHAs from work item details.
PR title/body generation for agents can pull git context via `src/main/text-generation/pull-request-context.ts` (diff stat, commits, bounded buffer).
## GitLab (`api.gl`)
Handlers mirror GitHub where the APIs align (`src/main/ipc/gitlab.ts`, `src/preload/gitlab.ts`). GitLab-specific behavior includes MR state values (`opened` \| `merged` \| `closed`), project refs with host, and paginated `glab` envelopes.
| Channel | Purpose |
| --- | --- |
| `gitlab:viewer` | Authenticated user |
| `gitlab:projectSlug` | Project ref for repo path |
| `gitlab:mrForBranch` | MR for branch + optional `linkedMRIid` |
| `gitlab:listWorkItems` | Combined MR + issue list |
| `gitlab:workItemDetails` | Body, discussions, pipeline jobs |
| `gitlab:mergeMR` / `closeMR` / `reopenMR` | MR lifecycle |
| `gitlab:todos` | Cross-project dashboard todos |
| `gitlab:workItemByPath` | Resolve item by host/path/iid |
Repo-path validation uses `assertRegisteredRepo` (same filesystem-auth boundary as GitHub).
## Linear (`api.linear`)
Linear does not use git remotes for API access. Connection state is stored in the main process; the renderer never holds the raw API key after connect.
| Channel | Purpose |
| --- | --- |
| `linear:connect` / `disconnect` | API key persistence |
| `linear:status` / `selectWorkspace` | Multi-workspace selection (`LinearWorkspaceSelection` = workspace id or `'all'`) |
| `linear:listIssues` | Filters: `assigned`, `created`, `all`, `completed` |
| `linear:createIssue` / `updateIssue` | Team-scoped issue CRUD |
| `linear:teamStates` / `teamLabels` / `teamMembers` | Editor metadata |
`buildLinearTeamUrl` and `getLinearOrganizationUrlKeyFromIssueUrl` in `src/shared/linear-links.ts` build team URLs from issue URLs.
## Diff annotation (local notes)
Local review notes are **not** the same as GitHub/GitLab inline review comments.
`DiffComment` objects live on `WorktreeMeta.diffComments` and persist through the normal worktree metadata path (`orca-data.json`). Fields include `filePath`, `lineNumber`, optional `startLine`, `body`, `side: 'modified'`, optional `source: 'diff' | 'markdown'`, and `sentAt` after hand-off to an agent.
The UI labels these as **Notes** in `DiffCommentCard` to avoid confusion with hosted PR review threads. `DiffNotesSendMenu` formats unsent notes and launches agent terminals via `formatDiffComments`.
Store API (`diffComments` slice): `addDiffComment`, `updateDiffComment`, `deleteDiffComment`, `markDiffCommentsSent`, `clearDeliveredDiffComments`, `clearDiffCommentsForFile`.
GitHub inline review uses `gh:addPRReviewComment` with commit and path line anchors; GitLab MR discussion uses `gitlab:addMRComment` for conversation-level comments in the work-item dialog.
## Pull-request context for agents
`pull-request-context.ts` gathers git comparison context (diff, log, merge-base) up to a 10 MiB buffer cap for AI-assisted PR title/body drafting, with fetch-before-compare when the base ref maps to a remote branch.
## Security and access control
- GitHub/GitLab/hosted-review repo operations require a **registered** Orca repo path (and matching `repoId` when supplied).
- Hosted-review create/eligibility resolves `worktreePath` against the repo’s worktree list; SSH paths use POSIX normalization without Windows `path.resolve`.
- Linear API keys are validated on connect; disconnect clears workspace-scoped credentials.
- GitHub Project and PR calls respect `gh` auth; `diagnoseAuth` explains scope gaps and `GITHUB_TOKEN` shadowing the keyring.
## SSH and remote runtimes
SSH-backed repos pass `connectionId` into git and provider clients. Hosted-review git preflight uses the SSH git provider’s `exec` instead of local `git`. Runtime RPC methods accept repo/worktree selectors and delegate to the same hosted-review implementation on the paired runtime.
Cross-repo GitHub task prefetch limits (`PER_REPO_FETCH_LIMIT`, `CROSS_REPO_DISPLAY_LIMIT` in `src/shared/work-items.ts`) apply to the Tasks sidebar aggregation layer.
## Provider comparison
| Concern | GitHub | GitLab | Linear |
| --- | --- | --- | --- |
| Auth | `gh auth` | `glab` auth | API key in Settings |
| Review artifact | PR (`linkedPR`) | MR (`linkedGitLabMR`) | Issue id (`linkedLinearIssue`) |
| Branch lookup | `gh:prForBranch` | `gitlab:mrForBranch` | N/A (issue link only) |
| Inline code review | `gh:addPRReviewComment` | MR discussions via details API | Comments on issues |
| Hosted create | `gh pr create` path | GitLab create flow | N/A |
| Extra surface | Projects V2, PR refresh coordinator | Project todos, `glab` MR merge methods | Teams, workflow states, projects |
Bitbucket, Azure DevOps, and Gitea participate in **hosted review** detection and linking slots but do not have full Tasks-provider parity with the three primary integrations.
## Related pages
<CardGroup>
<Card title="Worktrees and repos" href="/worktrees-and-repos">
Worktree create/list semantics, lineage, and where link fields are set on create.
</Card>
<Card title="Settings reference" href="/settings-reference">
GlobalSettings fields for task providers, GitHub Projects, GitLab projects, and review tools.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
connectionId routing, remote git exec, and GitHub work-item constraints on SSH remotes.
</Card>
<Card title="Repository hooks" href="/repository-hooks">
orca.yaml issue-command defaults that tie worktrees to linked GitHub issues.
</Card>
<Card title="Selectors and JSON output" href="/selectors-json-output">
Repo/worktree selector grammar for runtime RPC and CLI targeting.
</Card>
</CardGroup>
---
## 14. CLI core reference
> Flags and signatures for `open`, `serve`, `status`, `repo`, `worktree`, `terminal`, `file`, `environment`, and `agent hooks` command groups.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/14-cli-core-reference.md
- Generated: 2026-06-01T20:09:38.670Z
### Source Files
- `src/cli/specs/core.ts`
- `src/cli/specs/file.ts`
- `src/cli/specs/environment.ts`
- `src/cli/specs/agent-hooks.ts`
- `src/cli/args.ts`
- `src/cli/help.ts`
- `src/cli/handlers/core.ts`
---
title: "CLI core reference"
description: "Flags and signatures for `open`, `serve`, `status`, `repo`, `worktree`, `terminal`, `file`, `environment`, and `agent hooks` command groups."
---
The `orca` binary routes argv through `src/cli/args.ts` (parse, validate, positional normalization), matches a `CommandSpec` from `src/cli/specs/*.ts`, and dispatches handlers that call the local runtime socket or a paired remote runtime over WebSocket. Command usage strings, allowed flags, and per-command notes are the source of truth in those spec files; handlers in `src/cli/handlers/` enforce extra constraints (remote cwd, selector resolution, exit codes).
## Global flags and runtime targeting
These flags are allowed on every command in the groups below (`GLOBAL_FLAGS` in `src/cli/args.ts`):
| Flag | Role |
| --- | --- |
| `--help` | Print command or group help (`orca help <path>` also works). |
| `--json` | Emit machine-readable JSON (`ok`, `result`, `_meta`; failures use RPC error shape). |
| `--pairing-code <code>` | Target a remote runtime via `orca://pair#...` (or bare payload). |
| `--environment <selector>` | Target a saved environment by id or name. |
<ParamField body="pairing-code" type="string">
Mutually exclusive with `--environment`. Same constraint applies to `ORCA_PAIRING_CODE` / `ORCA_REMOTE_PAIRING` vs `ORCA_ENVIRONMENT`.
</ParamField>
<Warning>
`environment`, `serve`, and `agent` commands ignore `--pairing-code` and `--environment` (and suppress env-var fallback) so they always run against local user data.
</Warning>
Flag parsing accepts `--flag=value` so values can start with `--` (e.g. `--text=--help`). Unknown commands or flags fail with `invalid_argument` before any runtime connection attempt.
## Startup commands
### `orca open`
Launches the Orca app when needed and polls until the runtime is reachable (default 15s). Returns the same readiness shape as `status`.
```bash
orca open [--json]
```
### `orca serve`
Starts a **headless** Orca runtime in the foreground (no desktop window). Prints the runtime endpoint; stop with Ctrl+C. Forwards signals to the child process.
```bash
orca serve [--port <port>] [--pairing-address <host>] [--mobile-pairing] [--no-pairing] [--json]
```
| Flag | Notes |
| --- | --- |
| `--port` | Integer 0–65535; passed as `--serve-port` to the app executable. |
| `--pairing-address` | Address clients should use (LAN, Tailscale, SSH forward, tunnel). |
| `--mobile-pairing` | Mobile-scoped pairing QR/link instead of default runtime-environment pairing. |
| `--no-pairing` | Disables pairing output; **cannot** be combined with `--mobile-pairing`. |
With `--json`, the child receives `--serve-json`. When a web client bundle is available, the server may also print a browser URL with embedded pairing data.
### `orca status`
Reports app process, runtime reachability, runtime id, and graph state. Sets exit code **1** when the runtime is not reachable (human and JSON modes).
```bash
orca status [--json]
```
Human text fields include `appRunning`, `pid`, `runtimeState`, `runtimeReachable`, `runtimeId`, `graphState`. On a paired remote runtime, `app.running` is reported as true with `pid: null`.
```text
argv → parseArgs / validateCommandAndFlags
→ RuntimeClient (local metadata socket | WebSocket if paired)
→ handler → RPC method → printResult / reportCliError
```
## Environment commands
Saved environments are stored under the CLI user-data path and managed **locally** (no runtime RPC). `environment add` requires `--pairing-code` on the command line (also a global flag elsewhere).
| Command | Usage |
| --- | --- |
| `environment add` | `orca environment add --name <name> --pairing-code <code> [--json]` |
| `environment list` | `orca environment list [--json]` |
| `environment show` | `orca environment show --environment <selector> [--json]` |
| `environment rm` | `orca environment rm --environment <selector> [--json]` |
Successful saves are redacted for display (pairing secrets are not echoed). `environment show` / `rm` resolve `<selector>` by environment id or name.
## Repo commands
All repo commands call runtime RPC methods (`repo.list`, `repo.add`, `repo.show`, `repo.setBaseRef`, `repo.searchRefs`).
| Command | Usage | Required flags |
| --- | --- | --- |
| `repo list` | `orca repo list [--json]` | — |
| `repo add` | `orca repo add --path <path> [--json]` | `--path` |
| `repo show` | `orca repo show --repo <selector> [--json]` | `--repo` |
| `repo set-base-ref` | `orca repo set-base-ref --repo <selector> --ref <ref> [--json]` | `--repo`, `--ref` |
| `repo search-refs` | `orca repo search-refs --repo <selector> --query <text> [--limit <n>] [--json]` | `--repo`, `--query` |
<Note>
On a **remote** runtime, `repo add --path` must be an **absolute path on the server**. Relative paths are rejected because the CLI cwd is on the client machine.
</Note>
Repo selectors: `id:<id>`, `name:<name>`, `path:<path>`.
## Worktree commands
| Command | Usage | Notable flags |
| --- | --- | --- |
| `worktree list` | `orca worktree list [--repo <selector>] [--limit <n>] [--json]` | Filter by repo |
| `worktree show` | `orca worktree show --worktree <selector> [--json]` | — |
| `worktree current` | `orca worktree current [--json]` | Resolves enclosing worktree from **local** cwd → `id:<id>` |
| `worktree create` | See below | Parent lineage, hooks, activation |
| `worktree set` | `orca worktree set --worktree <selector> [--display-name <name>] [--issue <n>] [--comment <text>] [--parent-worktree <selector>] [--no-parent] [--json]` | Pass `--issue` with no value to clear |
| `worktree rm` | `orca worktree rm --worktree <selector> [--force] [--run-hooks] [--json]` | Archive hooks skipped unless `--run-hooks` |
| `worktree ps` | `orca worktree ps [--limit <n>] [--json]` | Orchestration-oriented summary |
### `worktree create`
```bash
orca worktree create --repo <selector> --name <name> \
[--base-branch <ref>] [--issue <number>] [--comment <text>] \
[--parent-worktree <selector>] [--no-parent] [--run-hooks] [--activate] [--json]
```
| Flag | Behavior |
| --- | --- |
| (default parent) | Infers parent from `ORCA_TERMINAL_HANDLE`, else cwd-enclosing worktree, unless `--no-parent` or explicit `--parent-worktree`. |
| `--parent-worktree` | Explicit parent; value `active` resolves like `worktree current`. |
| `--no-parent` | Independent worktree; mutually exclusive with `--parent-worktree`. |
| `--run-hooks` | Forces repo setup hooks per repository policy; also sets `activate` so hooks can run in the first terminal. |
| `--activate` | Reveals the new worktree in the app (also implied by `--run-hooks`). |
Non-JSON create may print `warning:` lines for hook failures and a `parent:` lineage summary on stderr.
Worktree selectors: `id:<id>`, `branch:<branch>`, `issue:<number>`, `path:<path>`, `active`, `current`. On remote runtimes, `active` / `current` are **invalid**—use an explicit server-side selector.
## Terminal commands
Terminal handles come from `orca terminal list --json` (`term_…`). When `--terminal` is omitted, the CLI resolves the **active** terminal in the current (or selected) worktree via `terminal.resolveActive`.
| Command | Usage |
| --- | --- |
| `terminal list` | `orca terminal list [--worktree <selector>] [--limit <n>] [--json]` |
| `terminal show` | `orca terminal show [--terminal <handle>] [--json]` |
| `terminal read` | `orca terminal read [--terminal <handle>] [--cursor <n>] [--limit <n>] [--json]` |
| `terminal send` | `orca terminal send [--terminal <handle>] [--text <text>] [--enter] [--interrupt] [--json]` |
| `terminal wait` | `orca terminal wait [--terminal <handle>] --for exit\|tui-idle [--timeout-ms <ms>] [--json]` |
| `terminal stop` | `orca terminal stop --worktree <selector> [--json]` |
| `terminal create` | `orca terminal create [--worktree <selector>] [--title <name>] [--command <text>] [--focus] [--json]` |
| `terminal rename` | `orca terminal rename [--terminal <handle>] [--title <text>] [--json]` |
| `terminal split` | `orca terminal split [--terminal <handle>] [--direction horizontal\|vertical] [--command <text>] [--json]` |
| `terminal switch` | `orca terminal switch [--terminal <handle>] [--json]` |
| `terminal focus` | Alias for `terminal switch` |
| `terminal close` | `orca terminal close [--terminal <handle>] [--json]` |
### `terminal read` cursors
`--cursor` must be a non-negative integer (from a prior read’s `nextCursor`). Use incremental reads to capture agent output: read before `send`, then `read --cursor <prev>` after `wait`.
### `terminal wait`
| `--for` value | Meaning |
| --- | --- |
| `exit` | Wait until the terminal process exits. |
| `tui-idle` | Wait until the TUI reports idle (agent UIs). |
Client RPC timeout is at least `timeout-ms + 5000` (cap 5 minutes default when unset). Exit code **1** when `satisfied` is false (including blocked waits).
<Warning>
Remote `terminal create` requires `--worktree` because client cwd cannot identify a server worktree.
</Warning>
Local interactive agent commands may use a renderer-backed terminal path when classified as such; `--focus` controls UI activation.
## File commands
Paths are **relative to the selected worktree**. Positional path is supported: `orca file open <path>` (normalized to `--path`).
| Command | Usage |
| --- | --- |
| `file open` | `orca file open <path> [--worktree <selector>] [--json]` |
| `file diff` | `orca file diff <path> [--staged] [--worktree <selector>] [--json]` |
| `file open-changed` | `orca file open-changed [--mode edit\|diff\|both] [--worktree <selector>] [--json]` |
| Flag | Behavior |
| --- | --- |
| `--staged` | (`file diff`) Open staged SCM diff instead of unstaged. |
| `--mode` | (`open-changed`) Default `diff`. `edit` skips deleted paths; `both` opens edit and diff targets. |
Worktree selection: explicit `--worktree`, else local cwd resolution; remote commands **require** `--worktree`. Changed files come from `git.status` for the worktree.
## Agent hooks commands
Controls Orca-managed agent status hooks (install state in persisted settings and managed hook files).
| Command | Usage |
| --- | --- |
| `agent hooks status` | `orca agent hooks status [--json]` |
| `agent hooks on` | `orca agent hooks on [--json]` |
| `agent hooks off` | `orca agent hooks off [--json]` |
`on` / `off` prefer updating a **reachable** runtime via `settings.update`; otherwise they patch `orca-data.json` offline (`appliedBy: runtime` vs `offline`). JSON/human output includes `agentStatusHooksEnabled`, `settingsPath`, `appliedBy`, and per-agent install `statuses`.
## JSON output and exit codes
<RequestExample>
```bash
orca worktree current --json
```
</RequestExample>
<ResponseExample>
```json
{
"id": "rpc-…",
"ok": true,
"result": { "worktree": { "id": "…", "branch": "…", "path": "…" } },
"_meta": { "runtimeId": "…" }
}
```
</ResponseExample>
Failures with `--json`: RPC failures print the server error object; local CLI errors use `{ "ok": false, "error": { "code", "message" }, "_meta": { "runtimeId": null } }`. Common codes include `invalid_argument`, `runtime_unavailable`, `selector_not_found`, `selector_ambiguous`, `runtime_open_timeout`, `incompatible_runtime`.
| Situation | Exit code |
| --- | --- |
| `status` with unreachable runtime | 1 |
| `terminal wait` with `satisfied: false` | 1 |
| Any caught CLI error | 1 (default) |
| `serve` child exit code | Propagated to `process.exitCode` |
## Help discovery
```bash
orca # root help
orca help worktree create # command help
orca worktree create --help # same
orca worktree # group listing
```
Per-flag descriptions are generated in `src/cli/help.ts` (`formatFlagHelp`).
## Related pages
<CardGroup>
<Card title="Selectors and JSON output" href="/selectors-json-output">
Selector grammar, global flags, JSON errors, and remote resolution rules in depth.
</Card>
<Card title="CLI scripting workflow" href="/cli-scripting-workflow">
End-to-end automation patterns with open, status, terminals, and worktree comments.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
Headless serve, pairing codes, and targeting remote runtimes.
</Card>
<Card title="Terminals and agents" href="/terminals-and-agents">
PTY lifecycle, agent detection, and when to use terminal CLI vs orchestration.
</Card>
<Card title="Repository hooks" href="/repository-hooks">
`orca.yaml` setup/archive hooks and `--run-hooks` on create/remove.
</Card>
</CardGroup>
---
## 15. CLI browser and computer reference
> Browser automation commands (navigation, interaction, cookies, storage, capture) and `computer` desktop accessibility commands with capability flags.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/15-cli-browser-and-computer-reference.md
- Generated: 2026-06-01T20:09:54.445Z
### Source Files
- `src/cli/specs/browser-basic.ts`
- `src/cli/specs/browser-advanced.ts`
- `src/cli/specs/computer.ts`
- `src/cli/specs/orchestration.ts`
- `src/cli/handlers/computer.ts`
- `src/cli/handlers/browser-capture.ts`
- `skills/computer-use/SKILL.md`
---
title: "CLI browser and computer reference"
description: "Browser automation commands (navigation, interaction, cookies, storage, capture) and `computer` desktop accessibility commands with capability flags."
---
The Orca CLI exposes two automation surfaces on top of the Orca runtime RPC layer: **browser** commands drive Orca’s embedded Chromium tabs through an `agent-browser` bridge, and **`orca computer`** commands drive native desktop apps through a platform computer-use provider. Command signatures live in `src/cli/specs/browser-basic.ts`, `src/cli/specs/browser-advanced.ts`, and `src/cli/specs/computer.ts`; handlers resolve targets in `src/cli/selectors.ts` and call `browser.*` or `computer.*` RPC methods.
```mermaid
sequenceDiagram
participant CLI as orca CLI
participant RT as Orca runtime RPC
participant BR as agent-browser bridge
participant CU as computer-use provider
CLI->>RT: browser.snapshot / browser.click / ...
RT->>BR: CDP session + queued command
BR-->>RT: BrowserSnapshotResult / action result
RT-->>CLI: JSON or formatted text
CLI->>RT: computer.getAppState / computer.click / ...
RT->>CU: accessibility + input + screenshot
CU-->>RT: ComputerSnapshotResult / ComputerActionResult
RT-->>CLI: JSON or formatted text
```
## Prerequisites
| Requirement | Verification |
| --- | --- |
| Orca desktop app or `orca serve` runtime reachable | `orca status --json` |
| Browser tab in a worktree (for browser commands) | `orca tab list --json` or `orca tab create --url <url>` |
| Computer-use permissions (macOS) | `orca computer permissions --json` |
| Provider feature support | `orca computer capabilities --json` |
Browser commands default to the **active tab** in the worktree enclosing the CLI’s current directory. Computer commands default the same worktree context unless `--session` is used.
## Global flags
All commands in this reference accept the global CLI flags from `GLOBAL_FLAGS` in `src/cli/args.ts`:
| Flag | Purpose |
| --- | --- |
| `--help` | Show command-specific help (same as `orca help <command>`) |
| `--json` | Emit the full RPC success envelope as JSON |
| `--environment <selector>` | Target a saved remote runtime |
| `--pairing-code <code>` | Pair to a runtime for this invocation |
Add `--worktree <selector>` on browser and most computer commands to pin a worktree. Selectors follow the grammar documented on the selectors page (`id:`, `path:`, `branch:`, `active`, `current`). On a **remote** runtime, `active` / `current` are rejected because they resolve from the client machine’s cwd, not the server.
## Browser targeting
| Flag | Applies to | Behavior |
| --- | --- | --- |
| `--worktree <selector>` | Most browser commands | Resolves tab scope; `all` on `tab list` / `tab current` lists across worktrees |
| `--page <browserPageId>` | Tab-targeted commands (see `supportsBrowserPageFlag`) | Targets a specific tab globally; optional `--worktree` validates ownership |
| *(default)* | Local CLI inside a managed worktree | Auto-resolves enclosing worktree from cwd |
| *(default)* | Remote CLI | Omits worktree; runtime uses server-side focus |
Interaction commands take **`--element <ref>`** where `ref` comes from the latest `orca snapshot` (for example `@e1`, `@e2`). Refs are valid only for the snapshot that produced them.
### Snapshot → act loop
<Steps>
<Step title="Capture state">
Run `orca snapshot` to get an accessibility tree, URL, title, and `refs` array.
</Step>
<Step title="Act on a ref">
Use `orca click --element @e3`, `orca fill --element @e2 --value "..."`, or similar.
</Step>
<Step title="Re-snapshot after navigation">
Re-run `orca snapshot` after navigation, DOM changes, or tab switches — stale refs return element errors.
</Step>
</Steps>
<RequestExample>
```bash
orca snapshot --json
orca click --element @e1 --json
orca wait --text "Dashboard" --timeout 30000 --json
```
</RequestExample>
## Browser command inventory
### Observation and navigation
| Command | Usage summary |
| --- | --- |
| `snapshot` | `orca snapshot [--worktree <selector>] [--json]` |
| `screenshot` | `orca screenshot [--format png\|jpeg] [--worktree <selector>] [--json]` |
| `full-screenshot` | Full-page capture beyond viewport |
| `goto` | `orca goto --url <url>` — 60s RPC timeout for network idle |
| `back` / `forward` / `reload` | History navigation |
| `eval` | `orca eval --expression <js>` in page context |
| `wait` | Wait for selector, text, URL pattern, load state, JS (`--fn`), visibility (`--state hidden\|visible`), or `--timeout` ms |
| `pdf` | Export active tab as PDF (base64 in JSON) |
| `scroll` | `orca scroll --direction up\|down [--amount <pixels>]` |
| `scrollintoview` | `orca scrollintoview --element <ref>` |
`wait` uses an extended RPC timeout (default 60s client-side, plus `--timeout` when set) so slow page conditions are not misreported as runtime unreachable.
### Element interaction
| Command | Required flags | Notes |
| --- | --- | --- |
| `click` | `--element <ref>` | |
| `dblclick` | `--element <ref>` | |
| `hover` | `--element <ref>` | |
| `fill` | `--element`, `--value` | Clear + fill |
| `type` | `--input <text>` | Types at current focus (no element) |
| `inserttext` | `--text` | Insert without key events |
| `select` | `--element`, `--value` | Dropdown |
| `check` / `uncheck` | `--element` | Checkbox/radio |
| `focus` / `clear` / `select-all` | `--element` | |
| `keypress` | `--key <name>` | Enter, Tab, Escape, ArrowDown, … |
| `drag` | `--from <ref>`, `--to <ref>` | |
| `upload` | `--element`, `--files <path,...>` | Comma-separated paths |
| `get` | `--what <property>` | `text`, `html`, `value`, `url`, `title`, `count`, `box`; optional `--element` |
| `is` | `--what <state>`, `--element` | `visible`, `enabled`, `checked` |
| `find` | `--locator`, `--value`, `--action` | Locators: `role`, `text`, `label`; optional `--text` |
| `highlight` | `--selector <ref>` | Visual highlight |
| `download` | `--selector`, `--path` | Trigger download to path |
### Mouse
| Command | Flags |
| --- | --- |
| `mouse move` | `--x`, `--y` |
| `mouse down` / `mouse up` | Optional `--button left\|right\|middle` |
| `mouse wheel` | `--dy` required; optional `--dx` |
### Tabs and session profiles
| Command | Purpose |
| --- | --- |
| `tab list` | List tabs; `--show-profile` adds profile column; `--worktree all` |
| `tab show` | `orca tab show --page <id>` |
| `tab current` | Active tab for worktree |
| `tab switch` | `--index <n>` or `--page <id>`; optional `--focus` to surface browser pane |
| `tab create` | Optional `--url`, `--profile <id>` |
| `tab close` | Optional `--index` |
| `tab profile list` | All session profiles |
| `tab profile create` | `--label <name>`; `--scope isolated\|imported` |
| `tab profile delete` | `--profile <id>` |
| `tab profile set` | `--page` or `--worktree` + `--profile` |
| `tab profile show` / `use-default` / `clone` | Profile binding and cloning |
Profile scopes are `default`, `isolated`, and `imported` (see `BrowserSessionProfileScope` in shared types).
### Cookies, storage, and environment
| Group | Commands |
| --- | --- |
| Cookies | `cookie get [--url]`, `cookie set` (`--name`, `--value`, `--domain`, `--path`, `--secure`, `--httpOnly`, `--sameSite`, `--expires`), `cookie delete` |
| Viewport | `viewport --width --height [--scale] [--mobile]` |
| Geolocation | `geolocation --latitude --longitude [--accuracy]` |
| Offline / device / headers | `set offline [--state on\|off]`, `set device --name`, `set headers --headers <json>`, `set credentials --user --pass`, `set media [--color-scheme] [--reduced-motion]` |
| Clipboard | `clipboard read`, `clipboard write --text` |
| Dialogs | `dialog accept [--text]`, `dialog dismiss` |
| Storage | `storage local get\|set\|clear`, `storage session get\|set\|clear` (each needs `--key`; set needs `--value`) |
### Capture and interception
| Command | Behavior |
| --- | --- |
| `capture start` / `capture stop` | Toggle console + network event capture |
| `console` | Show captured console entries; `--limit <n>` |
| `network` | Show captured network log; `--limit <n>` |
| `intercept enable` | Pause matching requests; `--patterns <glob,...>` |
| `intercept disable` | Disable interception |
| `intercept list` | List paused requests (`[id] METHOD url`) |
<Warning>
Per-request `intercept continue` / `intercept block` are not exposed in the CLI yet — the advanced spec notes they depend on future `agent-browser` support. Today interception is URL-pattern based only.
</Warning>
### Passthrough
```bash
orca exec --command "<agent-browser subcommand>" [--worktree <selector>] [--json]
```
`exec` forwards arbitrary `agent-browser` CLI syntax against the resolved tab. Prefer typed top-level `orca` commands when they exist — they have explicit flags, timeouts, and structured JSON types.
## Computer command reference
Computer commands are grouped under `orca computer <subcommand>`. Action commands return a fresh **`ComputerActionResult`** (snapshot + optional screenshot + action metadata) so agents can verify UI state without a separate `get-app-state` call.
### Capabilities
```bash
orca computer capabilities [--json]
```
Human output lists provider name, platform, protocol version, and enabled support groups. The JSON shape is `ComputerProviderCapabilities`:
| Group | Fields | Meaning |
| --- | --- | --- |
| `supports.apps` | `list`, `bundleIds`, `pids` | App discovery modes |
| `supports.windows` | `list`, `targetById`, `targetByIndex`, `focus`, `moveResize` | Window targeting |
| `supports.observation` | `screenshot`, `annotatedScreenshot`, `elementFrames`, `ocr` | Snapshot extras |
| `supports.actions` | `click`, `typeText`, `pressKey`, `hotkey`, `pasteText`, `scroll`, `drag`, `setValue`, `performAction` | Allowed mutations |
| `supports.surfaces` | `menus`, `dialogs`, `dock`, `menubar` | Platform UI surfaces |
Call `capabilities` before scripting portable automation — providers on macOS, Linux, and Windows differ.
### Discovery and permissions
| Command | Usage |
| --- | --- |
| `computer permissions` | macOS only — opens Orca Computer Use permission setup; no-op message on other platforms |
| `computer list-apps` | `orca computer list-apps [--worktree <selector>] [--json]` |
| `computer list-windows` | `orca computer list-windows --app <name\|bundle\|pid:N> [--worktree \| --session]` |
| `computer get-app-state` | Accessibility tree + screenshot for one app/window |
### App and window selectors
<ParamField body="--app" type="string" required>
Target application. Prefer bundle IDs from `list-apps` (for example `com.spotify.client`). Human names work when unambiguous. Use `pid:<number>` only when bundle/name matching fails.
</ParamField>
<ParamField body="--window-id" type="number">
Target a specific window by platform window id.
</ParamField>
<ParamField body="--window-index" type="number">
Target by zero-based index from `list-windows`.
</ParamField>
<ParamField body="--session" type="string">
Computer session id instead of worktree context (used with `list-windows` and action commands).
</ParamField>
### Action commands
| Command | Targeting | Notable flags |
| --- | --- | --- |
| `computer click` | `--element-index <n>` **or** `--x` + `--y` | `--click-count`, `--mouse-button left\|right\|middle` |
| `computer perform-secondary-action` | `--element-index`, `--action <name>` | Uses accessibility action names from snapshot |
| `computer scroll` | element index **or** coordinates | `--direction up\|down\|left\|right`, `--pages` |
| `computer drag` | `--from-element-index` + `--to-element-index` **or** coordinate pairs | |
| `computer type-text` | literal keystrokes | `--text` or `--text-stdin` |
| `computer press-key` | xdotool-style key names | `--key Return`, etc. |
| `computer hotkey` | platform-aware combos | `--key CmdOrCtrl+A` |
| `computer paste-text` | clipboard paste path | `--text` or `--text-stdin` |
| `computer set-value` | direct AX value write | `--value` or `--value-stdin` (empty allowed) |
### Observation flags on actions
| Flag | Effect |
| --- | --- |
| `--restore-window` | Ask provider to restore minimized/off-screen windows before acting |
| `--no-screenshot` | Skip screenshot capture in the returned state (faster; tree-only) |
Coordinates are **window-local** — use positions from the latest snapshot/screenshot for the same window.
### Snapshot–act loop (desktop)
<Steps>
<Step title="List apps">
`orca computer list-apps --json`
</Step>
<Step title="Capture app state">
`orca computer get-app-state --app <bundle> --json` — read `snapshot.treeText` and `element-index` values.
</Step>
<Step title="Perform one action">
`orca computer click --app <bundle> --element-index 42 --json` (or `set-value`, `scroll`, …).
</Step>
<Step title="Inspect action result">
Action responses include an updated snapshot; re-fetch with `get-app-state` if the UI changes asynchronously.
</Step>
</Steps>
<Element indexes go stale after navigation, focus changes, scrolling, or re-renders — refresh state before reusing an index.
### JSON screenshots
With `--json`, screenshot bytes are written to a temp file under the system temp directory (`orca-computer-use/`) and the JSON payload replaces inline `data` with `screenshot.path`, `dataOmitted: true`, and `expiresAt` (24h TTL). Read pixels from `path`, not from the RPC body.
### Computer error codes
| Code | Typical cause |
| --- | --- |
| `app_not_found` | Bad `--app`; rerun `list-apps` |
| `element_not_found` | Stale `--element-index` |
| `window_not_found` / `window_stale` | Invalid window target |
| `unsupported_capability` | Action not in `capabilities` |
| `permission_denied` | macOS Accessibility / Screen Recording |
| `action_not_supported` / `value_not_settable` | Wrong action for element role |
| `invalid_argument` | Conflicting flags (for example `--text` and `--text-stdin`) |
| `screenshot_failed` | Hidden/minimized/off-screen window |
## JSON output shape
`--json` prints the full `RuntimeRpcSuccess` envelope via `printResult` in `src/cli/format.ts`:
<ResponseField name="ok" type="boolean">
Always `true` on success paths.
</ResponseField>
<ResponseField name="id" type="string">
RPC request id.
</ResponseField>
<ResponseField name="result" type="object">
Command-specific payload (`BrowserSnapshotResult`, `ComputerActionResult`, etc.).
</ResponseField>
Failures throw `RuntimeClientError` / `RuntimeRpcFailureError` with `code` and `message` (for example `selector_not_found`, `invalid_argument`).
## Command help
Every spec entry supports:
```bash
orca help snapshot
orca snapshot --help
```
Help text is generated from `CommandSpec` records in `src/cli/specs/` — usage strings there are authoritative when flags differ between help groupings.
## Related pages
<CardGroup>
<Card title="Browser automation" href="/browser-automation">
Conceptual guide to Orca’s embedded browser, accessibility snapshots, and agent-browser integration.
</Card>
<Card title="CLI core reference" href="/cli-core-reference">
`open`, `status`, `worktree`, `terminal`, and other non-browser command groups.
</Card>
<Card title="Selectors and JSON output" href="/selectors-json-output">
Worktree selector grammar, `--json` errors, and remote-runtime resolution rules.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
Target a remote Orca runtime with `--environment` or `--pairing-code`.
</Card>
<Card title="CLI scripting workflow" href="/cli-scripting-workflow">
End-to-end agent automation combining terminals, worktrees, and browser/computer commands.
</Card>
</CardGroup>
---
## 16. Selectors and JSON output
> Global CLI flags (`--json`, `--help`, `--pairing-code`, `--environment`), worktree/repo selector grammar (`id:`, `path:`, `branch:`, `active`), JSON error shapes, and remote-runtime resolution rules.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/16-selectors-and-json-output.md
- Generated: 2026-06-01T20:09:49.333Z
### Source Files
- `src/cli/args.ts`
- `src/cli/selectors.ts`
- `src/cli/format.ts`
- `src/cli/runtime-client.ts`
- `src/shared/runtime-rpc-envelope.ts`
- `src/cli/index.test.ts`
---
title: "Selectors and JSON output"
description: "Global CLI flags (`--json`, `--help`, `--pairing-code`, `--environment`), worktree/repo selector grammar (`id:`, `path:`, `branch:`, `active`), JSON error shapes, and remote-runtime resolution rules."
---
The `orca` CLI parses global flags and typed selectors in `src/cli/args.ts` and `src/cli/selectors.ts`, resolves worktrees against the runtime RPC in `src/main/runtime/orca-runtime.ts`, and prints either human text or a shared RPC envelope (`src/shared/runtime-rpc-envelope.ts`) controlled by `--json`.
## Global flags
Four flags are global across command groups (`GLOBAL_FLAGS` in `src/cli/args.ts`). Each command’s `allowedFlags` in `src/cli/specs/` must include them explicitly; unknown flags fail validation before any runtime connection.
| Flag | Type | Role |
|------|------|------|
| `--help` | boolean | Print help for the current command path, or `orca help <command>` |
| `--json` | boolean | Emit machine-readable JSON on stdout for success and failure |
| `--pairing-code` | string | Connect to a remote runtime via `orca://pair#...` or bare pairing payload |
| `--environment` | string | Connect using a saved environment id or name from `orca-environments.json` |
<ParamField body="--pairing-code" type="string">
One-time remote pairing. Parsed by `parsePairingCode` in `src/shared/pairing.ts`. Also available as `ORCA_PAIRING_CODE` or `ORCA_REMOTE_PAIRING` when the flag is omitted.
</ParamField>
<ParamField body="--environment" type="string">
Saved remote target. Resolved from the Orca user-data store (`orca-environments.json`). Also available as `ORCA_ENVIRONMENT` when the flag is omitted.
</ParamField>
<Warning>
`--pairing-code` and `--environment` are mutually exclusive. Combining them throws `invalid_argument` before RPC calls.
</Warning>
### Commands that ignore remote selection
`orca environment`, `orca serve`, and `orca agent hooks` pass `null` into `RuntimeClient` so env-var fallbacks for pairing/environment do not apply. Environment management and headless `serve` always run against local user data even when `ORCA_ENVIRONMENT` is set.
### Flag parsing rules
`parseArgs` treats tokens starting with `--` as flags:
- `--flag=value` assigns the substring after `=` (required when the value itself starts with `--`, e.g. `--text=--help`).
- `--flag value` consumes the next token unless it also starts with `--`, in which case the flag becomes boolean `true`.
- Positional arguments that share names with declared positional flags (e.g. `orca automations show auto-1`) are normalized into flags; supplying both positional and `--flag` forms is an `invalid_argument` error.
## Remote runtime resolution
Remote mode is active when `RuntimeClient` holds a non-null pairing offer (`isRemote === true`). Resolution order in `resolveRemotePairing`:
1. Reject if both `--pairing-code` and `--environment` are set.
2. If `--environment` (or `ORCA_ENVIRONMENT`): load pairing offer from the saved environment store.
3. Else if `--pairing-code` (or `ORCA_PAIRING_CODE` / `ORCA_REMOTE_PAIRING`): `parsePairingCode`; invalid codes yield `invalid_argument`.
4. Else: local mode — Unix domain socket / local metadata from user data.
```mermaid
sequenceDiagram
participant CLI as orca CLI
participant RC as RuntimeClient
participant Store as orca-environments.json
participant RT as Remote runtime
CLI->>RC: new RuntimeClient(flags / env)
alt --environment
RC->>Store: resolveEnvironmentPairingOffer
Store-->>RC: PairingOffer
else --pairing-code / env var
RC->>RC: parsePairingCode
else local
RC->>RC: readMetadata (local socket)
end
RC->>RT: WebSocket RPC (non-status.get)
Note over RC,RT: status.get checks protocol compatibility first
```
After connect, non-`status.get` calls run `ensureRemoteRuntimeCompatible` once per client: protocol versions must pass `evaluateRuntimeCompat` or the CLI throws `incompatible_runtime`. Successful RPCs with `--environment` update `lastUsedAt` / `runtimeId` on the saved record.
### Environment selectors (saved runtimes)
Separate from worktree/repo selectors. `resolveEnvironmentFromStore` matches:
| Input | Match |
|-------|--------|
| Exact `id` | Single environment |
| `name` | Unique name match |
| Duplicate names | `invalid_argument` — use id |
| No match | `Unknown environment: …` |
`environment list` / `show` / `add` / `rm` redact `deviceToken` and `publicKeyB64` from JSON and text output via `redactRuntimeEnvironment`.
## Worktree and repo selectors
Runtime resolution lives in `OrcaRuntimeService.resolveWorktreeSelector` and `resolveRepoSelector`. The CLI may rewrite selectors before RPC (especially `active` / `current`).
### Worktree selector grammar
| Form | Runtime behavior |
|------|------------------|
| `id:<worktreeId>` | Exact worktree id (format `repoId::<absolute-path>`, separator `::`) |
| `path:<absolute-path>` | Path equality (normalized); if multiple repo registrations share the same git path, **first** candidate wins |
| `branch:<ref>` | Branch match via `branchSelectorMatches` (`refs/heads/foo` and `foo` equivalent) |
| `issue:<number>` | `linkedIssue` string match |
| Bare string | Tries id, then path, then branch |
| `active` (unresolved) | Runtime throws `selector_not_found` — CLI must resolve first |
<Note>
`active` and `current` are CLI-only shortcuts. They are **not** valid on the wire as literal `active` selectors.
</Note>
#### Local `active` / `current` resolution
On a **local** runtime only:
1. `worktree.list` with `limit: 10000`.
2. Find Orca-managed worktrees whose path encloses `process.cwd()` (longest path wins).
3. Rewrite to `id:<enclosingWorktree.id>` before RPC.
`normalizeWorktreeSelector('active', cwd)` without listing maps to `path:<resolved cwd>`; full resolution to `id:` happens in `resolveCurrentWorktreeSelector`.
On a **remote** runtime, `active` / `current` throw `invalid_argument` immediately — the client cwd is not on the server. Use explicit server-side selectors: `id:`, `branch:`, `issue:`, or `path:` with an **absolute server path**.
### Repo selector grammar
| Form | Runtime behavior |
|------|------------------|
| `id:<repoId>` | Exact registered repo id |
| `path:<path>` | Registered repo root path (normalized) |
| `name:<displayName>` | Exact display name |
| Bare string | Tries id, then path, then display name |
Ambiguous bare or typed matches → `selector_ambiguous`. No match → `repo_not_found` (repos) or `selector_not_found` (worktrees).
### Default worktree targeting (CLI)
| Context | Local default | Remote default |
|---------|---------------|----------------|
| `--worktree` omitted (browser, many terminals) | Resolve enclosing worktree from cwd; if none, omit filter | `undefined` — runtime uses server-side focus |
| `--worktree all` | No worktree filter | Same |
| `terminal create` | Auto-resolve from cwd | **Requires** explicit `--worktree` |
| `file` commands | Auto-resolve from cwd | **Requires** explicit `--worktree` |
| `repo add --path` | Resolve relative paths against CLI cwd | Path must be **absolute on the server** (`/`, `C:\`, `\\`, `//`) |
### Terminal handles
`--terminal <handle>` is not a selector prefix grammar item. Handles are opaque runtime-issued strings (from `orca terminal list --json`). When omitted, `getTerminalHandle` calls `terminal.resolveActive` in the resolved worktree context.
## JSON output
### Success responses
With `--json`, `printResult` writes pretty-printed JSON of the full RPC success envelope:
<ResponseField name="envelope" type="RuntimeRpcSuccess">
<ResponseField name="id" type="string">Request id (e.g. RPC id or `local` for purely local handlers)</ResponseField>
<ResponseField name="ok" type="true">Always `true`</ResponseField>
<ResponseField name="result" type="object">Command-specific payload</ResponseField>
<ResponseField name="_meta.runtimeId" type="string">Runtime that served the call; `local` for environment store commands</ResponseField>
</ResponseField>
Computer-use snapshots with base64 screenshot data: JSON replaces inline `data` with a temp file path under `$TMPDIR/orca-computer-use/`, sets `dataOmitted: true`, and adds `expiresAt` (24h TTL).
### Error responses
`reportCliError` always prints JSON to **stdout** when `--json` is set (stderr is used for human text mode). Exit code is set to `1`.
**Runtime RPC failure** — passthrough of the server envelope:
<RequestExample>
```json
{
"id": "req_1",
"ok": false,
"error": {
"code": "selector_not_found",
"message": "selector_not_found"
},
"_meta": {
"runtimeId": "runtime-1"
}
}
```
</RequestExample>
**Local CLI errors** (`RuntimeClientError`, validation, selector preflight) — synthesized envelope:
<RequestExample>
```json
{
"id": "local",
"ok": false,
"error": {
"code": "invalid_argument",
"message": "Remote terminal create requires --worktree because the client cwd cannot identify a server worktree."
},
"_meta": {
"runtimeId": null
}
}
```
</RequestExample>
| `error.code` (common) | Origin |
|------------------------|--------|
| `invalid_argument` | CLI validation, flag conflicts, remote preflight |
| `runtime_unavailable` | Local runtime not reachable; human mode appends “Run `orca open` first.” |
| `runtime_error` | Unclassified local errors |
| `incompatible_runtime` | Remote protocol version mismatch |
| `selector_not_found` | Runtime: no matching worktree/repo |
| `selector_ambiguous` | Runtime: multiple matches |
| `repo_not_found` | Runtime: repo selector miss |
| `LINEAGE_*` | Lineage errors; may include `error.data.nextSteps[]` |
Structured `error.data.nextSteps` (string array) is rendered in **human** mode as `Next step: …` lines under the message; JSON mode returns the raw `data` object on RPC failures.
### Runtime passthrough codes
The RPC error mapper in `src/main/runtime/rpc/errors.ts` forwards these codes unchanged to the CLI (message often equals code): `runtime_unavailable`, `selector_not_found`, `selector_ambiguous`, `terminal_handle_stale`, `terminal_not_writable`, `terminal_exited`, `terminal_gone`, `no_active_terminal`, `repo_not_found`, `timeout`, `invalid_limit`, plus computer-use codes and `LINEAGE_*` with optional `data`.
### Keepalive frames
WebSocket transports may emit `{ "_keepalive": true }` between request/response frames. These are not CLI output; they are stripped by envelope parsing (`RuntimeRpcEnvelopeSchema`).
## Selector resolution flow (runtime)
```text
selector string
|
+-- id: --> filter worktrees/repos by id
+-- path: --> filter by normalized path (worktree: dedupe to first if >1)
+-- branch: --> branchSelectorMatches
+-- issue: --> linkedIssue match (worktree only)
+-- name: --> displayName (repo only)
+-- bare --> try id, path, branch/displayName
|
v
0 matches --> selector_not_found / repo_not_found
1 match --> resolved record
2+ matches --> selector_ambiguous
```
## Scripting patterns
<Steps>
<Step title="Discover ids in JSON">
Run list commands with `--json`, read stable ids from `result` (e.g. `worktrees[].id`, `repos[].id`, `terminals[].handle`).
</Step>
<Step title="Pin targets with typed selectors">
Prefer `id:repo-1::/srv/orca/feature` on remote hosts; avoid `active`/`current` over SSH or pairing.
</Step>
<Step title="Parse failures structurally">
Check `ok === false`, branch on `error.code`, read `error.data.nextSteps` when present.
</Step>
</Steps>
<CodeGroup>
```bash title="Local worktree from cwd"
orca worktree current --json
```
```bash title="Remote explicit worktree"
orca terminal list \
--worktree 'id:repo-1::/srv/orca/feature' \
--environment desk \
--json
```
```bash title="Saved environment by name"
orca status --environment 'work-laptop' --json
```
</CodeGroup>
<Tip>
Command validation runs before runtime lookup, so typos in subcommands or disallowed flags surface as `invalid_argument` instead of `runtime_unavailable`.
</Tip>
## Related pages
<CardGroup>
<Card title="CLI core reference" href="/cli-core-reference">
Command groups, per-command flags, and handler entry points beyond global options.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
Headless `orca serve`, pairing codes, and saving environments targeted by `--environment`.
</Card>
<Card title="Worktrees and repos" href="/worktrees-and-repos">
Create/list/remove semantics that consume worktree and repo selectors.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
SSH relay constraints that compound remote selector rules.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Selector ambiguity, runtime reachability, and remote CLI failure modes.
</Card>
</CardGroup>
---
## 17. Settings reference
> `GlobalSettings` fields that control workspaces, terminals, agents, themes, notifications, and integration toggles—persisted by the main-process store.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/17-settings-reference.md
- Generated: 2026-06-01T20:11:25.344Z
### Source Files
- `src/shared/types.ts`
- `src/main/persistence/index.ts`
- `src/renderer/src/components/settings/RepositoryHooksSection.tsx`
- `docs/STYLEGUIDE.md`
- `src/renderer/src/assets/main.css`
- `components.json`
---
title: "Settings reference"
description: "`GlobalSettings` fields that control workspaces, terminals, agents, themes, notifications, and integration toggles—persisted by the main-process store."
---
`GlobalSettings` is the typed user-preference object inside Orca's main-process `PersistedState`. On desktop it is written to `orca-data.json` under Electron `userData` (path captured at startup via `initDataPath()` in `src/main/persistence.ts`). Defaults come from `getDefaultSettings()` in `src/shared/constants.ts`; the canonical field list and inline semantics live on `GlobalSettings` in `src/shared/types.ts`. The renderer reads and writes through IPC (`settings:get`, `settings:set`) and the Zustand `settings` slice (`src/renderer/src/store/slices/settings.ts`).
## What is and is not `GlobalSettings`
`PersistedState` bundles several persistence domains. Only `settings` is `GlobalSettings`.
| Domain | Type | Examples |
| --- | --- | --- |
| `settings` | `GlobalSettings` | Theme, workspace directory, terminal font, default agent |
| `ui` | `PersistedUIState` | Sidebar width, sort mode, workspace statuses, window bounds, browser defaults |
| `repos[]` | `Repo` | Per-repo hooks (`hookSettings`), issue source, symlink paths |
| Other top-level keys | Various | `sshTargets`, `automations`, `workspaceSession`, caches |
<Note>
`rightSidebarOpenByDefault` on `GlobalSettings` is deprecated. New sidebar open/closed state belongs on `PersistedUIState.rightSidebarOpen`. Load-time migration copies the legacy value when `ui.rightSidebarOpen` is absent.
</Note>
Keyboard shortcut overrides are stored in `~/.orca/keybindings.json`. `GlobalSettings.keybindings` is a legacy field migrated once from disk; new writes go to the file (`src/main/keybindings/keybinding-file.ts`).
## Persistence flow
```mermaid
flowchart LR
subgraph renderer["Renderer"]
UI["Settings panes"]
Store["Zustand settings slice"]
end
subgraph main["Main process"]
IPC["settings:get / settings:set"]
StoreMain["Store.updateSettings"]
Disk["orca-data.json"]
end
UI --> Store
Store --> IPC
IPC --> StoreMain
StoreMain --> Disk
StoreMain -->|"debounced 300ms"| Disk
```
On load, `Store.load()` shallow-merges `{ ...defaults.settings, ...parsed.settings }` and runs normalizers (notifications, voice, terminal quick commands, task providers, floating-workspace paths, one-shot migrations). On save, `scheduleSave()` debounces writes by **300 ms** and encrypts `opencodeSessionCookie` (and `ui.browserKagiSessionLink`) with Electron `safeStorage` when available.
### Update semantics
`updateSettings(updates: Partial<GlobalSettings>)` shallow-merges top-level keys. Nested blocks are special-cased:
| Nested key | Merge behavior |
| --- | --- |
| `notifications` | Deep-merge, then `normalizeNotificationSettings()` (sound id aliases, volume clamped 0–100) |
| `telemetry` | Deep-merge so partial consent updates do not drop `installId` |
| `terminalQuickCommands` | Replaced list passed through `normalizeTerminalQuickCommands()` |
| `visibleTaskProviders` / `defaultTaskSource` | Co-normalized via `normalizeTaskProviderSettings()` |
| `workspaceDir` / `nestWorkspaces` | May append prior layout to `workspaceDirHistory` |
IPC `settings:set` strips `floatingTerminalTrustedCwds` from renderer input (only the main-process directory picker may grant trust), sanitizes `floatingTerminalCwd`, applies `nativeTheme.themeSource` when `theme` changes, and toggles agent awake / hook installers when related flags change.
## Defaults and optional fields
`getDefaultSettings(homedir)` supplies every required field. Several keys are optional on the type for backward compatibility; load merge and UI code treat them as present at runtime:
| Optional key | Default hydration |
| --- | --- |
| `voice` | `getDefaultVoiceSettings()` merged on load |
| `githubProjects` | Empty pinned/recent/active structure in defaults |
| `gitlabProjects` | Filled by merge when GitLab UI writes |
| `commitMessageAi` | Default-on with empty model maps in defaults |
| `telemetry` | Populated by first-launch migration in `Store.load()` |
| `ctrlTabOrderMode`, `showMobileButton`, etc. | Readers default (e.g. MRU tab order, show mobile button) |
Default workspace root: `` `${homedir}/orca/workspaces` `` with `nestWorkspaces: true`.
## Field reference by area
Settings UI panes map to fields below (see `buildSettingsNavigationMetadata()` in `src/renderer/src/hooks/useSettingsNavigationMetadata.ts`). Per-repo hook scripts and policies are **not** `GlobalSettings`; they live on `Repo.hookSettings` and are edited in repository settings.
### Workspace and worktrees
| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `workspaceDir` | `string` | `~/orca/workspaces` | Root directory for Orca-created worktrees |
| `nestWorkspaces` | `boolean` | `true` | Nest each worktree under `workspaceDir/<repo>/...` |
| `workspaceDirHistory` | `OrcaWorkspaceLayout[]` | `[]` | Prior `(path, nestWorkspaces)` pairs when layout changes |
| `refreshLocalBaseRefOnWorktreeCreate` | `boolean` | `false` | Refresh local base ref when creating a worktree |
| `skipDeleteWorktreeConfirm` | `boolean` | `false` | Skip destructive worktree delete confirmation |
| `skipDeleteAutomationConfirm` | `boolean` | `false` | Skip automation delete confirmation |
| `setupScriptLaunchMode` | `'new-tab'` \| `'split-vertical'` \| `'split-horizontal'` | `'new-tab'` | Where repo setup script runs on create |
| `experimentalWorktreeSymlinks` | `boolean` | `false` | Opt-in symlink paths from primary checkout (also needs per-repo `symlinkPaths`) |
| `defaultTuiAgent` | `TuiAgent` \| `'blank'` \| `null` | `null` | Composer default agent (`null` = first detected) |
### Git, branches, and source control UI
| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `branchPrefix` | `'git-username'` \| `'custom'` \| `'none'` | `'git-username'` | Branch naming prefix mode |
| `branchPrefixCustom` | `string` | `''` | Custom prefix when `branchPrefix === 'custom'` |
| `enableGitHubAttribution` | `boolean` | `false` | Co-author attribution on commits |
| `showGitIgnoredFiles` | `boolean` | `true` | Show ignored files in explorer (optional on type) |
| `sourceControlViewMode` | `'list'` \| `'tree'` | `'list'` | Changes view layout |
| `diffDefaultView` | `'inline'` \| `'side-by-side'` | `'inline'` | Diff viewer mode |
| `combinedDiffFileTreeVisibleByDefault` | `boolean` | `false` | Combined diff file tree |
| `commitMessageAi` | `CommitMessageAiSettings` | enabled, empty maps | AI commit message agent/model/prompt config |
`CommitMessageAiSettings` fields: `enabled`, `agentId` (`TuiAgent` \| `'custom'` \| `null`), `selectedModelByAgent`, `selectedModelByAgentByHost`, `discoveredModelsByAgent`, `discoveredModelsByAgentByHost`, `selectedThinkingByModel`, `customPrompt`, `customAgentCommand`.
### Appearance and editor
| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `theme` | `'system'` \| `'dark'` \| `'light'` | `'system'` | App chrome + document theme |
| `appFontFamily` | `string` | product default font | UI font |
| `editorAutoSave` | `boolean` | `false` | Markdown/editor auto-save |
| `editorAutoSaveDelayMs` | `number` | constant default | Debounce for auto-save |
| `editorMinimapEnabled` | `boolean` | `false` | Editor minimap |
| `markdownReviewToolsEnabled` | `boolean` | `true` | Local markdown review UI |
| `showTitlebarAppName` | `boolean` | `true` | Titlebar branding |
| `showTasksButton` | `boolean` | `true` | Tasks sidebar entry |
| `showMobileButton` | `boolean` | `true` | Mobile shortcut in sidebar |
| `ctrlTabOrderMode` | `'mru'` \| `'sequential'` | `'mru'` | Ctrl+Tab ordering |
| `windowBackgroundBlur` | `boolean` | `false` | Window blur effect |
| `rightSidebarOpenByDefault` | `boolean` | `true` | **Deprecated** — use `PersistedUIState.rightSidebarOpen` |
### Input and clipboard
| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `primarySelectionMiddleClickPaste` | `boolean` | platform-aware | Middle-click paste from primary selection |
| `terminalClipboardOnSelect` | `boolean` | `false` | Copy selection to system clipboard on select |
| `terminalAllowOsc52Clipboard` | `boolean` | `false` | Allow OSC 52 clipboard writes (SSH/TUI) |
| `terminalRightClickToPaste` | `boolean` | `true` | Windows right-click paste |
| `terminalMacOptionAsAlt` | `'auto'` \| `'true'` \| `'false'` \| `'left'` \| `'right'` | `'auto'` | macOS Option key as Meta |
| `terminalJISYenToBackslash` | `boolean` | `false` | JIS ¥ key → backslash |
### Terminal
| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `terminalFontSize` | `number` | `14` | Font size |
| `terminalFontFamily` | `string` | platform default | Font family |
| `terminalFontWeight` | `number` | default weight | Font weight |
| `terminalLineHeight` | `number` | `1` | Line height multiplier |
| `terminalGpuAcceleration` | `'auto'` \| `'on'` \| `'off'` | `'auto'` | WebGL vs DOM (Linux `auto` → DOM in renderer) |
| `terminalLigatures` | `'auto'` \| `'on'` \| `'off'` | `'auto'` | Programming ligatures addon |
| `terminalCursorStyle` | `'bar'` \| `'block'` \| `'underline'` | `'bar'` | Cursor shape |
| `terminalCursorBlink` | `boolean` | `true` | Cursor blink |
| `terminalCursorOpacity` | `number` | optional | Cursor opacity |
| `terminalThemeDark` / `terminalThemeLight` | `string` | Ghostty/Tango names | Named terminal themes |
| `terminalUseSeparateLightTheme` | `boolean` | `true` | Separate light theme |
| `terminalDividerColorDark` / `Light` | `string` | hex colors | Pane divider colors |
| `terminalInactivePaneOpacity` / `terminalActivePaneOpacity` | `number` | `0.8` / `1` | Pane opacity |
| `terminalPaneOpacityTransitionMs` | `number` | `140` | Opacity transition |
| `terminalDividerThicknessPx` | `number` | `3` | Divider thickness |
| `terminalBackgroundOpacity` | `number` | optional | Background opacity |
| `terminalColorOverrides` | `TerminalColorOverrides` | optional | Per-color ANSI overrides |
| `terminalPaddingX` / `terminalPaddingY` | `number` | optional | Inner padding |
| `terminalMouseHideWhileTyping` | `boolean` | `false` | Hide pointer while typing |
| `terminalWordSeparator` | `string` | optional | Word boundary chars |
| `terminalFocusFollowsMouse` | `boolean` | `false` | Focus follows mouse |
| `terminalScrollbackBytes` | `number` | `10_000_000` | Scrollback limit |
| `terminalScopeHistoryByWorktree` | `boolean` | `true` | Per-worktree shell history |
| `terminalWindowsShell` | `string` | `'powershell.exe'` | Windows default shell binary |
| `terminalWindowsPowerShellImplementation` | `'auto'` \| `'powershell.exe'` \| `'pwsh.exe'` | `'auto'` | PowerShell 7 vs inbox |
| `terminalShortcutPolicy` | `'orca-first'` \| `'terminal-first'` | `'orca-first'` | Shortcut ownership vs TUIs |
| `terminalQuickCommands` | `TerminalQuickCommand[]` | built-in defaults | Saved commands (global or per-repo scope) |
### Floating workspace
| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `floatingTerminalEnabled` | `boolean` | `true` | Global floating workspace surface |
| `floatingTerminalCwd` | `string` | `'~'` | Initial cwd for new floating terminal tabs |
| `floatingTerminalTrustedCwds` | `string[]` | `[]` | Picker-approved directories (main-only writes) |
| `floatingTerminalTriggerLocation` | `'floating-button'` \| `'status-bar'` | `'floating-button'` | Toggle placement |
| `floatingTerminalDefaultedForAllUsers` | `boolean` | migration flag | One-shot default-on migration |
| `floatingTerminalCwdMigratedToAppWorkspace` | `boolean` | migration flag | Legacy cwd trust migration |
### Agents and hooks
| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `agentCmdOverrides` | `Partial<Record<TuiAgent, string>>` | `{}` | Override CLI binary per agent |
| `agentStatusHooksEnabled` | `boolean` | `true` | Install/manage global agent status hooks |
| `keepComputerAwakeWhileAgentsRun` | `boolean` | `false` | macOS awake assertions while agents work |
| `promptCacheTimerEnabled` | `boolean` | `false` | Show prompt-cache countdown UI |
| `promptCacheTtlMs` | `300_000` \| `3_600_000` | `300_000` | Cache TTL (5 min or 1 hr) |
`TuiAgent` is a large string union (`claude`, `codex`, `gemini`, `cursor`, `grok`, …) defined beside `GlobalSettings` in `src/shared/types.ts`.
### Tasks and integrations
| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `defaultTaskViewPreset` | `TaskViewPresetId` | `'all'` | Default Tasks view filter |
| `defaultTaskSource` | `TaskProvider` | `'github'` | Last-used provider (`github`, `gitlab`, `linear`) |
| `visibleTaskProviders` | `TaskProvider[]` | all three | Providers shown in UI |
| `defaultRepoSelection` | `string[]` \| `null` | `null` | Sticky-all vs frozen repo id list |
| `defaultLinearTeamSelection` | `string[]` \| `null` | `null` | Sticky-all vs frozen team ids |
| `githubProjects` | `GitHubProjectSettings` | empty structure | Pinned/recent/active GitHub Project |
| `gitlabProjects` | `GitLabProjectSettings` | optional | Pinned/recent GitLab projects |
| `openLinksInApp` | `boolean` | `true` | Open links in Orca browser vs system |
| `openInApplications` | `OpenInApplication[]` | `[]` | Extra “Open in …” launcher rows |
### Managed provider accounts
| Field | Type | Role |
| --- | --- | --- |
| `codexManagedAccounts` | `CodexManagedAccount[]` | Codex account switching (shared `~/.codex` prep) |
| `activeCodexManagedAccountId` | `string` \| `null` | Active Codex account |
| `claudeManagedAccounts` | `ClaudeManagedAccount[]` | Claude auth material (no `CLAUDE_CONFIG_DIR` fork) |
| `activeClaudeManagedAccountId` | `string` \| `null` | Active Claude account |
| `opencodeSessionCookie` | `string` | OpenCode Go session (encrypted on disk) |
| `opencodeWorkspaceId` | `string` | Optional workspace override for usage API |
| `geminiCliOAuthEnabled` | `boolean` | `false` | Opt-in Gemini CLI OAuth extraction for rate limits |
### Notifications
`notifications` is a required `NotificationSettings` object:
<ParamField body="enabled" type="boolean" default="true">
Master switch for native desktop notifications.
</ParamField>
<ParamField body="agentTaskComplete" type="boolean" default="true">
Notify when an agent task completes.
</ParamField>
<ParamField body="terminalBell" type="boolean" default="false">
Notify on terminal bell.
</ParamField>
<ParamField body="suppressWhenFocused" type="boolean" default="true">
Suppress notifications while Orca is focused.
</ParamField>
<ParamField body="customSoundId" type="enum" default="system">
Built-in ids: `system`, `two-tone`, `bong`, `thump`, `blip`, `sonar`, `blop`, `ding`, `clack`, `beep`, or `custom`.
</ParamField>
<ParamField body="customSoundPath" type="string | null" default="null">
Filesystem path when `customSoundId === 'custom'`.
</ParamField>
<ParamField body="customSoundVolume" type="number" default="100">
Clamped to 0–100 on save.
</ParamField>
Dispatch uses `NotificationDispatchRequest` (`source`, optional `paneKey`, agent metadata, etc.) from the main notification pipeline.
### Runtime environments and mobile
| Field | Type / values | Default | Role |
| --- | --- | --- | --- |
| `activeRuntimeEnvironmentId` | `string` \| `null` | `null` | Target for client-routed RPC (`null` = local desktop) |
| `experimentalMobile` | `boolean` | `false` | Mobile companion experimental features |
| `mobileAutoRestoreFitMs` | `number` \| `null` | `null` | Auto-restore desktop PTY size after mobile disconnect; clamped 5s–60m on read |
Switching `activeRuntimeEnvironmentId` clears renderer repo/worktree/terminal state and refetches from the new server (`switchRuntimeEnvironment` in the settings slice).
### Voice (dictation)
`voice` is optional on the type but merged to `VoiceSettings` on load:
| Field | Type | Default (via `getDefaultVoiceSettings`) |
| --- | --- | --- |
| `enabled` | `boolean` | `false` |
| `sttModel` | `string` | `''` |
| `modelsDir` | `string` | `''` |
| `language` | `string` | `'en'` |
| `dictationMode` | `'toggle'` \| `'hold'` | `'toggle'` |
| `terminalConfirmBeforeInsert` | `boolean` | `false` |
| `userModels` | `UserModelConfig[]` | `[]` |
### Privacy / telemetry
`telemetry` holds consent and identity only (no heartbeat counters):
| Field | Type | Role |
| --- | --- | --- |
| `optedIn` | `boolean` \| `null` | `true` new installs; `null` until banner for upgraded users |
| `installId` | `string` | Anonymous UUID v4 |
| `existedBeforeTelemetryRelease` | `boolean` | Cohort marker for first-launch banner |
Product telemetry emits `settings_changed` only for keys in `SETTINGS_CHANGED_WHITELIST` (`editorAutoSave`, `openLinksInApp`, `experimentalMobile`, `experimentalPet`, `experimentalActivity`, `experimentalWorktreeSymlinks`, `geminiCliOAuthEnabled`).
### Experimental flags
| Field | Default | UI surface |
| --- | --- | --- |
| `experimentalPet` | `false` | Animated pet overlay (visibility also in `PersistedUIState.petVisible`) |
| `experimentalActivity` | `false` | Left-sidebar Agents feed |
| `experimentalWorktreeSymlinks` | `false` | Worktree symlink creation |
| `experimentalSidekick` | legacy read | Migrated to `experimentalPet` on load |
Hidden experimental toggles (Shift-click Experimental in Settings) may expose additional controls; they still persist through the same `settings:set` path.
### One-shot migration guards
Several `*Migrated*` / `*Defaulted*` boolean fields exist only to preserve explicit user choices across default rollouts (`terminalMacOptionAsAltMigrated`, `experimentalActivityDefaultedOffForAllUsers`, `primarySelectionMiddleClickPasteDefaultedForTerminalDefaults`, etc.). Application code should not treat them as user-facing preferences; they are written during `Store.load()` when a migration runs.
## Web client storage
The paired web client (`src/renderer/src/web/web-preload-api.ts`) keeps a partial `GlobalSettings` blob in browser `localStorage`, merged with `getDefaultSettings('~')` and web-specific overrides (`floatingTerminalEnabled: false`, `activeRuntimeEnvironmentId` from the paired environment). It does not write `orca-data.json`; authoritative desktop state remains on the machine running the Orca main process.
## Programmatic access
```ts
// Renderer (after preload bridge)
const settings = await window.api.settings.get()
await window.api.settings.set({ theme: 'dark' })
// Main process
store.getSettings()
store.updateSettings({ agentStatusHooksEnabled: false })
```
Ghostty terminal import preview: `settings:previewGhosttyImport` returns a `GhosttyImportPreview` with `diff: Partial<GlobalSettings>` for applicable keys.
## Related pages
<CardGroup>
<Card title="Worktrees and repos" href="/worktrees-and-repos">
Workspace directory layout, repo registration, and per-repo fields outside `GlobalSettings`.
</Card>
<Card title="Repository hooks" href="/repository-hooks">
`Repo.hookSettings`, `orca.yaml` scripts, and hook command-source policies.
</Card>
<Card title="Terminals and agents" href="/terminals-and-agents">
PTY lifecycle, `TuiAgent` detection, env injection, and agent status hooks.
</Card>
<Card title="Runtime environments" href="/runtime-environments">
`activeRuntimeEnvironmentId`, pairing, and remote RPC routing.
</Card>
<Card title="Source control integrations" href="/source-control-integrations">
GitHub/GitLab project settings and review flows backed by `githubProjects` / `gitlabProjects`.
</Card>
<Card title="Telemetry and privacy" href="/telemetry-privacy">
Consent banner, `telemetry` fields, and event boundaries.
</Card>
</CardGroup>
---
## 18. Develop and build
> Local dev (`pnpm dev`), typecheck targets, native runtime rebuilds, electron-vite build graph, relay/computer native artifacts, and platform release builds.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/18-develop-and-build.md
- Generated: 2026-06-01T20:11:17.072Z
### Source Files
- `package.json`
- `.github/CONTRIBUTING.md`
- `config/scripts/run-electron-vite-dev.mjs`
- `config/scripts/run-electron-vite-build.mjs`
- `config/electron-builder.config.cjs`
- `config/scripts/ensure-native-runtime.mjs`
- `vite.web.config.ts`
---
title: "Develop and build"
description: "Local dev (`pnpm dev`), typecheck targets, native runtime rebuilds, electron-vite build graph, relay/computer native artifacts, and platform release builds."
---
Orca’s source build is a pnpm workspace on **Node 24** that compiles the Electron app through **electron-vite** (`out/main`, `out/preload`, `out/renderer`), emits a standalone **CLI** with `tsc` (`out/cli`), bundles **SSH relay** artifacts per platform (`out/relay`), and on macOS builds the **computer-use** helper app before **electron-builder** packages installers. `pnpm dev` runs `ensure-native-runtime` for Electron, then `config/scripts/run-electron-vite-dev.mjs`, which prepares dev-only CLI wrappers, optional web pairing assets, and launches `electron-vite dev`.
## Prerequisites
| Requirement | Source |
|-------------|--------|
| Node.js **24** | `package.json` → `engines.node` |
| pnpm **10.24+** | `packageManager` field in `package.json` |
```bash
pnpm install
```
`postinstall` runs `config/scripts/rebuild-native-deps.mjs`, which recompiles `better-sqlite3`, `node-pty`, and (on macOS/Linux) `cpu-features` against Electron’s embedded Node ABI via `@electron/rebuild`.
<Note>
Contributor PR checks mirror `.github/CONTRIBUTING.md`: `pnpm lint`, `pnpm typecheck`, `pnpm test`, and `pnpm build`.
</Note>
## Local development
### Start the desktop app
```bash
pnpm dev
```
Equivalent to:
1. `pnpm run ensure:electron-runtime` → `node config/scripts/ensure-native-runtime.mjs --runtime=electron`
2. `node config/scripts/run-electron-vite-dev.mjs` → spawns `electron-vite dev`
Variants:
| Script | Behavior |
|--------|----------|
| `pnpm dev-stable-name` | Passes `--stable-name` so macOS uses Electron’s stock app name (for tools that key off it) |
| `pnpm start` | `ensure:electron-runtime` then `electron-vite preview` (production-like bundle preview) |
| `pnpm dev:web` | Vite dev server for the pairing web client only (`vite.web.config.ts`, `127.0.0.1`) |
### What `run-electron-vite-dev.mjs` sets up
```mermaid
flowchart TB
subgraph prep [Dev runner prep]
A[ensure-native-runtime electron]
B[prepareDevCliWrapper → out/bin/orca-dev]
C[seedDevInstanceIdentityEnv]
D[prepareMacDevElectronApp optional]
E[prepareDevWebClient optional]
end
subgraph run [Launch]
F[electron-vite dev + remote-debugging-port]
end
A --> B --> C --> D --> E --> F
```
**Dev CLI (`orca-dev`).** Unless `ORCA_SKIP_DEV_CLI_PREPARE=1`, the runner writes `out/bin/orca-dev` (or `orca-dev.cmd` on Windows) with:
- `ORCA_USER_DATA_PATH` → isolated dev profile (`orca-dev` under Application Support / AppData / XDG config)
- `ORCA_APP_EXECUTABLE` → local Electron binary
- Prepends `out/bin` to `PATH`
The checked-in wrapper at `config/scripts/orca-dev` is also registered as an npm bin; `pnpm run build:cli` can symlink it to `/usr/local/bin/orca-dev` when permissions allow.
**Parallel worktrees.** Git branch and worktree name seed `ORCA_DEV_INSTANCE_LABEL`, `ORCA_DEV_DOCK_TITLE`, and a per-repo remote-debugging port (SHA1 of repo root → base port 9333–9532, with a 64-port sweep). Set `REMOTE_DEBUGGING_PORT` to override.
**macOS Dock identity.** On Darwin, the runner may copy `Electron.app` to `out/electron-dev/<hash>/` with a unique bundle ID and Dock title so multiple `pnpm dev` instances are distinguishable. Skip with `ORCA_SKIP_DEV_ELECTRON_APP_PREPARE=1` or use `dev-stable-name` / `ORCA_DEV_STABLE_NAME=1`.
**Web pairing bundle.** If `out/web/web-index.html` is missing or stale, dev logs a skip unless `ORCA_DEV_WEB_PREPARE=1` or sources changed; run `pnpm run build:web` when browser pairing is required.
**Host environment cleanup.** The runner deletes `ELECTRON_RUN_AS_NODE` before spawning electron-vite so `require('electron')` resolves to the real Electron API, not the npm stub.
### Renderer-only web client
`vite.web.config.ts` builds `src/renderer/web-index.html` to `out/web/` with `base: './'` for reverse-proxy path prefixes. Feature wall is enabled at compile time via `ORCA_FEATURE_WALL_ENABLED: 'true'`.
## Typecheck
Orca uses **`tsgo`** (TypeScript native preview) for day-to-day checks and keeps **`tsc`** aliases for compatibility.
| Script | Project | Scope |
|--------|---------|-------|
| `pnpm typecheck:node` / `pnpm tc:node` | `config/tsconfig.node.json` | `src/main`, `src/preload`, `src/shared`, `src/relay`, `electron.vite.config.*` |
| `pnpm typecheck:cli` / `pnpm tc:cli` | `config/tsconfig.tc.cli.json` | CLI + shared + selected main hook modules |
| `pnpm typecheck:web` / `pnpm tc:web` | `config/tsconfig.tc.web.json` | Renderer, preload API types, shared, select main IPC |
| `pnpm typecheck` / `pnpm tc` | All three sequentially | Full gate used in CI |
`tsc` equivalents: `typecheck:tsc`, `typecheck:tsc:node`, etc., pointing at `tsconfig.cli.json` / `tsconfig.web.json` without the `tc.*` path tweaks.
<Warning>
Project-owned types must live in `.ts` files under `src/preload` and `src/shared`, not `.d.ts` — CI rejects new `.d.ts` there because `skipLibCheck` can hide broken IPC signatures.
</Warning>
## Native module runtimes
Two ABIs matter: **system Node** (Vitest, relay esbuild target) and **Electron’s Node** (app, dev, packaged CLI via `ELECTRON_RUN_AS_NODE`).
### Ensure scripts
```bash
# Before dev / electron-vite preview / e2e — Electron ABI
pnpm run ensure:electron-runtime
# Before unit tests — Node ABI (also invoked by pnpm test)
node config/scripts/ensure-native-runtime.mjs --runtime=node
```
`ensure-native-runtime.mjs` loads `better-sqlite3` (in-memory DB) and `node-pty` (including Windows `conpty` vs `pty` by build number). On failure it runs `pnpm rebuild <modules>` (Node) or `rebuild-native-deps.mjs` (Electron).
### Manual rebuilds
| Command | When |
|---------|------|
| `pnpm run rebuild:electron` | Re-run postinstall Electron rebuild (`rebuild-native-deps.mjs`) |
| `pnpm run rebuild:node` | `pnpm rebuild better-sqlite3 node-pty` for Vitest |
| `ORCA_FORCE_NATIVE_REBUILD=1 pnpm run rebuild:electron` | Skip probe, force `@electron/rebuild` |
On Windows, postinstall may continue if a `.node` file is locked (`ORCA_STRICT_NATIVE_REBUILD` disables leniency). Close Orca/Electron processes before reinstalling.
## Build pipeline
### Full contributor build
```bash
pnpm build
```
Order (from `package.json`):
1. `pnpm typecheck`
2. `pnpm run build:relay`
3. `pnpm run build:computer-macos`
4. `pnpm run build:cli`
5. `pnpm run build:electron-vite`
6. `pnpm run build:web`
### Release build (CI / maintainers)
```bash
pnpm build:release
```
Same as `build` except **skips typecheck** and runs `pnpm run verify:computer-native` after the macOS computer helper build. Release workflows set `ORCA_BUILD_IDENTITY`, `ORCA_POSTHOG_WRITE_KEY`, and related vars so `electron.vite.config.ts` injects telemetry constants; local builds substitute literal `null`.
### electron-vite graph
`electron.vite.config.ts` defines three targets:
| Target | Entry highlights | Output role |
|--------|------------------|-------------|
| **main** | `index`, `daemon-entry`, `computer-sidecar`, `stt-worker`, `agent-hooks/managed-agent-hook-controls` | Main process; daemon entry must stay unpackable for `fork()` |
| **preload** | Preload bridge | IPC surface to renderer |
| **renderer** | React + Tailwind | Desktop UI |
`build:electron-vite` runs `config/scripts/run-electron-vite-build.mjs`, which invokes `electron-vite build` with `NODE_OPTIONS` including `--max-old-space-size=4096` to avoid renderer OOM on CI macOS runners.
Main-process `externalizeDeps` keeps most deps external except bundled xterm headless/serialize paths used from the unpacked daemon.
### CLI compile
```bash
pnpm run build:cli
```
Runs `tsc -p config/tsconfig.cli.json --outDir out` then `install-dev-cli.mjs` for the optional global `orca-dev` symlink. Production `orca` resolves from `out/cli/index.js` (`package.json` `bin.orca`).
### Relay artifacts
`pnpm run build:relay` esbuild-bundles `src/relay/relay.ts` into self-contained CommonJS per platform:
- `out/relay/linux-x64/relay.js`
- `out/relay/linux-arm64/relay.js`
- `out/relay/darwin-x64/relay.js`
- `out/relay/darwin-arm64/relay.js`
Native addons (`node-pty`, `@parcel/watcher`, `better-sqlite3`) stay **external**; remote hosts install or degrade gracefully. Each folder gets a `.version` file with content hash.
Packaged apps copy `out/relay` to `process.resourcesPath/relay` via electron-builder `extraResources`.
### Computer-use native artifacts
| Script | Platform | Output |
|--------|----------|--------|
| `build:computer-macos` | darwin only (no-op elsewhere) | Universal Swift binary + `Orca Computer Use.app` under `native/computer-use-macos/.build/release/` |
| (packaged) | win | `computer-use-windows/runtime.ps1` in resources |
| (packaged) | linux | `computer-use-linux/runtime.py` in resources |
`verify:computer-native` runs Swift tests, Python syntax/import checks, and PowerShell parse/handshake validation as appropriate for the host OS.
`build:mac:release` requires signing env vars verified by `verify-macos-release-env.mjs` (`APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`, `CSC_LINK`, `CSC_KEY_PASSWORD`). Local ad-hoc macOS builds use `pnpm build:mac` without `ORCA_MAC_RELEASE=1`.
## Platform installers (electron-builder)
Config: `config/electron-builder.config.cjs`. All platform targets run `pnpm build` (or release variant), `ensure:electron-runtime`, then electron-builder.
| Script | Artifact |
|--------|----------|
| `pnpm build:unpack` | `--dir` unpacked app for inspection |
| `pnpm build:mac` | DMG + ZIP (x64, arm64); ad-hoc signing unless `ORCA_MAC_RELEASE=1` |
| `pnpm build:mac:release` | Hardened runtime + notarization + `forceCodeSigning` |
| `pnpm build:win` | NSIS installer (`orca-windows-setup`) |
| `pnpm build:linux` | AppImage + deb (`orca-ide` package name; executable `orca-ide`) |
Notable packaging rules:
- **`asarUnpack`**: `out/cli`, `out/shared`, agent hook dirs, `daemon-entry.js`, `computer-sidecar.js`, `ws`, `tweetnacl`, `zod`, `yaml`, `sherpa-onnx*`, etc., so forked Node and `ELECTRON_RUN_AS_NODE` CLI can `require` across directories.
- **`extraResources`**: relay bundles, platform `orca` CLI shims, `agent-browser` binaries, computer-use runtimes, onboarding feature-wall media.
- **`npmRebuild: true`**: Rebuilds `node-pty` per target arch (avoids arm64 binaries inside x64 macOS DMGs).
- **macOS computer helper**: `afterPack` signs `Orca Computer Use.app` before the outer app is sealed.
Maintainer releases are cut from GitHub Actions **Cut Release** — not a local `pnpm release:*` script. See `.github/CONTRIBUTING.md` for version semantics and safety guarantees.
## Output layout
:::files
orca/
├── out/
│ ├── main/ # electron-vite main (+ daemon, sidecar, hooks)
│ ├── preload/
│ ├── renderer/
│ ├── cli/ # tsc-compiled CLI
│ ├── shared/ # shared modules used by CLI
│ ├── web/ # vite.web pairing client
│ ├── relay/ # per-platform relay.js bundles
│ ├── bin/ # dev orca-dev wrapper (created at dev time)
│ └── electron-dev/ # macOS per-instance Electron.app copies
├── native/
│ └── computer-use-macos/.build/release/
│ └── Orca Computer Use.app
└── dist/ # electron-builder installers (after platform build)
:::
Production entry: `package.json` `"main": "./out/main/index.js"`, `"bin": { "orca": "./out/cli/index.js" }`.
## Useful environment variables
| Variable | Effect |
|----------|--------|
| `ORCA_SKIP_DEV_CLI_PREPARE` | Skip `out/bin/orca-dev` generation |
| `ORCA_SKIP_DEV_ELECTRON_APP_PREPARE` | Skip macOS per-instance Electron.app copy |
| `ORCA_SKIP_DEV_WEB_PREPARE` | Skip web bundle freshness check/build in dev |
| `ORCA_DEV_WEB_PREPARE=1` | Force web client build when pairing HTML missing |
| `ORCA_DEV_USER_DATA_PATH` | Override dev profile directory |
| `ORCA_MAC_RELEASE=1` | Enable hardened runtime, notarization, strict signing |
| `ORCA_FORCE_NATIVE_REBUILD=1` | Force electron native rebuild |
| `ORCA_BUILD_IDENTITY` / `ORCA_POSTHOG_WRITE_KEY` | Official telemetry compile-time injection (CI only) |
| `ELECTRON_RUN_AS_NODE` | Stripped by dev runner; do not rely on it leaking into electron-vite |
## Verification commands
| Command | Purpose |
|---------|---------|
| `pnpm test` | Vitest unit tests (ensures Node native runtime first) |
| `pnpm test:e2e` | Playwright Electron (headless) |
| `pnpm smoke:computer` | Computer-use smoke script |
| `pnpm lint` | oxlint + styled-scrollbar check |
| `pnpm verify:macos-entitlements` | Entitlements plist consistency |
<Tip>
If `pnpm dev` fails immediately after switching Node versions or branches, run `pnpm run rebuild:electron` then retry. If tests fail on `better-sqlite3`, run `pnpm run rebuild:node`.
</Tip>
## Related pages
<CardGroup>
<Card title="Installation" href="/installation">
Node 24 and pnpm prerequisites for source builds, plus packaged installs when you do not need to compile locally.
</Card>
<Card title="Testing" href="/testing">
Vitest, Playwright Electron E2E, computer-use smoke paths, and SSH-localhost test flags.
</Card>
<Card title="Contributing" href="/contributing">
PR checklist, cross-platform constraints, and maintainer release workflow via GitHub Actions.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Native dependency failures, runtime reachability, and platform-specific build errors.
</Card>
</CardGroup>
---
## 19. Testing
> Unit tests (Vitest), Playwright Electron E2E, computer-use smoke scripts, and SSH-localhost E2E env flags documented in test helpers.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/19-testing.md
- Generated: 2026-06-01T20:11:07.430Z
### Source Files
- `package.json`
- `config/vitest.config.ts`
- `tests/playwright.config.ts`
- `tests/e2e/helpers/orca-app.ts`
- `tests/e2e/ssh-localhost.spec.ts`
- `config/scripts/computer-use-smoke.mjs`
- `.env.e2e`
---
title: "Testing"
description: "Unit tests (Vitest), Playwright Electron E2E, computer-use smoke scripts, and SSH-localhost E2E env flags documented in test helpers."
---
Orca’s test surface spans Vitest unit tests under `src/` and `config/scripts/`, Playwright Electron E2E in `tests/e2e/` (built with `electron-vite --mode e2e` so `window.__store` is exposed), optional Vitest-driven `computer` CLI E2E, a `smoke:computer` script for desktop accessibility snapshots, and an opt-in localhost SSH spec gated by `ORCA_E2E_SSH_LOCALHOST`.
## Test layers
```text
pnpm test Vitest (Node) — src/**, config/scripts/**
│
├─ PR CI (pr.yml) lint, typecheck, pnpm test, pnpm build
│
pnpm test:e2e Playwright _electron.launch → out/main/index.js
│ globalSetup: electron-vite build --mode e2e + seeded git repo
├─ electron-headless default; grepInvert @headful
└─ electron-headful ORCA_E2E_HEADFUL / @headful specs (pointer, WebGL)
pnpm test:e2e:computer Vitest — tests/e2e/computer-*.e2e.ts (ORCA_COMPUTER_E2E=1)
pnpm smoke:computer CLI smoke — config/scripts/computer-use-smoke.mjs
ORCA_E2E_SSH_LOCALHOST=1 ssh-localhost.spec.ts (+ build:relay in globalSetup)
```
<Note>
E2E is not part of the default PR verify job. It runs on a dedicated workflow (scheduled, `workflow_dispatch`, and from release cutting) with a separate build artifact and five-way sharding on Ubuntu.
</Note>
## Unit tests (Vitest)
| Item | Value |
| --- | --- |
| Command | `pnpm test` |
| Config | `config/vitest.config.ts` |
| Environment | `node` |
| Includes | `src/**/*.test.ts`, `src/**/*.test.tsx`, `config/scripts/**/*.test.mjs` |
| Pre-step | `ensure-native-runtime.mjs --runtime=node` (rebuilds `better-sqlite3` and `node-pty` for system Node) |
Vitest defines `ORCA_FEATURE_WALL_ENABLED: 'true'` and aliases `@renderer` / `@` to `src/renderer/src`. Tests live next to implementation (`src/main/`, `src/relay/`, renderer store slices, CLI paths, and similar).
PR CI (`/.github/workflows/pr.yml`) runs `pnpm rebuild better-sqlite3` before `pnpm test` because postinstall targets Electron’s ABI while Vitest executes under system Node.
### Renderer store slice tests
Pure Zustand slice behavior belongs in `src/renderer/src/store/slices/*.test.ts` (local `createTestStore()` helpers), not in Playwright specs. Reserve Electron E2E for DOM interaction, IPC, persistence, or multi-worktree lifecycle that unit tests cannot model faithfully.
## Playwright Electron E2E
| Script | Behavior |
| --- | --- |
| `pnpm run test:e2e` | `ensure:electron-runtime`, then Playwright project `electron-headless` |
| `pnpm run test:e2e:headful` | Same config, project `electron-headful` (`@headful` tests only) |
| `SKIP_BUILD=1 pnpm run test:e2e` | Reuse existing `out/` if `out/main/index.js` exists |
Config: `tests/playwright.config.ts` (`@stablyai/playwright-test`). Specs live in `tests/e2e/*.spec.ts`. Shared fixtures: `tests/e2e/helpers/orca-app.ts`.
### E2E build mode and `window.__store`
`.env.e2e` sets `VITE_EXPOSE_STORE=true`. That flag is applied only when the renderer is built with:
```bash
npx electron-vite build --mode e2e
```
`globalSetup` (`tests/e2e/global-setup.ts`) runs that build unless `SKIP_BUILD=1` and `out/main/index.js` already exist. A plain `pnpm build` / `pnpm build:electron-vite` **does not** expose the store; specs then hang on `waitForFunction(() => Boolean(window.__store))`.
Fast iteration:
<Steps>
<Step title="Build once for E2E">
Run `pnpm exec electron-vite build --mode e2e`.
</Step>
<Step title="Run tests without rebuild">
Run `SKIP_BUILD=1 pnpm run test:e2e` (optionally filter a file).
</Step>
</Steps>
### Lifecycle: setup, fixture, teardown
**globalSetup**
1. Builds Electron output (`--mode e2e`).
2. When `ORCA_E2E_SSH_LOCALHOST=1`, also runs `pnpm run build:relay` for localhost SSH relay deployment.
3. Creates a temp git repo (initial commit + secondary worktree on branch `e2e-secondary`) and writes its path to `orca-e2e-test-repo-path.txt` under the system temp directory.
**Per-test fixture** (`orca-app.ts`)
- Launches `_electron` against `out/main/index.js` with an isolated `ORCA_E2E_USER_DATA_DIR`.
- Seeds completed-onboarding `orca-data.json` by default (`dismissOnboarding: true`; `onboarding.spec.ts` can opt out).
- Strips `ELECTRON_RUN_AS_NODE` from the child env (host shells sometimes set it and break Playwright launch).
- Adds the seeded repo via `window.api.repos.add`, enables external worktree visibility, waits for `workspaceSessionReady`, and activates the primary worktree.
- Tears down: closes the app, cleans E2E daemons, removes userData.
**globalTeardown** removes the temp repo, sibling `orca-e2e-worktree-*` dirs, and the path file.
### Playwright projects and timing
| Setting | Value |
| --- | --- |
| `timeout` | 120_000 ms per test |
| `expect.timeout` | 10_000 ms |
| `fullyParallel` | `true` (headful drag/WebGL suites use `test.describe.serial` where needed) |
| `workers` | `2` on CI; default locally |
| `retries` | `0` |
| Trace / screenshot | `retain-on-failure` / `only-on-failure` |
| Project | `testMatch` | Filter |
| --- | --- | --- |
| `electron-headless` | `**/*.spec.ts` | `grepInvert: /@headful/` |
| `electron-headful` | `**/*.spec.ts` | `grep: /@headful/` |
Tag specs with `@headful` when they need a visible window, real pointer capture, or WebGL (for example terminal pane resize drags and dead-terminal reproduction). `ORCA_E2E_FORCE_HEADFUL=1` runs any spec headful without retagging.
On Linux CI, headless launch args disable GPU subprocess paths (`tests/e2e/helpers/electron-launch-args.ts`); runners use `xvfb-run` because Chromium still needs a display even when `ORCA_E2E_HEADLESS` suppresses `mainWindow.show()`.
### E2E authoring rules
From `tests/e2e/AGENTS.md`:
- Use `window.__store` and IPC for **setup** only.
- Final assertions must be **DOM-visible** (`getByRole`, `toBeVisible`, `toHaveText`, etc.).
- Do not assert store round-trips as a substitute for UI correctness.
Headless mode still drives the real DOM over CDP; store-only expectations miss render-layer regressions.
## E2E environment variables
Variables below are read in `tests/e2e/helpers/orca-app.ts`, `tests/e2e/global-setup.ts`, or `tests/e2e/ssh-localhost.spec.ts` unless noted.
### Harness and debugging
<ParamField body="SKIP_BUILD" type="string">
When set (any value) and `out/main/index.js` exists, `globalSetup` skips `electron-vite build --mode e2e`.
</ParamField>
<ParamField body="ORCA_E2E_USER_DATA_DIR" type="string">
Set by the fixture to a per-test temp dir. Main/preload read this for isolated persistence (`src/main/e2e-config.ts`, `src/preload/e2e-config.ts`).
</ParamField>
<ParamField body="ORCA_E2E_HEADLESS" type="string">
`1` suppresses showing the main window during headless project runs.
</ParamField>
<ParamField body="ORCA_E2E_HEADFUL" type="string">
`1` on headful project runs; also honored when `ORCA_E2E_FORCE_HEADFUL=1`.
</ParamField>
<ParamField body="ORCA_E2E_FORCE_HEADFUL" type="string">
`1` forces a visible window for any spec without switching Playwright projects.
</ParamField>
<ParamField body="ORCA_E2E_SLOWMO_MS" type="number">
Milliseconds of Playwright `slowMo` between actions (parsed at worker scope; invalid values log a warning and fall back to `0`).
</ParamField>
<ParamField body="ORCA_E2E_FORWARD_APP_LOGS" type="string">
`1` forwards Electron child stdout/stderr into the test runner (used on CI: `ORCA_E2E_FORWARD_APP_LOGS=1`).
</ParamField>
<ParamField body="ORCA_E2E_RECORD_VIDEO" type="string">
`1` records WebM under the test output directory (creates the dir first on Windows).
</ParamField>
### Localhost SSH E2E
<Warning>
`tests/e2e/ssh-localhost.spec.ts` is skipped unless `ORCA_E2E_SSH_LOCALHOST=1`. It requires a running local `sshd`, non-interactive key/agent auth, POSIX hook scripts, and (by default) remote agent hooks enabled. It is not run in standard CI shards.
</Warning>
| Variable | Role |
| --- | --- |
| `ORCA_E2E_SSH_LOCALHOST` | `1` enables the suite; triggers `build:relay` in `globalSetup` |
| `ORCA_E2E_SSH_HOST` | SSH host (default `127.0.0.1` when `ORCA_E2E_SSH_CONFIG_HOST` unset) |
| `ORCA_E2E_SSH_PORT` | Port (default `22`; validated 1–65535) |
| `ORCA_E2E_SSH_USER` | Username (falls back to `USER` / `USERNAME` / `os.userInfo()`) |
| `ORCA_E2E_SSH_CONFIG_HOST` | Use SSH config `Host` alias instead of explicit host |
| `ORCA_E2E_SSH_IDENTITY_FILE` | Optional identity file path |
| `ORCA_RELAY_PATH` | Relay bundle path; fixture sets `out/relay` when unset and SSH localhost mode is on |
| `ORCA_FEATURE_REMOTE_AGENT_HOOKS` | Skip when explicitly `0`; unset or non-empty non-`0` keeps remote hook tests enabled |
Example local run:
```bash
ORCA_E2E_SSH_LOCALHOST=1 pnpm run test:e2e tests/e2e/ssh-localhost.spec.ts
```
The spec connects to localhost SSH, registers a remote repo, exercises remote PTY output, agent-hook env vars (`ORCA_AGENT_HOOK_*`, `ORCA_PANE_KEY`), Codex hook POST relay, and keyboard interrupt behavior.
## Computer-use tests
Two complementary paths exercise desktop accessibility through the `orca computer` CLI.
### Smoke script (`pnpm smoke:computer`)
`config/scripts/computer-use-smoke.mjs` requires a built CLI at `out/cli/index.js` (`pnpm build:cli` first).
| Flag / env | Purpose |
| --- | --- |
| `--apps` | Comma-separated app names (default from `ORCA_COMPUTER_SMOKE_APPS` or Finder, TextEdit, Spotify, Slack, Microsoft Edge) |
| `--session` | Session id (default `computer-smoke-<pid>`) |
| `--screenshot` | Include screenshots in `get-app-state` |
| `ORCA_COMPUTER_SMOKE_USER_DATA_PATH` | Overrides dev userData (`orca-dev` per platform) |
The script lists running apps, snapshots preferred targets that are actually running, prints element/line counts, and exits `0` when no preferred apps are open (not a failure). Failures increment a counter and exit `1`.
### Vitest computer E2E (`pnpm test:e2e:computer`)
Config: `tests/e2e/vitest.config.ts` — includes `tests/e2e/computer-*.e2e.ts`, 60s test/hook timeouts, Node environment.
| File | Platform gate |
| --- | --- |
| `computer-mac.e2e.ts` | `darwin` + `ORCA_COMPUTER_E2E=1` |
| `computer-linux.e2e.ts` | Linux + `ORCA_COMPUTER_E2E=1` (+ `ACCESSIBILITY_ENABLED=1` on scheduled CI) |
| `computer-windows.e2e.ts` | Windows + `ORCA_COMPUTER_E2E=1` |
Helpers in `tests/e2e/helpers/computer-driver.ts` invoke `config/scripts/orca-dev` (or `out/cli/index.js` on Windows) unless `ORCA_COMPUTER_CLI` overrides the command. macOS specs drive TextEdit (`list-apps`, `get-app-state`, `click`, `type-text`, `paste-text`, hotkeys).
Workflow `.github/workflows/computer-e2e.yml`:
- **PR**: `verify:computer-native` + `build:cli` smoke on Ubuntu/Windows matrix only.
- **Scheduled / manual**: full platform E2E on macOS 14, Ubuntu 22.04 (xvfb + dbus), and Windows with `ORCA_COMPUTER_E2E=1`.
## CI summary
| Workflow | What runs |
| --- | --- |
| `pr.yml` → `verify` | `pnpm test` (Vitest), typecheck, lint, full `pnpm build` |
| `e2e.yml` | Separate build job uploads `out/`; five shards run `SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run test:e2e` under `xvfb-run`; failed shards upload `test-results/` traces |
| `computer-e2e.yml` | Native verify smoke on PR; platform computer Vitest on schedule/manual |
| `release-cut.yml` | Calls `e2e.yml` before release steps |
## Common failures
| Symptom | Likely cause | Fix |
| --- | --- | --- |
| All E2E hang at `window.__store` | `out/` built without `--mode e2e` | `pnpm exec electron-vite build --mode e2e` or full `pnpm run test:e2e` without `SKIP_BUILD` |
| Vitest fails loading `better-sqlite3` | Native module built for Electron ABI | `pnpm rebuild better-sqlite3` (PR CI does this automatically) |
| Linux E2E Chromium startup errors | GPU under Xvfb | Already mitigated via launch args + xvfb; check `ORCA_E2E_FORWARD_APP_LOGS=1` logs |
| SSH localhost skipped | Gate env unset | `ORCA_E2E_SSH_LOCALHOST=1` + local `sshd` + keys |
| Computer E2E skipped | Opt-in unset | `ORCA_COMPUTER_E2E=1` and platform-specific prerequisites |
<Tip>
Filter a single spec during development: `SKIP_BUILD=1 pnpm run test:e2e tests/e2e/worktree.spec.ts`. For headful debugging: `ORCA_E2E_FORCE_HEADFUL=1 ORCA_E2E_SLOWMO_MS=250 pnpm run test:e2e:headful tests/e2e/terminal-panes.spec.ts`.
</Tip>
## Related pages
<CardGroup>
<Card title="Develop and build" href="/develop-and-build">
Native runtime rebuilds, `electron-vite` build graph, and `build:computer-macos` artifacts that computer tests depend on.
</Card>
<Card title="Contributing" href="/contributing">
PR expectations, cross-platform constraints, and maintainer workflows that trigger E2E and release checks.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
Product behavior exercised by the localhost SSH E2E spec (targets, relay, remote PTY, agent hooks).
</Card>
<Card title="CLI browser and computer reference" href="/cli-browser-computer-reference">
`orca computer` commands used by smoke and Vitest computer E2E.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Runtime reachability, native dependency, and platform-specific failure modes that overlap with test setup.
</Card>
</CardGroup>
---
## 20. Contributing
> Contribution workflow, cross-platform/SSH/agent constraints from AGENTS.md, PR expectations, and maintainer release cutting via GitHub Actions.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/20-contributing.md
- Generated: 2026-06-01T20:10:49.719Z
### Source Files
- `.github/CONTRIBUTING.md`
- `AGENTS.md`
- `docs/STYLEGUIDE.md`
- `.github/workflows/release-cut.yml`
- `package.json`
---
title: "Contributing"
description: "Contribution workflow, cross-platform/SSH/agent constraints from AGENTS.md, PR expectations, and maintainer release cutting via GitHub Actions."
---
Contributions land through GitHub pull requests against `stablyai/orca`, with local validation via `pnpm lint`, `pnpm typecheck`, `pnpm test`, and `pnpm build`, agent-facing rules in `AGENTS.md`, and maintainer-only version tags cut exclusively by the **Cut Release** workflow (`.github/workflows/release-cut.yml`)—there are no `pnpm release:*` scripts in `package.json`.
## Prerequisites and local setup
| Requirement | Value |
| --- | --- |
| Node.js | `24` (`package.json` → `engines.node`) |
| Package manager | `pnpm@10.24.0` (pinned in `packageManager`) |
| Install | `pnpm install` |
| Dev app | `pnpm dev` (runs `ensure:electron-runtime` then electron-vite dev) |
<Note>
`pnpm prepare` installs Husky; the pre-commit hook runs `lint-staged` (oxlint + oxfmt on staged `*.{ts,tsx,js,jsx,mts,cts}` and oxfmt on `*.{json,css}`).
</Note>
For deeper build graphs, native runtime rebuilds, and platform packaging, see [Develop and build](/develop-and-build). For test targets beyond the default Vitest run, see [Testing](/testing).
## Scope and engineering constraints
Keep each change scoped to one user-facing improvement, bug fix, or focused refactor. Orca ships on **macOS, Linux, and Windows**, supports **local repos, remote runtimes, and SSH worktrees**, and integrates many **CLI agents** and **git providers**—generic behavior must stay provider-neutral with integration-specific logic behind explicit checks.
### Cross-platform (renderer, menus, paths)
| Surface | Rule |
| --- | --- |
| Keyboard shortcuts | Never hardcode `e.metaKey`. Use `navigator.userAgent.includes('Mac')` for `metaKey` vs `ctrlKey`. Electron menu accelerators use `CmdOrCtrl`. |
| Shortcut labels in UI | macOS: `⌘` / `⇧`; Linux/Windows: `Ctrl+` / `Shift+`. |
| File paths | `path.join` or Electron/Node path utilities—never assume `/` or `\`. |
These rules appear in both `AGENTS.md` and `docs/STYLEGUIDE.md` (Cross-platform section).
### SSH and remote execution
All changes must consider the SSH use case: do not assume a process, file, credential, shell, or network path exists only on the local machine. UI work should tolerate **50–200 ms** extra latency (deferred loading states, immediate disable on submit). Verify under SSH or simulated latency when touching loading, focus, or animation.
### Git providers and agents
- Source-control and review work must account for **GitLab** and other supported providers, not only GitHub. Avoid GitHub-only naming for generic review concepts.
- Agent and integration behavior stays **BYOC/BYOK**—no assumption of a single model vendor or hosted connector.
- When using `gh` in scripts or tooling, batch requests and respect API rate limits; keep commands portable across macOS, Linux, and Windows.
- **Do not commit PR evidence images**; attach screenshots and recordings to the PR conversation instead.
### Module naming and comments
- Avoid vague file or module names (`helpers`, `utils`, `common`, `misc`, `shared-stuff`). Name after the domain concept (`tab-group-state.ts`, `terminal-orphan-cleanup.ts`).
- Document **why** (one or two lines) when behavior follows a design doc or non-obvious constraint—do not restate what the code does.
### Worktree safety (agents editing the repo)
When using Orca worktrees or subagents, read and edit only in the **primary worktree checkout** for the task. Do not follow absolute paths from subagent output that point at a different repo root.
### UI and design system
UI changes must follow [`docs/STYLEGUIDE.md`](docs/STYLEGUIDE.md): tokens in `src/renderer/src/assets/main.css`, shadcn primitives under `src/renderer/src/components/ui/`, no invented hex when a token exists. Resolution order when the style guide is silent: sibling components → `components/ui/` primitives → `main.css` tokens → ask before inventing.
## Type declarations
Project-owned types belong in **`.ts` files**, not project-owned `.d.ts`. Ambient shims (`env.d.ts`, `vite/client.d.ts`) stay in `.d.ts`. With `skipLibCheck: true`, unresolved references in `.d.ts` can silently become `any` at call sites.
PR CI **fails** if any `*.d.ts` exists under `src/preload/` or `src/shared/` (see `.github/workflows/pr.yml`). Prefer `.ts` so `pnpm typecheck` actually validates IPC and shared contracts.
## Branch naming
Use descriptive prefixes:
- `fix/ctrl-backspace-delete-word`
- `feat/shift-enter-newline`
- `chore/update-contributor-guide`
Avoid vague names (`test`, `misc`, `changes`).
## Validation before opening a PR
Run the same commands contributors are expected to pass locally (from `.github/CONTRIBUTING.md`):
<CodeGroup>
```bash title="Lint"
pnpm lint
```
```bash title="Typecheck"
pnpm typecheck
```
```bash title="Unit tests"
pnpm test
```
```bash title="Full build"
pnpm build
```
</CodeGroup>
`pnpm lint` runs `oxlint` and `pnpm check:styled-scrollbars`. `pnpm typecheck` runs three `tsgo` projects (node, CLI, web). `pnpm test` ensures the native runtime for Node then runs Vitest. `pnpm build` runs typecheck plus relay, computer, CLI, electron-vite, and web builds.
### What PR CI runs
The **PR Checks** workflow (`.github/workflows/pr.yml`) on `pull_request` (opened, synchronize, reopened, ready_for_review) additionally runs:
| Step | Command / behavior |
| --- | --- |
| Lint | `pnpm exec oxlint --format github` |
| Styled scrollbars | `pnpm check:styled-scrollbars` |
| `.d.ts` guard | `find src/preload src/shared -name '*.d.ts'` must be empty |
| Feature wall budget | `pnpm check:feature-wall-assets` |
| macOS entitlements | `pnpm verify:macos-entitlements` |
| Typecheck | `pnpm typecheck` |
| Native module for tests | `pnpm rebuild better-sqlite3` |
| Test | `pnpm test` |
| Build | `pnpm build` |
Add **high-quality tests** for behavior changes and bug fixes—tests that would catch regressions, not shallow happy-path-only coverage. For UI or interaction changes, verify on every platform the change can affect.
## Pull request expectations
GitHub loads `.github/pull_request_template.md`. Each PR should:
<Steps>
<Step title="Explain the user-visible change">
Fill **Summary** with what users or operators will notice.
</Step>
<Step title="Document visuals">
Add screenshots or a screen recording for new or changed UI. If none, write `No visual change` under **Screenshots**.
</Step>
<Step title="Confirm local checks">
Check off `pnpm lint`, `pnpm typecheck`, `pnpm test`, `pnpm build`, and note added/updated tests (or why tests were not needed).
</Step>
<Step title="Attach AI review and security notes">
**AI Review Report**: cross-platform (macOS, Linux, Windows)—shortcuts, labels, paths, shell, Electron differences; SSH/remote/local compatibility; agent and integration compatibility; performance; UI quality when applicable.
**Security Audit**: input, command execution, paths, auth, secrets, dependencies, IPC.
</Step>
<Step title="Call out environment-specific behavior">
**Notes**: platform-specific, remote/SSH-specific, agent-specific, integration-specific, or git-provider-specific behavior and how you tested it.
</Step>
<Step title="Optional social shoutout">
Include your X handle if you want a merge shoutout on [@orca_build](https://x.com/orca_build).
</Step>
</Steps>
Stay focused on a single topic when possible. Do **not** bump `package.json` version or cut tags unless a maintainer requests it—releases are maintainer-managed.
## Agent authoring (`AGENTS.md`)
Coding agents (Cursor, Claude Code, Grok CLI, etc.) should load `AGENTS.md` at the repo root. It mirrors contributor constraints: design system, cross-platform shortcuts, SSH, git-provider neutrality, `gh` rate limits, no committed PR images, `.ts` over `.d.ts`, and worktree path discipline. UI agents should also respect `docs/STYLEGUIDE.md`.
<Info>
Orca does not require a specific LLM provider for contributions; bring your own agent and credentials (BYOC/BYOK).
</Info>
## Release process (maintainers)
Version bumps, tags, and published binaries are **not** part of normal contributor PRs. There is no local `pnpm release:*` script—releases run only from CI to avoid dirty trees, wrong branches, or stale `main`.
### Cut Release workflow
**Workflow file:** `.github/workflows/release-cut.yml`
**GitHub UI:** Actions → **Cut Release**
<ParamField body="kind" type="choice" required>
`rc` (default), `patch`, `minor`, or `major`. Scheduled runs force `rc`.
</ParamField>
<ParamField body="ref" type="string">
Branch, tag, or SHA to build from. Default `main`. Scheduled runs always use `main`.
</ParamField>
<ParamField body="dry_run" type="boolean">
On manual dispatch only: validate a scheduled RC slot without creating a tag.
</ParamField>
**Manual cut:** Run workflow → choose `kind` and `ref` → run.
**Scheduled RC:** Cron `*/5 10,11,22,23 * * *` (UTC) with a Pacific-time gate so roughly **two RC cuts per day** (3:00 and 15:00 PT). Idempotency uses `[rc-slot:YYYY-MM-DD-HH]` commit markers and latest RC tag timing.
### Version selection
Latest **stable** is read from GitHub releases: tag must match `^v[0-9]` and must **not** contain `-rc.` (tag shape is authoritative; not the GitHub prerelease flag).
| `kind` | Next version (example: latest stable `1.3.14`) |
| --- | --- |
| `rc` (no RC series for next patch base) | `1.3.15-rc.0` |
| `rc` (series `1.3.15-rc.2` exists) | `1.3.15-rc.3` |
| `patch` | `1.3.15` (ignores intermediate RCs) |
| `minor` | `1.4.0` |
| `major` | `2.0.0` |
RC math always anchors to **latest stable + patch** for the base series, then increments `-rc.N`.
### Safety and recovery
- **Stable guard:** `patch` / `minor` / `major` refuse if the new version is not strictly greater than latest stable (protects `electron-updater` semver on the `latest` channel).
- **Draft RC recovery:** Complete draft releases from prior runs are published before cutting a new RC; unpublished tags with draft/missing releases can be **resumed** instead of bumping `-rc.N+1` again.
- **Off-main `ref`:** Only the tag is pushed; `main` is never fast-forwarded from a non-tip ref.
- **`ref` = tip of `main`:** Version-bump commit is pushed to `main` and the tag so local `package.json` matches shipped builds.
- **Orphan tags:** If a tag exists but no published release, the workflow re-dispatches the build for that tag instead of failing on collision.
### Release pipeline jobs
```mermaid
stateDiagram-v2
[*] --> cut: workflow_dispatch or schedule
cut --> create_release: should_release
create_release --> e2e: tag scoped
create_release --> build: mac win linux matrix
e2e --> build: parallel visibility
build --> publish_release: assets verified draft
publish_release --> homebrew_bump: stable tag only
homebrew_bump --> [*]
publish_release --> [*]: RC skips homebrew
```
| Job | Role |
| --- | --- |
| `cut` | Resolve ref, compute version, bump `package.json`, commit, tag, push |
| `create-release` | Draft GitHub release with generated notes; `--prerelease` for `-rc.` tags |
| `e2e` | Reuses `.github/workflows/e2e.yml` on the tag (informational; publish does not wait on it) |
| `build` | Matrix: macOS, Windows, Linux—`pnpm build:release`, electron-builder `--publish always`, draft stays draft until final publish |
| `publish-release` | `verify-release-required-assets.mjs`, then `gh release edit --draft=false` with prerelease derived from tag |
| `homebrew-bump` | Stable tags only; calls `homebrew-bump.yml` |
Release builds set `ORCA_BUILD_IDENTITY` (`stable` vs `rc` from tag regex) and `ORCA_POSTHOG_WRITE_KEY` for official artifacts; macOS signing secrets are scoped to the mac job only.
### Common maintainer scenarios
| Scenario | Inputs |
| --- | --- |
| Normal stable release | `kind=patch`, `ref=main` |
| Ship good commit before bad tip on `main` | `kind=patch`, `ref=<good-sha>` (tag only; fix `main` separately) |
| Feature-branch RC | `kind=rc`, `ref=<branch-or-sha>` |
| Minor / major bump | `kind=minor` or `major`, `ref=main` |
| Validate scheduled RC slot | `dry_run=true`, `kind=rc` |
<Warning>
Contributors should not edit `package.json` `version` for release purposes. Maintainers use **Cut Release** only.
</Warning>
## Related pages
<CardGroup>
<Card title="Develop and build" href="/develop-and-build">
Local `pnpm dev`, typecheck targets, native rebuilds, and release build commands.
</Card>
<Card title="Testing" href="/testing">
Vitest, Playwright Electron E2E, and SSH-localhost test flags.
</Card>
<Card title="SSH remote worktrees" href="/ssh-remote-worktrees">
Remote execution constraints contributors must design for.
</Card>
<Card title="Source control integrations" href="/source-control-integrations">
Multi-provider review flows (GitHub, GitLab, and others).
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
CI/native dependency failures and runtime reachability issues.
</Card>
</CardGroup>
---
## 21. Troubleshooting
> Common failure modes: runtime not reachable, selector ambiguity on remote CLI, worktree delete preflight, terminal/orchestration timeouts, and platform-specific native dependency issues.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/21-troubleshooting.md
- Generated: 2026-06-01T20:12:47.184Z
### Source Files
- `src/cli/format.ts`
- `src/cli/runtime-client.ts`
- `docs/worktree-delete-preflight.md`
- `src/main/runtime/worktree-teardown.ts`
- `config/scripts/ensure-native-runtime.mjs`
- `src/cli/help.ts`
---
title: "Troubleshooting"
description: "Common failure modes: runtime not reachable, selector ambiguity on remote CLI, worktree delete preflight, terminal/orchestration timeouts, and platform-specific native dependency issues."
---
The Orca CLI talks to a running runtime over a local Unix socket or named pipe, or over an encrypted WebSocket when you pass `--pairing-code` / `--environment`. Most commands fail fast with stable error codes (`runtime_unavailable`, `selector_ambiguous`, `timeout`) that `formatCliError` and RPC `mapRuntimeError` turn into human text or JSON envelopes.
## Runtime not reachable
### Local desktop runtime
| Symptom | Typical cause | What to try |
| --- | --- | --- |
| `runtime_unavailable` + “Orca is not running. Run `orca open` first.” | No live runtime socket, or metadata missing | Run `orca open`, then `orca status` |
| `runtimeReachable: false` with `runtimeState: not_running` | App never started in this user-data dir | Start Orca; confirm `appRunning` becomes true |
| `runtimeState: stale_bootstrap` | Metadata file exists but socket does not answer | Quit stale Orca processes; restart the app |
| `runtimeState: starting` / `graph_not_ready` | App up, graph not hydrated yet | Wait and retry; check `graphState` in `orca status --json` |
| “Could not connect to the running Orca app” | Socket error while connecting | Restart Orca; on Windows verify named-pipe transport in metadata |
| “No compatible transport found” | Metadata lacks `unix` or `named-pipe` endpoint | Restart Orca so bootstrap rewrites `orca-runtime.json` |
| `runtime_timeout` | No RPC frame within client timeout (default 60s) | Retry; for long polls see terminal/orchestration sections |
| `runtime_open_timeout` | `orca open` waited 15s without reachability | Launch Orca manually; set `ORCA_APP_EXECUTABLE` or `ORCA_OPEN_COMMAND` for nonstandard installs |
<Steps>
<Step title="Verify local readiness">
```bash
orca status
orca status --json
```
Expect `runtimeReachable: true` and `runtimeState: ready` (or `graphState: ready` in JSON) before repo, worktree, or terminal commands.
</Step>
<Step title="Target the correct instance">
Dev and parallel installs use a separate user-data directory. Point the CLI at the same metadata Electron writes:
```bash
export ORCA_USER_DATA_PATH="$HOME/Library/Application Support/orca-dev" # example
orca status
```
Packaged defaults: macOS `~/Library/Application Support/orca`, Windows `%APPDATA%\orca`, Linux `$XDG_CONFIG_HOME/orca` (or `~/.config/orca`).
</Step>
</Steps>
### Headless `orca serve`
`orca serve` exposes the same RPC surface without the renderer. If the CLI still cannot connect, confirm serve wrote bootstrap metadata under the same `ORCA_USER_DATA_PATH` you use for other commands.
### Remote runtime (`--pairing-code`, `--environment`)
| Code | Meaning |
| --- | --- |
| `remote_runtime_unavailable` | WebSocket failed or closed mid-request |
| `runtime_timeout` | Remote RPC exceeded the client timeout |
| `incompatible_runtime` | Protocol version mismatch between CLI and server |
| `invalid_argument` | Bad pairing payload, endpoint, or both `--pairing-code` and `--environment` |
Remote pairing uses `sendRemoteRuntimeRequest` with the same keepalive-aware timeout extension as local long polls. After connect, the CLI checks `status.get` and blocks further RPCs when `evaluateRuntimeCompat` reports `client-too-old` or `server-too-old`.
<Warning>
Do not pass `--pairing-code` and `--environment` together. Use one targeting mechanism per invocation.
</Warning>
Environment names resolve from disk; duplicate names throw before any network call:
```
Environment name "<name>" is ambiguous; use the environment id.
```
## Selector ambiguity on remote CLI
Worktree and repo selectors are resolved server-side in `resolveWorktreeSelector` / `resolveRepoSelector`. When more than one record matches, the runtime throws `selector_ambiguous` (passthrough RPC code).
### Worktree selector grammar
| Form | Matches |
| --- | --- |
| `id:<uuid>` | Exact worktree id |
| `path:<absolute-path>` | Filesystem path (normalized compare) |
| `branch:<name>` | Branch ref (`refs/heads/…` normalized) |
| `issue:<number>` | Linked issue metadata |
| bare string | id, path, or branch (same rules as unprefixed) |
| `active` / `current` | **Local CLI only** — see below |
### Repo selector grammar
| Form | Matches |
| --- | --- |
| `id:<id>` | Repo id |
| `path:<path>` | Registered repo root |
| `name:<displayName>` | Display name |
| bare string | id, path, or display name |
### Remote CLI constraints
Paired CLIs cannot use cwd-derived shortcuts. `assertLocalCwdWorktreeSelector` rejects `active`, `current`, and the implicit cwd default used for browser/computer commands:
```
active is a local cwd shortcut and cannot be resolved against a remote runtime.
Pass an explicit server-side worktree selector such as id:<id>, branch:<branch>,
issue:<number>, or path:<absolute-server-path>.
```
<Steps>
<Step title="List candidates on the server">
```bash
orca worktree list --json --environment <id>
orca repo list --json --environment <id>
```
</Step>
<Step title="Disambiguate">
Prefer `id:` selectors in scripts. For branches shared across worktrees, narrow with `path:` on the server filesystem or tie-break with `issue:`.
Locally, `orca worktree current` resolves cwd to `id:<id>` so duplicate paths across repo registrations do not surface as `selector_ambiguous`.
</Step>
</Steps>
### JSON shape
With `--json`, failures return the RPC envelope:
```json
{
"ok": false,
"error": { "code": "selector_ambiguous", "message": "selector_ambiguous" }
}
```
Some lineage errors include `data.nextSteps`; the CLI prints those as “Next step: …” lines after the message.
## Worktree delete preflight
Non-force local deletes run `assertWorktreeCleanForRemoval` **before** PTY teardown. The helper runs `git status --porcelain --untracked-files=all` in the worktree directory; any output throws “Worktree has uncommitted or untracked changes.” Force deletes skip preflight and keep the historical order: kill PTYs, then `git worktree remove`.
```text
non-force local delete: preflight → killAllProcessesForWorktree → removeWorktree
force delete: kill → removeWorktree (no preflight)
SSH-backed repo: provider API only (no local PTY sweep)
```
### Failure classes
| Situation | PTYs killed? | User-visible error |
| --- | --- | --- |
| Dirty / untracked (non-force) | No | `formatWorktreeRemovalError` + porcelain stdout |
| Preflight git subprocess error | No | Same formatter with stderr/stdout |
| Orphan / missing worktree (`not a working tree`, `not a git repository`, `ENOENT`) | No | Falls through to existing orphan cleanup path |
| Successful preflight | Yes | Normal removal |
<Tip>
Commit, stash, or discard changes before `orca worktree rm`. Use `--force` only when you accept destroying dirty state and killing terminals first.
</Tip>
PTY teardown is best-effort across renderer graph, provider session list (`${worktreeId}@@` prefix), and `pty-registry`; git removal still proceeds even if some kills fail.
## Terminal and orchestration timeouts
### Client timeout policy
| Layer | Default | Long-poll behavior |
| --- | --- | --- |
| `RuntimeClient` socket | 60_000 ms | — |
| `terminal.wait` | Inner budget from `--timeout-ms`; RPC cap `timeoutMs + 5000` or **5 min** if unset | Client uses `inner + 10s` grace (`LONG_POLL_CLIENT_GRACE_MS`) |
| `orchestration.check --wait` | Server waiter uses `--timeout-ms` | Same client grace when `wait: true` |
| `orchestration.ask` | Default **600_000** ms server-side | Poll loop respects remaining budget |
| Transport keepalives | — | `_keepalive` frames refresh the client timer during long polls |
Local Unix transport and remote WebSocket both ignore keepalive lines and only settle on a terminal success/failure frame matching the request id.
### `orca terminal wait`
Supported conditions: `exit`, `tui-idle`.
| Outcome | `satisfied` | Exit code | Notes |
| --- | --- | --- | --- |
| Condition met | `true` | 0 | — |
| Blocked interactive prompt | `false` | 1 | `blockedReason` set (Codex trust/update/CWD/hooks, etc.) |
| Server `timeout` error | RPC failure | non-zero | Thrown when waiter budget expires |
| Client `runtime_timeout` | — | non-zero | Socket gave up before server responded |
`tui-idle` uses a **5 minute** default server timeout when `--timeout-ms` is omitted, so unrecognized agents do not hang forever. `exit` waits without a default cap unless you pass `--timeout-ms`.
Example:
```bash
orca terminal wait --terminal term_abc --for tui-idle --timeout-ms 120000 --json
```
Inspect `blockedReason` in JSON when `satisfied: false` — automation should send input or focus the pane instead of retrying the same wait.
### `orca orchestration check --wait`
Blocking check replaces sleep/poll loops. Pass `--wait` and optionally `--timeout-ms`. The server long-poll releases promptly when the client disconnects (`signal` abort), rather than holding the slot for the full timeout.
Ensure the experimental orchestration feature and runtime are running; otherwise RPCs return `runtime_unavailable`.
### Terminal read limits
`terminal read` may return `warning: output limited` with `oldestCursor` / `nextCursor`. Page retained output with `--cursor` and `--limit` instead of treating truncation as a hang.
## Platform-specific native dependency issues
Orca depends on native addons **`better-sqlite3`** and **`node-pty`**. Electron builds also rebuild **`cpu-features`** via `config/scripts/rebuild-native-deps.mjs`.
### Detection and auto-rebuild
`config/scripts/ensure-native-runtime.mjs` verifies loads in a **child process** (a failed `.node` load must not poison the parent):
| Flag | Runtime checked |
| --- | --- |
| `--runtime=node` | Node used by Vitest / CLI typecheck pipelines |
| `--runtime=electron` | Electron binary with `ELECTRON_RUN_AS_NODE=1` |
On failure it runs `pnpm rebuild <modules>` (Node) or `rebuild-native-deps.mjs` (Electron), then re-checks.
`pnpm dev`, `pnpm test`, and `ensure:electron-runtime` invoke this script automatically.
### Common load failures
| Message pattern | Likely cause | Fix |
| --- | --- | --- |
| `MODULE_VERSION` / ABI mismatch | Node/Electron upgraded without rebuild | `pnpm rebuild:node` or `pnpm rebuild:electron` |
| `better-sqlite3` fails on `new Database(':memory:')` | Binding not built for current ABI | Same rebuild; confirm install completed |
| `node-pty` / `pty.node` / `conpty.node` | Windows build < 18309 uses `pty`; newer uses `conpty` | Re-run ensure script on target OS |
| Script exits 1 after rebuild | Toolchain or headers missing | Install build tools; delete `node_modules` and `pnpm install` |
Manual checks:
```bash
node config/scripts/ensure-native-runtime.mjs --runtime=node
node config/scripts/ensure-native-runtime.mjs --runtime=electron
```
### macOS computer-use (optional)
Computer automation adds separate native artifacts (`build:computer-macos`, `verify:computer-native`). Failures there do not block core terminal/runtime features but break `orca computer *` until natives are built.
## Quick diagnostic map
```mermaid
flowchart TD
CLI[orca CLI command]
CLI --> Local{Remote flags?}
Local -->|no| Meta[Read orca-runtime.json]
Meta --> Sock[Unix / named-pipe RPC]
Local -->|yes| WS[E2EE WebSocket]
Sock --> Ready{runtime reachable?}
WS --> Ready
Ready -->|no| Open[orca open / fix pairing]
Ready -->|yes| Sel[Resolve selectors]
Sel -->|ambiguous| List[worktree/repo list --json]
Sel -->|ok| Op[terminal / worktree / orchestration RPC]
Op -->|timeout| Tune[Increase timeout-ms + grace]
Op -->|dirty delete| Clean[git status / --force]
```
## Related pages
<CardGroup>
<Card title="Runtime environments" href="/runtime-environments">
Pairing codes, saved environments, `orca serve`, and remote targeting flags.
</Card>
<Card title="Selectors and JSON output" href="/selectors-json-output">
Selector grammar, `--json` errors, and remote resolution rules.
</Card>
<Card title="CLI core reference" href="/cli-core-reference">
`open`, `status`, `worktree rm`, `terminal wait`, and environment commands.
</Card>
<Card title="Agent orchestration" href="/agent-orchestration">
Blocking `orchestration check`, timeouts, and coordinator prerequisites.
</Card>
<Card title="Develop and build" href="/develop-and-build">
Native rebuild scripts, Electron dev startup, and platform artifacts.
</Card>
</CardGroup>
---
## 22. Telemetry and privacy
> Consent gating, event schemas in `telemetry-events.ts`, dashboard-ready boundaries in repo docs, and what usage data Orca collects.
- Page Markdown: https://grok-wiki.com/public/docs/stablyai-orca-2036d532bf1c/pages/22-telemetry-and-privacy.md
- Generated: 2026-06-01T20:12:52.780Z
### Source Files
- `src/shared/telemetry-events.ts`
- `src/main/telemetry/client.ts`
- `src/main/telemetry/consent.ts`
- `docs/reference/telemetry-availability.md`
- `README.md`
---
title: "Telemetry and privacy"
description: "Consent gating, event schemas in `telemetry-events.ts`, dashboard-ready boundaries in repo docs, and what usage data Orca collects."
---
Orca’s anonymous product telemetry runs only in the Electron **main process** on official `stable`/`rc` CI builds: the renderer calls `telemetry:track` over IPC, main validates payloads against Zod schemas in `src/shared/telemetry-events.ts`, applies consent and burst limits, and sends events to PostHog (`https://us.i.posthog.com`) with `$process_person_profile: false`. The bundled `orca` CLI does not implement this lane.
## Architecture
```text
Renderer (track / setOptIn / acknowledgeBanner)
│ IPC
▼
src/main/ipc/telemetry.ts ── inject nth_repo_added / onboarding cohort
│
▼
src/main/telemetry/client.ts
1. shutdown gate
2. burst cap (before consent)
3. resolveConsent()
4. validator (eventSchemas + commonProps)
5. posthog-node capture
```
| Layer | Location | Role |
| --- | --- | --- |
| Event contracts | `src/shared/telemetry-events.ts` | Zod schemas, `EventMap`, enums, `COHORT_EXTENDED` / onboarding cohort sets |
| Consent | `src/main/telemetry/consent.ts` | `resolveConsent()` precedence; types in `src/shared/telemetry-consent-types.ts` |
| Transport | `src/main/telemetry/client.ts` | PostHog client, `track()`, `setOptIn()`, `trackAppOpenedOnce()` |
| IPC boundary | `src/main/ipc/telemetry.ts` | Renderer-facing handlers; main derives `via` for opt-in/out |
| Renderer wrapper | `src/renderer/src/lib/telemetry.ts` | Typed `track()`; no PostHog SDK in renderer |
| Dashboard boundaries | `docs/reference/telemetry-availability.md` | Append-only rollout timestamps for PostHog tiles (not sent on the wire) |
<Note>
Product telemetry is separate from the **error-tracking / observability** lane (`initObservability()` in `src/main/index.ts`). Crash and span data follow their own gates (`ORCA_DIAGNOSTICS_DISABLED`, etc.) and are not described by `telemetry-events.ts`.
</Note>
## When events transmit
All of the following must be true for a normal `track()` call to reach PostHog:
| Gate | Behavior |
| --- | --- |
| `TELEMETRY_ENABLED` | Compile-time flag in `client.ts` (currently `true`; release scripts grep this symbol) |
| Official build | `ORCA_BUILD_IDENTITY` is `stable` or `rc` **and** `ORCA_POSTHOG_WRITE_KEY` is non-empty (injected at CI build time via electron-vite `define`) |
| Consent | `resolveConsent()` returns `{ effective: 'enabled' }` |
| Transport initialized | Valid `installId`, common props pass `commonPropsSchema`, PostHog client created |
| Not shutting down | `shutdownTelemetry()` sets a drop gate before flush |
| Burst caps | Per-event token bucket, 1 000 events/session ceiling, unknown event names rejected |
Contributor builds (`pnpm dev`), local rebuilds, and tests get `IS_OFFICIAL_BUILD === false`: `track()` returns immediately with no network, burst, or consent work.
## Consent and gating
`resolveConsent(settings)` is the single authority. Environment overrides are **non-persistent** — they affect the current process only and never write `GlobalSettings.telemetry.optedIn`.
| Precedence | Condition | `effective` | `reason` (if disabled) |
| --- | --- | --- | --- |
| 1 | `DO_NOT_TRACK` is `1` or `true` (trimmed, case-insensitive) | `disabled` | `do_not_track` |
| 2 | `ORCA_TELEMETRY_DISABLED` is `1` or `true` | `disabled` | `orca_disabled` |
| 3 | Any listed CI env var is set and non-empty (`CI`, `GITHUB_ACTIONS`, `GITLAB_CI`, `CIRCLECI`, `TRAVIS`, `BUILDKITE`, `JENKINS_URL`, `TEAMCITY_VERSION`) | `disabled` | `ci` |
| 4 | `settings.telemetry.optedIn === true` | `enabled` | — |
| 5 | `settings.telemetry.optedIn === false` | `disabled` | `user_opt_out` |
| 6 | `settings.telemetry.optedIn === null` | `pending_banner` | — |
| — | Missing `telemetry` block (pre-migration) | `pending_banner` | — |
Misconfigured env values (for example `DO_NOT_TRACK=yes`) log once to stderr and are treated as unset.
### Persisted telemetry settings
Stored under `GlobalSettings.telemetry` (deep-merged on updates):
| Field | Meaning |
| --- | --- |
| `installId` | Anonymous UUID v4; PostHog `distinctId`; generated on first migration if missing |
| `optedIn` | `true` (new installs default on), `false` (user opt-out), or `null` (existing users awaiting banner) |
| `existedBeforeTelemetryRelease` | `true` if profile existed before telemetry shipped → first-launch banner; `false` for fresh installs (no banner) |
The block intentionally holds **only** consent and identity — no DAU counters, session heartbeats, or crash IDs (those live elsewhere).
### First-launch notice (existing users only)
Users with `existedBeforeTelemetryRelease === true` and `optedIn === null` see `FirstLaunchBanner` until they resolve it. **No events transmit** while consent is `pending_banner`, including `app_opened`.
| UI action | IPC | Persisted `optedIn` | Telemetry event |
| --- | --- | --- | --- |
| Got it / ✕ | `telemetry:acknowledgeBanner` | `true` | None (silent acknowledge) |
| Opt out | `telemetry:setOptIn(false)` | `false` | `telemetry_opted_out { via: 'first_launch_banner' }` (captured before SDK `optOut()`) |
| Privacy policy link | — | unchanged | none |
New installs (`existedBeforeTelemetryRelease === false`) have no first-launch UI; disclosure is at install time and `optedIn` defaults to `true`.
`app_opened` fires **once per session** after consent is enabled — on main window `did-finish-load` when `resolveConsent` is already `enabled`, or immediately after banner acknowledge / explicit opt-in from `setOptIn`.
### Settings → Privacy & Telemetry
`PrivacyPane` toggles `telemetrySetOptIn` unless env-blocked (`DO_NOT_TRACK`, `ORCA_TELEMETRY_DISABLED`, or CI). Effective state for helper text comes from `telemetry:getConsentState` (renderer cannot read env vars directly). Product copy links to `https://www.onorca.dev/docs/telemetry` (`PRIVACY_URL`).
`via` on opt-in/out events is **never** passed from the renderer. Main derives it:
- `first_launch_banner` — existing user, `optedIn === null`, incoming `false` (Turn off on notice)
- `settings` — all other flips (Privacy pane, post-banner changes)
Env-var disables do not emit `telemetry_opted_in` / `telemetry_opted_out`.
## Identity and common properties
Every transmitted event merges **common props** (validated once at `initTelemetry`):
| Property | Source |
| --- | --- |
| `app_version` | `app.getVersion()` |
| `platform`, `arch`, `os_release` | Node `os` module |
| `install_id` | Settings; min length 1, max 64 |
| `session_id` | New UUID per process |
| `orca_channel` | `stable` or `rc` from `ORCA_BUILD_IDENTITY` |
PostHog capture sets `distinctId: install_id` and `$process_person_profile: false` so anonymous events do not materialize person profiles. GeoIP is disabled on the SDK (`disableGeoip: true`).
## Event schema model
`src/shared/telemetry-events.ts` is the only contract source:
- **`eventSchemas`** — runtime validator input; adding a key updates compile-time `EventMap` via `z.infer`
- **`.strict()`** on every object — unknown keys fail validation and drop the event
- **Closed enums** — no raw `string` domains for product dimensions
- **String caps** — `.max(N)` on free-form fields (for example 64 on common props, 200 on `agent_hook_install_failed.error_message`)
- **Schema evolution** — breaking changes need a new event name (e.g. `agent_started_v2`); additive fields use `.optional()`
Main injects cohort fields at the IPC layer only where schemas declare them:
- **`nth_repo_added`** — repo count at emit time (`COHORT_EXTENDED` events); `0` means no repos yet, not “missing”
- **`cohort`** — `fresh_install` \| `upgrade_backfill` on onboarding events (`ONBOARDING_COHORT_SET`)
### Event families (registry)
| Family | Example event names | Typical dimensions |
| --- | --- | --- |
| Session / activation | `app_opened`, `repo_added`, `workspace_created`, `workspace_create_failed`, `agent_started`, `agent_error` | `method`, `source`, `agent_kind`, `launch_source`, `error_class`, `nth_repo_added` |
| Add-repo funnel | `add_repo_setup_step_action`, `add_repo_existing_workspaces_detected`, `setup_script_prompt_shown`, `setup_script_prompt_action` | setup actions, count buckets, provider enums |
| Onboarding wizard | `onboarding_started`, `onboarding_step_*`, `onboarding_completed`, `onboarding_dismissed`, `onboarding_agent_picked`, Ghostty import events, feature-setup events | `step`, `value_kind`, `cohort`, closed failure reasons |
| Feature wall | `feature_wall_opened`, `feature_wall_closed`, tile/group/feature events | `source`, `dwell_ms`, tour depth buckets |
| Settings | `settings_changed` | Whitelisted boolean keys only (`editorAutoSave`, `openLinksInApp`, experimental flags, etc.) — not the telemetry toggle |
| Consent audit | `telemetry_opted_in`, `telemetry_opted_out` | `via`: `first_launch_banner` \| `settings` |
| Agents / hooks | `agent_hook_install_failed`, `agent_hook_unattributed` | `agent` enum; truncated `error_message` or `reason` |
| Smart sort | `smart_sort_class_distribution`, `smart_sort_class_1_promotion`, `smart_to_recent_switch` | class counts, promotion `cause` |
`agent_error` and `workspace_create_failed` use **enum-only** error taxonomies — `error_message` / `error_stack` are deliberately absent and rejected by `.strict()`.
`AGENT_KIND_VALUES` maps product agent IDs (e.g. `claude-code` on the wire vs `claude` in launch settings) via `tuiAgentToAgentKind` in `src/shared/agent-kind.ts`.
## What is not collected (product telemetry)
By schema design and transport policy, product telemetry does **not** include:
- Repository paths, branch names, clone URLs, or file contents
- Prompts, terminal output, or diff text
- Raw error stacks on `agent_error` / `workspace_create_failed` (classified enums only)
- Usernames, emails, or API keys
- Per-event `env: dev` discriminator (non-official builds do not transmit at all)
The first-launch banner and Privacy pane state this explicitly: anonymous **counts and funnels** of feature usage and failure classes, not content.
<Warning>
`agent_hook_install_failed` allows `error_message` up to 200 characters (truncated at call sites). Treat this as config-shape errors, not user content — still bounded and enum-scoped elsewhere.
</Warning>
## IPC surface (renderer)
| Channel | Input | Notes |
| --- | --- | --- |
| `telemetry:track` | `name: string`, `props?: object` | Injects cohorts; validates in `track()` |
| `telemetry:setOptIn` | `optedIn: boolean` | ≤5 mutations/session; derives `via` |
| `telemetry:acknowledgeBanner` | — | Only when `existedBeforeTelemetryRelease && optedIn === null` |
| `telemetry:getConsentState` | — | Read-only; no rate limit |
Threat model: renderer content (agents, MCP, markdown) is untrusted. Handlers narrow types, cap consent mutations, and run burst limits **before** consent reads on `track`.
## Burst and validation
| Bucket | Limit |
| --- | --- |
| Per event name | 30/min (20/min for `agent_error`), token refill |
| Per session | 1 000 events total |
| Consent mutations | 5/session (`setOptIn` + `acknowledgeBanner`) |
`src/main/telemetry/validator.ts` wraps `eventSchemas[name].safeParse(props)`; failures are fail-closed with rate-limited `console.warn` (≤1 per event name per 60 s).
## Opt out (operator and user)
| Mechanism | Scope |
| --- | --- |
| Settings → Privacy → toggle off | Persists `optedIn: false`; `user_opt_out` |
| First-launch “Opt out” | Same + `telemetry_opted_out` with `via: first_launch_banner` |
| `DO_NOT_TRACK=1` or `true` | Process-wide disable |
| `ORCA_TELEMETRY_DISABLED=1` or `true` | Process-wide disable |
| CI env vars (see consent table) | Process-wide disable while CI is detected |
Unset env kill switches and restart to restore the stored preference.
## Dashboard-ready boundaries
`docs/reference/telemetry-availability.md` is the append-only reference for analytics authors. It records merge commits, first release, PostHog `first_seen_at_utc`, and `dashboard_ready_at_utc` per rollout — **not** shipped in event payloads.
Cross-cutting rules from that doc:
- Use `app_opened` as the return marker (once per session after gating).
- Do not treat missing `nth_repo_added` as `0`; missing means pre-rollout or classifier fail-soft.
- Fresh-install onboarding denominators anchor on `onboarding_started { cohort: 'fresh_install' }`.
- `workspace_created` means create-worktree IPC succeeded, not “user revealed workspace.”
- `agent_started` means PTY spawn with telemetry attached, not “user sent a prompt.”
When `dashboard_ready_at_utc` is `TBD`, avoid saving production cohort/retention tiles; QA charts must note the boundary is provisional.
## Privacy diagnostics (separate lane)
The Privacy settings pane also exposes **diagnostic bundles** (`PrivacyDiagnosticsSection`) for support tickets. That flow uses `window.api.diagnostics.*` and is user-initiated — it is not part of anonymous product telemetry or `telemetry-events.ts`.
## Related pages
<CardGroup>
<Card title="Settings reference" href="/settings-reference">
GlobalSettings fields including `telemetry` and experimental toggles whitelisted on `settings_changed`.
</Card>
<Card title="Terminals and agents" href="/terminals-and-agents">
PTY spawn, agent kinds, and hook telemetry that map to `agent_started` / `agent_error`.
</Card>
<Card title="Develop and build" href="/develop-and-build">
Why local dev builds do not transmit and how CI injects PostHog keys.
</Card>
<Card title="Contributing" href="/contributing">
PR workflow and release verification touching telemetry constants.
</Card>
</CardGroup>
---