All documentation

How OpenOrc works

The processes OpenOrc runs, how it talks to each agent, and the path a message takes from the composer and back.

The parts

OpenOrc is an Electron app with no language model of its own. It starts the agent CLIs you already installed and signed in to (Claude Code, Codex, and OpenCode) and talks to them through their machine-readable interfaces. Each CLI calls its model provider directly with your login; OpenOrc doesn't proxy that traffic.

The parts of OpenOrcOn your computer, the interface talks to the core, and the core talks to the agent command-line tools. The core writes to the SQLite database and runs Git in your repository. Agents edit files in the repository directly and call their model providers over the internet.The parts of OpenOrcOn your computer, the interface talks to the core, and the core talks to the agent command-line tools. The core writes to the SQLite database and runs Git in your repository. Agents edit files in the repository directly and call their model providers over the internet.
Everything inside the dashed line runs on your computer. Agent CLIs reach their model providers with your own logins; OpenOrc has no server of its own.
Interface
A React app in each window. Everything it shows from OpenOrc's records comes from the core.
Core
A Node.js process that owns the database, starts the agents, runs Git, and offers OpenOrc's tools to agents through a local Model Context Protocol (MCP) server.
Agent CLIs
One child process of the core per active run. Agents read and edit files and run commands in the thread's folder themselves.
Electron main
Creates the windows and starts the core. It also runs terminals and the Preview browser, and encrypts secrets.

Every process

ProcessStarted byWhat it does
Electron mainYour systemWindows and menus, terminal shells, Preview views, encrypted secrets, project icons, and app update checks on macOS and Windows
RendererMain, one per windowThe interface, sandboxed, with no Node.js access
Preview pagesMainOne renderer per Preview, in a separate browser session. Up to six are kept alive.
Visualization and MCP App framesThe windowOne sandboxed renderer per frame, so a stuck page can't hold up the others
CoreMainA Node.js process named openorc-core. It owns the SQLite database, starts agents, runs Git, serves OpenOrc's tools, and runs schedules, Slack, and upkeep.
Embedding workerCore, on first useA thread that computes memory search vectors, so loading the model never blocks the core
Editor workerCore, when you open a TypeScript or JavaScript fileA thread that runs TypeScript's language service for the editor. It stops after a while without use.
Formatter workerCore, on first useA thread that runs the copy of Prettier that ships with OpenOrc. A project's own formatter runs as its own process.
Agent CLIsCoreclaude, codex app-server, or opencode acp, one long-lived process per active run
Short-lived CLIsCoreSign-in checks, model lists, usage, skills, update checks, and background jobs such as titles, code explanations, and memory summaries
git and ghCoreWorktrees, diffs, checkpoints, commits, pushes, and pull requests
Login shellCoreRuns at startup, on Rescan, when Settings opens, and before agent update checks, to find the agents. Not on Windows, where OpenOrc searches PATH itself.
Setup scriptsCoreA project's setup script, run with /bin/bash -lc in each new worktree
Terminal shellsMainYour shell as a login shell, one per Terminal tab
Device helpersCore, while device support is onexpo-device-hub and agent-device, on your own Node.js, to start simulators and emulators and stream their screens

The core also listens on 127.0.0.1 for agents' tool calls, and, while you allow paired phones, on your network for the phone app. Network connections lists every server.

Source: apps/desktop/src/main/index.ts, apps/desktop/src/core/index.ts

How the interface talks to the core

When a window loads, it asks main for a connection to the core. Main creates a message channel, hands one end to the core and the other to the window, and takes no further part. The window asks main directly only for what needs Electron, such as terminals, the Preview, the folder picker, opening links, window controls, and app updates.

Every request is a small JSON object with a number the window chose:

{ "type": "rpc", "id": 42, "method": "runs.start", "params": { … } }
  • About 270 methods are grouped by name, such as threads.*, runs.*, tasks.*, review.*, pulls.*, and memory.*. Each has a parameter schema, and packages/protocol/src/rpc.ts collects them all.
  • The core checks the envelope, then the method's parameters, and replies with a result or an error. A malformed message is dropped without a reply. Requests have no timeout.
  • The core sends each reply only to the window that asked.

Besides replies, the core pushes these to every window:

