All pages
On this page

relay_tool Added in 0.9.4

With relay_tool an agent works with other sessions: it lists and reads them, sends one a prompt, starts a new one, waits for the reply, answers a question the other agent asked, stops or archives a session, and can ask to be woken when something happens in sessions it watches.

It reaches two kinds of relay:

  • This relay, the one the agent itself runs on. An agent can hand parts of its task to other sessions here. Off until you switch it on. Added in 0.9.7
  • Paired relays, other OAR relays you have paired with this one, for example the relay on your workstation and the one on a server. Locked in each conversation until you mention the relay.

The tool is the same for every provider that has it; see By provider for who does and under which name.

The name Added in 0.9.18 #

Up to 0.9.17 the tool was called remote_relay. It is now listed as relay_tool on every provider (Claude's agent sees mcp__relay__relay_tool), with the same actions and fields. Tool lines written under the old name still show as this tool.

  • Sessions that were running when you updated. A session's agent gets its tool list when its worker starts, and workers keep running across the relay's restart. Such a session's agent still has the tool as remote_relay until its worker starts again, for example after Kill session. Its calls keep working: they reach the relay the same way under either name. Where the relay runs as the Linux service, the workers may stop with the relay, and then come back with the new name.
  • OAR's own MCP server, which Grok and Copilot's extension engine use, still runs a call to remote_relay, but no longer lists it.
  • Your own instructions. If your instructions, permission rules or hooks name remote_relay or mcp__relay__remote_relay, change them to relay_tool or mcp__relay__relay_tool: outside OAR's MCP server the old name is refused.

What you see #

  • Approval cards. A question card asks before the first session an agent starts in a conversation, before it first watches sessions, and before every write action while the conversation is in the ask or plan relay mode.
  • The tool call in the reply. The agent's activity shows each call with a 🛰️, the action, the relay and the session.
  • "via agent". A session an agent started carries via agent · "title" in the conversation list and under its title, naming the conversation the agent runs in. Added in 0.9.11 It sits in a session group under that conversation, Added in 0.9.16 or in an existing group the agent named.
  • "↗ from …". A prompt that an agent sent carries a badge with the sending relay, session and model on the receiving side.
  • Watching. A conversation that watches others shows watching N sessions in its header; the chip lists its subscriptions, each with an Unsubscribe button. A watched session shows a watched badge.

Switching it on #

relay_tool is offered to agents when at least one of these is true:

To let agents work on Do this
this relay Turn on Settings → Relays → Agent sessions → Agents may start and use sessions on this relay. Off by default.
another relay Pair it under Settings → Relays, then mention it in the conversation (@name).

A session finds out whether it gets the tool when its agent process starts, so a conversation that was already running may not have it until its agent has started again.

Naming a relay #

Every action except list_relays works on one relay, named in relay:

  • "this", or this relay's own name, is the relay the agent runs on. It needs no mention.
  • A paired relay is named by its relay name or its host, as list_relays shows it.

For the four subscription actions, Added in 0.9.16 the three schedule actions and the three question actions, relay may be left out and means "this". Schedules work on this relay only.

Locked and unlocked #

A paired relay stays locked in a conversation until one of these happens:

  • You mention it in one of your messages: type @ and pick it, or write its name or host name. A relay whose name is an IP address or localhost only counts with a leading @.
  • An agent on that relay writes to the conversation. The relay is then open so the agent here can answer.

Once unlocked, it stays open for the rest of the conversation. A locked relay refuses every action except list_relays; the agent is told to ask you to mention the relay.

Permissions and approvals #

What a paired relay allows #

Each paired relay has an Agents may level under Settings → Relays. New pairings start at Full.

Level Actions
Read only (read) relay_info, usage, list_sessions, read_session, read_task_output, wait, subscribe, unsubscribe, list_subscriptions, transfer_subscriptions
Read and prompt (prompt) the above, and send
Full (full) the above, and create_session, Added in 0.9.16 update_session, answer_question, stop, archive

list_relays needs no level. This relay itself always counts as Full.

On the other side, Accept prompts from other relays' agents (Settings → Relays on the paired relay) switches off everything another relay's agents could change or watch there: prompting, starting, answering, stopping and archiving sessions, and subscribing to them. Listing and reading sessions, reading task output, waiting, relay_info and usage still work.

When you are asked #

Situation What happens
The conversation is in the ask or plan relay mode and the agent calls a write action (send, create_session, update_session, answer_question, stop, archive) A question card asks you every time.
Added in 0.9.16 First update_session of a conversation on a session of a relay that this conversation did not start (yours, another agent's, or the one that started the agent) A card names the session and what would change. Allow covers that conversation's later changes to such sessions on that relay. Deny refuses the call. See update_session.
First create_session on this relay in a conversation An approval card shows the provider, the folder or repository, Added in 0.9.16 the group it names, and the prompt. Allow covers every session that conversation's agent starts later, also after a relay restart. Deny refuses that one call.
First subscribe of a conversation on a relay, and every subscribe to all An approval card names the sessions and the relay.
transfer_subscriptions Always asks.
The agent has sent 30 prompts to other agents without a message from you The next one asks first. Allow starts the count again. Added in 0.9.16 Only a message you wrote resets the count; relay events, the usage-limit follow-up and the note after a restart do not. Schedules into other sessions count too (Schedules).
Added in 0.9.16 schedule for another session in the ask or plan mode A question card names the time and the prompt. A schedule for the agent's own conversation asks nothing.

Rules for sessions on this relay #

  • At most 4 at work. Of the sessions one conversation's agent started, 4 may have a turn queued or running at the same time. A fifth is refused until one has finished.
  • One level only. A session that an agent created cannot create sessions itself, Added in 0.9.16 unless you switched on Agents in this conversation may start sessions for it. list_sessions and read_session then show "mayCreateSessions": true. No agent can set that switch.
  • Not on itself. An agent cannot send to, wait for, stop or archive the conversation it runs in, nor answer its own question, Added in 0.9.16 nor change its settings.
  • Your accounts. Sessions an agent starts use this relay's provider accounts and count against their usage.
  • The relay never archives these sessions by itself. They stay in your list until you archive or delete them, or the agent archives one.

Across relays #

  • A prompt that has already crossed two relays is not passed on to a third one unless you unlocked that relay yourself. Reading and waiting are never limited by this.
  • The prompt the other agent receives starts with a line that says another agent is talking to it: [Remote prompt from an agent on relay "…" · session "…" · model · acting for the user].
  • Turns started this way send no "reply ready" notification on the other relay. Question cards still notify.
  • Remote addresses must use https://. Plain http:// is accepted only for loopback and private network ranges.

Actions #

Every call names an action. The table shows what each needs; the level is the lowest Agents may setting that allows it on a paired relay.

Action What it does Needs Level
list_relays the relays and whether each is unlocked none
relay_info providers, models and folders of a relay relay read
usage what each provider's plan has left relay read
list_sessions find sessions relay read
read_session read a session's recent messages relay, session read
read_task_output read the end of a background command's output relay, session, task_id read
send prompt an existing session relay, session, text prompt
create_session start a session with a first prompt relay, text full
update_session Added in 0.9.16 change a session's settings for its next turns relay, session, one setting full
wait keep waiting for a reply relay, session, message_id read
answer_question answer a question the other agent asked relay, question_id, answer or choices full
stop stop the running turn relay, session full
archive archive a session relay, session full
subscribe be woken by events of sessions sessions read
unsubscribe end a subscription subscription_id read
list_subscriptions show subscriptions read
transfer_subscriptions take over another conversation's subscriptions from read
schedule Added in 0.9.16 have this relay queue a prompt later, once or repeated text, at or every_minutes this relay only
list_schedules Added in 0.9.16 show the schedules of a conversation this relay only
unschedule Added in 0.9.16 end a schedule schedule_id this relay only
ask Added in 0.9.16 ask you a question without waiting for the answer question none for its own session; prompt for another
list_questions Added in 0.9.16 show the conversation's asks read
withdraw Added in 0.9.16 take an ask back question_id read

A result is JSON. A call that worked has "ok": true, the relay it worked on and a one-line summary; a call that failed has "ok": false, a code and an error text (Errors). The examples below show the main fields, with made-up names and ids.

Added in 0.9.16 An argument an action does not take is never dropped without a word. create_session and update_session refuse the call with REMOTE_RELAY_INVALID_INPUT, name their own arguments and change nothing: create_session does not take: session. Its arguments: relay, text, provider, model, cwd, repo, branch, mode, effort, title, group, wait_seconds. Nothing was created. Every other action still runs and lists the arguments it did not take in ignored on its result, up to 20. An empty value (null, "", false or an empty list) counts as no argument, because some providers send every field.

Finding relays and sessions #

list_relays #

Lists the paired relays with name, url, online, version, permission and unlocked. When agent sessions are on, this relay comes first, marked "self": true. Also returns the wait limit of this relay. It is the one action that works without a mention and without a relay.

{ "action": "list_relays" }
{
  "ok": true,
  "summary": "list_relays: 2 relays, 1 unlocked",
  "self": { "name": "desk" },
  "relays": [
    { "name": "desk", "self": true, "online": true, "version": "0.9.19", "permission": "full", "unlocked": true },
    { "name": "build-box", "url": "https://build-box.example.com", "online": true, "version": "0.9.19", "permission": "full", "unlocked": false }
  ],
  "limits": { "maxWaitSeconds": 600, "maxActiveSessions": 4 }
}

relay_info #

What a relay offers, so the agent can choose a provider, model and folder for a new session: its name, version and platform, whether it accepts agent prompts, each provider with its default model, models and reasoning efforts, the default and recent working folders, and the relay modes.

{ "action": "relay_info", "relay": "build-box" }

Added in 0.9.16 It also lists the relay's session groups in groups, the ones a create_session there may name: each with id, name (what the sidebar shows; the head session's title for a group nobody named) and memberCount, the group with the latest activity first. Only groups with at least one session that is not deleted are listed, at most 50; groupsTotal says how many there are when there are more. Member ids and titles are not in it. When the relay's groups cannot be read, for example because it runs an older OAR, groups is null and groupsNote says why and that create_session cannot take group there.

"groups": [
  { "id": "grp_…", "name": "Release 2.4", "memberCount": 3 },
  { "id": "grp_…", "name": "Review: server/", "memberCount": 2 }
]

usage Added in 0.9.10 #

What each provider's plan has left on a relay (see Check Usage), so an agent can check before it starts work that costs: per provider a status and its meters (used, allowance, remaining, utilization, reset time), and whether a Claude usage limit has paused turns there. Providers that are not set up or not tracked say so. Account names, addresses and tokens are never included.

{ "action": "usage", "relay": "this" }

list_sessions #

Lists sessions with id, title, provider, model, state, cwd, the time of the last activity and a message count. state is running_turn, background_only (the agent has answered, background work still runs) or idle; tasks lists the background agents and shell tasks.

Added in 0.9.11 A session in a session group also has group: its id, its name and the session's role in it, head or member. Added in 0.9.16 create_session can put the session it creates into an existing group, and update_session can move a session into a group, a new one included, or out of it. Renaming, dissolving and rearranging groups stay with you.

Parameter Type Default
scope active | recent | all recent active: working now. recent: newest first. all: page through everything.
query text only sessions whose title contains this
limit 1–100 20 page size
cursor text the nextCursor of the previous page
{ "action": "list_sessions", "relay": "build-box", "scope": "active" }

read_session #

Reads the most recent messages of a session, together with what is going on there: its state, a reply still being written (inFlight), and any pendingQuestions waiting for an answer (Added in 0.9.16 each with kind: ask for an ask, ask_user for a card the session waits on). Added in 0.9.11 For a session in a group, group adds the other members, up to 50, each with id, title, role, state and archived when it is archived.

Parameter Type Default
session id required from list_sessions or create_session
last 1–50 10 how many recent messages
before text the olderCursor of the previous read, to page back
include_activity true | false false include the tool activity lines of each reply
max_chars 500–50000 12000 text budget for the whole result; long messages are cut and marked truncated
{ "action": "read_session", "relay": "build-box", "session": "3f9c…", "last": 5 }

Added in 0.9.12 Each of the session's tasks also has its taskId, output (whether there is output to read with read_task_output) and, for a command whose output is in a file, health: minutesSinceOutput, quiet and quietAfterMinutes (the relay's quiet setting) while it runs, and the test-run counts when there are any.

