@oar-sh/oar 0.9.13 → 0.9.15

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.
Files changed (74) hide show
  1. package/.github/extensions/web-relay/extension.mjs +9 -0
  2. package/.github/extensions/web-relay/server-lifecycle/managed-server.mjs +15 -1
  3. package/.github/extensions/web-relay/skills/question-bridge.mjs +4 -1
  4. package/CHANGELOG.md +396 -0
  5. package/README.md +12 -12
  6. package/bin/oar.js +677 -103
  7. package/package.json +7 -5
  8. package/server/claude-cloud-worker/claude-cloud-session-process.mjs +10 -4
  9. package/server/claude-worker/claude-session-process.mjs +39 -13
  10. package/server/claude-worker/claude-turn-publisher.mjs +20 -1
  11. package/server/claude-worker/subagent-timeline-recorder.mjs +75 -8
  12. package/server/copilot-worker/copilot-ask-user-tool.mjs +1 -1
  13. package/server/copilot-worker/copilot-prompt-context.mjs +3 -1
  14. package/server/copilot-worker/copilot-question-bridge.mjs +11 -6
  15. package/server/copilot-worker/copilot-sdk-adapter.mjs +11 -0
  16. package/server/copilot-worker/copilot-sdk-session-process.mjs +14 -1
  17. package/server/cursor-worker/cursor-ask-user-tool.mjs +1 -1
  18. package/server/cursor-worker/cursor-sdk-adapter.mjs +22 -7
  19. package/server/cursor-worker/cursor-session-worker.mjs +9 -2
  20. package/server/cursor-worker/cursor-turn-runner.mjs +18 -3
  21. package/server/grok-worker/grok-sdk-adapter.mjs +14 -0
  22. package/server/grok-worker/grok-turn-runner.mjs +13 -3
  23. package/server/public/app/api-client.js +3 -1
  24. package/server/public/app/ask-user-view.js +29 -4
  25. package/server/public/app/conversation-provider-indicator.mjs +18 -8
  26. package/server/public/app/question-answer-rule.mjs +15 -0
  27. package/server/public/app/question-multi-select.mjs +9 -2
  28. package/server/public/app/relay-event-view.mjs +5 -1
  29. package/server/public/app/settings-modal.js +17 -3
  30. package/server/public/index.html +9 -5
  31. package/server/relay-exit-codes.mjs +6 -0
  32. package/server/relay-self-restart.mjs +6 -1
  33. package/server/relay-tools.md +14 -11
  34. package/server/repositories/message-repository.mjs +29 -28
  35. package/server/repositories/subagent-timeline-statements.mjs +2 -0
  36. package/server/routes/ask-user-routes.mjs +41 -7
  37. package/server/routes/messages-routes.mjs +65 -10
  38. package/server/routes/preview-routes.mjs +73 -7
  39. package/server/routes/sessions-routes.mjs +49 -16
  40. package/server/server-runtime.mjs +156 -94
  41. package/server/services/cursor-session-root-service.mjs +70 -6
  42. package/server/services/data-dir-resolution.mjs +208 -19
  43. package/server/services/earlier-database-service.mjs +631 -0
  44. package/server/services/oar-cli-helpers.mjs +18 -5
  45. package/server/services/oar-state-layout.mjs +107 -0
  46. package/server/services/oar-state-migration-service.mjs +387 -96
  47. package/server/services/package-data-rescue-preinstall.mjs +22 -0
  48. package/server/services/package-data-rescue.mjs +137 -0
  49. package/server/services/preview-registry-service.mjs +3 -2
  50. package/server/services/push-dispatch-service.mjs +33 -6
  51. package/server/services/relay-event-service.mjs +183 -5
  52. package/server/services/relay-restart-queue.mjs +10 -3
  53. package/server/services/relay-singleton-guard.mjs +300 -36
  54. package/server/services/remote-relay-caller-context.mjs +7 -2
  55. package/server/services/remote-relay-dispatcher.mjs +30 -9
  56. package/server/services/session-activity-tracker.mjs +52 -15
  57. package/server/services/session-worker-launch-service.mjs +31 -1
  58. package/server/services/stale-turn-recovery.mjs +99 -0
  59. package/server/services/subagent-timeline-service.mjs +4 -1
  60. package/server/services/windows-autostart-service.mjs +415 -45
  61. package/server/services/windows-boot-autostart-service.mjs +962 -185
  62. package/shared/preview-instructions.mjs +17 -12
  63. package/shared/preview-tool-core.mjs +32 -14
  64. package/shared/question-answer-rule.mjs +63 -0
  65. package/shared/relay-events.mjs +54 -4
  66. package/shared/remote-relay-contract.mjs +20 -10
  67. package/shared/subagent-timeline.mjs +4 -0
  68. package/shared/windows-system-tools.mjs +25 -0
  69. package/shared/worker-runtime/config-loader.mjs +54 -8
  70. package/shared/worker-runtime/prompt-context.mjs +3 -1
  71. package/shared/worker-runtime/working-directory.mjs +65 -0
  72. package/.github/extensions/web-relay/README.md +0 -190
  73. package/server/README.md +0 -2187
  74. 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