MessageWhat it carries
frameNew agent events for one run, at most one frame per run every 16 ms
invalidateNames of data that changed, such as threads. The window waits 50 ms to collect more, then fetches only what is on screen again.
notifyA desktop notification, sent only while notifications are on. The window shows it only if you aren't looking at that thread.
startup, readyStartup progress, then the core's process ID and MCP port

Frames feed a live transcript store, separate from fetched data, and the screen updates at most once per animation frame. Opening a long thread loads its last 4 turns, then 4 more at a time as you scroll up. Before the core reads stored events, it flushes what is still in flight, so a loaded transcript never misses events.

Source: packages/protocol/src/rpc.ts, packages/core/src/openorc.ts (handle), packages/core/src/frames.ts, apps/desktop/src/renderer/src/lib/rpc.ts

How OpenOrc runs each agent

Finding the agents

Apps started from the Dock or a menu don't get your terminal's PATH, so on macOS and Linux the core asks your login shell. It runs your shell with -ilc and a short script that prints PATH and where codex, claude, and opencode are. The shell gets 8 seconds and a minimal environment, and only those answers are kept. On Windows there is no probe: OpenOrc looks for each agent on the PATH it started with, trying the extensions PATHEXT lists, so npm's .cmd launchers count. OPENORC_CODEX_BIN, OPENORC_CLAUDE_BIN, or OPENORC_OPENCODE_BIN, set before OpenOrc starts, take precedence.

Each agent is then checked with its own commands: --version, plus codex login status, claude auth status, or opencode auth list to see whether you are signed in. Results are cached for 60 seconds. These checks never read the credentials the tools store. Only Settings → Cloud looks for API keys to copy, as Cloud threads describes.

Speaking to them

AgentCommandProtocolOpenOrc's brief goes in
Claude Codeclaude -pOne JSON message per line over standard input and output--append-system-prompt
Codexcodex app-serverJSON-RPC over standard input and outputThe session's developer instructions
OpenCodeopencode acpAgent Client Protocol over standard input and outputA text block before your first message

Each process gets its own address on OpenOrc's MCP server, which carries OpenOrc's tools: tasks, memory, messages between threads, the browser, and approvals. Claude Code reads the address from a private file; Codex and OpenCode receive it over standard input. When an agent wants permission, the request comes back to OpenOrc, which shows it in the thread or decides it by the mode. What the brief says is on Instructions.

One process per run

  • A thread keeps its agent process between turns, so the next message starts at once. An idle process closes after 10 minutes by default (Close idle processes after, under General).
  • Task runs and replies in task discussions close their process after every turn.
  • Changing the agent, the mode, or the permission level starts a new process with your next message. It resumes the session where that is possible.
  • Stopping a process sends SIGTERM to its process group, then SIGKILL after 2 seconds. On Windows, taskkill ends the process and every process it started. An idle Claude process first has its input closed, so it can exit on its own.

Live controls

ControlClaude CodeCodexOpenCode
Message during a turnRead at the next tool boundarySteers the running turnWaits in the queue
Stop the turnInterrupt requestturn/interruptsession/cancel
Change model or effortIn placeWith the next turnIn place
Fast modeWhere your account has itPriority service tierNot available
Usage limitsAsked of Claude CodeRead from your accountNot available

Stopping a turn keeps the process and the session, so the next message continues with the same context. See Agents and models for choosing models and effort.

Source: packages/core/src/services/shell-environment.ts, system.ts, packages/agents/src/claude/adapter.ts, codex/adapter.ts, acp/adapter.ts, process-lifetime.ts

The path a message takes