read_task_output Added in 0.9.12 #

Reads the end of a background shell command's or monitor's output, the same output the task console shows: while the task runs, and for an hour after it ended. An agent can check whether a build on another relay is still busy, has gone quiet or has failed tests, without asking the agent that started it.

Parameter Type Default
session id required the session that runs the task
task_id id required the taskId from read_session's tasks
lines 1–500 200 how many of the last lines

The result has task (description, type, start, status and exitCode), tail (the lines, as plain text without colours, a repeated line marked [×N]), truncated when there is more before them, stall (minutes since the last output, and quiet against the relay's setting) while the task runs, and counts with the runner and its passed, failed and skipped tests when the output is a test run. Credentials are blanked as in the console. A note says when only the end of the output exists, when the output is no longer there, or when the agent's process was asked for it and the call should be repeated in a few seconds.

{ "action": "read_task_output", "relay": "build-box", "session": "3f9c…", "task_id": "b7k2…", "lines": 50 }
{
  "ok": true,
  "summary": "read_task_output → build-box session 3f9c2a1e task b7k2m9q4: 50 lines, running, last output 14 min ago, 412 passed, 2 failed",
  "task": { "id": "b7k2…", "description": "npm test", "type": "local_bash", "status": "running", "exitCode": null },
  "stall": { "minutes": 14, "since": "2026-10-18T09:12:00.000Z", "quiet": true, "quietAfterMinutes": 10 },
  "counts": { "runner": "vitest", "passed": 412, "failed": 2, "skipped": 0, "final": false },
  "tail": ["…"],
  "truncated": true
}

A task that is not running and did not end within the last hour answers REMOTE_RELAY_NOT_FOUND; a relay older than 0.9.12 answers REMOTE_RELAY_UNSUPPORTED.

Prompting #

send #

Queues a prompt in an existing session and waits for the reply.

Parameter Type Default
session id required
text text, up to 100,000 characters required the prompt
wait_seconds 0–3600 120 how long to wait for the reply; 0 returns at once. See Waiting.
if_busy queue | fail queue queue adds the prompt behind the running turn, or steers into it where the provider supports that. fail refuses when the session is busy.
mode plan | ask | agent | autopilot the caller's mode relay mode for that turn
model text the session keeps its own switches the session to this model. Added in 0.9.13 Checked first: see Which models are accepted
effort text the session keeps its own reasoning effort, for example low, medium, high; the other relay decides whether its model takes it. A DeepSeek session runs low, high or max and maps other names onto them, medium onto high for example (DeepSeek)
provider claude | claude-api the session keeps its billing Added in 0.9.18 only for a Claude session: claude (the subscription) or claude-api (the API key). Naming the other billing switches the session from its next turn, together with this message. See Switching billing
{ "action": "send", "relay": "build-box", "session": "3f9c…", "text": "Did last night's deploy go through? Answer in two lines." }
{
  "ok": true,
  "relay": "build-box",
  "status": "done",
  "session": "3f9c…",
  "message_id": "b41e…",
  "reply": { "text": "Yes. The deploy finished at 02:14 and the health check is green.", "model": "…" }
}

Switching a Claude session's billing Added in 0.9.18 #

send with provider moves a Claude session between the subscription and the Claude API key and sends the message, in one call: a refused switch sends nothing, and a refused message switches nothing. The other billing must be set up on the target relay. The approval card, where one is asked, says Switches billing to API key or Switches billing to subscription.

  • A switch the target relay refuses comes back with REMOTE_RELAY_PROVIDER_UNAVAILABLE when Claude API or Claude is not set up there or the monthly cap is reached, and REMOTE_RELAY_UNKNOWN_MODEL when the session's model is not on the other side's list. remoteCode carries the relay's own code, for example CLAUDE_API_CAP_REACHED.
  • A relay from before 0.9.18 cannot switch: the call is refused with REMOTE_RELAY_UNSUPPORTED, and nothing is sent.
  • Without provider, the session keeps its billing. A send to a session on the key while the cap is reached is refused, and nothing is queued: send with provider: "claude" to move it to the subscription, or ask the user to raise the cap.

create_session #

Starts a new session and gives it its first prompt. The new session knows nothing of the conversation it was started from, so the prompt has to stand on its own.

Takes text, wait_seconds, mode, model and effort as send does, and:

Parameter Type Default
provider github | openai | claude | cursor | grok | claude-cloud | deepseek Added in 0.9.17 | claude-api Added in 0.9.18 the caller's provider must be enabled on the target relay. Added in 0.9.19 The tool takes any provider id; the relay the calling session runs on checks it against the providers it has now, as relay_info lists them, and names them when it refuses one. The target relay must still have that provider enabled. A session started on 0.9.19 or later can therefore use the providers a later update adds, without a restart
model text the caller's model when the target offers it, else the target's default Added in 0.9.13 checked first: see Which models are accepted
cwd path the target's default folder working folder. Not with claude-cloud.
repo owner/repo or a GitHub URL only with claude-cloud, and required there: the repository the cloud session clones
branch branch name the repository's default branch only with claude-cloud
title text the first line of the prompt session title
group a group id (grp_…) or name, up to 200 characters see below Added in 0.9.16 an existing session group on the target relay to put the new session in. See Putting it into a group

What the agent leaves out follows the session it works in, as far as the target relay offers it. An OpenAI Image chat cannot be started this way.

{
  "action": "create_session",
  "relay": "this",
  "title": "Review: server/",
  "cwd": "/home/dev/projects/shop",
  "text": "Review the code under server/ for missing input checks. List findings with file and line. Change nothing."
}

A Claude Cloud session takes a repository instead of a folder:

{
  "action": "create_session",
  "relay": "this",
  "provider": "claude-cloud",
  "repo": "example-org/sample-repo",
  "branch": "feature/banner",
  "text": "On branch feature/banner, change the sample banner text to \"Hello\" and push. Report what changed.",
  "wait_seconds": 60
}

The result is the same as for send, plus what the session was started with: provider, model, mode, effort, title, and repo / branch for a cloud session. Added in 0.9.16 It also has group, the session group the new session went into, as list_sessions shows it (id, name, role), or null.

Putting it into a group Added in 0.9.16 #

Without group, a session started on this relay joins the group of the session that started it, or starts a group with that session as its head. A session started on a paired relay goes into no group.

With group, the new session goes into that existing group instead, on this relay or on a paired one. The agent names the group by its id, or by its name as relay_info lists it, in any case. An empty group counts as none.

{
  "action": "create_session",
  "relay": "build-box",
  "group": "Release 2.4",
  "text": "Run the release checklist in docs/RELEASE.md up to the tag. Report each step."
}

The relay looks the group up on the target before it shows any approval card, as it does for the model, and the card names it (Group: Release 2.4 (grp_…)). Nothing is created when:

  • the target has no group by that id or name. The call is refused with REMOTE_RELAY_UNKNOWN_GROUP, and error and groups list the groups there (up to 20, groupsTotal when there are more): Relay "build-box" has no session group "Relase". Groups there: Release 2.4 (grp_…), … No session was created.
  • the name fits more than one group. The same code, with the groups of that name: … Pass the id.
  • the group was dissolved between the lookup and the start. The same code.
  • the target's groups cannot be read. A relay too old for group answers REMOTE_RELAY_UNSUPPORTED (The target relay doesn't support groups on create_session yet: …); one that does not answer or refuses the token answers with its usual code.

