@oar-sh/oar 0.9.4 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/web-relay/polling/polling-loop.mjs +12 -32
- package/.github/extensions/web-relay/skills/question-bridge.mjs +14 -39
- package/CHANGELOG.md +199 -0
- package/README.md +35 -6
- package/package.json +2 -1
- package/server/claude-worker/claude-remote-relay-tool.mjs +1 -0
- package/server/claude-worker/claude-session-process.mjs +411 -19
- package/server/claude-worker/claude-session-worker.mjs +5 -0
- package/server/claude-worker/claude-turn-failure.mjs +92 -0
- package/server/claude-worker/claude-turn-publisher.mjs +52 -3
- package/server/claude-worker/sdk-message-normalizer.mjs +13 -1
- package/server/copilot-worker/copilot-model-switch.mjs +29 -3
- package/server/copilot-worker/copilot-question-bridge.mjs +2 -4
- package/server/copilot-worker/copilot-runtime-tree.mjs +91 -0
- package/server/copilot-worker/copilot-sdk-adapter.mjs +48 -8
- package/server/copilot-worker/copilot-sdk-session-process.mjs +897 -166
- package/server/copilot-worker/copilot-sdk-session-worker.mjs +5 -0
- package/server/cursor-worker/cursor-sdk-adapter.mjs +80 -13
- package/server/cursor-worker/cursor-session-worker.mjs +5 -0
- package/server/cursor-worker/cursor-turn-runner.mjs +5 -2
- package/server/db-schema.mjs +7 -0
- package/server/grok-worker/acp-client.mjs +68 -12
- package/server/grok-worker/grok-sdk-adapter.mjs +4 -0
- package/server/grok-worker/grok-session-worker.mjs +5 -0
- package/server/grok-worker/grok-turn-runner.mjs +6 -2
- package/server/public/app/api-client.js +17 -2
- package/server/public/app/bootstrap.js +3 -0
- package/server/public/app/composer-control-state.mjs +4 -3
- package/server/public/app/conversation-view.js +97 -4
- package/server/public/app/journal-view.js +6 -0
- package/server/public/app/markdown-strikethrough.mjs +31 -0
- package/server/public/app/relay-board-inline.mjs +70 -0
- package/server/public/app/relay-board-view.js +157 -61
- package/server/public/app/relay-error-ctas.mjs +12 -3
- package/server/public/app/socket-handlers.js +9 -0
- package/server/public/app/steered-turn-merge.mjs +10 -1
- package/server/public/app/store.js +4 -1
- package/server/public/app/subagent-fold.mjs +61 -0
- package/server/public/app/usage-limit-ui.js +225 -0
- package/server/public/index.html +97 -4
- package/server/repositories/message-repository.mjs +10 -1
- package/server/repositories/question-repository.mjs +1 -0
- package/server/repositories/remote-relay-repository.mjs +30 -0
- package/server/routes/messages-routes.mjs +93 -17
- package/server/routes/relay-board-routes.mjs +56 -2
- package/server/routes/sessions-routes.mjs +40 -2
- package/server/server-runtime.mjs +33 -2
- package/server/services/preview-proxy-server.mjs +64 -36
- package/server/services/process-list-service.mjs +122 -0
- package/server/services/relay-console-log.mjs +199 -0
- package/server/services/remote-relay-caller-context.mjs +45 -20
- package/server/services/remote-relay-dispatcher.mjs +328 -21
- package/server/services/remote-relay-inbound.mjs +35 -0
- package/server/services/session-worker-launch-service.mjs +17 -2
- package/server/services/session-worker-process-service.mjs +85 -32
- package/server/services/session-worker-registry-service.mjs +1 -1
- package/server/services/session-worker-supervisor-service.mjs +33 -1
- package/server/services/usage-limit-pause-service.mjs +441 -0
- package/shared/ask-user-bridge.mjs +14 -39
- package/shared/claude-usage-limit.mjs +257 -0
- package/shared/control-poller.mjs +69 -20
- package/shared/failure-note-text.mjs +38 -0
- package/shared/question-wait.mjs +108 -0
- package/shared/remote-relay-contract.mjs +33 -2
- package/shared/steer-settle-failure.mjs +22 -0
- package/shared/steer-settle-markers.mjs +24 -0
- package/shared/worker-runtime/process-tree.mjs +483 -0
- package/shared/worker-runtime/relay-errors.mjs +31 -0
- package/shared/worker-runtime/send-and-wait-errors.mjs +4 -4
- package/shared/worker-runtime/turn-liveness.mjs +249 -0
- package/shared/worker-runtime/worker-log-file.mjs +97 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "../runtime/send-and-wait-errors.mjs";
|
|
7
7
|
import { getActiveSession } from "../runtime/session-registry.mjs";
|
|
8
8
|
import { DEFAULT_QUESTION_TIMEOUT_MS } from "../../../../shared/question-timeout.mjs";
|
|
9
|
-
import {
|
|
9
|
+
import { waitForRelayQuestion } from "../../../../shared/question-wait.mjs";
|
|
10
10
|
import { EMPTY_TURN_COMPLETION_NOTE } from "../../../../shared/empty-turn-completion.mjs";
|
|
11
11
|
import { stripPromptContextPrefix } from "../skills/prompt-context.mjs";
|
|
12
12
|
|
|
@@ -473,37 +473,17 @@ export function createPollingLoop({
|
|
|
473
473
|
return null;
|
|
474
474
|
};
|
|
475
475
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
timedOut: false,
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
if (question.status === "timed_out" || question.status === "cancelled") {
|
|
491
|
-
return {
|
|
492
|
-
answer: QUESTION_TIMEOUT_CONTINUATION_TEXT,
|
|
493
|
-
structuredAnswer: null,
|
|
494
|
-
timedOut: true,
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
if (Date.now() - started >= timeoutMs) {
|
|
498
|
-
await api("POST", `/api/relay-question/${questionId}/timeout`, {}).catch(() => {});
|
|
499
|
-
return {
|
|
500
|
-
answer: QUESTION_TIMEOUT_CONTINUATION_TEXT,
|
|
501
|
-
structuredAnswer: null,
|
|
502
|
-
timedOut: true,
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
await sleep(pollIntervalMs);
|
|
506
|
-
}
|
|
476
|
+
// The wait survives a relay that is briefly not there
|
|
477
|
+
// (shared/question-wait.mjs).
|
|
478
|
+
function waitForRelayQuestionAnswer(questionId, timeoutMs = DEFAULT_QUESTION_TIMEOUT_MS, pollIntervalMs = 1500) {
|
|
479
|
+
return waitForRelayQuestion({
|
|
480
|
+
api,
|
|
481
|
+
questionId,
|
|
482
|
+
deadlineMs: timeoutMs,
|
|
483
|
+
pollMs: pollIntervalMs,
|
|
484
|
+
sleep,
|
|
485
|
+
dbg,
|
|
486
|
+
});
|
|
507
487
|
}
|
|
508
488
|
|
|
509
489
|
async function processPendingSdkSessionDeletes() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getActiveSession } from '../runtime/session-registry.mjs';
|
|
2
|
-
import {
|
|
2
|
+
import { createRelayQuestion, waitForRelayQuestion } from "../../../../shared/question-wait.mjs";
|
|
3
3
|
import { extractRequestedSchema } from "../../../../shared/question-schema.mjs";
|
|
4
4
|
|
|
5
5
|
export function createQuestionBridge({
|
|
@@ -49,39 +49,18 @@ export function createQuestionBridge({
|
|
|
49
49
|
return Number.isFinite(fallback) && fallback >= 0 ? fallback : 0;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
: null,
|
|
65
|
-
timedOut: false,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
if (question.status === "timed_out" || question.status === "cancelled") {
|
|
69
|
-
return {
|
|
70
|
-
answer: QUESTION_TIMEOUT_CONTINUATION_TEXT,
|
|
71
|
-
structuredAnswer: null,
|
|
72
|
-
timedOut: true,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
if (Date.now() - started >= effectiveTimeoutMs) {
|
|
76
|
-
await api("POST", `/api/relay-question/${questionId}/timeout`, {}).catch(() => {});
|
|
77
|
-
return {
|
|
78
|
-
answer: QUESTION_TIMEOUT_CONTINUATION_TEXT,
|
|
79
|
-
structuredAnswer: null,
|
|
80
|
-
timedOut: true,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
await sleep(questionPollMs);
|
|
84
|
-
}
|
|
52
|
+
// The wait survives a relay that is briefly not there
|
|
53
|
+
// (shared/question-wait.mjs).
|
|
54
|
+
function waitForRelayQuestionAnswer(questionId, timeoutMs = null) {
|
|
55
|
+
return waitForRelayQuestion({
|
|
56
|
+
api,
|
|
57
|
+
questionId,
|
|
58
|
+
deadlineMs: resolveQuestionWaitTimeoutMs(timeoutMs),
|
|
59
|
+
pollMs: questionPollMs,
|
|
60
|
+
sleep,
|
|
61
|
+
trimAnswer: false,
|
|
62
|
+
dbg,
|
|
63
|
+
});
|
|
85
64
|
}
|
|
86
65
|
|
|
87
66
|
async function forwardRelayQuestion(request) {
|
|
@@ -112,11 +91,7 @@ export function createQuestionBridge({
|
|
|
112
91
|
request: serializeRequest(request),
|
|
113
92
|
};
|
|
114
93
|
|
|
115
|
-
const
|
|
116
|
-
const questionId = created?.question?.id;
|
|
117
|
-
if (!questionId) {
|
|
118
|
-
throw new Error("Relay question could not be created");
|
|
119
|
-
}
|
|
94
|
+
const questionId = await createRelayQuestion({ api, payload: questionPayload, sleep, dbg });
|
|
120
95
|
|
|
121
96
|
dbg(
|
|
122
97
|
"relay question created",
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,205 @@ All notable changes to OAR are documented here. The format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.9.6] — 2026-09-29
|
|
9
|
+
|
|
10
|
+
Highlights: a Claude turn pauses at the usage limit and carries on after the
|
|
11
|
+
reset, a command that prints nothing no longer fails its turn, and agents on
|
|
12
|
+
paired relays can work with each other for a whole session.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- The relay keeps a copy of its console in `relay-console.log` in its log
|
|
17
|
+
folder (`server/logs` unless you moved it), with a time stamp per line. The
|
|
18
|
+
file is rotated at 5 MB and three older copies are kept. The relay's token is
|
|
19
|
+
never written to it. Set `OAR_NO_CONSOLE_LOG=1` to switch it off.
|
|
20
|
+
- **Claude usage limit:** a Claude turn that runs into the subscription's
|
|
21
|
+
usage limit is paused instead of failed. Your message closes with a note,
|
|
22
|
+
the relay queues a message of its own ("continue where you left off") and
|
|
23
|
+
sends it a minute after the limit resets. A banner above the composer says
|
|
24
|
+
when, with **Resume now** and **Cancel**. The pause survives a relay
|
|
25
|
+
restart. A reset more than six hours away (a weekly limit) waits for you to
|
|
26
|
+
resume it.
|
|
27
|
+
- A banner warns in Claude conversations when the usage passes 90 % of a
|
|
28
|
+
limit, with the time of the reset, and can be hidden until the next one.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Remote relays:** agents on paired relays can now work with each other for
|
|
33
|
+
a whole session. A relay you mentioned stays in reach whatever prompts arrive
|
|
34
|
+
later, and a relay whose agent wrote to a conversation is open to it, so the
|
|
35
|
+
agent that was asked can answer, ask back or report later. Until now a prompt
|
|
36
|
+
from another relay's agent took the tool away for the rest of the turn, also
|
|
37
|
+
in a turn you had started yourself. A prompt that crossed two relays is still
|
|
38
|
+
not passed on to a third relay you did not unlock; reading and waiting are no
|
|
39
|
+
longer limited. After 30 prompts to other relays' agents without a message
|
|
40
|
+
from you, the next one asks you first.
|
|
41
|
+
- Subagents are folded to their header inside the running turn, so a turn with
|
|
42
|
+
many of them stays short to scroll. The header says how many steps the
|
|
43
|
+
subagent has taken and shows the latest one, as far as there is room. A tap
|
|
44
|
+
on the header unfolds a subagent and folds it again; what you chose by hand
|
|
45
|
+
stays. The Stop button of a finished subagent is gone.
|
|
46
|
+
- **Copilot, Cursor, Grok:** how long a turn may stay quiet now depends on what
|
|
47
|
+
the agent is doing: 2 minutes with nothing in flight, 5 minutes during a model
|
|
48
|
+
request, 30 minutes while a tool runs. Until now every silence of 2 minutes
|
|
49
|
+
failed the turn, including a command that simply printed nothing. The failure
|
|
50
|
+
note says how long the silence was and what was running. The three windows can
|
|
51
|
+
be set with `OAR_TURN_STALL_IDLE_MS`, `OAR_TURN_STALL_MODEL_MS` and
|
|
52
|
+
`OAR_TURN_STALL_TOOL_MS`.
|
|
53
|
+
- **Copilot:** a turn kept past 2 minutes of silence is checked: a runtime that
|
|
54
|
+
no longer answers fails the turn at once. The watchdog also covers the start of
|
|
55
|
+
a session, where a runtime that did not answer used to hold the message without
|
|
56
|
+
any failure, and a Stop during that start now takes effect at once.
|
|
57
|
+
- **Cursor:** a stalled run is cancelled, so the next message does not wait for
|
|
58
|
+
it.
|
|
59
|
+
- A plan board is part of the reply it belongs to: its content and its buttons
|
|
60
|
+
are shown inside the agent's reply bubble (inside the running turn's bubble
|
|
61
|
+
until the reply is there) instead of on a card of their own at the end of the
|
|
62
|
+
transcript. When the reply already is the plan, the plan is shown once, with
|
|
63
|
+
the buttons below it. After you chose, the plan stays and the bubble says
|
|
64
|
+
what was chosen.
|
|
65
|
+
- The choice on a plan board also sets the mode of the session: after
|
|
66
|
+
"Implement in autopilot" the composer says Autopilot, after "Stop here and
|
|
67
|
+
prompt myself" it says Agent, and the next message carries the work on. The
|
|
68
|
+
session used to stay in Plan, so the next message planned again. "Stop here"
|
|
69
|
+
leaves the mode as it is.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- A session worker that was idle and whose process has ended (stopped from
|
|
74
|
+
outside, or crashed between turns) is no longer listed as ready with its old
|
|
75
|
+
process id. The next message started a new worker before as well; the list
|
|
76
|
+
now says what is there, about a minute and a half after the process ended.
|
|
77
|
+
- A reply that quotes or describes a failure note (an agent reporting on a
|
|
78
|
+
failed test, for example) is no longer taken for a failure itself. The relay
|
|
79
|
+
cut such a reply off at the quoted error code, put its own advice behind it
|
|
80
|
+
and stored the turn as failed. Where a failure is known from its text alone,
|
|
81
|
+
that text is now kept whole.
|
|
82
|
+
- A single `~` in a reply ("about", as in "~35 s … ~7 min") no longer strikes
|
|
83
|
+
out the text up to the next one. Strikethrough needs two tildes on each side:
|
|
84
|
+
`~~text~~`. The page now names the version of its Markdown library (15.0.12,
|
|
85
|
+
the one it was served until now) instead of taking whatever the CDN hands out.
|
|
86
|
+
- **Copilot:** a command the agent had started no longer runs on after its
|
|
87
|
+
runtime is gone. When the runtime died or was killed in the middle of a turn,
|
|
88
|
+
the turn failed as it should, but the command kept running with nobody left
|
|
89
|
+
to read its result. The relay now stops what such a runtime leaves behind
|
|
90
|
+
(first gently, then by force), and the failure note says that a command that
|
|
91
|
+
was still running is being stopped, as far as that is known at that moment:
|
|
92
|
+
the note comes at once and does not wait for the list of processes. A command
|
|
93
|
+
the agent started to outlive the session (`detach`) is left alone, and so are
|
|
94
|
+
the commands of a runtime that shuts down in good order.
|
|
95
|
+
- **Grok:** a quiet-turn window that is set for the model request or for a tool
|
|
96
|
+
is kept when the shared watchdog is switched off by the environment, and the
|
|
97
|
+
watchdog looks often enough for the shortest window that is set.
|
|
98
|
+
- **Windows:** stopping or deleting a session could end the worker of another
|
|
99
|
+
session as well. Windows hands the process id of an ended process out again,
|
|
100
|
+
and a worker whose creator was long gone could so look like a child of the
|
|
101
|
+
session that was being stopped. A process older than its parent is no longer
|
|
102
|
+
taken for its child, and the worker of another session never is.
|
|
103
|
+
- **Copilot:** a message sent while a command is running no longer cuts the
|
|
104
|
+
command short. It waits until the running tool call has ended and is steered
|
|
105
|
+
in then; the composer reads "Queue" for as long. Sent at once, the message
|
|
106
|
+
made the runtime push the command to the background: the turn ended early
|
|
107
|
+
with the answer to the new message alone, or failed with "No response was
|
|
108
|
+
returned", and the command's result arrived later in a turn of its own.
|
|
109
|
+
- A plan board's action ("Implement in autopilot" and the like) queues its
|
|
110
|
+
follow-up again. It failed with an error and left the board marked as acted
|
|
111
|
+
on, without the message that carries the plan out.
|
|
112
|
+
- A finished subagent no longer turns back to "Running" in the running turn
|
|
113
|
+
when another line of activity arrives or the page reloads.
|
|
114
|
+
- **Previews:** the first page load after a dev server was restarted no longer
|
|
115
|
+
ends in "The upstream connection failed: ECONNRESET". The preview lane keeps
|
|
116
|
+
its connections to the dev server, and the one it had kept was closed by the
|
|
117
|
+
restart. A request without a body is now sent once more on a connection of
|
|
118
|
+
its own: it reaches the restarted server, or the page says that nothing is
|
|
119
|
+
listening on the port.
|
|
120
|
+
- Links in the file viewer and in plan boards have a colour that can be read in
|
|
121
|
+
dark mode; they used the browser's default blue.
|
|
122
|
+
- A question or approval card no longer answers itself when the relay is
|
|
123
|
+
briefly unreachable, for example while it restarts. One failed check used to
|
|
124
|
+
end the wait: the agent was told to use its own judgement, or the approval
|
|
125
|
+
was refused, while the card stayed on screen. The wait now lasts through the
|
|
126
|
+
outage, for every provider.
|
|
127
|
+
- **Copilot:** a turn that fails is reported at once. The failure used to wait
|
|
128
|
+
for the runtime to finish stopping, which took three minutes on a runtime that
|
|
129
|
+
had stopped answering; stopping it is now bounded, and the next message waits
|
|
130
|
+
for it before a new runtime starts.
|
|
131
|
+
- **Copilot:** a failed turn keeps what the agent had written, above the failure
|
|
132
|
+
note. The note says how many tool calls ran and that their changes are still in
|
|
133
|
+
place, and it no longer advises restarting the relay. A message steered into
|
|
134
|
+
the failed turn gets a short marker with Resend instead of a second copy of
|
|
135
|
+
the failure.
|
|
136
|
+
- **Copilot:** a slow relay no longer makes a busy runtime look silent.
|
|
137
|
+
- **Copilot:** a reply the relay could not take (it was restarting, the
|
|
138
|
+
connection was reset) is offered again for up to five minutes. One failed
|
|
139
|
+
attempt used to put the message back in the queue, which threw the finished
|
|
140
|
+
answer away and ran the prompt a second time. A reply that cannot be saved at
|
|
141
|
+
all ends as a failure that says so, and nothing is run twice.
|
|
142
|
+
- **Copilot:** what the agent does by itself after a background task (a
|
|
143
|
+
continuation) is kept while the relay restarts. Its row was given up after
|
|
144
|
+
about a second, and the whole reply with it.
|
|
145
|
+
- **Copilot:** a relay that accepts live output and does not answer no longer
|
|
146
|
+
holds the turn: a live update waits 10 seconds at most, and after one that
|
|
147
|
+
ran out, live updates are skipped for 30 seconds. Tool lines skipped then are
|
|
148
|
+
missing from the transcript; the reply is not affected.
|
|
149
|
+
- **Copilot:** Stop works on a turn that is stuck. A runtime that does not
|
|
150
|
+
answer the Stop within 10 seconds is ended with the turn, and a turn the user
|
|
151
|
+
stopped ends as stopped even when the runtime died or went silent after it.
|
|
152
|
+
- **Copilot:** a runtime that exits, or stops answering, while the agent works
|
|
153
|
+
by itself after a background task is cleared away; the next message used to
|
|
154
|
+
fail on it. A lost connection during a model change is reported as that, not
|
|
155
|
+
as "pick another model".
|
|
156
|
+
- **Copilot:** a failed turn tells its failure once. A message sent into the
|
|
157
|
+
turn that the agent had not started on gets the short marker with Resend,
|
|
158
|
+
like one it had taken in.
|
|
159
|
+
- **Copilot:** background agents and shells are no longer closed with the
|
|
160
|
+
runtime because its list of them could not be read.
|
|
161
|
+
- **Stop button:** Stop keeps working for a message that waits on a question
|
|
162
|
+
card while the agent starts and finishes other work, and for a second message
|
|
163
|
+
stopped while the same turn goes on. It used to do nothing in both cases.
|
|
164
|
+
- **Copilot:** a turn the runtime itself ended with an error no longer tells
|
|
165
|
+
you to restart the relay. The worker starts a fresh runtime by itself; a
|
|
166
|
+
restart repairs nothing and costs every other running session.
|
|
167
|
+
- The note under a failed turn no longer advises restarting the relay: a restart
|
|
168
|
+
repairs nothing there and interrupts every other running session. It now says
|
|
169
|
+
to send the message again and to include the error code in a report.
|
|
170
|
+
- **Claude:** a turn the Claude account refuses (billing, an expired sign-in,
|
|
171
|
+
access the organisation has switched off) says to check the account or to sign
|
|
172
|
+
in again in Settings → Providers → Claude, with a button that opens the panel,
|
|
173
|
+
instead of "Retry the message". Its error code names the failure, for example
|
|
174
|
+
`relay.claude-billing-error`; it used to read `relay.claude-success`.
|
|
175
|
+
|
|
176
|
+
## [0.9.5] — 2026-09-27
|
|
177
|
+
|
|
178
|
+
Highlights: a Claude reply on the wrong row no longer blocks the conversation,
|
|
179
|
+
workers on Windows write a log, and agents can set the effort of a session on
|
|
180
|
+
another relay.
|
|
181
|
+
|
|
182
|
+
### Added
|
|
183
|
+
|
|
184
|
+
- **Remote relays**: an agent can set the reasoning effort of a session it starts or
|
|
185
|
+
prompts on another relay (`effort` on `create_session` and `send`), and `relay_info`
|
|
186
|
+
lists the efforts each model there takes.
|
|
187
|
+
|
|
188
|
+
### Changed
|
|
189
|
+
|
|
190
|
+
- Session workers on Windows now write the same `worker-<session>.log` as on
|
|
191
|
+
Linux, in the relay's log directory; the console window keeps showing the
|
|
192
|
+
output.
|
|
193
|
+
|
|
194
|
+
### Fixed
|
|
195
|
+
|
|
196
|
+
- **Remote relays:** waiting for a remote agent's reply no longer stops at an
|
|
197
|
+
interim answer ("still waiting for …") while the agent goes on in a turn of
|
|
198
|
+
its own without a background task, for example while its subagents work.
|
|
199
|
+
- A Claude reply published as a *background continuation* instead of under
|
|
200
|
+
your message no longer blocks the conversation for up to 5 minutes: after
|
|
201
|
+
about 45 seconds of silence your message closes with a small muted note
|
|
202
|
+
naming that reply, and the next message runs normally. The note sends no
|
|
203
|
+
second notification, and an agent on a paired relay receives the reply
|
|
204
|
+
itself, not the note. A stray re-init of the Claude CLI between turns no
|
|
205
|
+
longer sends the reply there in the first place.
|
|
206
|
+
|
|
8
207
|
## [0.9.4] — 2026-09-27
|
|
9
208
|
|
|
10
209
|
Highlights: remote relays (pair OAR relays and let an agent on one work on the
|
package/README.md
CHANGED
|
@@ -78,7 +78,7 @@ curl -fsSL oar.sh/install | sh
|
|
|
78
78
|
The script checks for Node.js 22.13 or newer, runs `npm install -g @oar-sh/oar`, and then hands off to `oar setup` (with `--defaults` when there is no terminal to ask questions in). It refuses to run as root, because OAR installs per user. `OAR_VERSION=x.y.z` pins a version, `OAR_CHANNEL=beta` follows the beta channel, and `OAR_DRY_RUN=1` only prints what it would do:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
curl -fsSL oar.sh/install | OAR_VERSION=0.9.
|
|
81
|
+
curl -fsSL oar.sh/install | OAR_VERSION=0.9.6 sh
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Read it before you run it: [oar.sh/install](https://oar.sh/install).
|
|
@@ -89,7 +89,7 @@ Read it before you run it: [oar.sh/install](https://oar.sh/install).
|
|
|
89
89
|
irm oar.sh/install.ps1 | iex
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
The PowerShell installer (PowerShell 5 or newer) does the same: it checks Node.js, runs `npm install -g @oar-sh/oar`, then `oar setup`. It reads the same `OAR_VERSION`, `OAR_CHANNEL`, and `OAR_DRY_RUN` variables, for example `$env:OAR_VERSION = '0.9.
|
|
92
|
+
The PowerShell installer (PowerShell 5 or newer) does the same: it checks Node.js, runs `npm install -g @oar-sh/oar`, then `oar setup`. It reads the same `OAR_VERSION`, `OAR_CHANNEL`, and `OAR_DRY_RUN` variables, for example `$env:OAR_VERSION = '0.9.6'` before the command.
|
|
93
93
|
|
|
94
94
|
### npm
|
|
95
95
|
|
|
@@ -326,6 +326,22 @@ Use **🤗 Select Models** to choose which variants show up in the composer, the
|
|
|
326
326
|
|
|
327
327
|
## Highlights
|
|
328
328
|
|
|
329
|
+
### New in 0.9.6
|
|
330
|
+
|
|
331
|
+
- **Claude pauses at the usage limit**: a turn that runs into the subscription's usage limit is paused instead of failed. A banner above the composer says when it carries on, with **Resume now** and **Cancel**; after the reset the relay sends a message of its own that quotes what was refused. The pause survives a relay restart, and a reset more than six hours away waits for you. A second banner warns when the usage passes 90 % of a limit.
|
|
332
|
+
- **A quiet command no longer fails its turn** (Copilot, Cursor, Grok): how long a turn may stay silent depends on what the agent is doing: 2 minutes with nothing in flight, 5 minutes during a model request, 30 minutes while a tool runs.
|
|
333
|
+
- **Copilot turns that fail, fail well**: the failure shows at once, what the agent had written stays above the note, Stop works on a stuck turn, a finished reply survives a relay restart, and the commands of a runtime that died are stopped. A message you send while a command runs waits for the command instead of cutting it short.
|
|
334
|
+
- **Agents on paired relays can answer each other**: a relay you mentioned stays in reach for the whole session, and an agent that was asked from another relay can answer, ask back or report later. See [Remote relays](#remote-relays).
|
|
335
|
+
- **Less scrolling in a running turn**: subagents are folded to a one-line header that says what they are doing, and a plan board sits inside the reply it belongs to; its choice also sets the session's mode.
|
|
336
|
+
- **Failure notes that help**: no more advice to restart the relay, and a Claude account problem (billing, sign-in, access) says what to check. The relay keeps its console in `relay-console.log`.
|
|
337
|
+
|
|
338
|
+
### New in 0.9.5
|
|
339
|
+
|
|
340
|
+
- **A misplaced Claude reply no longer blocks the conversation**: when a reply is published as a *background continuation* instead of under your message, the message closes after about 45 seconds of silence with a small note naming that reply, and the next message runs normally. It used to hold every later message for up to 5 minutes.
|
|
341
|
+
- **Effort for remote sessions**: an agent can set the reasoning effort of a session it starts or prompts on another relay; a new session takes the calling session's effort when the model there supports it. See [Remote relays](#remote-relays).
|
|
342
|
+
- **Waiting on another relay is steadier**: the wait no longer stops at an interim answer while the remote agent goes on in a turn of its own, for example while its subagents work.
|
|
343
|
+
- **Worker logs on Windows**: session workers now write the same `worker-<session>.log` as on Linux, in the relay's log directory, while the console window keeps showing the output.
|
|
344
|
+
|
|
329
345
|
### New in 0.9.4
|
|
330
346
|
|
|
331
347
|
- **Remote relays**: pair two OAR relays in **Settings → Relays** by pasting the other relay's web address; the pairing works both ways. An agent in any session can then work on the other relay when you ask it to: list and read its sessions, prompt one, start a new one, wait for the reply, and pass a question the remote agent asks back to you. See [Remote relays](#remote-relays).
|
|
@@ -442,7 +458,9 @@ Use **➡️ Share conversation** in the `⋯` menu to publish a read-only link.
|
|
|
442
458
|
|
|
443
459
|
### The conversation menu
|
|
444
460
|
|
|
445
|
-
Besides the entries above, the `⋯` menu holds **✍️ Edit conversation title**, **🖥️ Inspect tmux console** (a read-only view of the session's tmux pane), **🤗 Select Models**, **⚙️ Settings**, **🌄 Restart web relay** (queued until the current turn is idle), **💤 Suspend host
|
|
461
|
+
Besides the entries above, the `⋯` menu holds **✍️ Edit conversation title**, **🖥️ Inspect tmux console** (a read-only view of the session's tmux pane), **🤗 Select Models**, **⚙️ Settings**, **🌄 Restart web relay** (queued until the current turn is idle), **💤 Suspend host** (Windows; see below), and **☠️ Kill session** (stops the conversation's worker; an active turn then needs a retry or a new message). Its header shows the queue counts with **🚮 Empty queue**.
|
|
462
|
+
|
|
463
|
+
**💤 Suspend host** puts the machine the relay runs on to sleep, once the work is done. The confirmation lists what is still active. The relay then queues the suspend and fires it after 2 minutes with nothing active: no queued, running or parked turn, no background task of a live worker, and no open GitHub Actions run in the repository of a conversation that was busy since the request (read through `gh`; a repository whose state cannot be read blocks for 15 minutes, then is ignored). With nothing running when you confirm, it fires after a 30-second countdown. While a suspend is queued, every device shows a banner with the blockers or the countdown and a **Cancel** button, and a push notification reports the suspend. A relay restart drops a queued suspend and says so. **Show Suspend host action** in Settings hides the entry per browser.
|
|
446
464
|
|
|
447
465
|
## Settings and configuration reference
|
|
448
466
|
|
|
@@ -539,6 +557,8 @@ The config file is `server/config.json` in a git checkout and `~/.oar/config.jso
|
|
|
539
557
|
| `COPILOT_WEB_RELAY_LOG_DIR` | Log directory |
|
|
540
558
|
| `OAR_STATE_ROOT` | State root of a global install, instead of `~/.oar` / `%APPDATA%\oar` |
|
|
541
559
|
| `OAR_NO_UPDATE_CHECK=1` | Never contact oar.sh, not even for a manual update check |
|
|
560
|
+
| `OAR_HOST_SUSPEND_DRY_RUN=1` | **Suspend host** logs the suspend instead of sleeping the machine |
|
|
561
|
+
| `OAR_MCP_SERVER=0` | Do not attach OAR's MCP server to Grok and the Copilot extension engine; they get the preview instructions as text again, and no `remote_relay` tool |
|
|
542
562
|
| `COPILOT_SDK_PATH` | The Copilot CLI runtime's `copilot-sdk` directory, instead of the newest one in the CLI's package cache |
|
|
543
563
|
| `COPILOT_PKG_DIR` | An additional Copilot CLI package cache to search |
|
|
544
564
|
| `GH_TOKEN`, `GITHUB_TOKEN` | GitHub token for the Copilot usage card, instead of `gh auth token` |
|
|
@@ -666,14 +686,23 @@ locked in a conversation until you mention it in one of your messages: type `@`
|
|
|
666
686
|
from the list, or write its name or its host name. An IP address or `localhost` never counts as a
|
|
667
687
|
host name; a relay whose name is itself an address is unlocked only with a leading `@`. The
|
|
668
688
|
mention unlocks that relay for the rest of
|
|
669
|
-
the conversation
|
|
670
|
-
|
|
689
|
+
the conversation, whatever prompts arrive in it later.
|
|
690
|
+
|
|
691
|
+
**Agents can answer each other.** When an agent on a paired relay writes to a conversation, that
|
|
692
|
+
relay is open to the conversation from then on, within what **Agents may** allows, so the agent
|
|
693
|
+
that was asked can answer, ask back or report later. A prompt from an agent never unlocks any
|
|
694
|
+
other relay. A prompt that already crossed two relays is not passed on to a third one unless you
|
|
695
|
+
unlocked that relay yourself; reading and waiting are never limited. When the agent of a
|
|
696
|
+
conversation has sent 30 prompts to agents on other relays without a message from you, the next
|
|
697
|
+
one asks you first with a question card; **Allow** starts the count again.
|
|
671
698
|
|
|
672
699
|
**What agents can do.** Every provider gets the same `remote_relay` tool (Claude, Cursor and
|
|
673
700
|
Copilot directly; Grok and the Copilot extension engine through OAR's MCP server). Per paired
|
|
674
701
|
relay, **Agents may** limits it to *read only*, *read and prompt* (existing sessions) or *full*
|
|
675
702
|
(also create sessions, answer questions, stop turns, archive). In the *ask* and *plan* relay
|
|
676
|
-
modes, every write action first asks you with a question card.
|
|
703
|
+
modes, every write action first asks you with a question card. For a session it starts, an agent
|
|
704
|
+
can set the provider, model, relay mode and reasoning effort; what it leaves out follows the
|
|
705
|
+
session it works in, as far as the other relay offers it.
|
|
677
706
|
|
|
678
707
|
When an agent prompts a session on another relay, that relay shows a **↗ from …** badge with the
|
|
679
708
|
sending relay, session and model on the message, and marks sessions an agent created in the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oar-sh/oar",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OAR — Open Agent Relay: drive your local coding agents from any browser",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"server/uploads/image_input.md",
|
|
31
31
|
"shared/",
|
|
32
32
|
"!shared/**/*.test.mjs",
|
|
33
|
+
"!shared/**/*-test-harness.mjs",
|
|
33
34
|
"!shared/**/fixtures/",
|
|
34
35
|
".github/extensions/web-relay/",
|
|
35
36
|
"!.github/extensions/web-relay/**/*.test.mjs",
|