@oar-sh/oar 0.9.13 → 0.9.14
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/extension.mjs +9 -0
- package/.github/extensions/web-relay/server-lifecycle/managed-server.mjs +15 -1
- package/.github/extensions/web-relay/skills/question-bridge.mjs +4 -1
- package/CHANGELOG.md +302 -0
- package/README.md +12 -12
- package/bin/oar.js +155 -94
- package/package.json +5 -5
- package/server/README.md +296 -149
- package/server/claude-cloud-worker/claude-cloud-session-process.mjs +10 -4
- package/server/claude-worker/claude-session-process.mjs +39 -13
- package/server/claude-worker/claude-turn-publisher.mjs +20 -1
- package/server/claude-worker/subagent-timeline-recorder.mjs +75 -8
- package/server/copilot-worker/copilot-ask-user-tool.mjs +1 -1
- package/server/copilot-worker/copilot-prompt-context.mjs +3 -1
- package/server/copilot-worker/copilot-question-bridge.mjs +11 -6
- package/server/copilot-worker/copilot-sdk-adapter.mjs +11 -0
- package/server/copilot-worker/copilot-sdk-session-process.mjs +14 -1
- package/server/cursor-worker/cursor-ask-user-tool.mjs +1 -1
- package/server/cursor-worker/cursor-sdk-adapter.mjs +22 -7
- package/server/cursor-worker/cursor-session-worker.mjs +9 -2
- package/server/cursor-worker/cursor-turn-runner.mjs +18 -3
- package/server/grok-worker/grok-sdk-adapter.mjs +14 -0
- package/server/grok-worker/grok-turn-runner.mjs +13 -3
- package/server/public/app/api-client.js +3 -1
- package/server/public/app/ask-user-view.js +29 -4
- package/server/public/app/conversation-provider-indicator.mjs +18 -8
- package/server/public/app/question-answer-rule.mjs +15 -0
- package/server/public/app/question-multi-select.mjs +9 -2
- package/server/public/app/relay-event-view.mjs +5 -1
- package/server/public/index.html +9 -5
- package/server/relay-exit-codes.mjs +6 -0
- package/server/relay-self-restart.mjs +6 -1
- package/server/relay-tools.md +14 -11
- package/server/repositories/message-repository.mjs +29 -28
- package/server/repositories/subagent-timeline-statements.mjs +2 -0
- package/server/routes/ask-user-routes.mjs +41 -7
- package/server/routes/messages-routes.mjs +65 -10
- package/server/routes/preview-routes.mjs +73 -7
- package/server/routes/sessions-routes.mjs +4 -2
- package/server/server-runtime.mjs +156 -94
- package/server/services/cursor-session-root-service.mjs +70 -6
- package/server/services/data-dir-resolution.mjs +208 -19
- package/server/services/earlier-database-service.mjs +631 -0
- package/server/services/oar-cli-helpers.mjs +6 -1
- package/server/services/oar-state-layout.mjs +107 -0
- package/server/services/oar-state-migration-service.mjs +387 -96
- package/server/services/package-data-rescue-preinstall.mjs +22 -0
- package/server/services/package-data-rescue.mjs +137 -0
- package/server/services/preview-registry-service.mjs +3 -2
- package/server/services/push-dispatch-service.mjs +33 -6
- package/server/services/relay-event-service.mjs +183 -5
- package/server/services/relay-restart-queue.mjs +10 -3
- package/server/services/relay-singleton-guard.mjs +300 -36
- package/server/services/remote-relay-caller-context.mjs +7 -2
- package/server/services/remote-relay-dispatcher.mjs +30 -9
- package/server/services/session-activity-tracker.mjs +52 -15
- package/server/services/session-worker-launch-service.mjs +31 -1
- package/server/services/stale-turn-recovery.mjs +99 -0
- package/server/services/subagent-timeline-service.mjs +4 -1
- package/server/services/windows-autostart-service.mjs +329 -31
- package/server/services/windows-boot-autostart-service.mjs +188 -60
- package/shared/preview-instructions.mjs +17 -12
- package/shared/preview-tool-core.mjs +32 -14
- package/shared/question-answer-rule.mjs +63 -0
- package/shared/relay-events.mjs +54 -4
- package/shared/remote-relay-contract.mjs +20 -10
- package/shared/subagent-timeline.mjs +4 -0
- package/shared/windows-system-tools.mjs +25 -0
- package/shared/worker-runtime/config-loader.mjs +54 -8
- package/shared/worker-runtime/prompt-context.mjs +3 -1
- package/shared/worker-runtime/working-directory.mjs +65 -0
- package/server/uploads/image_input.md +0 -540
|
@@ -13,6 +13,7 @@ import path from "path";
|
|
|
13
13
|
import { generateHighEntropyToken } from "./utils/token-generation.mjs";
|
|
14
14
|
import { delay, killProcessTree, sleep } from "./utils/process-utils.mjs";
|
|
15
15
|
import {
|
|
16
|
+
carryOverPackageConfig,
|
|
16
17
|
resolveRelayPaths,
|
|
17
18
|
loadTokenFromConfig,
|
|
18
19
|
loadRelayInstructionsFromFile,
|
|
@@ -60,7 +61,13 @@ const {
|
|
|
60
61
|
LOG_DIR,
|
|
61
62
|
SERVER_LOG_PATH,
|
|
62
63
|
SERVER_ERR_PATH,
|
|
64
|
+
RELAY_ENV,
|
|
63
65
|
} = resolveRelayPaths(import.meta.url);
|
|
66
|
+
// A global install's config moves to the state root with the relay's data:
|
|
67
|
+
// the one the package held before is copied there once (never over one).
|
|
68
|
+
const CARRIED_OVER_CONFIG = RELAY_ENV.COPILOT_WEB_RELAY_CONFIG
|
|
69
|
+
? carryOverPackageConfig({ serverDir: SERVER_DIR, configPath: CONFIG_PATH, dataDir: RELAY_ENV.COPILOT_WEB_RELAY_DATA_DIR })
|
|
70
|
+
: null;
|
|
64
71
|
const SERVER_URL = resolveRelayServerUrl({ configPath: CONFIG_PATH });
|
|
65
72
|
const SERVER_START_TIMEOUT_MS = 20_000;
|
|
66
73
|
const NODE_BIN = process.env.COPILOT_WEB_RELAY_NODE || "node";
|
|
@@ -78,6 +85,7 @@ const MODEL_SNAPSHOT_MIN_INTERVAL_MS = 30_000;
|
|
|
78
85
|
const BANNER_DEDUPE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
79
86
|
const BANNER_DEDUPE_COOLDOWN_MS = 15 * 1000;
|
|
80
87
|
const { dbg } = createDebugLogger({ logDir: LOG_DIR });
|
|
88
|
+
if (CARRIED_OVER_CONFIG) dbg("copied the relay config", CARRIED_OVER_CONFIG, "to", CONFIG_PATH);
|
|
81
89
|
const api = createApiClient({
|
|
82
90
|
serverUrl: SERVER_URL,
|
|
83
91
|
token: TOKEN,
|
|
@@ -117,6 +125,7 @@ const managedServerLifecycle = createManagedServerLifecycle({
|
|
|
117
125
|
serverDir: SERVER_DIR,
|
|
118
126
|
serverLogPath: SERVER_LOG_PATH,
|
|
119
127
|
serverErrPath: SERVER_ERR_PATH,
|
|
128
|
+
relayEnv: RELAY_ENV,
|
|
120
129
|
relayPort: Number(new URL(SERVER_URL).port) || 3333,
|
|
121
130
|
nodeBin: NODE_BIN,
|
|
122
131
|
serverStartTimeoutMs: SERVER_START_TIMEOUT_MS,
|
|
@@ -10,6 +10,9 @@ export function createManagedServerLifecycle({
|
|
|
10
10
|
serverDir,
|
|
11
11
|
serverLogPath,
|
|
12
12
|
serverErrPath,
|
|
13
|
+
// The state root's folders a global install pins (resolveRelayPaths'
|
|
14
|
+
// RELAY_ENV); what the CLI's own environment names wins.
|
|
15
|
+
relayEnv = {},
|
|
13
16
|
relayPort = 3333,
|
|
14
17
|
nodeBin,
|
|
15
18
|
serverStartTimeoutMs,
|
|
@@ -24,6 +27,9 @@ export function createManagedServerLifecycle({
|
|
|
24
27
|
let restartTimer = null;
|
|
25
28
|
let restartAttempts = 0;
|
|
26
29
|
let attemptedSqliteRepair = false;
|
|
30
|
+
// The relay stopped on purpose (RELAY_STOP_EXIT_CODE): its reason is in
|
|
31
|
+
// its log, and starting it again at once cannot help.
|
|
32
|
+
let stoppedOnPurpose = false;
|
|
27
33
|
// Keep in sync with server/relay-self-restart.mjs. Passing this on argv (not
|
|
28
34
|
// in the env) is deliberate: this extension's own env is inherited from the
|
|
29
35
|
// relay worker that launched the CLI, so an env flag here would be read by
|
|
@@ -33,6 +39,7 @@ export function createManagedServerLifecycle({
|
|
|
33
39
|
const RESTART_BACKOFF_MS = [1000, 2000, 5000, 10000, 20000];
|
|
34
40
|
// Keep in sync with server/relay-exit-codes.mjs
|
|
35
41
|
const RELAY_RESTART_EXIT_CODE = 75;
|
|
42
|
+
const RELAY_STOP_EXIT_CODE = 78;
|
|
36
43
|
const STARTUP_LOG_TAIL_BYTES = 24 * 1024;
|
|
37
44
|
const STATUS_PATH = "/api/status";
|
|
38
45
|
const RELAY_PORT = Number.isInteger(Number(relayPort)) && Number(relayPort) > 0
|
|
@@ -198,6 +205,7 @@ export function createManagedServerLifecycle({
|
|
|
198
205
|
async function waitForServerReady(timeoutMs = serverStartTimeoutMs) {
|
|
199
206
|
const deadline = Date.now() + timeoutMs;
|
|
200
207
|
while (Date.now() < deadline) {
|
|
208
|
+
if (stoppedOnPurpose) return false;
|
|
201
209
|
try {
|
|
202
210
|
await api("GET", STATUS_PATH);
|
|
203
211
|
return true;
|
|
@@ -280,12 +288,16 @@ export function createManagedServerLifecycle({
|
|
|
280
288
|
|
|
281
289
|
dbg("starting managed web server", serverDir);
|
|
282
290
|
managedServerOwned = true;
|
|
291
|
+
stoppedOnPurpose = false;
|
|
283
292
|
startedOwnedProcess = true;
|
|
284
293
|
const serverArgs = ["server.js", RELAY_SUPERVISED_FLAG, "--token", token];
|
|
285
294
|
if (legacyOwnerPidWatchdog) {
|
|
286
295
|
serverArgs.push("--owner-pid", String(process.pid));
|
|
287
296
|
}
|
|
288
297
|
const serverEnv = { ...process.env };
|
|
298
|
+
for (const [key, value] of Object.entries(relayEnv || {})) {
|
|
299
|
+
if (!String(serverEnv[key] || "").trim() && value) serverEnv[key] = value;
|
|
300
|
+
}
|
|
289
301
|
if (startupWorkspaceRoot) {
|
|
290
302
|
serverEnv.COPILOT_WORKSPACE_ROOT = startupWorkspaceRoot;
|
|
291
303
|
}
|
|
@@ -300,13 +312,14 @@ export function createManagedServerLifecycle({
|
|
|
300
312
|
managedServerProc.once("exit", (code, signal) => {
|
|
301
313
|
const normalizedCode = Number.isInteger(Number(code)) ? Number(code) : null;
|
|
302
314
|
const intentionalRestart = normalizedCode === RELAY_RESTART_EXIT_CODE;
|
|
315
|
+
stoppedOnPurpose = normalizedCode === RELAY_STOP_EXIT_CODE;
|
|
303
316
|
dbg(
|
|
304
317
|
"managed web server exited",
|
|
305
318
|
`code=${code ?? "null"}`,
|
|
306
319
|
`signal=${signal ?? "none"}`,
|
|
307
320
|
intentionalRestart ? "intentionalRestart=true" : "intentionalRestart=false",
|
|
308
321
|
);
|
|
309
|
-
const shouldRestart = desiredRunning && managedServerOwned && !stoppingManagedServer;
|
|
322
|
+
const shouldRestart = desiredRunning && managedServerOwned && !stoppingManagedServer && !stoppedOnPurpose;
|
|
310
323
|
closeManagedServerStreams();
|
|
311
324
|
managedServerProc = null;
|
|
312
325
|
managedServerOwned = false;
|
|
@@ -357,6 +370,7 @@ export function createManagedServerLifecycle({
|
|
|
357
370
|
closeManagedServerStreams();
|
|
358
371
|
managedServerProc = null;
|
|
359
372
|
managedServerOwned = false;
|
|
373
|
+
if (stoppedOnPurpose) markNonRetryable(error);
|
|
360
374
|
if (desiredRunning && !stoppingManagedServer && error?.relayRetryable !== false) {
|
|
361
375
|
scheduleManagedServerRestart("start-failed");
|
|
362
376
|
}
|
|
@@ -77,7 +77,10 @@ export function createQuestionBridge({
|
|
|
77
77
|
mode: activeMsg?.relayMode || "agent",
|
|
78
78
|
prompt: extractQuestionPrompt(request),
|
|
79
79
|
choices,
|
|
80
|
-
|
|
80
|
+
// Free text unless the request says otherwise, as the runtime's ask_user
|
|
81
|
+
// and the SDK engine's bridge read it: the relay refuses typed text on a
|
|
82
|
+
// card sent with allowFreeform:false.
|
|
83
|
+
allowFreeform: allowFreeform ?? true,
|
|
81
84
|
requestedSchema: requestedSchema || undefined,
|
|
82
85
|
sdk_session_id: activeSession?.sdkSessionId || undefined,
|
|
83
86
|
timeout_ms: questionTimeoutMs,
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,308 @@
|
|
|
3
3
|
All notable changes to OAR are documented here. The format follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow semver.
|
|
5
5
|
|
|
6
|
+
## [0.9.14] — 2026-10-21
|
|
7
|
+
|
|
8
|
+
Highlights: a relay started by Windows autostart, or by a plain
|
|
9
|
+
`gh copilot`, now runs on the same data folder as `oar start`, outside the
|
|
10
|
+
installed package, so an update no longer takes its conversations with it;
|
|
11
|
+
the update copies that data before npm removes the old version, and the
|
|
12
|
+
relay takes it over at its next start (read the upgrade note first). A
|
|
13
|
+
relay never moves to another database unannounced, a lock an unclean end
|
|
14
|
+
left no longer blocks the next start, autostart waits for a late drive, and
|
|
15
|
+
Cursor conversations keep their agent across updates. An agent can ask to
|
|
16
|
+
be woken when the relay runs again (`relay_restarted`), its preview tool
|
|
17
|
+
reaches only its own conversation's previews, approval cards take a choice
|
|
18
|
+
instead of typed text, and a conversation whose folder is gone says so.
|
|
19
|
+
|
|
20
|
+
### Upgrade notes
|
|
21
|
+
|
|
22
|
+
- **Windows autostart users, and anyone whose relay a plain `gh copilot`
|
|
23
|
+
started: save your data before this update.** Before 0.9.14, those relays
|
|
24
|
+
ran on the database and uploads inside the installed package, and
|
|
25
|
+
`npm i -g` deletes them with the old version. This update copies them
|
|
26
|
+
first, while it installs, to `rescued-package-data/<time>/` in the OAR
|
|
27
|
+
data folder (`%APPDATA%\oar\data`, `~/.oar/data`), and the relay takes
|
|
28
|
+
the copy over at its next start. npm 10 and 11 run that copy (11.21 with
|
|
29
|
+
a warning), and if it fails they put the old version back with its data.
|
|
30
|
+
npm 12 skips it unless `@oar-sh/oar` is allowed: the install then
|
|
31
|
+
succeeds and the old data is gone. With `strict-allow-scripts`, npm 11
|
|
32
|
+
and 12 refuse the install and change nothing; `--ignore-scripts` skips
|
|
33
|
+
the copy everywhere, and the native modules (better-sqlite3, koffi) too,
|
|
34
|
+
so the relay cannot start afterwards: never update OAR with
|
|
35
|
+
`--ignore-scripts`. On npm 12, update with the relay stopped:
|
|
36
|
+
`npm i -g @oar-sh/oar --allow-scripts=@oar-sh/oar,better-sqlite3,koffi`.
|
|
37
|
+
An `allow-scripts` entry in `.npmrc` does not help the **Update** button
|
|
38
|
+
or `oar update` of 0.9.13: their own `--allow-scripts` replaces it. To be
|
|
39
|
+
safe, do this update by hand:
|
|
40
|
+
1. Close every `gh copilot` session (its extension starts the relay
|
|
41
|
+
again), then `oar stop`, and wait until `oar status` says it is not
|
|
42
|
+
running. If 0.9.13's `oar stop` does not stop a relay the
|
|
43
|
+
system-startup launcher started, end the relay before installing:
|
|
44
|
+
end the relay's processes in Task Manager (node.exe running OAR).
|
|
45
|
+
2. Run `npm root -g` and open `<that folder>/@oar-sh/oar/server`
|
|
46
|
+
(usually `%APPDATA%\npm\node_modules\@oar-sh\oar\server`). Copy
|
|
47
|
+
**both** the `data` and the `uploads` folders, with every file in
|
|
48
|
+
them, to a safe place.
|
|
49
|
+
3. `npm i -g @oar-sh/oar --allow-scripts=@oar-sh/oar,better-sqlite3,koffi`.
|
|
50
|
+
4. Before starting the relay, copy both folders back into the same
|
|
51
|
+
`server` folder.
|
|
52
|
+
5. `oar start`. In `relay-console.log` in the OAR `logs` folder, look
|
|
53
|
+
for `[data] Brought over the database …`: your conversations should
|
|
54
|
+
be back. A database that was already in the OAR data folder is kept
|
|
55
|
+
in `backups/<time>/` beside it. The folder brought over keeps its
|
|
56
|
+
database as `copilot.db.adopted-<time>`; the others looked at keep
|
|
57
|
+
theirs as `copilot.db.older-<time>` (if a rescue copy was used, the
|
|
58
|
+
package's own database ends up `.older-`).
|
|
59
|
+
6. If the relay stops with `[data] Not starting: each database has
|
|
60
|
+
changes newer than the other's`, both hold work; the error line names
|
|
61
|
+
both folders, and one may be under
|
|
62
|
+
`rescued-package-data/<time>/data`. Pick the one to keep, and move
|
|
63
|
+
the other one's `copilot.db`, `copilot.db-wal` and `copilot.db-shm`
|
|
64
|
+
into a folder of your own (do not delete them). Then `oar start`
|
|
65
|
+
again.
|
|
66
|
+
7. If you already updated without saving and nothing was copied, the
|
|
67
|
+
database from the package can't be recovered by OAR.
|
|
68
|
+
- The npm package guard allows 8 MiB unpacked and 530 entries (was 7.75
|
|
69
|
+
MiB and 520); the take-over of earlier databases, the install-time copy
|
|
70
|
+
and the lock checks grew the package to about 7.75 MiB and 521 entries.
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- **Woken when the relay runs again** (`relay_restarted`, opt-in per
|
|
75
|
+
conversation). A conversation that queued a relay restart, or watched
|
|
76
|
+
sessions, slept after the restart until someone wrote, because the timers
|
|
77
|
+
its CLI had scheduled might not survive it. An agent can now subscribe to
|
|
78
|
+
`relay_restarted` through `remote_relay` (no sessions needed, no approval
|
|
79
|
+
card, this relay only); after each start it gets one relay event with when
|
|
80
|
+
the relay came back and how it stopped (who requested the restart, when
|
|
81
|
+
and why, or that it did not record how it stopped). Never part of the
|
|
82
|
+
default events. A restart requested while a turn it woke still runs does
|
|
83
|
+
not wake that conversation (a one-shot subscription held back so ends),
|
|
84
|
+
archived and deleted conversations are not woken, and each start is told
|
|
85
|
+
once.
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
|
|
89
|
+
- **Help texts and CLI messages say what the relay does.** **Accept prompts
|
|
90
|
+
from other relays' agents** lists everything it refuses when off, watching
|
|
91
|
+
sessions included; **Show Suspend host action** says it is a switch of this
|
|
92
|
+
browser and suspend works on Windows relays only; the Cursor session token
|
|
93
|
+
names the real usage bars; the Copilot engine applies whenever a
|
|
94
|
+
conversation's worker starts. `oar` points to **Settings → General →
|
|
95
|
+
Autostart (Windows)**, says the service starts again at boot with
|
|
96
|
+
lingering, sends a failed start or restart through the service to
|
|
97
|
+
`journalctl --user -u oar` instead of `server-err.log`, words a busy port
|
|
98
|
+
one way, and says an already running relay keeps its version and settings
|
|
99
|
+
until `oar restart`.
|
|
100
|
+
- **Agent guidance.** The ask_user example in the Copilot engines' relay
|
|
101
|
+
guidance is a real question instead of quiz placeholders, the guidance and
|
|
102
|
+
the Cursor and Copilot ask_user descriptions give the choice counts the
|
|
103
|
+
relay accepts (it keeps the first 8), the remote relay section names every
|
|
104
|
+
subscription action and `usage`, and the restart note says a queued
|
|
105
|
+
restart waits for every running turn and holds new messages.
|
|
106
|
+
- **Docs brought in line with the code**: the paired-relay switch, watch
|
|
107
|
+
approvals per relay, no push for agent-started turns, provider rebinding of
|
|
108
|
+
unstarted chats, OpenAI key removal, `oar stop`/`oar restart` under the
|
|
109
|
+
systemd service (they end running turns and workers), Claude turns on the
|
|
110
|
+
bundled Claude Code, the config keys and environment variables, which
|
|
111
|
+
variables reach workers started under tmux, the worker supervisor and the
|
|
112
|
+
database migrations.
|
|
113
|
+
- **Uploads follow the config's `dataDir`.** It moved the database but
|
|
114
|
+
left uploads in `server/uploads` unless `COPILOT_WEB_RELAY_DATA_DIR` was
|
|
115
|
+
set; they now go to `<dataDir>/uploads` as well. Uploads earlier versions
|
|
116
|
+
left in `server/uploads`, or that a state migration copied into the state
|
|
117
|
+
root's `uploads/`, are still read from there in place: every attachment
|
|
118
|
+
opens after the update, and nothing in those folders is moved or deleted.
|
|
119
|
+
- `@anthropic-ai/claude-agent-sdk` 0.3.304 (was 0.3.292; bundled Claude Code
|
|
120
|
+
2.1.304). Claude now forks sub-agents that inherit the conversation
|
|
121
|
+
(`subagent_type: "fork"`) by default. With thinking turned off, a model that
|
|
122
|
+
cannot run without thinking now uses adaptive thinking. A full disk during a
|
|
123
|
+
login refresh is reported as a failed request instead of a sign-in error.
|
|
124
|
+
Fixes include an approved tool call that could run twice on resume, model
|
|
125
|
+
switches and interrupts waiting behind a context-usage check, and
|
|
126
|
+
`hook_progress` messages piling up in memory.
|
|
127
|
+
- `@cursor/sdk` 1.0.38 (was 1.0.36), which uses the relay's own `zod`
|
|
128
|
+
instead of carrying a copy of zod 3.
|
|
129
|
+
- `@github/copilot-sdk` 1.0.23 (was 1.0.17; bundled Copilot CLI 1.0.100).
|
|
130
|
+
The Copilot workers and the web-relay extension are unchanged: under the
|
|
131
|
+
Copilot CLI both run the SDK that comes with the installed CLI. **About
|
|
132
|
+
OAR** shows the new version.
|
|
133
|
+
|
|
134
|
+
### Fixed
|
|
135
|
+
|
|
136
|
+
- **Windows autostart starts the relay on the same data as `oar start`.**
|
|
137
|
+
The sign-in entry and the system-startup launcher set only the config
|
|
138
|
+
file, so a relay they started kept its database, uploads and log inside
|
|
139
|
+
the installed package: other conversations and settings than with
|
|
140
|
+
`oar start`, and gone with the next update. Both now also set the data
|
|
141
|
+
and log folders `oar start` uses (`%APPDATA%\oar\data` and
|
|
142
|
+
`%APPDATA%\oar\logs` on a global install). Once it runs, the relay brings
|
|
143
|
+
an entry or launcher that an earlier version (0.9.0 on) generated for
|
|
144
|
+
this install and its config up to date, the pre-rebrand
|
|
145
|
+
`copilot-remote-web-relay.cmd` under its own name, so the first start
|
|
146
|
+
after the update (by `oar start`, `oar update` or the autostart itself)
|
|
147
|
+
repairs it. An entry edited by hand, one that points at another config
|
|
148
|
+
file, and one of a relay started on data folders of its own are left
|
|
149
|
+
alone: switch autostart off and on in Settings to replace those. Rewriting
|
|
150
|
+
a launcher while the relay it started still runs is safe: its cmd window
|
|
151
|
+
ends with the relay instead of starting it a second time. The launchers
|
|
152
|
+
now switch cmd to UTF-8, so a user folder with non-ASCII characters in its
|
|
153
|
+
name no longer garbles the paths in them. Conversations a relay started
|
|
154
|
+
by autostart kept inside the package folder are not carried over.
|
|
155
|
+
- **A relay never moves to another database unannounced.** Before its own
|
|
156
|
+
database opens, a global install's relay pinned to the OAR data folder
|
|
157
|
+
looks once at each database it may have run on until now (the one inside
|
|
158
|
+
the package, the install-time copies of it): a newer one is copied in
|
|
159
|
+
after a `quick_check` of the copy, with the uploads and Cursor agent
|
|
160
|
+
stores (never over a file there), the older one in place is kept in
|
|
161
|
+
`backups/<time>/`, and one line says so. Each one looked at is renamed
|
|
162
|
+
in place to `copilot.db.adopted-<time>` or `copilot.db.older-<time>`,
|
|
163
|
+
never deleted. When neither is clearly newer, or a relay may still run
|
|
164
|
+
on the other, the relay stops with one line naming both and exit code
|
|
165
|
+
78, which its supervisors, and a systemd unit written by this version's
|
|
166
|
+
`oar service install`, do not retry. The swap survives a kill. A global
|
|
167
|
+
install always runs on the state root's data folder, however it is
|
|
168
|
+
started; a `dataDir` in config.json that an earlier autostart relay used
|
|
169
|
+
is copied from once and left exactly as it is. A git checkout and a
|
|
170
|
+
relay on another state root are left alone. A first start that finds an earlier launcher
|
|
171
|
+
but no database of it says the update may have removed it. A large
|
|
172
|
+
database takes minutes: the relay logs its size at the start, progress
|
|
173
|
+
at least once a minute and each step's time at the end, and `oar start`
|
|
174
|
+
meanwhile says it is still bringing it over. A second `oar start` then
|
|
175
|
+
no longer removes the relay's lock and starts a second relay.
|
|
176
|
+
- **A relay lock an unclean end left no longer blocks the next start.** A
|
|
177
|
+
lock from before the last boot, or whose pid now belongs to a process
|
|
178
|
+
started after it, is stale; a lock still held stops the start with one
|
|
179
|
+
line naming it and exit code 78. A lock is never seen half written, and
|
|
180
|
+
one that cannot be read is read again and held unless its time is more
|
|
181
|
+
than 10 s off (an empty one a power loss left). Two starts recovering
|
|
182
|
+
the same stale lock no longer both run.
|
|
183
|
+
- **A `TEMP` or `TMP` set with quotes, or naming a missing folder, is fixed
|
|
184
|
+
for the relay** on Windows (quotes off, else the user's temp folder).
|
|
185
|
+
- **The relay a plain `gh copilot` starts keeps its data outside the
|
|
186
|
+
package.** After `oar copilot` installed the Copilot CLI extension for
|
|
187
|
+
every folder, a plain `gh copilot` ran its relay on `server/data` and
|
|
188
|
+
`server/config.json` inside the installed package, on every platform. A
|
|
189
|
+
global install's extension now starts it on the OAR state root's
|
|
190
|
+
config, data and logs, as `oar start` does, and copies a config the
|
|
191
|
+
package held there once. The database from the package is saved only
|
|
192
|
+
where the install-time copy ran (see the upgrade note).
|
|
193
|
+
- **Windows autostart waits for a late drive.** Both the sign-in entry and
|
|
194
|
+
the system-startup launcher now wait up to 5 minutes in all for the
|
|
195
|
+
`%TEMP%` folder and the drive of the data folder (a RAM disk or a drive a
|
|
196
|
+
startup script mounts later), where the system-startup launcher waited
|
|
197
|
+
2 minutes for `%TEMP%` only and the sign-in entry not at all. A wait that
|
|
198
|
+
runs out writes one line naming what is still missing, then starts the
|
|
199
|
+
relay anyway: `oar-web-relay-launcher.log` beside the system-startup
|
|
200
|
+
launcher, `oar-web-relay.log` in the OAR folder (`%APPDATA%\oar`) for the
|
|
201
|
+
sign-in entry. A data folder on a network share is not waited for. The
|
|
202
|
+
first start after the update brings a generated entry or launcher up to
|
|
203
|
+
date without an admin confirmation; edited ones stay as they are. A relay
|
|
204
|
+
whose data or upload folder cannot be created (its drive still missing, a
|
|
205
|
+
file in its place) now stops with one line naming the folder and exit
|
|
206
|
+
code 78 instead of an error trace. A `TEMP`
|
|
207
|
+
set with quotes (`"D:\My Temp"`) no longer stops the system-startup
|
|
208
|
+
launcher before it starts the relay.
|
|
209
|
+
- **Cursor conversations keep their agent across updates.** The Cursor
|
|
210
|
+
worker kept its per-conversation agent stores in
|
|
211
|
+
`server/data/cursor-agents` inside the installed package, so an update
|
|
212
|
+
deleted them and a Cursor conversation went on with a new agent that knew
|
|
213
|
+
nothing of the earlier turns. The stores now live in `cursor-agents` in
|
|
214
|
+
the relay's data folder (`~/.oar/data` on a global install, unchanged
|
|
215
|
+
`server/data` in a checkout); `CURSOR_AGENT_STORE_DIR` still overrides
|
|
216
|
+
it. The copies the state migration made there are read again, as they
|
|
217
|
+
were at the migration. A conversation with a store in both places uses
|
|
218
|
+
the one changed last, and one with a store only at the old place keeps
|
|
219
|
+
using it there; nothing is moved or deleted.
|
|
220
|
+
- **`oar setup --migrate-from` works after the installer has run.** The
|
|
221
|
+
state migration wrote its marker even when it found nothing to bring
|
|
222
|
+
over, and every later `--migrate-from <dir>` then did nothing without a
|
|
223
|
+
word. The marker is now written only after a real copy, and a folder
|
|
224
|
+
named with `--migrate-from` is always migrated, with a line saying what
|
|
225
|
+
was copied or why nothing was. It still never overwrites: a `config.json`
|
|
226
|
+
already in the state root is kept; the empty database the installer's
|
|
227
|
+
relay created is moved into `backups/` (the relay has to be stopped
|
|
228
|
+
first, `oar stop`), and a database with conversations makes it refuse
|
|
229
|
+
with what to do instead.
|
|
230
|
+
The same folder again only says it is already migrated, and a refused
|
|
231
|
+
`oar copilot --migrate-from` warns and starts the session anyway. While
|
|
232
|
+
the migration copies a database it holds the relay's lock, so no relay
|
|
233
|
+
starts on it in between. Migrated uploads now land in `data/uploads`,
|
|
234
|
+
where a global install's relay reads them (they went to an `uploads`
|
|
235
|
+
folder nothing read); uploads an earlier migration left in
|
|
236
|
+
`~/.oar/uploads` are merged into `data/uploads` at the next relay start
|
|
237
|
+
(nothing is overwritten or deleted). A migration that fails its check
|
|
238
|
+
removes only what it copied, no longer the whole state folder, and never
|
|
239
|
+
a link or file that was there before; a database it moved aside and
|
|
240
|
+
cannot put back stays in `backups/`. Symlinked folders in the checkout
|
|
241
|
+
are copied as real folders.
|
|
242
|
+
- **An agent's preview tool reaches only its own conversation's previews.**
|
|
243
|
+
The tool's `list` showed every live preview of the relay with its token,
|
|
244
|
+
the only credential of a preview, and `close` closed any of them. `list`
|
|
245
|
+
now shows the calling conversation's previews, and `close` of another
|
|
246
|
+
conversation's preview is refused with a message saying so. Copilot and
|
|
247
|
+
Grok sessions that use the HTTP API are taught the calls with their
|
|
248
|
+
conversation written in. The relay enforces this for every preview
|
|
249
|
+
request that names a conversation (the `conversationId` query or the
|
|
250
|
+
worker's conversation header); the web UI's Settings › Previews and
|
|
251
|
+
`/preview list` still show and close every preview. Agents present the
|
|
252
|
+
same relay token as the owner, so an agent that calls the API by hand
|
|
253
|
+
without naming a conversation still sees them all; see
|
|
254
|
+
docs/preview-servers.md.
|
|
255
|
+
- **A conversation whose working directory is gone says so.** A Claude turn
|
|
256
|
+
in a folder that had been deleted or renamed failed with "native binary …
|
|
257
|
+
exists but failed to launch", blaming the system's libc; a Grok turn said
|
|
258
|
+
the Grok CLI was not installed, a Copilot (SDK engine) turn showed the
|
|
259
|
+
spawn's `ENOENT`. Claude, Copilot, Cursor and Grok workers now check the
|
|
260
|
+
folder before they start their CLI or agent in it, and the turn fails with
|
|
261
|
+
the folder's path and what to do: restore it, or choose another one with
|
|
262
|
+
**⋯ → Change CWD**, then send the message again. A worker that cannot be
|
|
263
|
+
started at all for that reason (a launch without tmux, as on Windows)
|
|
264
|
+
names the folder in its launch note instead of a missing `node` or
|
|
265
|
+
`cmd.exe`.
|
|
266
|
+
- **Answering a question no longer gets a long turn requeued by Max turn
|
|
267
|
+
duration.** Answering one of a turn's questions restarts the max turn
|
|
268
|
+
duration's clock, but only when the relay picked the turns to requeue; the
|
|
269
|
+
requeue itself still counted from the turn's start and took every turn
|
|
270
|
+
past it whenever another turn was due. Such a turn was sent again
|
|
271
|
+
mid-answer, without the retry limit and without the page hearing of it.
|
|
272
|
+
The relay now requeues exactly the turns it picked, by the same rule. The
|
|
273
|
+
settings help and the docs now say where the limit counts from, and that
|
|
274
|
+
Grok also enforces it itself, counted from the start of the prompt and
|
|
275
|
+
without pausing for a remote relay approval card.
|
|
276
|
+
- **A resumed sub-agent shows its record and is listed while it runs.** When
|
|
277
|
+
a Claude session sent a finished background agent a new message, the task
|
|
278
|
+
panel showed it running, but opening it said "This agent's record could
|
|
279
|
+
not be loaded", and the session's task list (`read_session`,
|
|
280
|
+
`list_sessions`) did not list it at all: the relay took it for the
|
|
281
|
+
agent's first run, which had ended. The resumed agent now
|
|
282
|
+
opens its own record, which goes on where the first run ended (a note
|
|
283
|
+
marks where it resumed) and reads running again until it ends; it is a
|
|
284
|
+
running task of the session again, with a start and an end of its own.
|
|
285
|
+
- **Approval cards take a choice, not typed text, and every one reads it the
|
|
286
|
+
same way.** OAR's own Allow / Deny cards (agent sessions, watching
|
|
287
|
+
sessions, write actions in the ask and plan modes) and Claude Cloud's
|
|
288
|
+
permission cards showed a reply box, and whatever was typed there ("yes",
|
|
289
|
+
"ok", "allow it") counted as a denial; Claude Cloud even denied a typed
|
|
290
|
+
"allow". These cards have no reply box now, the relay refuses typed text
|
|
291
|
+
for any card that does not take it (the card stays open), and a choice
|
|
292
|
+
counts however it is typed. A card created through the API with choices
|
|
293
|
+
and no `allowFreeform` is such a card too (the flag defaults to `false`
|
|
294
|
+
when choices are given). Copilot's permission card keeps its reply box,
|
|
295
|
+
and now says that a typed reply refuses and tells Copilot why. A card's
|
|
296
|
+
header (**Agent sessions**, **Relay events**, **Remote relay**, Claude's
|
|
297
|
+
question headers) is shown above the question, and a question's push
|
|
298
|
+
notification names the provider that asks instead of always saying
|
|
299
|
+
"Copilot needs your input".
|
|
300
|
+
- **Reasoning effort reaches every Cursor model that takes one.** Cursor
|
|
301
|
+
models whose effort parameter is named `reasoning_effort` (Claude Haiku 5.5,
|
|
302
|
+
Claude Sonnet 5.5, Grok 4.7, Gemini 4 Argon, Gemini 3.8 Flash, Muse Wonder
|
|
303
|
+
1.0) offered only None in the composer and could not be sent an effort.
|
|
304
|
+
They now list their tiers once the Cursor models are discovered again
|
|
305
|
+
(**Refresh** in **Select Models**, or saving the Cursor key), and the
|
|
306
|
+
chosen effort is sent with the turn.
|
|
307
|
+
|
|
6
308
|
## [0.9.13] — 2026-10-20
|
|
7
309
|
|
|
8
310
|
Highlights: a queued relay restart now shows every page what it waits for,
|
package/README.md
CHANGED
|
@@ -45,29 +45,29 @@ flowchart LR
|
|
|
45
45
|
<table>
|
|
46
46
|
<tr>
|
|
47
47
|
<td width="42%" rowspan="4" valign="top">
|
|
48
|
-
<a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.
|
|
48
|
+
<a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.14"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-mobile.light.png?v=0.9.14"><img src="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.14" alt="OAR on a phone: a finished agent turn in a conversation, with the composer below"></picture></a>
|
|
49
49
|
<br><sub>On a phone: the same conversations, installable as an app.</sub>
|
|
50
50
|
</td>
|
|
51
51
|
<td width="58%" valign="top">
|
|
52
|
-
<a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.
|
|
52
|
+
<a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.14"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-agents.light.png?v=0.9.14"><img src="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.14" alt="Background subagents running in parallel in the task panel, each with live token counts, elapsed time, and its own Stop button"></picture></a>
|
|
53
53
|
<br><sub>Agents working in parallel, each with live token counts and its own Stop.</sub>
|
|
54
54
|
</td>
|
|
55
55
|
</tr>
|
|
56
56
|
<tr>
|
|
57
57
|
<td valign="top">
|
|
58
|
-
<a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.
|
|
58
|
+
<a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.14"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-question.light.png?v=0.9.14"><img src="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.14" alt="A question card in the middle of a turn: the agent offers several choices and a free-text reply"></picture></a>
|
|
59
59
|
<br><sub>When the agent needs a decision, it asks, and waits for your answer.</sub>
|
|
60
60
|
</td>
|
|
61
61
|
</tr>
|
|
62
62
|
<tr>
|
|
63
63
|
<td valign="top">
|
|
64
|
-
<a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.
|
|
64
|
+
<a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.14"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pingcf-thoughts.light.png?v=0.9.14"><img src="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.14" alt="An expanded Thoughts block showing the agent's reasoning above its reply"></picture></a>
|
|
65
65
|
<br><sub>Read the reasoning, not just the answer.</sub>
|
|
66
66
|
</td>
|
|
67
67
|
</tr>
|
|
68
68
|
<tr>
|
|
69
69
|
<td valign="top">
|
|
70
|
-
<a href="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.
|
|
70
|
+
<a href="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.14"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-watching.light.png?v=0.9.14"><img src="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.14" alt="A session watching two other sessions: a finished reply arrives as a compact relay-event row, the watching 2 sessions list is open with Unsubscribe, and a request to also watch a session on a paired relay waits on an Allow / Deny card"></picture></a>
|
|
71
71
|
<br><sub>Agents that watch agents: woken by events, approving nothing.</sub>
|
|
72
72
|
</td>
|
|
73
73
|
</tr>
|
|
@@ -86,7 +86,7 @@ flowchart LR
|
|
|
86
86
|
|
|
87
87
|
You need Node.js 22.13 or newer (the macOS and Linux installer brings its own when yours is missing or older) and an account with the agent you want to use; see [Prerequisites](docs/install.md#prerequisites). There are also installs [with npm](docs/install.md#npm) and [from a git checkout](docs/install.md#from-a-git-checkout).
|
|
88
88
|
|
|
89
|
-
`oar` alone opens a menu. `oar start`, `stop`, `restart` and `status` control the relay, `oar service install` starts it at
|
|
89
|
+
`oar` alone opens a menu. `oar start`, `stop`, `restart` and `status` control the relay, `oar service install` starts it at boot or sign-in (a systemd user service on Linux, the sign-in autostart on Windows), `oar doctor` shows what it uses, and `oar update` installs a new release (as does **⚙️ Settings → General → Check for updates**). All the details are in [Installing and running OAR](docs/install.md): [the `oar` command](docs/install.md#the-oar-command), [where OAR keeps its files](docs/install.md#where-oar-keeps-its-files), and [updating](docs/install.md#updating).
|
|
90
90
|
|
|
91
91
|
<a id="providers"></a>
|
|
92
92
|
<a id="claude-cloud"></a>
|
|
@@ -116,11 +116,11 @@ API keys are entered in **⚙️ Settings** and stay on the relay host. The Clau
|
|
|
116
116
|
<table>
|
|
117
117
|
<tr>
|
|
118
118
|
<td width="62%" valign="top">
|
|
119
|
-
<a href="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.
|
|
119
|
+
<a href="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.14"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-steering.light.png?v=0.9.14"><img src="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.14" alt="A running Claude turn with a message steered into it mid-turn, the reply streaming with its tool calls, and the Steer button in the composer"></picture></a>
|
|
120
120
|
<br><sub>Send a message while the agent works, and it steers the running turn.</sub>
|
|
121
121
|
</td>
|
|
122
122
|
<td width="38%" valign="top">
|
|
123
|
-
<a href="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.
|
|
123
|
+
<a href="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.14"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/usage-copilot-compact.light.png?v=0.9.14"><img src="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.14" alt="The GitHub Copilot card in Check Usage: premium requests and plan quota with reset dates, and the cost of the last turn"></picture></a>
|
|
124
124
|
<br><sub>What each plan has left, in one place.</sub>
|
|
125
125
|
</td>
|
|
126
126
|
</tr>
|
|
@@ -129,7 +129,7 @@ API keys are entered in **⚙️ Settings** and stay on the relay host. The Clau
|
|
|
129
129
|
- **Watch a turn live**: the reply, the agent's thoughts and every tool call appear as they happen, and each sub-agent gets a bubble of its own. **Stop** is always one tap away ([While a turn runs](docs/web-ui.md#while-a-turn-runs)).
|
|
130
130
|
- **Steer or queue**: a message sent during a turn goes into it (Claude, Copilot) or waits behind it ([While a turn runs](docs/web-ui.md#while-a-turn-runs)).
|
|
131
131
|
- **Answer questions**: the agent asks on a card with one-click choices, checkmarks or free text, and waits for you ([Question cards](docs/web-ui.md#question-cards)).
|
|
132
|
-
- **Background work**: a task panel lists the commands and sub-agents that outlive a reply, each with its own **Stop** and its full record; a command's console shows its output live, says when it has gone quiet and counts a test run's passes and failures ([Background tasks](docs/web-ui.md#background-tasks)).
|
|
132
|
+
- **Background work**: a task panel lists the commands and sub-agents that outlive a reply, each with its own **Stop** and its full record; a command's console shows its output live, says when it has gone quiet and counts a test run's passes and failures; the panel and its consoles come back after a relay restart, and a queued restart shows what it waits for and asks after a maximum wait ([Background tasks](docs/web-ui.md#background-tasks), [After a relay restart](docs/web-ui.md#after-a-relay-restart)).
|
|
133
133
|
- **Choose how the agent works**: `ask`, `plan`, `agent` or `autopilot`, plus model and reasoning effort, per message ([Relay modes](docs/web-ui.md#relay-modes)).
|
|
134
134
|
- **Files and git**: browse the workspace, preview files, review git changes in a diff viewer, and publish a dev server as a preview link ([Files, git, and previews](docs/web-ui.md#files-git-and-previews), [preview servers](docs/preview-servers.md)).
|
|
135
135
|
- **Usage and context**: plan limits and credits of every provider, and what fills the model's context window ([Usage and context](docs/web-ui.md#usage-and-context)).
|
|
@@ -137,7 +137,7 @@ API keys are entered in **⚙️ Settings** and stay on the relay host. The Clau
|
|
|
137
137
|
- **Share** a conversation as a read-only link, hide single messages and the host's paths from it, and see when it was opened ([Sharing](docs/web-ui.md#sharing)).
|
|
138
138
|
- **On your phone**: install OAR as an app and get push notifications ([Notifications and the app](docs/web-ui.md#notifications-and-the-app)).
|
|
139
139
|
|
|
140
|
-
The full tour is in [Using the web UI](docs/web-ui.md); every setting and config key is in [Settings and configuration](docs/configuration.md). What changed in each release is in the [changelog](CHANGELOG.md), with [0.9.
|
|
140
|
+
The full tour is in [Using the web UI](docs/web-ui.md); every setting and config key is in [Settings and configuration](docs/configuration.md). What changed in each release is in the [changelog](CHANGELOG.md), with [0.9.14](CHANGELOG.md#0914--2026-10-21) as the latest.
|
|
141
141
|
|
|
142
142
|
## Remote access
|
|
143
143
|
|
|
@@ -171,7 +171,7 @@ The relay keeps both tunnels running and reconnects them by itself. Beyond your
|
|
|
171
171
|
|
|
172
172
|
- **Paired relays**: connect the OAR on your workstation with the OAR on a server. An agent on one can then read, prompt and start sessions on the other, once you mention that relay with `@`.
|
|
173
173
|
- **Agent sessions**: an agent hands parts of its task to new sessions on its own relay, waits for them and reads their results. It is off until you switch it on, the first session asks for your approval, and at most 4 run at once.
|
|
174
|
-
- **Watching sessions**: an agent subscribes to other sessions and is woken when a question waits, an error happens, or a reply arrives, instead of polling.
|
|
174
|
+
- **Watching sessions**: an agent subscribes to other sessions and is woken when a question waits, an error happens, or a reply arrives, instead of polling; if it asks, also when the relay runs again after a restart.
|
|
175
175
|
|
|
176
176
|
```mermaid
|
|
177
177
|
flowchart LR
|
|
@@ -188,7 +188,7 @@ flowchart LR
|
|
|
188
188
|
|
|
189
189
|
You stay in charge: per paired relay you decide whether agents may only read, also prompt, or do everything, and in the `ask` and `plan` modes every write asks you first. A subscription only notifies; it approves nothing.
|
|
190
190
|
|
|
191
|
-
<p><a href="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.
|
|
191
|
+
<p><a href="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.14"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-cross-relay.light.png?v=0.9.14"><img src="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.14" alt="A session watching two sessions on a paired relay: a reply arrives as a relay-event row, and after a short outage the events the other relay kept arrive as one replayed row" width="720"></picture></a></p>
|
|
192
192
|
|
|
193
193
|
How pairing, permissions and limits work is in [Agents working together](docs/agents-together.md).
|
|
194
194
|
|