relay_tool Added in 0.9.4
With relay_ 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_. It is now listed as
relay_ on every provider (Claude's agent sees mcp__),
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_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.relay - OAR's own MCP server, which Grok and Copilot's extension engine use,
still runs a call to
remote_, but no longer lists it.relay - Your own instructions. If your instructions, permission rules or hooks
name
remote_orrelay mcp__, change them torelay__ remote_ relay relay_ortool mcp__: outside OAR's MCP server the old name is refused.relay__ relay_ tool
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_ 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_ 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_shows it.relays
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 orlocalhostonly 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_; 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_, usage, list_, read_, read_, wait, subscribe, unsubscribe, list_, transfer_ |
Read and prompt (prompt) |
the above, and send |
Full (full) |
the above, and create_, Added in 0.9.16 update_, answer_, stop, archive |
list_ 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_ 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_, update_, answer_, stop, archive) |
A question card asks you every time. |
Added in 0.9.16 First update_ 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_. |
First create_ 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_ |
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_andsessions read_then showsession "may. No agent can set that switch.Create Sessions": true - Not on itself. An agent cannot
sendto,waitfor,stoporarchivethe 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://. Plainhttp://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_ |
the relays and whether each is unlocked | none | |
relay_ |
providers, models and folders of a relay | relay |
read |
usage |
what each provider's plan has left | relay |
read |
list_ |
find sessions | relay |
read |
read_ |
read a session's recent messages | relay, session |
read |
read_ |
read the end of a background command's output | relay, session, task_ |
read |
send |
prompt an existing session | relay, session, text |
prompt |
create_ |
start a session with a first prompt | relay, text |
full |
update_ |
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_ |
read |
answer_ |
answer a question the other agent asked | relay, question_, 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_ |
read |
list_ |
show subscriptions | read | |
transfer_ |
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_ |
this relay only |
list_ |
Added in 0.9.16 show the schedules of a conversation | this relay only | |
unschedule |
Added in 0.9.16 end a schedule | schedule_ |
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_ |
Added in 0.9.16 show the conversation's asks | read | |
withdraw |
Added in 0.9.16 take an ask back | question_ |
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_ and update_ refuse the call with
REMOTE_, name their own arguments and change nothing:
create_
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_ there may name: each with id, name (what the sidebar shows; the
head session's title for a group nobody named) and member, the group
with the latest activity first. Only groups with at least one session that is
not deleted are listed, at most 50; groups 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 groups says why and that create_ 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_,
background_ (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_ can put the session it creates into an existing group, and
update_ 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 next 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 (in), and any
pending waiting for an answer (Added in 0.9.16 each with kind:
ask for an ask, ask_ 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_ or create_ |
last |
1–50 | 10 | how many recent messages |
before |
text | the older of the previous read, to page back |
|
include_ |
true | false | false | include the tool activity lines of each reply |
max_ |
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 task, output
(whether there is output to read with read_)
and, for a command whose output is in a file, health: minutes,
quiet and quiet (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 | required | the task from read_'s tasks |
lines |
1–500 | 200 | how many of the last lines |
The result has task (description, type, start, status and exit),
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_; a relay older than 0.9.12 answers
REMOTE_.
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_ |
0–3600 | 120 | how long to wait for the reply; 0 returns at once. See Waiting. |
if_ |
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- |
the session keeps its billing | Added in 0.9.18 only for a Claude session: claude (the subscription) or claude- (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_when Claude API or Claude is not set up there or the monthly cap is reached, andRELAY_ PROVIDER_ UNAVAILABLE REMOTE_when the session's model is not on the other side's list.RELAY_ UNKNOWN_ MODEL remotecarries the relay's own code, for exampleCode CLAUDE_.API_ CAP_ REACHED - A relay from before 0.9.18 cannot switch: the call is refused with
REMOTE_, and nothing is sent.RELAY_ UNSUPPORTED - Without
provider, the session keeps its billing. Asendto a session on the key while the cap is reached is refused, and nothing is queued: send withprovider: "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_, mode, model and effort as send does, and:
| Parameter | Type | Default | |
|---|---|---|---|
provider |
github | openai | claude | cursor | grok | claude- | deepseek Added in 0.9.17 | claude- 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_ 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-. |
repo |
owner/ or a GitHub URL |
only with claude-, and required there: the repository the cloud session clones |
|
branch |
branch name | the repository's default branch | only with claude- |
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_ 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_ 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.).
Nothing is created when:
- the target has no group by that id or name. The call is refused with
REMOTE_, andRELAY_ UNKNOWN_ GROUP errorandgroupslist the groups there (up to 20,groupswhen there are more):Total 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
groupanswersREMOTE_(RELAY_ UNSUPPORTED The target relay doesn't support groups on create_); one that does not answer or refuses the token answers with its usual code.session yet: …
When the session was created but did not land in the group asked for, the
result has group, its summary ends with (no group: …), and the
note asks the agent to tell you. You can then move the session by hand.
create_ takes an existing group only. To put the session into a new
group, the agent calls update_ 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_ |
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_ |
true | false | whether a Claude session thinks on its next turns (thinking) |
thinking_ |
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. may 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 group. 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_ 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_; a conversation with no running turn to ask in
gets REMOTE_. 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_ 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_.
Which models are accepted Added in 0.9.13 #
A model in send or create_ is checked against the list New
Chat offers for the provider on the target relay, the same list
relay_ shows, before any approval card. A model that is
not on it is refused with REMOTE_, 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-, not Claude's
claude-. Copilot refuses another provider's id before anything is created: with COPILOT_ and the close match, or, in create_, with the code of the one other provider that lists the id, such as CLAUDE_. 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- |
gpt- 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
model 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
model 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_ did not get in time.
| Parameter | Type | Default | |
|---|---|---|---|
session |
id | required | |
message_ |
id | required | the message_ that send or create_ returned |
wait_ |
0–3600 | 120 |
{ "action": "wait", "relay": "build-box", "session": "3f9c…", "message_id": "b41e…", "wait_seconds": 300 }
Waiting and what comes back #
send, create_ and wait wait for the reply for as long as the
agent asks in wait_, 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_ 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_ |
The other agent asked a question. pending 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_, and open, 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, required | the id from pending |
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_ refuses
it with REMOTE_ 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_ and task_, with subagent_ |
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_ reply or a task's description and summary must match; other events pass |
|
stalled_ |
1–1440 | 15 | quiet minutes while work runs before stalled |
heartbeat_ |
1–1440 | none | adds a heartbeat this often while a watched turn or task runs, so the watcher can tell its watching works |
digest_ |
1–1440 | none | Added in 0.9.16 gathers the subscription's events into one message every N minutes. See Fewer wake-ups |
quiet_ |
{"from":"HH: |
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_, question_ and error are never held
back by any of them; every other event, task_, stalled and
all_ included, is.
{
"action": "subscribe",
"sessions": ["all"],
"exclude": ["77aa…"],
"digest_minutes": 30,
"quiet_hours": { "from": "23:00", "to": "06:00", "tz": "Europe/Berlin" }
}
digest_. 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 saysminutes Digest of 4 events (subscription sub_. A digest of a single event reads as that event.… every 30 min; questions and errors come at once): quiet_. A daily window of local time in an IANA time zone.hours fromandtoare times of day (HH:,MM 00:to00 23:) 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 line59 Held during quiet hours (23:No heartbeat is made inside it. An event a digest would deliver inside the window moves to its end.00–06: 00 Europe/ Berlin): 3 events, delivered now that the window has ended; questions and errors came at once. exclude. A subscription toallsessions ignores these sessions, also forall_,idle stalledand 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_, quiet_, or quiet_. A quiet_ 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_ and gets the finished reply back from that
call or a later wait, that turn's turn_ does not wake it a second time;
while it still waits, the turn_ is held. The turn's errors and questions
still come. A prompt it did not wait for (wait_ and no wait)
wakes it with turn_ as before, and so does a turn_ that ends a
once subscription.
Events #
| Event | Fires when | Carries |
|---|---|---|
question_ |
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_ |
question_ |
Added in 0.9.16 you answered or dismissed an ask. Not named in events: a subscription that takes question_ takes it too, and the conversation that asked always gets it, without a subscription |
question_, 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_ |
a session replied | the start of the reply |
task_ |
a background agent or shell task started. Only when named in events. |
the task's description |
task_ |
a background agent or shell task finished. Only when named in events. |
description, outcome, summary |
subagent_ |
Added in 0.9.13 a background sub-agent finished. Its line in the message says task_ |
description, outcome, summary |
task_ |
a background agent or shell task failed | description, outcome, summary |
stalled |
a session went quiet for stalled_ while work still runs |
the quiet minutes |
all_ |
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_, error,
turn_, subagent_, task_, stalled and all_. 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_ in place of subagent_, and task_ 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_ 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_andasked errorstill come at once (Added in 0.9.16 andquestion_). When this digest and a subscription'sanswered digest_meet, oneminutes Digest ofline names both. - Added in 0.9.16 A subscription's
digest_andminutes quiet_hold its other events longer.hours
| 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_ and a link_ 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_, stalled and the heartbeat. Replayed events keep to
the digest and quiet hours like any other; the link_ 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_, 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 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,
stalled and heartbeat, Added in 0.9.16 and digest,
quiet and exclude when it has them. An active subscription also says
how many of its events its digest or quiet hours hold right now (held)
and when the first of them is due (next, 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_.
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- |
when to queue it; with every_ the first time. Not in the past, at most 366 days ahead. A time without a zone is refused |
|
every_ |
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_ |
end after the first firing. false needs every_ |
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_ 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 (
skippedin the list).Count - 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_ shows held and held.
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 end (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), set when another
conversation set it, title, the start of its text, at, every,
once, next, last, fired, skipped when it
skipped any, and held with held while it is held.
unschedule #
Ends one schedule of the conversation: one that fires into it or that it set.
| Parameter | Type | |
|---|---|---|
schedule_ |
id, required | the id schedule or list_ 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_.
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_ 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_, with question_, 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_ refuses an ask.
Gating.
- An ask about the agent's own conversation,
list_andquestions withdrawneed 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_. 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_ 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, 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_ page |
20 by default, 100 at most |
read_ messages |
10 by default, 50 at most |
read_ text |
12,000 characters by default, 50,000 at most |
read_ |
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_ |
a parameter is missing or wrong; error says which |
REMOTE_ |
no relay by that name, or the name fits more than one |
REMOTE_ |
the paired relay is not unlocked in this conversation: mention it |
REMOTE_ |
agent sessions are switched off on this relay |
REMOTE_ |
the relay's Agents may level does not allow this action |
REMOTE_ |
the other relay does not accept prompts from other relays' agents |
REMOTE_ |
you chose Deny on the approval card |
REMOTE_ |
an approval was needed, but the calling session had no turn running to ask it in |
REMOTE_ |
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_ |
Added in 0.9.16 update_ on a session the conversation did not start, and you chose Deny (update_) |
REMOTE_ |
4 sessions this conversation's agent started are already at work |
REMOTE_ |
the agent aimed the action at its own conversation |
REMOTE_ |
the prompt has crossed two relays and the next one is not unlocked by you |
REMOTE_ |
too many calls; the result says when to try again |
REMOTE_ |
send with if_ and the session is busy |
REMOTE_ |
no such session, message or question |
REMOTE_ |
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 remote |
REMOTE_ |
Added in 0.9.16 create_ 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_ |
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_ |
Added in 0.9.16 20 asks are already pending for this conversation, or for that session (Questions that don't block) |
REMOTE_ |
Added in 0.9.16 the session already has 20 schedules firing into it, or the conversation has set 20 (Schedules) |
REMOTE_ |
the conversation already holds as many subscriptions as it may |
REMOTE_ |
the other relay does not answer |
REMOTE_ |
the other relay refused the stored token |
REMOTE_ |
the other relay runs an older OAR that lacks this action |
REMOTE_ |
the other relay answered with this HTTP status |
REMOTE_ |
something went wrong inside the relay |