All documentation

Conversations, tasks, and runs

The records OpenOrc keeps for your work and the path a message takes from the composer to an agent and back.

Checked against the source on September 27, 2026.

The records

OpenOrc keeps describing work, doing work, and talking about work as separate records. You can save an idea without starting an agent, and a conversation can outlive any one agent process.

RecordTableWhat it holds
ProjectprojectsAn imported repository: its path, remote, default branch, and settings such as a setup script and the files to copy into new worktrees. The Workspace is a built-in project row with the ID openorc-workspace.
ThreadthreadsA conversation: its current agent, model, effort, Fast mode, mode, permission level, and where it works (the checkout, its own worktree, or a Workspace folder). Threads have no status.
TasktasksA title, a Markdown spec, a status, labels, a priority, the conversation that created it, if any, and, once work starts, its execution conversation. Statuses: proposed, backlog, in progress, review, done, archived.
RunrunsOne agent process: the agent and model, the working folder, the provider's session ID, the state (starting, running, success, error, cancelled), token usage, and the final reply. Each run belongs to exactly one thread, task, or task comment.
TurneventsNot a table. A turn is the events between turn.started and turn.completed.
Commenttask_commentsA message in a task's discussion, with one reply attempt per addressed agent.
ScheduleschedulesA prompt, an interval in minutes, and the agent settings to use, plus a record of every firing.

Conversation titles

A thread's title starts as the first line of your first message, up to 72 characters. When the first turn ends and the title is still that line, OpenOrc asks a small model for a better one. It sends this prompt, followed by the first 1,500 characters of your message and of the agent's reply:

Name this conversation between a user and a coding agent.
Return ONLY minified JSON, no prose, no code fences: {"title":string}
Rules: 3 to 6 words, sentence case, no quotes and no trailing period. Name the subject, not the request: "Sidebar collapse and traffic lights", not "Fix the sidebar".

The Harness setting, under Text generation in Memory & models, decides which model answers:

  • Automatic · the conversation's agent, the default, uses the conversation's own agent, so the text goes to the provider that already has it: Claude Haiku 4.5 through Claude Code, the first Codex model named nano, luna, or mini, or the first OpenCode model marked free, nano, flash-lite, mini, or haiku. If the agent has no such model, the conversation keeps its first line.
  • A chosen harness names every conversation, whichever agent the conversation uses, with that harness's small model or the model you pick.
  • Off · use opening message keeps the first line.

The request runs the agent's CLI once, in the system's temporary folder, with no tools or MCP servers and 30 seconds to answer: claude -p … --max-turns 1 --strict-mcp-config, codex exec --sandbox read-only -c mcp_servers={}, or opencode run --standalone with every permission denied. If you rename the conversation before the answer arrives, your name stays.

Source: packages/core/src/services/threads.ts (onTurnCompleted), text-generation.ts, packages/memory/src/text-generator.ts

A message, step by step

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.

In the interface

When you press Send, the composer picks one of three requests:

  • threads.start for a new conversation.
  • runs.send when the conversation's agent process is alive and idle, and nothing that requires a restart has changed.
  • runs.start otherwise, which starts a new process that resumes the conversation's session.

In the core

For runs.start, the core:

  1. Validates the request and its parameters in OpenOrc.handle (see Interface to core).
  2. In ThreadService.continueThread, resolves the conversation's project and folder, decides whether to resume the last session or start fresh with a handoff, and takes a shared lease on the folder, so exclusive Git operations cannot run while an agent is busy there.
  3. It saves the agent, model, effort, mode, and permission level on the thread, so the next message uses them.
  4. WorkspaceService.prepareThread creates the worktree if the conversation uses one and it does not exist yet. See Worktrees.
  5. RunService.start inserts the run row, records your message as an event, and builds OpenOrc's instructions for this conversation. It then calls the provider's adapter, which spawns the CLI.

With runs.send, RunService.send applies any model, effort, or Fast mode change to the live session, then passes your message to the process that is already running.

While the agent works

Every event from the adapter goes through RunService.emit, which does four things:

  • Captures plan updates into the conversation's plan document.
  • Queues the event for the ledger, written in batches every 50 ms or 500 events.
  • Queues it for the window, sent at most once per run every 16 ms.
  • Passes it to Slack and task discussions when they are watching this run.

When the agent edits files (any finished tool that is not read-only), OpenOrc refreshes the Changes panel, at most once per second, plus every 8 seconds while the turn runs.

When the turn ends

  1. The core saves the token usage and the final reply on the run.
  2. In a Git project, it records a snapshot of the conversation's files, so each turn's changes can be shown separately.
  3. It starts the idle timer that will close the process, sends a desktop notification, and delivers the next queued message, if there is one.

When the process finally exits, the run gets its final state: error if the process failed, cancelled if OpenOrc closed it in the middle of a turn, success otherwise. The workspace lease is released, and if memory is on the run is queued for extraction.

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