When the session was created but did not land in the group asked for, the result has groupWarning, its summary ends with (no group: …), and the note asks the agent to tell you. You can then move the session by hand.

create_session takes an existing group only. To put the session into a new group, the agent calls update_session with that name afterwards. Renaming, dissolving and rearranging groups stay with you.

update_session Added in 0.9.16 #

Changes another session's settings for its next turns, without prompting it, and answers the settings it has now and what changed. It works on this relay and on a paired one.

Parameter Type
session id, required the session to change
model text the session's model from now on. Checked as for send: see Which models are accepted. A Grok session keeps the model it was started with
effort text the reasoning effort from now on; one its model takes. Not for Claude Cloud
mode plan | ask | agent | autopilot the session's relay mode
auto_compact_window tokens, or 0 the auto-compact window: 100,000, 150,000, 200,000, 300,000, 500,000 or 1,000,000; another number goes to the nearest, and 0 is the model's default
title text the new title
group group id or name, or "none" moves the session into that group; a name the relay does not have makes a new group; "none" (or null) takes the session out of its group
thinking_enabled true | false whether a Claude session thinks on its next turns (thinking)
thinking_display summarized | omitted whether a Claude session's thinking text shows in its transcript
cwd path the session's working folder. Its CLI restarts there

At least one setting is needed. Any other argument is refused and nothing is changed. mayCreateSessions is not among the settings: Whether a session may start sessions is the owner's setting alone.