One message, from the composer to the transcriptThe composer sends a request to the core. The core starts or reuses the agent CLI, which calls its provider. The CLI's output goes through an adapter that translates it into shared events. The events feed the live transcript and the SQLite ledger.One message, from the composer to the transcriptThe composer sends a request to the core. The core starts or reuses the agent CLI, which calls its provider. The CLI's output goes through an adapter that translates it into shared events. The events feed the live transcript and the SQLite ledger.
The live transcript and the ledger receive the same events, except native provider output, which goes to a log file. The ledger clears streaming fragments once the finished item is stored. Frames go to the interface at most every 16 ms per run; the ledger writes in batches every 50 ms.
  1. You press Send. A new thread sends threads.start. While the agent is working, the composer sends runs.send if the turn can take the message, or threads.queue to hold it. Between turns it sends runs.send when the thread's process is still running and nothing that needs a restart has changed, and runs.start otherwise.
  2. The core checks the request and works out the thread's folder, agent, model, and mode. It decides whether to resume the agent's last session or start fresh with a handoff, and takes a shared hold on the folder, so Git operations that need it alone wait or fail while the agent works.
  3. If the thread uses a worktree that doesn't exist yet, the core creates it and runs the project's setup script.
  4. For runs.start, the core records the run and your message. In a Git project, before a thread's first run, it saves the starting files so the first change can be undone. Then it builds the brief and starts the agent CLI. For runs.send, it passes the message to the process that is already running.
  5. The CLI calls its provider and reports what it does. An adapter translates that into OpenOrc's events, and each event goes to the database in batches (every 50 ms or 500 events), to the window in frames, and to Slack or a task discussion when they are watching. The Changes panel refreshes at most once a second, and every 8 seconds while the turn runs.
  6. When the turn ends, the core saves the token usage and the final reply. In a Git project it records a checkpoint of the thread's files, so each turn's changes can be shown and undone on their own. It sends a notification and the next queued message, if there is one.
  7. When the process exits, the run gets its final state, the hold on the folder is released, and, if memory is on, the run is queued for summarizing.

Source: packages/core/src/services/threads.ts (continueThread), run-launch.ts, runs.ts, run-settlement.ts, apps/desktop/src/renderer/src/components/Conversation.tsx

Runs, turns, and sessions

Run
The life of one agent process: its agent and model, its folder, the provider's session ID, its state (starting, running, success, error, or cancelled), token usage, and final reply. Each run belongs to exactly one thread, task, or task comment.
Turn
One request and the agent's work until it stops. A run can hold many turns.
Session
The provider's own conversation. OpenOrc stores its ID, and the next run with the same agent resumes the newest one. A fork uses the provider's own fork call, so it gets a session of its own.
Event
One of 26 shared types that every adapter translates into: session and turn events, text and thinking, tool calls and output, file changes, status updates, approvals, plans, and subagents.

One agent can't continue another's session. Switching a thread to an agent that hasn't worked in it starts a fresh session with a handoff: your last 4 requests (up to 400 characters each) and the previous agent's last reply (up to 1,500 characters), added to the new agent's brief. In a project thread, switching back to an agent that already worked there resumes its own older session. In a Workspace thread, every change of agent or folder starts fresh with a handoff.

A Claude Code session belongs to the Claude account that made it. Moving a thread to another Claude account copies its newest session into that account's folder, and the next run resumes the copy as a fork.

When an agent runs subagents of its own, the adapter reports each one as it starts and as it changes. The core gives each subagent a child thread under the parent, with the subagent's events in a run of its own, so its work reads like any other conversation. See Subagents.

If a provider reports that a session no longer exists, the thread offers Start a fresh session with a handoff.

Startup, shutdown, and recovery

Startup

  1. Main picks the data folder, takes the single-instance lock (a second copy focuses the first window and quits), starts the core, and opens the window.
  2. On macOS and Linux, the core stops any agent processes that the last session of OpenOrc left behind, using the records it keeps in the data folder.
  3. It opens the database and applies any pending migrations.
  4. It recovers from the last session: runs that were still open are marked as failed with OpenOrc closed while this session was open. Send a message to continue where you left off. Interrupted discussion replies and plans are marked as such.
  5. On macOS and Linux, it asks your login shell for PATH and the agents. Then it starts the MCP server and tells every window it is ready.
  6. It starts its timers and sends messages still waiting in a thread's queue, which can start an agent right after launch.

The timers wake snoozed threads and check schedules every 60 seconds, check open pull requests every 5 minutes, run database upkeep after 60 seconds and then every 6 hours, and check for agent updates after 30 seconds and then every 6 hours.

Shutdown

Main closes every terminal and Preview, then asks the core to shut down and waits up to 12 seconds. The core denies pending approvals, closes every agent process, flushes the database and the provider log, and closes. If you quit while an agent is working, its turn stops and the run is recorded as failed; your next message resumes the session.

If the core process crashes, windows aren't told, and requests wait. Reload the window (⌘R) to start a new core. See Known issues.

Source: packages/core/src/openorc.ts (create, close), packages/agents/src/process-recovery.ts, packages/core/src/services/runs.ts

Next steps

Checked against OpenOrc's source on November 9, 2026.