Sending while an agent works

  • Enter sends your message into the running turn when the provider can take it: Codex receives it with turn/steer, and Claude reads it at its next tool boundary. When that is not possible, for example because you changed the agent or the mode, the message is queued instead.
  • Queue holds a message until the turn ends, and one message is delivered each time a turn ends. Send now on a queued message moves it into the running turn.
  • Stop interrupts the turn but keeps the process and its session, so the next message continues the same context.

The queue is saved per conversation in the thread_queue table, so it survives a restart. When OpenOrc starts again, it sends the messages that were still waiting, which can start an agent. A message that was being delivered when OpenOrc closed is not sent again on its own: it waits in the queue until you check the conversation and press Retry, or remove it.

OpenCode cannot receive messages mid-turn. Pressing Enter while it works shows an error and keeps your text, so use Queue with OpenCode.

An ordinary conversation has at most one live agent process; a team preview conversation can run several. There is no global limit on how many conversations can run at once. Several conversations in the same checkout can write to it at the same time. Exclusive operations, such as committing or moving a conversation, first ask idle agent processes in that folder to close; if one is busy, the operation fails. Only team preview workspace operations wait, up to 10 minutes.

Source: packages/core/src/services/thread-message-queue.ts, packages/db/src/thread-queue.ts

Messages between conversations

An agent can message another conversation in the same project with thread_send, and you can pass a message on from one conversation to another. The message arrives labelled with where it came from: "Message from the thread "<title>" (id <id>):". If the receiving agent is working, the message joins its current turn; otherwise it starts a new turn.

  • Permissions. The receiving conversation acts on the message with its own mode. A message to a conversation in the same mode or a stricter one is delivered at once. A message to a more permissive one asks you first, and from Plan it is refused. See Permissions and app tools.
  • Chains. A conversation that receives a message can message back, and so on. After 4 agent messages in a row with no one writing, thread_send stops and tells the agent to ask you. A message from a person in a conversation, in the app, through Slack, or from a schedule, resets that conversation's count. The counts live in the core's memory, so a restart resets them too.
  • Retries. A retried call with the same request key is delivered once. Without a key, the same text to the same conversation from the same run counts as a retry.
  • Team conversations. A message to a team conversation goes to its lead as queued direction, and only while the team is running. These messages skip the permission check and the chain limit above.

Source: packages/core/src/services/thread-agent-tools.ts (toolThreadSend), thread-message-queue.ts (send), packages/core/src/mcp-host/threads.ts

Changing agent, model, or mode

  • Model, effort, and Fast mode change without restarting Claude or Codex. OpenCode changes model and effort in place and has no Fast mode.
  • Mode and permission level changes apply from your next message, which closes the process and starts one that resumes the session with the new settings. A turn already running continues as it was, except that switching to Autonomous approves OpenOrc's waiting permission prompts right away, apart from typing into a password field, and approves the agent's later permission requests in that turn too.
  • Agent changes start that provider's own session. See Sessions and switching for how the handoff works.

OpenOrc has two modes. Plan is for investigating and proposing; Act carries out the work, at one of three permission levels: Review everything, Accept edits, or Autonomous (Codex shows them as Review changes, Ask for approval, and Full access). Each provider receives its own native settings for the chosen mode. OpenCode offers only Autonomous in Act, and on Windows, Claude cannot use Plan or Accept edits.

Plans

In Plan mode, each provider writes its plan in its own way, and OpenOrc saves it as the conversation's plan document:

  • Claude writes a plan file and asks to leave Plan mode. OpenOrc saves the plan and declines the request.
  • Codex produces native plan items.
  • OpenCode calls OpenOrc's plan_write tool with the full document.

Each revision is stored in conversation_plans. Choosing Implement this plan starts an Act turn with the permission level you pick under Implementation mode and the message "Implement the approved plan, revision N (<plan ID>). Follow the selected execution mode.", followed by the plan. Each revision can be implemented once; a second request returns the first run instead of starting another.

Tasks

A task is a document first. Creating one, from the Tasks page or by an agent with the task_create tool, saves it with status backlog and starts nothing. When an agent creates a task, OpenOrc links it to that agent's conversation. When an agent creates a task and its conversation already has a task that is not archived with the same title, or mostly the same words, OpenOrc returns that task instead of creating a duplicate.

Starting a task

A task's work happens in one conversation, its execution conversation. Open thread, on the task list or the task's page, opens it. If the task has none yet, OpenOrc reuses the conversation that created the task when that conversation is idle, not archived, has nothing queued, works in the task's location, and uses the model that will do the work. Otherwise it creates a conversation named after the task, with a draft in the message box: the task's title, ID, and spec, the previous result if the task ran before, and a summary of the conversation that created it. Opening a conversation sends nothing and does not change the task's status.

Asked to work on a task, the agent calls task_start, as its instructions tell it to. That marks the task in progress and makes the calling conversation its execution conversation, and the same agent does the work there. No second agent is started. task_start is refused in Plan mode, and when the task already has another execution conversation.