{ "action": "update_session", "relay": "this", "session": "3f9c…", "model": "claude-sonnet-5-5", "effort": "low", "group": "Release 2.4" }
{
  "ok": true,
  "summary": "update_session → desk session 3f9c2a1e: model, effort, group",
  "session": "3f9c…",
  "settings": {
    "model": "claude-sonnet-5-5", "effort": "low", "mode": "agent", "auto_compact_window": "auto",
    "title": "Review: server/", "group": { "id": "grp_…", "name": "Release 2.4", "role": "member" },
    "thinking_enabled": true, "thinking_display": "summarized", "cwd": "/home/dev/projects/shop"
  },
  "changed": ["model", "effort", "group"]
}

A change to a new folder also says whether the CLI was restarted (relaunched), and a new group comes back as groupCreated. A call that would change nothing answers changed: [] and asks nobody. note carries what the agent should know, for example a window taken as the nearest one, or that the window and thinking apply to Claude sessions only (Added in 0.9.18 Claude API sessions included) and change nothing on another provider. Added in 0.9.17 A DeepSeek session gets that note too, but there the window does take effect, and 0 means 400,000 tokens; thinking_enabled does not, as a DeepSeek session always thinks.

Who may change what. An agent changes freely only a session that its own conversation started: on this relay, or on a paired relay when that relay recorded this conversation as the starter. Any other session, yours, another agent's or the one that started the agent, needs your Allow first. A question card asks once per conversation and relay, names the session and lists the change (Model: … · Effort: … · Group: …); Allow covers that conversation's later changes to such sessions there. Deny refuses the call with REMOTE_RELAY_PERMISSION; a conversation with no running turn to ask in gets REMOTE_RELAY_NO_ACTIVE_TURN. The other relay holds to the same rule itself. An agent never changes the conversation it runs in. In the ask and plan modes the usual card for a write action applies as well.

Before anything changes, the relay checks the whole call on the target: the model and the effort, the group, the folder, and whether you must be asked. An unknown model, an unknown group id, a group name that fits two groups, a folder the relay may not use, or a new folder for a busy session refuses the call before any card, and nothing is changed.

Groups.

  • A name the relay does not have makes a new group with that name. A group id must exist (REMOTE_RELAY_UNKNOWN_GROUP).
  • A group you made is never removed this way. When an agent takes out its last session, the group stays and shows in your sidebar as group · empty: rename or dissolve it from its menu, or let an agent name it again. Nothing removes it by itself.
  • A group an agent made, by name or by starting sessions, goes once it is empty.

The folder. A new cwd restarts the session's CLI in that folder. It is refused with REMOTE_RELAY_BUSY while a turn is queued or running there, or a background task runs: The session cannot move to another folder now: …. The folder must be one the relay may use. A session that has not started yet keeps the folder for its first start. If the CLI does not restart, the folder is still saved for its next start, and the answer says so. Not for Claude Cloud, which works on a clone of its repository.

The mode set this way lasts until the next send that names another mode or comes from a caller in another mode: a send without mode passes the caller's own.

If a step fails after others were written, the answer still lists in changed what was written. An open page shows every change at once. A relay that runs an older OAR answers REMOTE_RELAY_UNSUPPORTED.

Which models are accepted Added in 0.9.13 #

A model in send or create_session is checked against the list New Chat offers for the provider on the target relay, the same list relay_info shows, before any approval card. A model that is not on it is refused with REMOTE_RELAY_UNKNOWN_MODEL, and nothing is created or sent:

Unknown model 'gpt-5-4' for Copilot on relay "build-box"; did you mean 'gpt-5.4'? Valid ids: … Nothing was sent.

The result also carries suggestions, the close matches, and models, the valid ids. Before 0.9.13 such an id created a session on another model, which later send calls with that id could not reach.

Added in 0.9.19 Copilot model ids use dots: claude-opus-5.5, not Claude's claude-opus-5-5. Copilot refuses another provider's id before anything is created: with COPILOT_MODEL_UNAVAILABLE and the close match, or, in create_session, with the code of the one other provider that lists the id, such as CLAUDE_PROVIDER_REQUIRED. A Copilot session never moves to another provider because of its model name.

The agent passes What runs
An id from the list, in any casing That model, in the list's casing
opus, sonnet or haiku (Claude) The newest listed Claude model of that name. With [1m], its long-context variant when listed
A Copilot id with its effort, such as gpt-5.4-high gpt-5.4 with effort high, when the list offers that effort for the model. An effort passed as well wins
auto Passed on

Where the model that runs differs from the one asked for, the result's modelNote says so. When the target relay has no list for the provider yet (its model discovery has not run or failed), the model goes on unchecked and modelNote says that too. A relay also refuses an unknown Copilot model when another relay's agent starts a session on it.

wait #

Keeps waiting for a reply that send or create_session did not get in time.

Parameter Type Default
session id required
message_id id required the message_id that send or create_session returned
wait_seconds 0–3600 120
{ "action": "wait", "relay": "build-box", "session": "3f9c…", "message_id": "b41e…", "wait_seconds": 300 }

Waiting and what comes back #

send, create_session and wait wait for the reply for as long as the agent asks in wait_seconds, up to Longest wait per tool call on the relay the agent runs on (Settings → Relays → Agent sessions): 10 minutes by default, adjustable from 2 to 60 minutes. When a call asked for more than the relay allows, the result says so in a note. When the time is up, the agent gets what there is so far and can call wait again.

A turn that waits still counts toward Max turn duration under Settings → General.

The result's status says where the other session stands:

status Meaning
queued The prompt is accepted, nothing runs yet. Also what wait_seconds: 0 returns.
running The time was up while the turn was still running (progress holds the reply so far and its activity), or the agent has answered but keeps working in the background (reply holds its latest reply).
waiting_for_answer The other agent asked a question. pendingQuestions lists it with its id, text and options.
done Finished. reply holds the answer.
failed The turn failed; the reply is the relay's failure text.
stopped The turn was stopped before it answered.
cancelled Nothing is queued or running and there is no reply.
duplicate The other relay took the message as a repeat of one it already has.

Every such result carries session and message_id, and openUrl, a link that opens the session, when the relay has one.

Steering another session #

answer_question #

Answers a question card that is waiting in another session. Agents are told to ask you first, unless you told them to decide themselves.

Parameter Type
question_id id, required the id from pendingQuestions
choices list of option labels, up to 50 for a question with options
answer text a free-text answer

One of choices and answer is required. An agent cannot answer its own question, and it cannot answer a relay's approval card: those are yours. Added in 0.9.16 Nor can it answer an ask: answer_question refuses it with REMOTE_RELAY_FORBIDDEN before it calls anyone.

{ "action": "answer_question", "relay": "build-box", "question_id": "q_7d2…", "choices": ["Merge"] }

stop #

Stops the turn that is running in a session, as Stop does in the UI. The result says whether a stop was requested or nothing was running.

{ "action": "stop", "relay": "this", "session": "3f9c…" }

archive #

Archives a session.

{ "action": "archive", "relay": "this", "session": "3f9c…" }

Watching sessions #

Added in 0.9.10 Instead of asking again and again, an agent can subscribe to sessions and be woken when something happens there. It works on this relay (with agent sessions switched on) and on a paired relay.

A subscription only notifies. It approves nothing and answers nothing; what an event asks for is decided as before, by you or by the agent within what it may do anyway.

subscribe #

Parameter Type Default
sessions up to 50 session ids, or ["all"] required all is every session of the relay, later ones included
events list of event names all but task_started and task_finished, with subagent_finished Added in 0.9.13 see The default events
once true | false false end the subscription after it fired. With match: after the first match.
match regular expression, up to 200 characters, case-insensitive a turn_end reply or a task's description and summary must match; other events pass
stalled_minutes 1–1440 15 quiet minutes while work runs before stalled
heartbeat_minutes 1–1440 none adds a heartbeat this often while a watched turn or task runs, so the watcher can tell its watching works
digest_minutes 1–1440 none Added in 0.9.16 gathers the subscription's events into one message every N minutes. See Fewer wake-ups
quiet_hours {"from":"HH:MM","to":"HH:MM","tz":"<IANA zone>"} none Added in 0.9.16 holds the subscription's events inside this daily window and delivers them when it ends
exclude up to 50 session ids none Added in 0.9.16 only with sessions: ["all"]: sessions the subscription leaves out. A comma-separated text works too
relay relay name this

A conversation can hold 20 subscriptions at a time. The agent's own conversation is never watched.

{ "action": "subscribe", "sessions": ["3f9c…", "a12b…"], "events": ["question_asked", "error", "all_idle"] }
{
  "ok": true,
  "summary": "subscribe → desk: 2 sessions (sub_ab12cd34ef)",
  "subscription": {
    "id": "sub_ab12cd34ef",
    "sessions": [ { "id": "3f9c…", "title": "Review: server/" }, { "id": "a12b…", "title": "Review: web/" } ],
    "events": ["question_asked", "error", "all_idle"],
    "once": false,
    "stalledMinutes": 15
  }
}

Fewer wake-ups Added in 0.9.16 #

Every wake-up is a full turn of the watcher. Three options of subscribe ask for fewer. question_asked, question_answered and error are never held back by any of them; every other event, task_failed, stalled and all_idle included, is.