- allowFreeform: allowFreeform ?? !choices.length,
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,402 @@
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.15] — 2026-10-22
7
+
8
+ Highlights: `oar setup` asks how the relay starts on Windows, at sign-in or
9
+ at boot before anyone signs in, and `oar autostart` switches between them
10
+ later. `oar uninstall` takes OAR off the machine and keeps your data unless
11
+ you ask for `--purge`. A declined administrator prompt changes nothing, boot
12
+ tasks work in profile folders with spaces or non-ASCII names, and a
13
+ hand-made task, or one only an administrator can read, is never deleted
14
+ without a copy. The package is smaller.
15
+
16
+ ### Upgrade notes
17
+
18
+ - **From 0.9.14 with a hand-made autostart task:** switching to boot mode
19
+ in 0.9.14 could delete an existing task of the same name (`oar-relay` or
20
+ the pre-rebrand name) without a copy. 0.9.15 keeps such a task and asks
21
+ for `--replace`, which saves a copy first.
22
+
23
+ ### Added
24
+
25
+ - **`oar setup` asks how the relay starts** on Windows: at sign-in, at boot
26
+ (before anyone signs in; one administrator prompt) or not at all, with the
27
+ current mode as the default. `--autostart signin|boot|off` answers it
28
+ without a question.
29
+ - **`oar autostart signin|boot|off|status`.** Switching to or from boot mode
30
+ asks for an administrator once, only for the schtasks call; sign-in mode
31
+ needs none. A declined prompt changes nothing. `status` lists each entry,
32
+ whether OAR generated it, and which other OAR install a stale one starts.
33
+ `--replace` replaces hand-made entries with generated ones, keeping a copy
34
+ of each (`<name>.bak`: a launcher's beside it, a task's exported XML and a
35
+ Startup entry's in the OAR folder); nothing else ever does. On Linux it is
36
+ the systemd user service (`oar service`); elsewhere it says it is not
37
+ supported.
38
+ - **`oar uninstall [--purge]`** stops the relay, takes out the autostart
39
+ entries OAR generated and the Copilot extension wrapper OAR wrote for this
40
+ install (a changed one, or one of another install, stays and is named),
41
+ and has npm remove the package once `oar` has ended. **The data stays** (config,
42
+ database, uploads, logs in the state root), and the command says where.
43
+ `--purge` deletes the state root after you type `delete`; a data folder
44
+ outside it, one in a git checkout, and `rescued-package-data` are each
45
+ deleted only after a question of their own; a drive root, the home folder
46
+ or a folder above it, a system folder and a folder without OAR's config or
47
+ database in it are refused, whatever is typed. A relay that holds its lock
48
+ without answering (bringing over a database) stops the command before it
49
+ deletes anything. Programs that still run from the package (session
50
+ workers) are named: npm leaves the files they hold in a hidden `.oar-*`
51
+ folder, which the log names. A step
52
+ that fails stops the command, which says what is done and what is left.
53
+
54
+ ### Changed
55
+
56
+ - The two developer READMEs (`server/README.md` with the HTTP API, and the
57
+ web-relay extension's) are no longer in the npm package; they are on
58
+ [GitHub](https://github.com/oar-sh/oar).
59
+
60
+ ### Fixed
61
+
62
+ - **A hand-made autostart entry is never deleted or written over.** Leaving
63
+ boot mode deleted a boot task or launcher edited by hand, and *Off* also
64
+ a task OAR did not make; switching to sign-in replaced an edited Startup
65
+ entry. Only entries exactly as an OAR version generated them are changed
66
+ now, in the web UI as in `oar autostart`: a hand-made entry that would stay
67
+ beside the new mode, or be replaced by it, stops the switch with the same
68
+ message. Picking *At system startup* no longer replaces a hand-edited
69
+ launcher or a task someone else made, the pre-rebrand one included. A boot
70
+ task is OAR's own only while it is exactly the one OAR registers: the
71
+ account, the run level, one action, one boot trigger and its settings.
72
+ - **Boot mode switches on and off with a space, an apostrophe or non-ASCII
73
+ characters in the profile path.** The elevated step was a script file
74
+ passed unquoted, and read in the wrong encoding; it now travels as an
75
+ encoded command with every path quoted. A failed switch is no longer
76
+ reported as a declined prompt: it puts back the tasks it deleted from their
77
+ copies and keeps the copies, and a declined one leaves no file behind.
78
+ Windows PowerShell can report a declined prompt with an error that does not
79
+ stop it, or with no process at all; that read as confirmed, so leaving boot
80
+ mode deleted the launcher but kept the task, and switching it on removed
81
+ the sign-in entry without registering the task. Both now count as declined.
82
+ Leaving boot mode in the web UI switches the sign-in entry only after the
83
+ prompt was confirmed. Leaving boot mode removes the task XML and the record of
84
+ the sweep too, and the web UI names a Startup entry OAR did not make.
85
+ - **A boot task with non-ASCII characters in its paths reads right.**
86
+ schtasks writes its XML in the console's code page, so a profile path like
87
+ `C:\Users\Jürgen` came back garbled and OAR took its own task for a
88
+ hand-made one: switching boot on reported a failure and deleted the new
89
+ launcher, and *Off* refused to remove the task. Such a task is now read
90
+ again through PowerShell in UTF-8. A task that still cannot be read is
91
+ never taken as missing, removed, put back or someone else's, and never
92
+ deleted without a copy: nothing is changed while it cannot be read, and
93
+ after a confirmed prompt OAR says it could not check, and undoes nothing.
94
+ - **A boot task only an administrator can read is never deleted.** One that
95
+ stores another account's password answers "Access is denied" to OAR; that
96
+ read as no task, so `oar autostart boot` deleted it without a copy. Any
97
+ failed read of a task that is there now counts as unreadable: status says
98
+ so, and OAR changes nothing, `--replace` included.
99
+
100
+ ## [0.9.14] — 2026-10-21
101
+
102
+ Highlights: a relay started by Windows autostart, or by a plain
103
+ `gh copilot`, now runs on the same data folder as `oar start`, outside the
104
+ installed package, so an update no longer takes its conversations with it;
105
+ the update copies that data before npm removes the old version, and the
106
+ relay takes it over at its next start (read the upgrade note first). A
107
+ relay never moves to another database unannounced, a lock an unclean end
108
+ left no longer blocks the next start, autostart waits for a late drive, and
109
+ Cursor conversations keep their agent across updates. An agent can ask to
110
+ be woken when the relay runs again (`relay_restarted`), its preview tool
111
+ reaches only its own conversation's previews, approval cards take a choice
112
+ instead of typed text, and a conversation whose folder is gone says so.
113
+
114
+ ### Upgrade notes
115
+
116
+ - **Windows autostart users, and anyone whose relay a plain `gh copilot`
117
+ started: save your data before this update.** Before 0.9.14, those relays
118
+ ran on the database and uploads inside the installed package, and
119
+ `npm i -g` deletes them with the old version. This update copies them
120
+ first, while it installs, to `rescued-package-data/<time>/` in the OAR
121
+ data folder (`%APPDATA%\oar\data`, `~/.oar/data`), and the relay takes
122
+ the copy over at its next start. npm 10 and 11 run that copy (11.21 with
123
+ a warning), and if it fails they put the old version back with its data.
124
+ npm 12 skips it unless `@oar-sh/oar` is allowed: the install then
125
+ succeeds and the old data is gone. With `strict-allow-scripts`, npm 11
126
+ and 12 refuse the install and change nothing; `--ignore-scripts` skips
127
+ the copy everywhere, and the native modules (better-sqlite3, koffi) too,
128
+ so the relay cannot start afterwards: never update OAR with
129
+ `--ignore-scripts`. On npm 12, update with the relay stopped:
130
+ `npm i -g @oar-sh/oar --allow-scripts=@oar-sh/oar,better-sqlite3,koffi`.
131
+ An `allow-scripts` entry in `.npmrc` does not help the **Update** button
132
+ or `oar update` of 0.9.13: their own `--allow-scripts` replaces it. To be
133
+ safe, do this update by hand:
134
+ 1. Close every `gh copilot` session (its extension starts the relay
135
+ again), then `oar stop`, and wait until `oar status` says it is not
136
+ running. If 0.9.13's `oar stop` does not stop a relay the
137
+ system-startup launcher started, end the relay before installing:
138
+ end the relay's processes in Task Manager (node.exe running OAR).
139
+ 2. Run `npm root -g` and open `<that folder>/@oar-sh/oar/server`
140
+ (usually `%APPDATA%\npm\node_modules\@oar-sh\oar\server`). Copy
141
+ **both** the `data` and the `uploads` folders, with every file in
142
+ them, to a safe place.
143
+ 3. `npm i -g @oar-sh/oar --allow-scripts=@oar-sh/oar,better-sqlite3,koffi`.
144
+ 4. Before starting the relay, copy both folders back into the same
145
+ `server` folder.
146
+ 5. `oar start`. In `relay-console.log` in the OAR `logs` folder, look
147
+ for `[data] Brought over the database …`: your conversations should
148
+ be back. A database that was already in the OAR data folder is kept
149
+ in `backups/<time>/` beside it. The folder brought over keeps its
150
+ database as `copilot.db.adopted-<time>`; the others looked at keep
151
+ theirs as `copilot.db.older-<time>` (if a rescue copy was used, the
152
+ package's own database ends up `.older-`).
153
+ 6. If the relay stops with `[data] Not starting: each database has
154
+ changes newer than the other's`, both hold work; the error line names
155
+ both folders, and one may be under
156
+ `rescued-package-data/<time>/data`. Pick the one to keep, and move
157
+ the other one's `copilot.db`, `copilot.db-wal` and `copilot.db-shm`
158
+ into a folder of your own (do not delete them). Then `oar start`
159
+ again.
160
+ 7. If you already updated without saving and nothing was copied, the
161
+ database from the package can't be recovered by OAR.
162
+ - The npm package guard allows 8 MiB unpacked and 530 entries (was 7.75
163
+ MiB and 520); the take-over of earlier databases, the install-time copy
164
+ and the lock checks grew the package to about 7.75 MiB and 521 entries.
165
+
166
+ ### Added
167
+
168
+ - **Woken when the relay runs again** (`relay_restarted`, opt-in per
169
+ conversation). A conversation that queued a relay restart, or watched
170
+ sessions, slept after the restart until someone wrote, because the timers
171
+ its CLI had scheduled might not survive it. An agent can now subscribe to
172
+ `relay_restarted` through `remote_relay` (no sessions needed, no approval
173
+ card, this relay only); after each start it gets one relay event with when
174
+ the relay came back and how it stopped (who requested the restart, when
175
+ and why, or that it did not record how it stopped). Never part of the
176
+ default events. A restart requested while a turn it woke still runs does
177
+ not wake that conversation (a one-shot subscription held back so ends),
178
+ archived and deleted conversations are not woken, and each start is told
179
+ once.
180
+
181
+ ### Changed
182
+
183
+ - **Help texts and CLI messages say what the relay does.** **Accept prompts
184
+ from other relays' agents** lists everything it refuses when off, watching
185
+ sessions included; **Show Suspend host action** says it is a switch of this
186
+ browser and suspend works on Windows relays only; the Cursor session token
187
+ names the real usage bars; the Copilot engine applies whenever a
188
+ conversation's worker starts. `oar` points to **Settings → General →
189
+ Autostart (Windows)**, says the service starts again at boot with
190
+ lingering, sends a failed start or restart through the service to
191
+ `journalctl --user -u oar` instead of `server-err.log`, words a busy port
192
+ one way, and says an already running relay keeps its version and settings
193
+ until `oar restart`.
194
+ - **Agent guidance.** The ask_user example in the Copilot engines' relay
195
+ guidance is a real question instead of quiz placeholders, the guidance and
196
+ the Cursor and Copilot ask_user descriptions give the choice counts the
197
+ relay accepts (it keeps the first 8), the remote relay section names every
198
+ subscription action and `usage`, and the restart note says a queued
199
+ restart waits for every running turn and holds new messages.
200
+ - **Docs brought in line with the code**: the paired-relay switch, watch
201
+ approvals per relay, no push for agent-started turns, provider rebinding of
202
+ unstarted chats, OpenAI key removal, `oar stop`/`oar restart` under the
203
+ systemd service (they end running turns and workers), Claude turns on the
204
+ bundled Claude Code, the config keys and environment variables, which
205
+ variables reach workers started under tmux, the worker supervisor and the
206
+ database migrations.
207
+ - **Uploads follow the config's `dataDir`.** It moved the database but
208
+ left uploads in `server/uploads` unless `COPILOT_WEB_RELAY_DATA_DIR` was
209
+ set; they now go to `<dataDir>/uploads` as well. Uploads earlier versions
210
+ left in `server/uploads`, or that a state migration copied into the state
211
+ root's `uploads/`, are still read from there in place: every attachment
212
+ opens after the update, and nothing in those folders is moved or deleted.
213
+ - `@anthropic-ai/claude-agent-sdk` 0.3.304 (was 0.3.292; bundled Claude Code
214
+ 2.1.304). Claude now forks sub-agents that inherit the conversation
215
+ (`subagent_type: "fork"`) by default. With thinking turned off, a model that
216
+ cannot run without thinking now uses adaptive thinking. A full disk during a
217
+ login refresh is reported as a failed request instead of a sign-in error.
218
+ Fixes include an approved tool call that could run twice on resume, model
219
+ switches and interrupts waiting behind a context-usage check, and
220
+ `hook_progress` messages piling up in memory.
221
+ - `@cursor/sdk` 1.0.38 (was 1.0.36), which uses the relay's own `zod`
222
+ instead of carrying a copy of zod 3.
223
+ - `@github/copilot-sdk` 1.0.23 (was 1.0.17; bundled Copilot CLI 1.0.100).
224
+ The Copilot workers and the web-relay extension are unchanged: under the
225
+ Copilot CLI both run the SDK that comes with the installed CLI. **About
226
+ OAR** shows the new version.
227
+
228
+ ### Fixed
229
+
230
+ - **Windows autostart starts the relay on the same data as `oar start`.**
231
+ The sign-in entry and the system-startup launcher set only the config
232
+ file, so a relay they started kept its database, uploads and log inside
233
+ the installed package: other conversations and settings than with
234
+ `oar start`, and gone with the next update. Both now also set the data
235
+ and log folders `oar start` uses (`%APPDATA%\oar\data` and
236
+ `%APPDATA%\oar\logs` on a global install). Once it runs, the relay brings
237
+ an entry or launcher that an earlier version (0.9.0 on) generated for
238
+ this install and its config up to date, the pre-rebrand
239
+ `copilot-remote-web-relay.cmd` under its own name, so the first start
240
+ after the update (by `oar start`, `oar update` or the autostart itself)
241
+ repairs it. An entry edited by hand, one that points at another config
242
+ file, and one of a relay started on data folders of its own are left
243
+ alone: switch autostart off and on in Settings to replace those. Rewriting
244
+ a launcher while the relay it started still runs is safe: its cmd window
245
+ ends with the relay instead of starting it a second time. The launchers
246
+ now switch cmd to UTF-8, so a user folder with non-ASCII characters in its
247
+ name no longer garbles the paths in them. Conversations a relay started
248
+ by autostart kept inside the package folder are not carried over.
249
+ - **A relay never moves to another database unannounced.** Before its own
250
+ database opens, a global install's relay pinned to the OAR data folder
251
+ looks once at each database it may have run on until now (the one inside
252
+ the package, the install-time copies of it): a newer one is copied in
253
+ after a `quick_check` of the copy, with the uploads and Cursor agent
254
+ stores (never over a file there), the older one in place is kept in
255
+ `backups/<time>/`, and one line says so. Each one looked at is renamed
256
+ in place to `copilot.db.adopted-<time>` or `copilot.db.older-<time>`,
257
+ never deleted. When neither is clearly newer, or a relay may still run
258
+ on the other, the relay stops with one line naming both and exit code
259
+ 78, which its supervisors, and a systemd unit written by this version's
260
+ `oar service install`, do not retry. The swap survives a kill. A global
261
+ install always runs on the state root's data folder, however it is
262
+ started; a `dataDir` in config.json that an earlier autostart relay used
263
+ is copied from once and left exactly as it is. A git checkout and a
264
+ relay on another state root are left alone. A first start that finds an earlier launcher
265
+ but no database of it says the update may have removed it. A large
266
+ database takes minutes: the relay logs its size at the start, progress
267
+ at least once a minute and each step's time at the end, and `oar start`
268
+ meanwhile says it is still bringing it over. A second `oar start` then
269
+ no longer removes the relay's lock and starts a second relay.
270
+ - **A relay lock an unclean end left no longer blocks the next start.** A
271
+ lock from before the last boot, or whose pid now belongs to a process
272
+ started after it, is stale; a lock still held stops the start with one
273
+ line naming it and exit code 78. A lock is never seen half written, and
274
+ one that cannot be read is read again and held unless its time is more
275
+ than 10 s off (an empty one a power loss left). Two starts recovering
276
+ the same stale lock no longer both run.
277
+ - **A `TEMP` or `TMP` set with quotes, or naming a missing folder, is fixed
278
+ for the relay** on Windows (quotes off, else the user's temp folder).
279
+ - **The relay a plain `gh copilot` starts keeps its data outside the
280
+ package.** After `oar copilot` installed the Copilot CLI extension for
281
+ every folder, a plain `gh copilot` ran its relay on `server/data` and
282
+ `server/config.json` inside the installed package, on every platform. A
283
+ global install's extension now starts it on the OAR state root's
284
+ config, data and logs, as `oar start` does, and copies a config the
285
+ package held there once. The database from the package is saved only
286
+ where the install-time copy ran (see the upgrade note).
287
+ - **Windows autostart waits for a late drive.** Both the sign-in entry and
288
+ the system-startup launcher now wait up to 5 minutes in all for the
289
+ `%TEMP%` folder and the drive of the data folder (a RAM disk or a drive a
290
+ startup script mounts later), where the system-startup launcher waited
291
+ 2 minutes for `%TEMP%` only and the sign-in entry not at all. A wait that
292
+ runs out writes one line naming what is still missing, then starts the
293
+ relay anyway: `oar-web-relay-launcher.log` beside the system-startup
294
+ launcher, `oar-web-relay.log` in the OAR folder (`%APPDATA%\oar`) for the
295
+ sign-in entry. A data folder on a network share is not waited for. The
296
+ first start after the update brings a generated entry or launcher up to
297
+ date without an admin confirmation; edited ones stay as they are. A relay
298
+ whose data or upload folder cannot be created (its drive still missing, a
299
+ file in its place) now stops with one line naming the folder and exit
300
+ code 78 instead of an error trace. A `TEMP`
301
+ set with quotes (`"D:\My Temp"`) no longer stops the system-startup
302
+ launcher before it starts the relay.
303
+ - **Cursor conversations keep their agent across updates.** The Cursor
304
+ worker kept its per-conversation agent stores in
305
+ `server/data/cursor-agents` inside the installed package, so an update
306
+ deleted them and a Cursor conversation went on with a new agent that knew
307
+ nothing of the earlier turns. The stores now live in `cursor-agents` in
308
+ the relay's data folder (`~/.oar/data` on a global install, unchanged
309
+ `server/data` in a checkout); `CURSOR_AGENT_STORE_DIR` still overrides
310
+ it. The copies the state migration made there are read again, as they
311
+ were at the migration. A conversation with a store in both places uses
312
+ the one changed last, and one with a store only at the old place keeps
313
+ using it there; nothing is moved or deleted.
314
+ - **`oar setup --migrate-from` works after the installer has run.** The
315
+ state migration wrote its marker even when it found nothing to bring
316
+ over, and every later `--migrate-from <dir>` then did nothing without a
317
+ word. The marker is now written only after a real copy, and a folder
318
+ named with `--migrate-from` is always migrated, with a line saying what
319
+ was copied or why nothing was. It still never overwrites: a `config.json`
320
+ already in the state root is kept; the empty database the installer's
321
+ relay created is moved into `backups/` (the relay has to be stopped
322
+ first, `oar stop`), and a database with conversations makes it refuse
323
+ with what to do instead.
324
+ The same folder again only says it is already migrated, and a refused
325
+ `oar copilot --migrate-from` warns and starts the session anyway. While
326
+ the migration copies a database it holds the relay's lock, so no relay
327
+ starts on it in between. Migrated uploads now land in `data/uploads`,
328
+ where a global install's relay reads them (they went to an `uploads`
329
+ folder nothing read); uploads an earlier migration left in
330
+ `~/.oar/uploads` are merged into `data/uploads` at the next relay start
331
+ (nothing is overwritten or deleted). A migration that fails its check
332
+ removes only what it copied, no longer the whole state folder, and never
333
+ a link or file that was there before; a database it moved aside and
334
+ cannot put back stays in `backups/`. Symlinked folders in the checkout
335
+ are copied as real folders.
336
+ - **An agent's preview tool reaches only its own conversation's previews.**
337
+ The tool's `list` showed every live preview of the relay with its token,
338
+ the only credential of a preview, and `close` closed any of them. `list`
339
+ now shows the calling conversation's previews, and `close` of another
340
+ conversation's preview is refused with a message saying so. Copilot and
341
+ Grok sessions that use the HTTP API are taught the calls with their
342
+ conversation written in. The relay enforces this for every preview
343
+ request that names a conversation (the `conversationId` query or the
344
+ worker's conversation header); the web UI's Settings › Previews and
345
+ `/preview list` still show and close every preview. Agents present the
346
+ same relay token as the owner, so an agent that calls the API by hand
347
+ without naming a conversation still sees them all; see
348
+ docs/preview-servers.md.
349
+ - **A conversation whose working directory is gone says so.** A Claude turn
350
+ in a folder that had been deleted or renamed failed with "native binary …
351
+ exists but failed to launch", blaming the system's libc; a Grok turn said
352
+ the Grok CLI was not installed, a Copilot (SDK engine) turn showed the
353
+ spawn's `ENOENT`. Claude, Copilot, Cursor and Grok workers now check the
354
+ folder before they start their CLI or agent in it, and the turn fails with
355
+ the folder's path and what to do: restore it, or choose another one with
356
+ **⋯ → Change CWD**, then send the message again. A worker that cannot be
357
+ started at all for that reason (a launch without tmux, as on Windows)
358
+ names the folder in its launch note instead of a missing `node` or
359
+ `cmd.exe`.
360
+ - **Answering a question no longer gets a long turn requeued by Max turn
361
+ duration.** Answering one of a turn's questions restarts the max turn
362
+ duration's clock, but only when the relay picked the turns to requeue; the
363
+ requeue itself still counted from the turn's start and took every turn
364
+ past it whenever another turn was due. Such a turn was sent again
365
+ mid-answer, without the retry limit and without the page hearing of it.
366
+ The relay now requeues exactly the turns it picked, by the same rule. The
367
+ settings help and the docs now say where the limit counts from, and that
368
+ Grok also enforces it itself, counted from the start of the prompt and
369
+ without pausing for a remote relay approval card.
370
+ - **A resumed sub-agent shows its record and is listed while it runs.** When
371
+ a Claude session sent a finished background agent a new message, the task
372
+ panel showed it running, but opening it said "This agent's record could
373
+ not be loaded", and the session's task list (`read_session`,
374
+ `list_sessions`) did not list it at all: the relay took it for the
375
+ agent's first run, which had ended. The resumed agent now
376
+ opens its own record, which goes on where the first run ended (a note
377
+ marks where it resumed) and reads running again until it ends; it is a
378
+ running task of the session again, with a start and an end of its own.
379
+ - **Approval cards take a choice, not typed text, and every one reads it the
380
+ same way.** OAR's own Allow / Deny cards (agent sessions, watching
381
+ sessions, write actions in the ask and plan modes) and Claude Cloud's
382
+ permission cards showed a reply box, and whatever was typed there ("yes",
383
+ "ok", "allow it") counted as a denial; Claude Cloud even denied a typed
384
+ "allow". These cards have no reply box now, the relay refuses typed text
385
+ for any card that does not take it (the card stays open), and a choice
386
+ counts however it is typed. A card created through the API with choices
387
+ and no `allowFreeform` is such a card too (the flag defaults to `false`
388
+ when choices are given). Copilot's permission card keeps its reply box,
389
+ and now says that a typed reply refuses and tells Copilot why. A card's
390
+ header (**Agent sessions**, **Relay events**, **Remote relay**, Claude's
391
+ question headers) is shown above the question, and a question's push
392
+ notification names the provider that asks instead of always saying
393
+ "Copilot needs your input".
394
+ - **Reasoning effort reaches every Cursor model that takes one.** Cursor
395
+ models whose effort parameter is named `reasoning_effort` (Claude Haiku 5.5,
396
+ Claude Sonnet 5.5, Grok 4.7, Gemini 4 Argon, Gemini 3.8 Flash, Muse Wonder
397
+ 1.0) offered only None in the composer and could not be sent an effort.
398
+ They now list their tiers once the Cursor models are discovered again
399
+ (**Refresh** in **Select Models**, or saving the Cursor key), and the
400
+ chosen effort is sent with the turn.
401
+
6
402
  ## [0.9.13] — 2026-10-20
7
403
 
8
404
  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.13"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-mobile.light.png?v=0.9.13"><img src="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.13" alt="OAR on a phone: a finished agent turn in a conversation, with the composer below"></picture></a>
48
+ <a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.15"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-mobile.light.png?v=0.9.15"><img src="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.15" 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.13"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-agents.light.png?v=0.9.13"><img src="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.13" alt="Background subagents running in parallel in the task panel, each with live token counts, elapsed time, and its own Stop button"></picture></a>
52
+ <a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.15"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-agents.light.png?v=0.9.15"><img src="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.15" 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.13"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-question.light.png?v=0.9.13"><img src="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.13" alt="A question card in the middle of a turn: the agent offers several choices and a free-text reply"></picture></a>
58
+ <a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.15"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-question.light.png?v=0.9.15"><img src="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.15" 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.13"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pingcf-thoughts.light.png?v=0.9.13"><img src="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.13" alt="An expanded Thoughts block showing the agent's reasoning above its reply"></picture></a>
64
+ <a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.15"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pingcf-thoughts.light.png?v=0.9.15"><img src="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.15" 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.13"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-watching.light.png?v=0.9.13"><img src="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.13" 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>
70
+ <a href="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.15"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-watching.light.png?v=0.9.15"><img src="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.15" 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 every login, `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).
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.13"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-steering.light.png?v=0.9.13"><img src="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.13" 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>
119
+ <a href="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.15"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-steering.light.png?v=0.9.15"><img src="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.15" 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.13"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/usage-copilot-compact.light.png?v=0.9.13"><img src="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.13" 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>
123
+ <a href="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.15"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/usage-copilot-compact.light.png?v=0.9.15"><img src="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.15" 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.13](CHANGELOG.md#0913--2026-10-20) as the latest.
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.15](CHANGELOG.md#0915--2026-10-22) 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.13"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-cross-relay.light.png?v=0.9.13"><img src="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.13" 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>
191
+ <p><a href="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.15"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-cross-relay.light.png?v=0.9.15"><img src="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.15" 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