@oar-sh/oar 0.9.5 → 0.9.7
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 +340 -0
- package/README.md +196 -26
- package/package.json +2 -1
- package/server/README.md +174 -7
- package/server/claude-cloud-worker/claude-cloud-event-normalizer.mjs +340 -0
- package/server/claude-cloud-worker/claude-cloud-session-process.mjs +1325 -0
- package/server/claude-cloud-worker/claude-cloud-session-worker.mjs +155 -0
- package/server/claude-worker/claude-remote-relay-tool.mjs +2 -0
- package/server/claude-worker/claude-sdk-adapter.mjs +18 -2
- package/server/claude-worker/claude-session-process.mjs +151 -1
- package/server/claude-worker/claude-session-worker.mjs +6 -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/cursor-worker/cursor-sdk-adapter.mjs +80 -13
- package/server/cursor-worker/cursor-turn-runner.mjs +5 -2
- package/server/db-schema.mjs +24 -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-turn-runner.mjs +6 -2
- package/server/migrations/0006-workspace-root-settings.mjs +67 -0
- package/server/migrations/0007-claude-cloud.mjs +82 -0
- package/server/public/app/agent-sessions-settings-ui.js +197 -0
- package/server/public/app/agent-sessions-settings.mjs +134 -0
- package/server/public/app/api-client.js +160 -6
- package/server/public/app/attachments-view.js +20 -1
- package/server/public/app/attribution-control.mjs +46 -0
- package/server/public/app/bootstrap.js +347 -31
- package/server/public/app/claude-cloud-conversation-ui.js +110 -0
- package/server/public/app/claude-cloud-settings-ui.js +374 -0
- package/server/public/app/claude-cloud-ui.mjs +611 -0
- package/server/public/app/composer-control-state.mjs +15 -3
- package/server/public/app/context-menu.mjs +140 -0
- package/server/public/app/context-tier-options.mjs +4 -0
- package/server/public/app/conversation-origin-ui.js +27 -0
- package/server/public/app/conversation-provider-indicator.mjs +22 -4
- package/server/public/app/conversation-view.js +178 -8
- package/server/public/app/cwd-picker.js +12 -0
- package/server/public/app/journal-view.js +704 -28
- package/server/public/app/markdown-strikethrough.mjs +31 -0
- package/server/public/app/math-dollars.mjs +89 -0
- package/server/public/app/model-selector-options.mjs +1 -1
- package/server/public/app/plan-usage-view.mjs +36 -8
- 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 +28 -5
- package/server/public/app/remote-relay-origin-view.mjs +120 -4
- package/server/public/app/router.js +49 -12
- package/server/public/app/settings-modal.js +33 -2
- package/server/public/app/settings-tabs.js +1 -1
- package/server/public/app/socket-handlers.js +60 -0
- 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 +471 -8
- package/server/relay-tools.md +13 -1
- package/server/repositories/message-repository.mjs +10 -1
- package/server/repositories/question-repository.mjs +1 -0
- package/server/repositories/remote-relay-repository.mjs +72 -1
- package/server/repositories/session-repository.mjs +47 -1
- package/server/routes/claude-cloud-routes.mjs +140 -0
- package/server/routes/git-routes.mjs +37 -0
- package/server/routes/messages-routes.mjs +202 -26
- package/server/routes/relay-board-routes.mjs +56 -2
- package/server/routes/remote-relay-routes.mjs +15 -1
- package/server/routes/sessions-routes.mjs +538 -72
- package/server/server-runtime.mjs +291 -27
- package/server/services/claude-account-usage-service.mjs +176 -0
- package/server/services/claude-cloud-repo-service.mjs +408 -0
- package/server/services/claude-cloud-session-service.mjs +326 -0
- package/server/services/claude-cloud-settings-service.mjs +269 -0
- package/server/services/git-remote-service.mjs +115 -0
- package/server/services/plan-usage-claude-cloud.mjs +211 -0
- package/server/services/plan-usage-claude.mjs +202 -40
- package/server/services/plan-usage-contract.mjs +39 -3
- package/server/services/plan-usage-service.mjs +82 -2
- package/server/services/preview-proxy-server.mjs +64 -36
- package/server/services/process-list-service.mjs +122 -0
- package/server/services/provider-reasoning-effort.mjs +3 -0
- package/server/services/relay-console-log.mjs +199 -0
- package/server/services/remote-relay-caller-context.mjs +78 -19
- package/server/services/remote-relay-client.mjs +9 -0
- package/server/services/remote-relay-dispatcher.mjs +451 -39
- package/server/services/remote-relay-inbound.mjs +83 -1
- package/server/services/remote-relay-loopback.mjs +245 -0
- package/server/services/remote-relay-registry-service.mjs +52 -0
- package/server/services/session-worker-launch-service.mjs +100 -15
- package/server/services/session-worker-process-service.mjs +410 -138
- package/server/services/session-worker-registry-service.mjs +1 -1
- package/server/services/session-worker-stop-service.mjs +24 -13
- package/server/services/session-worker-supervisor-service.mjs +89 -2
- package/server/services/usage-limit-pause-service.mjs +443 -0
- package/server/services/worker-launch-note-service.mjs +151 -0
- package/shared/ask-user-bridge.mjs +14 -39
- package/shared/claude-attribution.mjs +117 -0
- package/shared/claude-cloud/api-client.mjs +809 -0
- package/shared/claude-cloud/base-url.mjs +47 -0
- package/shared/claude-cloud/credentials.mjs +221 -0
- package/shared/claude-cloud/repo-url.mjs +111 -0
- 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/provider-routing.mjs +4 -2
- package/shared/question-wait.mjs +108 -0
- package/shared/remote-relay-contract.mjs +167 -22
- package/shared/remote-relay-tool-core.mjs +11 -8
- package/shared/steer-settle-failure.mjs +17 -0
- package/shared/steer-settle-markers.mjs +8 -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
|
@@ -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,346 @@ All notable changes to OAR are documented here. The format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.9.7] — 2026-10-03
|
|
9
|
+
|
|
10
|
+
Highlights: Claude Cloud chats (experimental, off by default) run Claude Code
|
|
11
|
+
in a sandbox at Anthropic on a GitHub repository; conversations can be
|
|
12
|
+
archived and have a context menu in the list; agents can start and use
|
|
13
|
+
sessions on the relay they run on; commits of Claude sessions say "Open
|
|
14
|
+
Agent Relay"; Check Usage opens at once.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Archive conversations, and a context menu in the list.** Every
|
|
19
|
+
conversation row has an **Archive** button beside Delete (and the chat's
|
|
20
|
+
`⋯` menu an **Archive conversation** entry). An archived chat leaves the
|
|
21
|
+
list and its session worker is stopped; the 🗄 toggle at the top of the
|
|
22
|
+
sidebar shows the archived chats, each with **Unarchive**. A Claude Cloud
|
|
23
|
+
chat's cloud session is archived and unarchived at Anthropic with it. A
|
|
24
|
+
message into an archived chat (an agent's, say) brings it back by itself;
|
|
25
|
+
the composer of an archived chat says so instead of sending. A right-click
|
|
26
|
+
on a row — a long press on a phone — opens a menu with Open, Edit title,
|
|
27
|
+
Stop turn, Kill session, Archive/Unarchive and Delete.
|
|
28
|
+
- **Commits made from OAR say so.** Claude sessions now end their commit
|
|
29
|
+
messages with `Co-authored-by: Open Agent Relay (<model>) <no-reply@oar.sh>`
|
|
30
|
+
— the model's name in the parentheses, e.g. `Claude Fable 5.1` — and pull
|
|
31
|
+
request bodies with `🤖 Generated with [Open Agent Relay](https://oar.sh)`,
|
|
32
|
+
instead of Claude Code's own lines. Settings → Providers → Claude has the
|
|
33
|
+
choice: **OAR** (the default), **vanilla** (Claude Code's own attribution)
|
|
34
|
+
or **off** (none); a repo folder can override it from the 🧠 context modal
|
|
35
|
+
of any of its Claude sessions. A change reaches running sessions with their
|
|
36
|
+
next message. Claude Cloud chats follow the same setting: the sandbox gets
|
|
37
|
+
it with the first message and again when it changes, and in OAR mode the
|
|
38
|
+
`Claude-Session` link is left out of the commit as well. Other providers
|
|
39
|
+
are not touched: their tools have no such setting.
|
|
40
|
+
- **Claude Cloud (experimental): chats that run in a sandbox at Anthropic.**
|
|
41
|
+
A new provider, off by default. A Claude Cloud chat runs Claude Code in
|
|
42
|
+
Anthropic's cloud, on its own clone of a GitHub repository instead of on
|
|
43
|
+
the relay host. It is billed to the Claude account the host's Claude CLI
|
|
44
|
+
is logged in to, and its results come back as branches the agent pushes.
|
|
45
|
+
Switch it on in Settings → Providers → Claude Cloud and choose the cloud
|
|
46
|
+
environment and a default model there; it needs the Claude CLI logged in
|
|
47
|
+
with a claude.ai account on the relay host, GitHub connected to that
|
|
48
|
+
account, and the Claude GitHub app allowed on the repository.
|
|
49
|
+
- **New Chat** fills **Repository** and **Branch** from the folder you
|
|
50
|
+
pick, offers the repositories the Claude GitHub app can reach (the ones
|
|
51
|
+
used here before first) and the branches of the picked repository, lets
|
|
52
|
+
you type both, and warns about commits that are not pushed, changes
|
|
53
|
+
that are not committed and a repository the app cannot reach.
|
|
54
|
+
- **In the chat** the reply streams in live, the agent's questions and
|
|
55
|
+
permission prompts arrive as cards, **Stop** interrupts the turn, images
|
|
56
|
+
can be attached, and the model can be changed between turns. A line
|
|
57
|
+
above the composer links the session on claude.ai and every branch the
|
|
58
|
+
agent pushed to its comparison on GitHub, and shows the cost so far.
|
|
59
|
+
- **Turns** survive a restart of the worker or the relay without sending
|
|
60
|
+
your message twice. A turn stays open while the agent's background work
|
|
61
|
+
runs, a turn the session starts by itself (after long background work,
|
|
62
|
+
or from a message sent on claude.ai) appears as a reply of its own, and
|
|
63
|
+
a turn refused at the usage limit is paused until the reset like a
|
|
64
|
+
local Claude turn.
|
|
65
|
+
- **Commits** follow the relay's attribution setting (see above), and
|
|
66
|
+
deleting or archiving the chat archives the cloud session.
|
|
67
|
+
- **Not there:** relay modes, a reasoning effort, steering during a turn,
|
|
68
|
+
files other than images and anything else from the relay host, and the
|
|
69
|
+
relay's tools and previews inside the sandbox.
|
|
70
|
+
- **Before you switch it on:** the provider uses the Claude CLI's stored
|
|
71
|
+
login to call the Anthropic API endpoints the CLI itself uses. These
|
|
72
|
+
are not a documented public API and may change. OAR only reads the
|
|
73
|
+
token: it never stores or logs it and sends it nowhere but to
|
|
74
|
+
Anthropic; when it has run out, OAR lets the CLI refresh it. While the
|
|
75
|
+
provider is off the relay fetches nothing with that login, except for
|
|
76
|
+
cloud chats you created before. See "Claude Cloud" in the README.
|
|
77
|
+
`scripts/claude-cloud-live-check.mjs` checks a running relay's Claude
|
|
78
|
+
Cloud end to end.
|
|
79
|
+
- **Check Usage: live Claude limits and a Claude Cloud card.** With Claude
|
|
80
|
+
Cloud switched on, the Claude card reads the 5-hour, weekly and per-model
|
|
81
|
+
limits live from the account when you open the modal, instead of showing
|
|
82
|
+
the reading of your last Claude turn, and a limit that is getting close
|
|
83
|
+
carries Anthropic's warning. The card also shows which product the week
|
|
84
|
+
went to (Claude Code, chats and the others) and the organisation's prepaid
|
|
85
|
+
credits. The new Claude Cloud card shows the account's cloud credit (used,
|
|
86
|
+
left, and when it expires) and what the cloud chats on this relay have
|
|
87
|
+
cost as Anthropic reports it, in total and for the chat you opened the
|
|
88
|
+
modal from; a credit that is offered but not yet claimed is mentioned with
|
|
89
|
+
a link. The limit-reset vouchers (full reset, 5-hour reset) stay
|
|
90
|
+
claude.ai-only: no endpoint the CLI's login reaches carries them, so the
|
|
91
|
+
card links to the usage page instead. With Claude Cloud off, the Claude
|
|
92
|
+
card is what it was.
|
|
93
|
+
- **Agent sessions: an agent can hand parts of its task to other sessions on
|
|
94
|
+
the same relay.** The `remote_relay` tool now also works on the relay the
|
|
95
|
+
agent runs on: it starts sessions there, waits for them and reads their
|
|
96
|
+
replies, and each one is an ordinary conversation in your list. Off by
|
|
97
|
+
default; switch it on in Settings → Relays → Agent sessions (**Agents may
|
|
98
|
+
start and use sessions on this relay**). No relay has to be paired. The
|
|
99
|
+
first time the agent of a conversation starts a session, a card asks you
|
|
100
|
+
once for that conversation. At most 4 sessions it started may be at work at
|
|
101
|
+
the same time, a session that an agent created cannot start sessions
|
|
102
|
+
itself, and an agent cannot prompt, stop or archive the conversation it
|
|
103
|
+
runs in. A session can use any provider that is enabled on the relay
|
|
104
|
+
(image chats excepted); a Claude Cloud chat is started with the new `repo`
|
|
105
|
+
and `branch` arguments, and an agent on a paired relay can now start a
|
|
106
|
+
cloud chat here the same way. Sessions an agent starts use this relay's
|
|
107
|
+
accounts, show **via agent** with the title of the conversation they came
|
|
108
|
+
from in the sidebar and the header (a click opens it), and are never
|
|
109
|
+
archived automatically. The new
|
|
110
|
+
**Longest wait per tool call** slider in the same place sets how long one
|
|
111
|
+
call may wait for a reply: 10 minutes as before, up to 60; it applies to
|
|
112
|
+
waits on paired relays too. See "Agent sessions" in the README.
|
|
113
|
+
|
|
114
|
+
### Changed
|
|
115
|
+
|
|
116
|
+
- **Check Usage opens at once.** The modal shows the reading it showed last
|
|
117
|
+
time immediately and reads only the open tab's provider live (the card says
|
|
118
|
+
"updating…" meanwhile), instead of fetching every provider and showing a
|
|
119
|
+
spinner until the slowest answered. A tab you switch to is read live when
|
|
120
|
+
its reading is older than a minute; **Refresh** reads the open tab only.
|
|
121
|
+
The relay keeps the last live answer of every provider, so the other cards
|
|
122
|
+
stay filled (`GET /api/usage?providers=…`).
|
|
123
|
+
- **Windows: the relay no longer stops while it reads the process list.**
|
|
124
|
+
Before every launch, every kill and every check of a worker that went
|
|
125
|
+
quiet, the relay reads the list of all processes through PowerShell. It
|
|
126
|
+
did so synchronously: half a second to a second and a half on an idle
|
|
127
|
+
machine, several seconds under load, during which the relay answered
|
|
128
|
+
nothing — under CI load that was enough for the tunnel to report 502 for
|
|
129
|
+
the whole relay. The list is now read off the relay's thread; callers that
|
|
130
|
+
ask at the same time share one read, and a read is reused for 1.5 s. A
|
|
131
|
+
worker that went quiet is checked by its pid first and by the list last.
|
|
132
|
+
`GET /api/status` → `sessionWorker.processList` shows when the list was
|
|
133
|
+
last read, how long it took and whether it failed; a read slower than two
|
|
134
|
+
seconds is logged. A kill or a workspace relaunch that cannot read the
|
|
135
|
+
list answers 409 with the reason instead of failing with 500 or stopping
|
|
136
|
+
blind.
|
|
137
|
+
|
|
138
|
+
### Fixed
|
|
139
|
+
|
|
140
|
+
- **A reply that names an error code is a reply.** A reply a session
|
|
141
|
+
worker published was read as a failed turn when its one paragraph led up
|
|
142
|
+
to "Error code: relay.…" — a short report on a test that had failed with
|
|
143
|
+
that code, for instance. Workers always send the failure as a record, so
|
|
144
|
+
their replies are no longer read for one; the page's fix buttons under a
|
|
145
|
+
failure follow the same rule and no longer appear under a reply that
|
|
146
|
+
mentions a code in a quotation, a code span, a table or a list.
|
|
147
|
+
- **Two prices in one paragraph were shown as a formula.** In a reply such
|
|
148
|
+
as `this run cost $4.20, the earlier one $3.80`, everything between the
|
|
149
|
+
two dollar signs was rendered as maths. A dollar sign now starts a formula
|
|
150
|
+
only when it is written the way inline maths is: no space after the
|
|
151
|
+
opening sign, no space before the closing one, and no digit right after
|
|
152
|
+
it. Prices stay text; `$x^2$` is still maths. A wide inline formula also
|
|
153
|
+
no longer makes the whole message scroll sideways: it stays inside the
|
|
154
|
+
bubble and scrolls within itself.
|
|
155
|
+
|
|
156
|
+
- **Windows: no worker could start once an agent had run a command with an
|
|
157
|
+
arrow or a bullet in it.** Before every launch the relay reads the list of
|
|
158
|
+
all processes through PowerShell. Under the console's OEM code page
|
|
159
|
+
PowerShell wrote characters such as `→` and `•` as control bytes, the list
|
|
160
|
+
stopped being valid JSON, and every launch on the relay failed: each session
|
|
161
|
+
you wrote to turned yellow after six tries and stayed so, for every
|
|
162
|
+
provider, until the relay was restarted. The list is now written as UTF-8, a
|
|
163
|
+
control byte that still gets through is tolerated, a list the relay cannot
|
|
164
|
+
read no longer stops a launch, and a session whose launches were exhausted
|
|
165
|
+
is tried once more every minute instead of waiting for a restart.
|
|
166
|
+
- **A session whose worker cannot be started now says so.** Until now such a
|
|
167
|
+
session showed a yellow dot and nothing else. The relay now writes one note
|
|
168
|
+
into the conversation — why the worker could not be started, in one
|
|
169
|
+
sentence plus the cause — and keeps that same note up to date: it tries
|
|
170
|
+
again every minute for ten minutes, then stops and the note gets a
|
|
171
|
+
**Retry** button for once the cause is fixed; when the worker starts, the
|
|
172
|
+
note says so. Your message stays queued the whole time, and a queued
|
|
173
|
+
restart of the relay no longer waits for a message the relay has stopped
|
|
174
|
+
trying to deliver.
|
|
175
|
+
- **The Claude usage card showed extra-usage credits 100 times too high.**
|
|
176
|
+
Claude reports these amounts in the currency's minor units (cents), and
|
|
177
|
+
the card showed them as dollars: 12.50 used of a 50.00 limit read as 1250
|
|
178
|
+
of 5000. The percentage was right.
|
|
179
|
+
|
|
180
|
+
## [0.9.6] — 2026-09-29
|
|
181
|
+
|
|
182
|
+
Highlights: a Claude turn pauses at the usage limit and carries on after the
|
|
183
|
+
reset, a command that prints nothing no longer fails its turn, and agents on
|
|
184
|
+
paired relays can work with each other for a whole session.
|
|
185
|
+
|
|
186
|
+
### Added
|
|
187
|
+
|
|
188
|
+
- The relay keeps a copy of its console in `relay-console.log` in its log
|
|
189
|
+
folder (`server/logs` unless you moved it), with a time stamp per line. The
|
|
190
|
+
file is rotated at 5 MB and three older copies are kept. The relay's token is
|
|
191
|
+
never written to it. Set `OAR_NO_CONSOLE_LOG=1` to switch it off.
|
|
192
|
+
- **Claude usage limit:** a Claude turn that runs into the subscription's
|
|
193
|
+
usage limit is paused instead of failed. Your message closes with a note,
|
|
194
|
+
the relay queues a message of its own ("continue where you left off") and
|
|
195
|
+
sends it a minute after the limit resets. A banner above the composer says
|
|
196
|
+
when, with **Resume now** and **Cancel**. The pause survives a relay
|
|
197
|
+
restart. A reset more than six hours away (a weekly limit) waits for you to
|
|
198
|
+
resume it.
|
|
199
|
+
- A banner warns in Claude conversations when the usage passes 90 % of a
|
|
200
|
+
limit, with the time of the reset, and can be hidden until the next one.
|
|
201
|
+
|
|
202
|
+
### Changed
|
|
203
|
+
|
|
204
|
+
- **Remote relays:** agents on paired relays can now work with each other for
|
|
205
|
+
a whole session. A relay you mentioned stays in reach whatever prompts arrive
|
|
206
|
+
later, and a relay whose agent wrote to a conversation is open to it, so the
|
|
207
|
+
agent that was asked can answer, ask back or report later. Until now a prompt
|
|
208
|
+
from another relay's agent took the tool away for the rest of the turn, also
|
|
209
|
+
in a turn you had started yourself. A prompt that crossed two relays is still
|
|
210
|
+
not passed on to a third relay you did not unlock; reading and waiting are no
|
|
211
|
+
longer limited. After 30 prompts to other relays' agents without a message
|
|
212
|
+
from you, the next one asks you first.
|
|
213
|
+
- Subagents are folded to their header inside the running turn, so a turn with
|
|
214
|
+
many of them stays short to scroll. The header says how many steps the
|
|
215
|
+
subagent has taken and shows the latest one, as far as there is room. A tap
|
|
216
|
+
on the header unfolds a subagent and folds it again; what you chose by hand
|
|
217
|
+
stays. The Stop button of a finished subagent is gone.
|
|
218
|
+
- **Copilot, Cursor, Grok:** how long a turn may stay quiet now depends on what
|
|
219
|
+
the agent is doing: 2 minutes with nothing in flight, 5 minutes during a model
|
|
220
|
+
request, 30 minutes while a tool runs. Until now every silence of 2 minutes
|
|
221
|
+
failed the turn, including a command that simply printed nothing. The failure
|
|
222
|
+
note says how long the silence was and what was running. The three windows can
|
|
223
|
+
be set with `OAR_TURN_STALL_IDLE_MS`, `OAR_TURN_STALL_MODEL_MS` and
|
|
224
|
+
`OAR_TURN_STALL_TOOL_MS`.
|
|
225
|
+
- **Copilot:** a turn kept past 2 minutes of silence is checked: a runtime that
|
|
226
|
+
no longer answers fails the turn at once. The watchdog also covers the start of
|
|
227
|
+
a session, where a runtime that did not answer used to hold the message without
|
|
228
|
+
any failure, and a Stop during that start now takes effect at once.
|
|
229
|
+
- **Cursor:** a stalled run is cancelled, so the next message does not wait for
|
|
230
|
+
it.
|
|
231
|
+
- A plan board is part of the reply it belongs to: its content and its buttons
|
|
232
|
+
are shown inside the agent's reply bubble (inside the running turn's bubble
|
|
233
|
+
until the reply is there) instead of on a card of their own at the end of the
|
|
234
|
+
transcript. When the reply already is the plan, the plan is shown once, with
|
|
235
|
+
the buttons below it. After you chose, the plan stays and the bubble says
|
|
236
|
+
what was chosen.
|
|
237
|
+
- The choice on a plan board also sets the mode of the session: after
|
|
238
|
+
"Implement in autopilot" the composer says Autopilot, after "Stop here and
|
|
239
|
+
prompt myself" it says Agent, and the next message carries the work on. The
|
|
240
|
+
session used to stay in Plan, so the next message planned again. "Stop here"
|
|
241
|
+
leaves the mode as it is.
|
|
242
|
+
|
|
243
|
+
### Fixed
|
|
244
|
+
|
|
245
|
+
- A session worker that was idle and whose process has ended (stopped from
|
|
246
|
+
outside, or crashed between turns) is no longer listed as ready with its old
|
|
247
|
+
process id. The next message started a new worker before as well; the list
|
|
248
|
+
now says what is there, about a minute and a half after the process ended.
|
|
249
|
+
- A reply that quotes or describes a failure note (an agent reporting on a
|
|
250
|
+
failed test, for example) is no longer taken for a failure itself. The relay
|
|
251
|
+
cut such a reply off at the quoted error code, put its own advice behind it
|
|
252
|
+
and stored the turn as failed. Where a failure is known from its text alone,
|
|
253
|
+
that text is now kept whole.
|
|
254
|
+
- A single `~` in a reply ("about", as in "~35 s … ~7 min") no longer strikes
|
|
255
|
+
out the text up to the next one. Strikethrough needs two tildes on each side:
|
|
256
|
+
`~~text~~`. The page now names the version of its Markdown library (15.0.12,
|
|
257
|
+
the one it was served until now) instead of taking whatever the CDN hands out.
|
|
258
|
+
- **Copilot:** a command the agent had started no longer runs on after its
|
|
259
|
+
runtime is gone. When the runtime died or was killed in the middle of a turn,
|
|
260
|
+
the turn failed as it should, but the command kept running with nobody left
|
|
261
|
+
to read its result. The relay now stops what such a runtime leaves behind
|
|
262
|
+
(first gently, then by force), and the failure note says that a command that
|
|
263
|
+
was still running is being stopped, as far as that is known at that moment:
|
|
264
|
+
the note comes at once and does not wait for the list of processes. A command
|
|
265
|
+
the agent started to outlive the session (`detach`) is left alone, and so are
|
|
266
|
+
the commands of a runtime that shuts down in good order.
|
|
267
|
+
- **Grok:** a quiet-turn window that is set for the model request or for a tool
|
|
268
|
+
is kept when the shared watchdog is switched off by the environment, and the
|
|
269
|
+
watchdog looks often enough for the shortest window that is set.
|
|
270
|
+
- **Windows:** stopping or deleting a session could end the worker of another
|
|
271
|
+
session as well. Windows hands the process id of an ended process out again,
|
|
272
|
+
and a worker whose creator was long gone could so look like a child of the
|
|
273
|
+
session that was being stopped. A process older than its parent is no longer
|
|
274
|
+
taken for its child, and the worker of another session never is.
|
|
275
|
+
- **Copilot:** a message sent while a command is running no longer cuts the
|
|
276
|
+
command short. It waits until the running tool call has ended and is steered
|
|
277
|
+
in then; the composer reads "Queue" for as long. Sent at once, the message
|
|
278
|
+
made the runtime push the command to the background: the turn ended early
|
|
279
|
+
with the answer to the new message alone, or failed with "No response was
|
|
280
|
+
returned", and the command's result arrived later in a turn of its own.
|
|
281
|
+
- A plan board's action ("Implement in autopilot" and the like) queues its
|
|
282
|
+
follow-up again. It failed with an error and left the board marked as acted
|
|
283
|
+
on, without the message that carries the plan out.
|
|
284
|
+
- A finished subagent no longer turns back to "Running" in the running turn
|
|
285
|
+
when another line of activity arrives or the page reloads.
|
|
286
|
+
- **Previews:** the first page load after a dev server was restarted no longer
|
|
287
|
+
ends in "The upstream connection failed: ECONNRESET". The preview lane keeps
|
|
288
|
+
its connections to the dev server, and the one it had kept was closed by the
|
|
289
|
+
restart. A request without a body is now sent once more on a connection of
|
|
290
|
+
its own: it reaches the restarted server, or the page says that nothing is
|
|
291
|
+
listening on the port.
|
|
292
|
+
- Links in the file viewer and in plan boards have a colour that can be read in
|
|
293
|
+
dark mode; they used the browser's default blue.
|
|
294
|
+
- A question or approval card no longer answers itself when the relay is
|
|
295
|
+
briefly unreachable, for example while it restarts. One failed check used to
|
|
296
|
+
end the wait: the agent was told to use its own judgement, or the approval
|
|
297
|
+
was refused, while the card stayed on screen. The wait now lasts through the
|
|
298
|
+
outage, for every provider.
|
|
299
|
+
- **Copilot:** a turn that fails is reported at once. The failure used to wait
|
|
300
|
+
for the runtime to finish stopping, which took three minutes on a runtime that
|
|
301
|
+
had stopped answering; stopping it is now bounded, and the next message waits
|
|
302
|
+
for it before a new runtime starts.
|
|
303
|
+
- **Copilot:** a failed turn keeps what the agent had written, above the failure
|
|
304
|
+
note. The note says how many tool calls ran and that their changes are still in
|
|
305
|
+
place, and it no longer advises restarting the relay. A message steered into
|
|
306
|
+
the failed turn gets a short marker with Resend instead of a second copy of
|
|
307
|
+
the failure.
|
|
308
|
+
- **Copilot:** a slow relay no longer makes a busy runtime look silent.
|
|
309
|
+
- **Copilot:** a reply the relay could not take (it was restarting, the
|
|
310
|
+
connection was reset) is offered again for up to five minutes. One failed
|
|
311
|
+
attempt used to put the message back in the queue, which threw the finished
|
|
312
|
+
answer away and ran the prompt a second time. A reply that cannot be saved at
|
|
313
|
+
all ends as a failure that says so, and nothing is run twice.
|
|
314
|
+
- **Copilot:** what the agent does by itself after a background task (a
|
|
315
|
+
continuation) is kept while the relay restarts. Its row was given up after
|
|
316
|
+
about a second, and the whole reply with it.
|
|
317
|
+
- **Copilot:** a relay that accepts live output and does not answer no longer
|
|
318
|
+
holds the turn: a live update waits 10 seconds at most, and after one that
|
|
319
|
+
ran out, live updates are skipped for 30 seconds. Tool lines skipped then are
|
|
320
|
+
missing from the transcript; the reply is not affected.
|
|
321
|
+
- **Copilot:** Stop works on a turn that is stuck. A runtime that does not
|
|
322
|
+
answer the Stop within 10 seconds is ended with the turn, and a turn the user
|
|
323
|
+
stopped ends as stopped even when the runtime died or went silent after it.
|
|
324
|
+
- **Copilot:** a runtime that exits, or stops answering, while the agent works
|
|
325
|
+
by itself after a background task is cleared away; the next message used to
|
|
326
|
+
fail on it. A lost connection during a model change is reported as that, not
|
|
327
|
+
as "pick another model".
|
|
328
|
+
- **Copilot:** a failed turn tells its failure once. A message sent into the
|
|
329
|
+
turn that the agent had not started on gets the short marker with Resend,
|
|
330
|
+
like one it had taken in.
|
|
331
|
+
- **Copilot:** background agents and shells are no longer closed with the
|
|
332
|
+
runtime because its list of them could not be read.
|
|
333
|
+
- **Stop button:** Stop keeps working for a message that waits on a question
|
|
334
|
+
card while the agent starts and finishes other work, and for a second message
|
|
335
|
+
stopped while the same turn goes on. It used to do nothing in both cases.
|
|
336
|
+
- **Copilot:** a turn the runtime itself ended with an error no longer tells
|
|
337
|
+
you to restart the relay. The worker starts a fresh runtime by itself; a
|
|
338
|
+
restart repairs nothing and costs every other running session.
|
|
339
|
+
- The note under a failed turn no longer advises restarting the relay: a restart
|
|
340
|
+
repairs nothing there and interrupts every other running session. It now says
|
|
341
|
+
to send the message again and to include the error code in a report.
|
|
342
|
+
- **Claude:** a turn the Claude account refuses (billing, an expired sign-in,
|
|
343
|
+
access the organisation has switched off) says to check the account or to sign
|
|
344
|
+
in again in Settings → Providers → Claude, with a button that opens the panel,
|
|
345
|
+
instead of "Retry the message". Its error code names the failure, for example
|
|
346
|
+
`relay.claude-billing-error`; it used to read `relay.claude-success`.
|
|
347
|
+
|
|
8
348
|
## [0.9.5] — 2026-09-27
|
|
9
349
|
|
|
10
350
|
Highlights: a Claude reply on the wrong row no longer blocks the conversation,
|