{
  "action": "subscribe",
  "sessions": ["all"],
  "exclude": ["77aa…"],
  "digest_minutes": 30,
  "quiet_hours": { "from": "23:00", "to": "06:00", "tz": "Europe/Berlin" }
}
  • digest_minutes. The subscription's events are gathered and come as one message every N minutes: the first N minutes after the subscription was made at the earliest, each later one N minutes after the last. The message says Digest of 4 events (subscription sub_… every 30 min; questions and errors come at once):. A digest of a single event reads as that event.
  • quiet_hours. A daily window of local time in an IANA time zone. from and to are times of day (HH:MM, 00:00 to 23:59) and must differ; the window may run past midnight. What happens inside it waits and comes as one message when the window ends, with the line Held during quiet hours (23:00–06:00 Europe/Berlin): 3 events, delivered now that the window has ended; questions and errors came at once. No heartbeat is made inside it. An event a digest would deliver inside the window moves to its end.
  • exclude. A subscription to all sessions ignores these sessions, also for all_idle, stalled and the heartbeat. With a list of sessions it is refused: exclude goes with sessions ["all"] only; a list of sessions names what it watches.

Clock changes. The window follows the time zone's clock. On the night the clocks go back and to comes twice, the window ends the first time and is not entered again in the repeated hour. On the night they go forward and skip to, it ends the moment they jump. One known limit: a from inside the repeated hour starts the window on the first pass, and on the second pass the minutes before from count as outside it.

A wrong value is refused with what is wrong, for example quiet_hours from and to must be times of day as "HH:MM" (00:00 to 23:59), quiet_hours from and to must differ, or quiet_hours tz "Berlin" is not an IANA time zone (for example "Europe/Berlin", "America/New_York", "UTC"). A quiet_hours with every field empty counts as none.

  • With once. The event that ends a one-shot subscription still keeps to its digest and quiet hours: the subscription ends at once, the event comes when they let it.
  • Several subscriptions. An event that several of the watcher's subscriptions took goes when the first of them lets it go; a subscription without options lets it go at once.
  • The hourly cap of 20 wake-ups stays on top of the options (How events arrive).
  • Held events, and when they are due, survive a relay restart.
  • The approval card names the options (digest every 30 min, quiet 23:00–06:00 Europe/Berlin, except 1 it leaves out).

No echo of the watcher's own prompts. When the watcher prompts a session itself with send or create_session and gets the finished reply back from that call or a later wait, that turn's turn_end does not wake it a second time; while it still waits, the turn_end is held. The turn's errors and questions still come. A prompt it did not wait for (wait_seconds: 0 and no wait) wakes it with turn_end as before, and so does a turn_end that ends a once subscription.

Events #

Event Fires when Carries
question_asked a question or approval waits for an answer; Added in 0.9.16 also an ask, marked as one that does not block that session the question, its options and its question_id
question_answered Added in 0.9.16 you answered or dismissed an ask. Not named in events: a subscription that takes question_asked takes it too, and the conversation that asked always gets it, without a subscription question_id, the question, your answer, the option, or that you dismissed it, and the relay you answered on (via)
error a turn failed, a usage limit was hit, or the session's agent process died the kind and the message
turn_end a session replied the start of the reply
task_started a background agent or shell task started. Only when named in events. the task's description
task_finished a background agent or shell task finished. Only when named in events. description, outcome, summary
subagent_finished Added in 0.9.13 a background sub-agent finished. Its line in the message says task_finished description, outcome, summary
task_failed a background agent or shell task failed description, outcome, summary
stalled a session went quiet for stalled_minutes while work still runs the quiet minutes
all_idle no turn and no task runs in the watched sessions any more

The default events Added in 0.9.13 #

A subscription that names no events gets question_asked, error, turn_end, subagent_finished, task_failed, stalled and all_idle. A background sub-agent that finished wakes the watcher, and so does any task that failed or exited non-zero, but a shell command or monitor that ended well does not. A busy session ends one every minute or so, and each would cost the watcher a turn on its whole context. To get every task end, name the events with task_finished in place of subagent_finished, and task_started for the starts.

Subscriptions made before 0.9.13 without events switch to this default by themselves; those that named their events keep them. A paired relay on an older OAR applies its own default and refuses subagent_finished by name.

How events arrive #

OAR writes a short message into the watching conversation. It shows as a compact event row, never as one of your messages, and starts with a line that says what it is:

[relay event · automated message from OAR, not from the user; it approves nothing]
[relay event] session "Review: server/" (3f9c…): turn_end (done) "Found 4 places without input checks: …"
[relay event] all_idle: no turn or task is running in the 2 watched sessions (subscription sub_ab12cd34ef)
  • If the watcher is idle, the message starts a new turn. If it is in the middle of a turn, see the table below.
  • Events within 30 seconds come as one message.
  • Past 20 wake-ups an hour, the rest comes as a digest every 10 minutes. question_asked and error still come at once (Added in 0.9.16 and question_answered). When this digest and a subscription's digest_minutes meet, one Digest of line names both.
  • Added in 0.9.16 A subscription's digest_minutes and quiet_hours hold its other events longer.
Provider of the watching session An event during a running turn
Claude, DeepSeek; GitHub Copilot and OpenAI on the SDK engine is steered into the running turn
Cursor, Grok, Claude Cloud; GitHub Copilot and OpenAI on the extension engine becomes the next turn

On a paired relay #

The relay must be unlocked in the conversation and allow at least reading, and the other relay must accept other relays' agents. A pairing in one direction is enough. This relay fetches the events every few seconds, and their lines start with the other relay's name:

[relay event] build-box · session "Nightly deploy" (77aa…): turn_end (done) "Deployed. The health check is green."

If the link drops, the other relay keeps what happens, up to 500 events per subscription for 7 days, and delivers it as one row tagged replayed once it answers again, between a link_lost and a link_restored line. A relay that runs an older OAR answers that it needs an update.

Added in 0.9.16 The digest and quiet hours of a subscription to a paired relay are applied here, where the watcher is; exclude is also passed to the other relay, for all_idle, stalled and the heartbeat. Replayed events keep to the digest and quiet hours like any other; the link_restored line then says how many of them are held and come later. A paired relay on an older OAR does not know exclude: this relay still drops the events of those sessions, but that relay counts them for all_idle, for stalls and in the heartbeat's list.

unsubscribe #

Ends one subscription, or with "all" every subscription the conversation holds on the named relay.

