@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.
Files changed (72) 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 +302 -0
  5. package/README.md +12 -12
  6. package/bin/oar.js +155 -94
  7. package/package.json +5 -5
  8. package/server/README.md +296 -149
  9. package/server/claude-cloud-worker/claude-cloud-session-process.mjs +10 -4
  10. package/server/claude-worker/claude-session-process.mjs +39 -13
  11. package/server/claude-worker/claude-turn-publisher.mjs +20 -1
  12. package/server/claude-worker/subagent-timeline-recorder.mjs +75 -8
  13. package/server/copilot-worker/copilot-ask-user-tool.mjs +1 -1
  14. package/server/copilot-worker/copilot-prompt-context.mjs +3 -1
  15. package/server/copilot-worker/copilot-question-bridge.mjs +11 -6
  16. package/server/copilot-worker/copilot-sdk-adapter.mjs +11 -0
  17. package/server/copilot-worker/copilot-sdk-session-process.mjs +14 -1
  18. package/server/cursor-worker/cursor-ask-user-tool.mjs +1 -1
  19. package/server/cursor-worker/cursor-sdk-adapter.mjs +22 -7
  20. package/server/cursor-worker/cursor-session-worker.mjs +9 -2
  21. package/server/cursor-worker/cursor-turn-runner.mjs +18 -3
  22. package/server/grok-worker/grok-sdk-adapter.mjs +14 -0
  23. package/server/grok-worker/grok-turn-runner.mjs +13 -3
  24. package/server/public/app/api-client.js +3 -1
  25. package/server/public/app/ask-user-view.js +29 -4
  26. package/server/public/app/conversation-provider-indicator.mjs +18 -8
  27. package/server/public/app/question-answer-rule.mjs +15 -0
  28. package/server/public/app/question-multi-select.mjs +9 -2
  29. package/server/public/app/relay-event-view.mjs +5 -1
  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 +4 -2
  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 +6 -1
  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 +329 -31
  61. package/server/services/windows-boot-autostart-service.mjs +188 -60
  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/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,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.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.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.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.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.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.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.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.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.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.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 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.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.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.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.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.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.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.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