A task whose status is Proposed shows a Start button on its card. Start picks the conversation the same way, except that the creating conversation is reused only in Act mode, a new conversation starts in Act, and an execution conversation in Plan mode shows an error instead. It sends this first message and moves the task to in progress:

Work on task "<title>" (<id>) in this conversation.

Task: <title> (<id>)

<spec>

In a conversation created for the task, the draft follows, and any images in the spec are listed last. From then on it is an ordinary conversation, and the agent is told to record progress with task_update.

A task's Execution location, Local checkout or Worktree, applies when work starts. It defaults to where the creating conversation works, or else to the New thread workspace setting. Once work has started, changing the location moves the execution conversation and its uncommitted changes. If that conversation created the task or holds other tasks, move it from the conversation's menu instead (see Moving a conversation).

Source: packages/core/src/services/thread-task-execution.ts (spawnTask, openTaskThread, startTaskInThread), thread-agent-tools.ts (toolStart)

Task discussions

A task's Comments section lets you ask one or more models about the task before any work starts. Mentioning a model in a comment, as @<model> - <effort>, addresses it; up to 8 models can be addressed at once, and each replies independently.

Each addressed model gets its own run, and these runs are read-only:

  • They always run in Plan mode at the Review everything level.
  • They have only three OpenOrc tools: ask_user, approve, and task_comment_intent.
  • They never change the task's status or create a conversation.

The run's message contains the task spec, up to the last 24,000 characters of earlier discussion (labeled "context, not new instructions"), and your latest comment. Replying to a model's comment resumes that model's session, even after a restart.

From discussion to work

Each model must classify your latest comment with task_comment_intent: discussion, clarification, or execution. Execution counts only if the model quotes words that appear in your latest comment. The task description and other agents' replies never count.

  • If you addressed one model and it classifies the request as execution, work begins after its reply succeeds. When the task's execution conversation has already started, the request is queued there as a follow-up, using that conversation's current model and settings. Otherwise the work starts like Start above, with that model in a fresh session. If the task's execution conversation, or the conversation that created the task when there is none, is in Plan mode, the reply shows an error and nothing starts.
  • If you addressed several models, OpenOrc asks you which one should implement it.

Mentions inside an agent's reply never wake another agent. "Ask tagged agents" on a saved description sends a fixed request, "Please discuss the saved task description with me.", and the agents are told that the description is not permission to start.

Source: packages/core/src/services/task-comments.ts, packages/protocol/src/task-comments.ts

Schedules

A schedule runs a prompt every N minutes. The core checks schedules every 60 seconds, so schedules only run while OpenOrc is open. The first firing is one interval after you create the schedule.

  • Each firing starts a new conversation titled with the schedule's name, the date, and the time, using the agent, model, mode, and permission level saved with the schedule.
  • If the app was closed through several intervals, the missed firings collapse into one, and the next firing is one interval later.
  • A firing is skipped while an earlier firing of the same schedule is still being prepared.
  • Every firing is recorded in schedule_firings with a snapshot of the settings it used. If OpenOrc crashes while a firing is being prepared, the firing resumes about a minute after the next start; quitting normally cancels it.

Source: packages/core/src/services/schedules.ts

Attachments

Files you attach are copied into attachments/ in the profile folder. Images get a random name; other files get a random ID followed by their original file name.

  • Images must be PNG, JPEG, GIF, or WebP, up to 20 MB, 40 megapixels, and 16,384 pixels per side. OpenOrc checks that the file really is the format its type claims.
  • Other files can be up to 20 MB and are stored as they are.

How they reach the agent depends on the provider. Claude receives the file paths in the message and opens them with its Read tool. Codex receives images as local image inputs and other files as paths. OpenCode receives images inline as base64 and other files as paths.

When a tool returns an image as base64, the ledger writes it to tool-images/<run>/ and stores a link instead, which keeps the database small.

Source: packages/core/src/services/attachments.ts, tool-images.ts

Recovery

If OpenOrc exits without shutting down cleanly, the next start repairs what it can:

  • Runs that were still open are marked as errors with the message "OpenOrc closed while this session was open. Send a message to continue where you left off." Sending a message resumes the provider session.
  • Tasks worked on in a conversation keep their status. A task with its own run, such as a team assignment, moves from in progress to review if that run was interrupted and its status was never set by hand or by an agent.
  • Task discussion replies in progress are marked as failed, and unfinished plans as interrupted.
  • Schedule firings that were being prepared are resumed.

Queued messages are kept, as Sending while an agent works describes. Pending approval requests live only in memory, so they are lost. Agent processes that were running are not tracked across restarts.

When a provider reports that a session is gone, the conversation offers "Start a fresh session with a handoff", which carries the recent context into a new session.

Source: packages/core/src/services/runs.ts (recoverInterrupted), thread-message-queue.ts (recoverQueue), task-comments.ts, schedules.ts