Parameter Type
subscription_id id or "all", required the id that subscribe returned
{ "action": "unsubscribe", "subscription_id": "sub_ab12cd34ef" }

list_subscriptions #

Shows the conversation's subscriptions: the active ones and those that ended in the last hour, with the reason. With watcher, shows another conversation's subscriptions, which is how an agent looks before it takes them over.

Parameter Type Default
watcher conversation id this conversation
{ "action": "list_subscriptions" }

Each subscription shows its sessions, events, once, match, stalledMinutes and heartbeatMinutes, Added in 0.9.16 and digestMinutes, quietHours and exclude when it has them. An active subscription also says how many of its events its digest or quiet hours hold right now (heldCount) and when the first of them is due (nextDeliveryAt, null when none is held). An ended one says so only while its last event is still held, as after a once that fired inside quiet hours.

{
  "id": "sub_ab12cd34ef",
  "scope": "all",
  "sessions": "all",
  "digestMinutes": 30,
  "quietHours": { "from": "23:00", "to": "06:00", "tz": "Europe/Berlin" },
  "exclude": ["77aa…"],
  "heldCount": 3,
  "nextDeliveryAt": "2026-10-24T04:00:00.000Z"
}

transfer_subscriptions #

Moves every subscription of another conversation to this one, for example when a new session replaces an old watcher. Always asks you on an approval card.

Parameter Type
from conversation id, required the conversation whose subscriptions move here
{ "action": "transfer_subscriptions", "from": "c0de…" }

How long a subscription lives #

Subscriptions survive a relay restart. One ends when:

  • the agent calls unsubscribe, or you press Unsubscribe in the watcher's list;
  • the watcher or the watched session is archived;
  • it fired and was made with once;
  • for a paired relay, the pairing is removed.

Schedules Added in 0.9.16 #

An agent can have this relay queue a prompt later, into its own conversation or another session here. Unlike a timer of the agent's own CLI, a schedule survives relay restarts and updates. Scheduled prompts explains it from your side.

Schedules need Agents may start and use sessions on this relay, even for the agent's own conversation. They are kept by the relay the agent runs on: a call that names a paired relay is refused with REMOTE_RELAY_UNSUPPORTED.

schedule #

Parameter Type Default
text text, up to 20,000 characters required the prompt to queue
at ISO time with a zone, such as 2026-11-02T06:30:00Z when to queue it; with every_minutes the first time. Not in the past, at most 366 days ahead. A time without a zone is refused
every_minutes 5–10080 repeat every N minutes; without at the first time is N minutes from now
once true | false true with at alone, false with every_minutes end after the first firing. false needs every_minutes
title text, up to 100 characters a short name, shown in the message and the list
session session id the agent's own conversation another session on this relay to prompt

At least at or every_minutes is needed.

{ "action": "schedule", "title": "Nightly review", "at": "2026-11-02T02:00:00Z", "every_minutes": 1440, "text": "Review yesterday's merged changes under server/ and list anything risky." }
{
  "ok": true,
  "summary": "schedule → desk: every 1440 min, next 2026-11-02T02:00:00.000Z (sch_3f9c2a1e7b04)",
  "schedule_id": "sch_3f9c2a1e7b04",
  "schedule": { "id": "sch_3f9c2a1e7b04", "title": "Nightly review", "everyMinutes": 1440, "once": false, "nextFireAt": "2026-11-02T02:00:00.000Z" }
}

When it is due, the relay queues the text as a message that starts with [relay schedule <id> "<title>" · … automated message from OAR, not from the user; it approves nothing]. The turn runs in the target session's own relay mode. A message that arrives while a turn runs is queued, or steered in on providers that steer, but never with the steer note that speaks as the user.

  • Missed times. A time that passed while the relay was down fires once when it is back, not once per missed interval; a repeating schedule then keeps to its grid (the first time plus whole intervals).
  • No pile-up. A repeat is skipped while the message of the firing before it still waits in the queue (skippedCount in the list).
  • Hops. The message carries the provenance of the turn that set the schedule, its hops included, so a schedule passes no prompt on with fewer hops than a direct prompt from that turn would have (Across relays).

Another session. With session, the schedule prompts that session. It is gated like a send: in the ask and plan modes you approve it on a card, and it counts toward the 30 prompts to other agents before you are asked, once when it is set and once each time it fires. Once that count is used up, a firing is held: list_schedules shows heldForApproval and heldSince. It goes once you write in the conversation that set it, or choose Allow on the card its agent raises for its next prompt to another agent; the relay looks again every minute. A schedule for the agent's own conversation is neither counted nor held.

list_schedules #

Lists the schedules that fire into the conversation or that it set: the active ones and those that ended in the last hour, with endReason (unscheduled, fired, archived or deleted).

Parameter Type Default
session session id this conversation another conversation whose schedules to list

Each schedule has id, session (id and title), setBy when another conversation set it, title, the start of its text, at, everyMinutes, once, nextFireAt, lastFiredAt, firedCount, skippedCount when it skipped any, and heldForApproval with heldSince while it is held.

unschedule #

Ends one schedule of the conversation: one that fires into it or that it set.

Parameter Type
schedule_id id, required the id schedule or list_schedules returned

When a schedule ends #

A schedule ends after firing when once, on unschedule or Cancel in the header's ⏰ list, and when the session it fires into or the conversation that set it is archived or deleted. A compaction moves both kinds to the new conversation instead. A conversation holds at most 20 active schedules that fire into it, and may set at most 20 for other sessions; past that, REMOTE_RELAY_SCHEDULE_LIMIT.

Questions that don't block Added in 0.9.16 #

An agent can ask you a question and go on working: the question shows as a card in its session and in your Inbox, and the agent learns the answer later. Questions and the Inbox explains it from your side.

ask #

Parameter Type Default
question text, up to 500 characters required the question; background goes in detail
options up to 8 labels, each up to 120 characters answers shown as buttons; you can always type one of your own
recommendation text, up to 300 characters the answer the agent recommends: one of the options, or free text
detail text, up to 2000 characters background, shown folded, as plain text
urgency normal | now normal now also sends you a push notification
session session id the agent's own conversation a session to ask on behalf of, on the named relay
{ "action": "ask", "question": "Ship the migration tonight or wait for the review?", "options": ["Tonight", "Wait"], "recommendation": "Wait", "detail": "The review covers the index change; the rest is tested." }
{ "ok": true, "question_id": "ask_7d2…", "status": "pending", "summary": "ask → desk: question ask_7d2… pending" }

ask returns at once with question_id and status: "pending". The agent should go on with what does not depend on the answer and not ask the same thing again while it is pending.

The answer. When you answer or dismiss the question, the agent gets the relay event question_answered, with question_id, your answer, the choice you picked, dismissed when you dismissed it, and via, the relay you answered on: the one the ask is kept on, or a paired relay whose Inbox you used. It needs no subscription. The event is urgent: no digest or quiet hours hold it back. It is automated and approves nothing else, and it does not count as a message from you for the loop guard: answering an ask does not reset the count of prompts agents may send each other.

Who answers. Only you, in the session or in an Inbox (this relay's, or that of a relay paired with it). answer_question refuses an ask.

Gating.

  • An ask about the agent's own conversation, list_questions and withdraw need no setting and no mention.
  • With session, the agent asks on behalf of another session: on this relay with Agents may start and use sessions on this relay, on a paired relay with Agents may at least Read and prompt and the relay unlocked. Such an ask is a write: in the ask and plan modes you approve it on a card, and it keeps to the hop limit (Across relays). The loop guard does not count it. The card shows in that session, and both it and the asking conversation get the answer.
  • An ask about a session on a paired relay is kept on that relay; this relay checks every 5 seconds for its answer while it is open (every 30 seconds while that relay does not answer). A relay that runs an older OAR answers REMOTE_RELAY_UNSUPPORTED.

At most 20 asks may be pending per conversation that asks, and 20 per session they are about; past that, REMOTE_RELAY_QUESTION_LIMIT. Asks do not expire.

list_questions #

Lists the conversation's asks: the pending ones, and those answered or dismissed or withdrawn in the last day, with their status and answer. With session, the asks about another session. On a paired relay it lists the asks this conversation asked there; read_session there lists every pending question of a session.

Parameter Type Default
session session id this conversation another session whose asks to list

withdraw #

Takes back an ask that is still pending. Only the conversation that asked may withdraw it.

Parameter Type
question_id id, required the id ask returned

An ask also ends when its session is archived or deleted (nobody is told), and when the pairing with the relay it is kept on is removed. A compaction moves its asks to the new conversation.

Limits #

Limit Value
Calls per conversation 60 a minute
Write actions per conversation 10 a minute
Sessions at work that one conversation's agent started on this relay 4
Wait per call 120 s by default; up to the relay's Longest wait per tool call (10 min by default, 2–60 min)
Prompt length (text) 100,000 characters
Agent-to-agent prompts without a message from you 30, then you are asked
Relays a prompt may cross 2, unless you unlocked the next relay yourself
list_sessions page 20 by default, 100 at most
read_session messages 10 by default, 50 at most
read_session text 12,000 characters by default, 50,000 at most
read_task_output 200 lines by default, 500 at most, and 32 KB of text
Subscriptions per conversation 20
Sessions per subscription 50, or all
Events kept by a paired relay while the link is down 500 per subscription, for 7 days

Errors #

A refused call comes back with a code the agent can act on.

Code Meaning
REMOTE_RELAY_INVALID_INPUT a parameter is missing or wrong; error says which
REMOTE_RELAY_UNKNOWN no relay by that name, or the name fits more than one
REMOTE_RELAY_LOCKED the paired relay is not unlocked in this conversation: mention it
REMOTE_RELAY_LOCAL_DISABLED agent sessions are switched off on this relay
REMOTE_RELAY_FORBIDDEN the relay's Agents may level does not allow this action
REMOTE_INBOUND_DISABLED the other relay does not accept prompts from other relays' agents
REMOTE_RELAY_APPROVAL_DENIED you chose Deny on the approval card
REMOTE_RELAY_NO_ACTIVE_TURN an approval was needed, but the calling session had no turn running to ask it in
REMOTE_RELAY_NESTED_SESSION a session that an agent created tried to create a session, Added in 0.9.16 and you have not allowed it for that session
REMOTE_RELAY_PERMISSION Added in 0.9.16 update_session on a session the conversation did not start, and you chose Deny (update_session)
REMOTE_RELAY_SESSION_LIMIT 4 sessions this conversation's agent started are already at work
REMOTE_RELAY_OWN_SESSION the agent aimed the action at its own conversation
REMOTE_RELAY_HOP_LIMIT the prompt has crossed two relays and the next one is not unlocked by you
REMOTE_RELAY_RATE_LIMITED too many calls; the result says when to try again
REMOTE_RELAY_BUSY send with if_busy: "fail" and the session is busy
REMOTE_RELAY_NOT_FOUND no such session, message or question
REMOTE_RELAY_PROVIDER_UNAVAILABLE the provider is not enabled on the target relay; Added in 0.9.18 also a billing switch the target refuses because Claude API or Claude is not set up there or the monthly cap is reached, with the relay's own code in remoteCode
REMOTE_RELAY_UNKNOWN_GROUP Added in 0.9.16 create_session named a session group the target relay does not have, or a name that fits more than one; groups lists the groups there (Putting it into a group)
REMOTE_RELAY_UNKNOWN_MODEL Added in 0.9.13 the target relay does not list that model for the provider; suggestions and models say what it offers (Which models are accepted)
REMOTE_RELAY_QUESTION_LIMIT Added in 0.9.16 20 asks are already pending for this conversation, or for that session (Questions that don't block)
REMOTE_RELAY_SCHEDULE_LIMIT Added in 0.9.16 the session already has 20 schedules firing into it, or the conversation has set 20 (Schedules)
REMOTE_RELAY_SUBSCRIPTION_LIMIT the conversation already holds as many subscriptions as it may
REMOTE_RELAY_OFFLINE the other relay does not answer
REMOTE_RELAY_UNAUTHORIZED the other relay refused the stored token
REMOTE_RELAY_UNSUPPORTED the other relay runs an older OAR that lacks this action
REMOTE_RELAY_HTTP_<status> the other relay answered with this HTTP status
REMOTE_RELAY_INTERNAL something went wrong inside the relay