@oar-sh/oar 0.9.2 → 0.9.4

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 (138) hide show
  1. package/.github/extensions/web-relay/extension.mjs +6 -1
  2. package/.github/extensions/web-relay/skills/tool-activity.mjs +7 -0
  3. package/CHANGELOG.md +336 -0
  4. package/README.md +455 -423
  5. package/bin/oar.js +25 -10
  6. package/package.json +11 -5
  7. package/server/README.md +44 -4
  8. package/server/claude-worker/claude-remote-relay-tool.mjs +99 -0
  9. package/server/claude-worker/claude-sdk-adapter.mjs +26 -4
  10. package/server/claude-worker/claude-session-process.mjs +1073 -69
  11. package/server/claude-worker/claude-session-worker.mjs +45 -4
  12. package/server/claude-worker/claude-turn-publisher.mjs +81 -20
  13. package/server/claude-worker/claude-worker-link-wiring.mjs +9 -0
  14. package/server/claude-worker/sdk-message-normalizer.mjs +7 -0
  15. package/server/copilot-worker/copilot-ask-user-tool.mjs +89 -0
  16. package/server/copilot-worker/copilot-continuation-signals.mjs +55 -0
  17. package/server/copilot-worker/copilot-question-bridge.mjs +34 -12
  18. package/server/copilot-worker/copilot-remote-relay-tool.mjs +59 -0
  19. package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +113 -14
  20. package/server/copilot-worker/copilot-sdk-session-process.mjs +2349 -498
  21. package/server/copilot-worker/copilot-sdk-session-worker.mjs +82 -20
  22. package/server/cursor-worker/cursor-remote-relay-tool.mjs +65 -0
  23. package/server/cursor-worker/cursor-turn-runner.mjs +45 -2
  24. package/server/cursor-worker/sdk-message-normalizer.mjs +25 -0
  25. package/server/db-schema.mjs +28 -0
  26. package/server/grok-worker/acp-client.mjs +92 -6
  27. package/server/grok-worker/grok-turn-runner.mjs +59 -8
  28. package/server/grok-worker/sdk-message-normalizer.mjs +13 -0
  29. package/server/mcp/oar-mcp-server.mjs +473 -0
  30. package/server/migrations/0004-steer-settle-markers.mjs +119 -0
  31. package/server/migrations/0005-remote-relays.mjs +90 -0
  32. package/server/public/app/action-confirmations.js +141 -13
  33. package/server/public/app/api-client.js +125 -2
  34. package/server/public/app/ask-user-view.js +102 -13
  35. package/server/public/app/attachments-view.js +37 -2
  36. package/server/public/app/background-task-timeout-options.mjs +9 -0
  37. package/server/public/app/background-tasks-view.mjs +33 -18
  38. package/server/public/app/bootstrap.js +55 -14
  39. package/server/public/app/composer-control-state.mjs +52 -34
  40. package/server/public/app/conversation-deep-link.mjs +42 -0
  41. package/server/public/app/conversation-draft-sync.mjs +187 -0
  42. package/server/public/app/conversation-list-filter.mjs +86 -0
  43. package/server/public/app/conversation-provider-indicator.mjs +8 -0
  44. package/server/public/app/conversation-view.js +1016 -200
  45. package/server/public/app/emoji-view.js +2 -0
  46. package/server/public/app/font-scaling.js +4 -37
  47. package/server/public/app/host-suspend-ui.js +272 -0
  48. package/server/public/app/journal-view.js +149 -6
  49. package/server/public/app/mention-autocomplete.mjs +298 -0
  50. package/server/public/app/model-selector-options.mjs +1 -1
  51. package/server/public/app/push-settings.js +1 -0
  52. package/server/public/app/pwa-install.js +8 -3
  53. package/server/public/app/question-multi-select.mjs +52 -0
  54. package/server/public/app/relay-prompt-sanitizer.mjs +61 -3
  55. package/server/public/app/remote-relay-origin-view.mjs +76 -0
  56. package/server/public/app/remote-relay-shared.mjs +85 -0
  57. package/server/public/app/remote-relays-settings.js +618 -0
  58. package/server/public/app/remote-relays-store.mjs +126 -0
  59. package/server/public/app/settings-modal.js +12 -8
  60. package/server/public/app/settings-tabs.js +1 -1
  61. package/server/public/app/socket-handlers.js +37 -1
  62. package/server/public/app/steered-turn-merge.mjs +100 -0
  63. package/server/public/app/store.js +126 -15
  64. package/server/public/app/sync-outbox.mjs +16 -8
  65. package/server/public/app/thinking-follow.js +260 -0
  66. package/server/public/app/tool-activity-emoji.mjs +40 -0
  67. package/server/public/app/transcript-viewport-anchor.mjs +210 -0
  68. package/server/public/index.html +463 -17
  69. package/server/public/sw.js +7 -0
  70. package/server/relay-tools.md +7 -1
  71. package/server/repositories/message-repository.mjs +68 -18
  72. package/server/repositories/question-repository.mjs +18 -0
  73. package/server/repositories/remote-relay-repository.mjs +75 -0
  74. package/server/repositories/session-repository.mjs +13 -32
  75. package/server/routes/ask-user-routes.mjs +42 -2
  76. package/server/routes/messages-routes.mjs +572 -56
  77. package/server/routes/remote-relay-routes.mjs +133 -0
  78. package/server/routes/sessions-routes.mjs +378 -84
  79. package/server/server-runtime.mjs +303 -25
  80. package/server/services/github-ci-activity-service.mjs +199 -0
  81. package/server/services/host-suspend-command.mjs +38 -0
  82. package/server/services/host-suspend-service.mjs +254 -0
  83. package/server/services/live-turn-picker.mjs +47 -0
  84. package/server/services/push-dispatch-service.mjs +37 -4
  85. package/server/services/relay-prompt-sanitizer.mjs +17 -2
  86. package/server/services/relay-question-context.mjs +46 -0
  87. package/server/services/remote-relay-caller-context.mjs +341 -0
  88. package/server/services/remote-relay-client.mjs +332 -0
  89. package/server/services/remote-relay-dispatcher.mjs +1500 -0
  90. package/server/services/remote-relay-inbound.mjs +187 -0
  91. package/server/services/remote-relay-pairing.mjs +189 -0
  92. package/server/services/remote-relay-registry-service.mjs +581 -0
  93. package/server/services/sdk-session-import-service.mjs +49 -7
  94. package/server/services/session-worker-launch-service.mjs +131 -12
  95. package/server/services/session-worker-registry-service.mjs +75 -0
  96. package/server/services/session-worker-supervisor-service.mjs +7 -1
  97. package/server/services/session-worker-websocket-service.mjs +23 -1
  98. package/server/services/steer-resend.mjs +45 -0
  99. package/shared/background-task-timeout.mjs +12 -4
  100. package/shared/control-poller.mjs +4 -1
  101. package/shared/question-multi-select.mjs +16 -0
  102. package/shared/relay-stop-failure.mjs +15 -0
  103. package/shared/remote-relay-contract.mjs +539 -0
  104. package/shared/remote-relay-mentions.mjs +129 -0
  105. package/shared/remote-relay-tool-core.mjs +314 -0
  106. package/shared/slash-command-guard.mjs +76 -0
  107. package/shared/steer-note.mjs +66 -0
  108. package/shared/steer-settle-failure.mjs +89 -0
  109. package/shared/steer-settle-markers.mjs +18 -0
  110. package/shared/worker-crash-guard.mjs +40 -25
  111. package/shared/worker-runtime/api-client.mjs +112 -22
  112. package/shared/worker-runtime/heartbeat.mjs +55 -1
  113. package/shared/worker-runtime/runner-link-wiring.mjs +44 -0
  114. package/shared/worker-runtime/worker-websocket-link.mjs +119 -8
  115. package/server/claude-worker/claude-session-test-harness.mjs +0 -250
  116. package/server/claude-worker/fixtures/workflow-run-record.json +0 -1
  117. package/server/copilot-worker/copilot-sdk-test-harness.mjs +0 -382
  118. package/server/copilot-worker/fixtures/abort-turn.json +0 -588
  119. package/server/copilot-worker/fixtures/ask-user-turn.json +0 -1154
  120. package/server/copilot-worker/fixtures/background-timer-continuation.json +0 -445
  121. package/server/copilot-worker/fixtures/background-timer-turn.json +0 -497
  122. package/server/copilot-worker/fixtures/happy-turn.json +0 -566
  123. package/server/copilot-worker/fixtures/quota-turn.json +0 -647
  124. package/server/copilot-worker/fixtures/reasoning-turn.json +0 -625
  125. package/server/copilot-worker/fixtures/resume-turn.json +0 -700
  126. package/server/copilot-worker/fixtures/subagent-turn.json +0 -1378
  127. package/server/copilot-worker/fixtures/tool-permission-turn.json +0 -1293
  128. package/server/routes/messages-routes-test-harness.mjs +0 -111
  129. package/server/services/fixtures/claude-auth-stub.cmd +0 -1
  130. package/server/services/fixtures/claude-auth-stub.mjs +0 -76
  131. package/server/services/fixtures/claude-auth-stub.sh +0 -4
  132. package/server/services/fixtures/cli-install-stub.cmd +0 -1
  133. package/server/services/fixtures/cli-install-stub.mjs +0 -98
  134. package/server/services/fixtures/cli-install-stub.sh +0 -4
  135. package/server/services/fixtures/grok-stub.cmd +0 -1
  136. package/server/services/fixtures/grok-stub.mjs +0 -89
  137. package/server/services/fixtures/grok-stub.sh +0 -4
  138. package/shared/fixtures/copilot-catalog-raw-2026-09-07.json +0 -2363
@@ -97,7 +97,12 @@ const bannerStateStore = createBannerStateStore({
97
97
 
98
98
  const buildPromptWithRelayContext = createRelayPromptBuilder({
99
99
  toolInstructions: RELAY_TOOL_INSTRUCTIONS,
100
- getPreviewInstructions: createPreviewInstructionsProvider({ api }),
100
+ // With the OAR MCP server attached (session-worker-launch-service sets the
101
+ // flag), the CLI has a real `preview` tool: the HTTP-API instructions would
102
+ // only contradict it, so the section is dropped.
103
+ getPreviewInstructions: process.env.OAR_MCP_SERVER_ATTACHED === "1"
104
+ ? null
105
+ : createPreviewInstructionsProvider({ api }),
101
106
  });
102
107
 
103
108
  const managedServerLifecycle = createManagedServerLifecycle({
@@ -3,6 +3,8 @@ import {
3
3
  formatToolResultActivity as formatSharedToolResultActivity,
4
4
  formatVoteMemoryActivity,
5
5
  } from "../../../../shared/tool-activity.mjs";
6
+ import { isRemoteRelayToolName } from "../../../../shared/remote-relay-tool-core.mjs";
7
+ import { summarizeRemoteRelayCall } from "../../../../shared/remote-relay-contract.mjs";
6
8
 
7
9
  export function normalizeActivityText(value, maxToolDetailLength = 140) {
8
10
  const text = String(value || "").trim();
@@ -133,6 +135,11 @@ export function formatToolActivity(request, maxToolDetailLength = 140) {
133
135
  if (lower.includes("report_intent")) {
134
136
  return detail ? `● ${detail}` : "● Working…";
135
137
  }
138
+ // The OAR MCP server's tool reaches the CLI as `oar-remote_relay`: show the
139
+ // same line every other engine writes for it.
140
+ if (isRemoteRelayToolName(rawName)) {
141
+ return `Tool (remote_relay): ${summarizeRemoteRelayCall(toolArgsSnapshot(request))}`;
142
+ }
136
143
  return detail ? `Tool (${rawName}): ${detail}` : `Tool (${rawName})`;
137
144
  }
138
145
 
package/CHANGELOG.md CHANGED
@@ -3,6 +3,342 @@
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
+ ## [Unreleased]
7
+
8
+ ## [0.9.4] — 2026-09-27
9
+
10
+ Highlights: remote relays (pair OAR relays and let an agent on one work on the
11
+ other), follow toggles for the running reply, and a Suspend host that waits for
12
+ agents, background tasks and CI to finish.
13
+
14
+ ### Added
15
+
16
+ - **Remote relays**: pair OAR relays in **Settings → Relays** by pasting the other
17
+ relay's web address; the pairing works both ways. Agents of every provider get a
18
+ `remote_relay` tool to list and read the other relay's sessions, prompt one, start
19
+ a new one, wait for its reply and relay its questions to you. A relay unlocks in a
20
+ conversation only once you mention it (`@` picks it from a list). Per relay, agents
21
+ may read, prompt or do everything; in ask and plan mode each write action asks you
22
+ first. The receiving relay marks such messages with a **↗ from …** badge. Tokens
23
+ stay on the relay; agents never see them.
24
+ - **OAR MCP server** (`server/mcp/oar-mcp-server.mjs`): Grok and the Copilot
25
+ extension engine now get the `preview` and `remote_relay` tools as real tools
26
+ instead of instruction text.
27
+ - **Follow the live reply**: three toggles in the running reply's header keep
28
+ its thoughts, its answer or its tool list pinned just above the composer as
29
+ the reply grows. Scrolling the transcript by hand turns it off; typing in the
30
+ composer does not. The choice is remembered per conversation until the page
31
+ reloads.
32
+ - A queued relay restart shows a banner on every client, with a **Cancel**
33
+ button (a new localhost-only cancel endpoint behind it).
34
+
35
+ ### Changed
36
+
37
+ - **Suspend host waits for the work to finish.** Instead of sleeping the
38
+ machine at once, the relay queues the suspend and fires it after 2 minutes
39
+ with nothing active: no queued, running or parked turn, no background task of
40
+ a live worker, and no open GitHub Actions run in a busy conversation's
41
+ repository (read through `gh`; a repository whose state cannot be read blocks
42
+ for 15 minutes, then is ignored). With nothing running at confirm time it
43
+ fires after a 30-second countdown. The confirmation lists what is still
44
+ active, every client shows a banner with the blockers or the countdown and a
45
+ **Cancel** button, and a push notification reports the suspend, or its drop
46
+ when the relay restarts. Suspend remains Windows-only;
47
+ `OAR_HOST_SUSPEND_DRY_RUN=1` logs instead of suspending.
48
+ - **Stop** asks for confirmation before cancelling the running turn.
49
+ - The **Install app name** setting is now called **Relay name**. Your value is
50
+ kept. It still labels the installed app, and it is the name paired relays
51
+ know this relay by; when it is empty, paired relays see the machine's
52
+ hostname.
53
+ - Dependency updates clear all five `npm audit` findings, one of them rated
54
+ high: express 4.22.3, body-parser 1.20.8, qs 6.16.0, fast-uri 3.1.8 and
55
+ hono 4.13.9. All were inside the declared ranges, so `package.json` is
56
+ unchanged; a git checkout gets them with `npm ci`.
57
+
58
+ ### Fixed
59
+
60
+ - Switching the Claude model in a conversation that already had replies no
61
+ longer strands the next message. The reply to the message that carried the
62
+ switch landed on a *continuation* row while the message itself stayed
63
+ processing, and every later message waited behind it for up to 5 minutes, or
64
+ until the session was killed.
65
+ - **Requests on an idle connection are no longer reset.** The relay closed a
66
+ kept-alive connection after 5 seconds without traffic, so a request sent on
67
+ it a moment later could be reset before the relay read a byte. Behind a
68
+ reverse proxy or tunnel that pools its connections this showed up as an
69
+ occasional 502, and a send was not repeated. Idle connections now stay open
70
+ for 65 seconds.
71
+ - After a send, the transcript stays at the newest message when the composer
72
+ grows (a session note or a warning banner appearing under it). It used to
73
+ slip up until the draft was saved.
74
+
75
+ ## [0.9.3] — 2026-09-27
76
+
77
+ Highlights: mid-turn steering on Claude and Copilot, Copilot background tasks on
78
+ par with Claude's, multi-select question cards, drafts that stay safe across
79
+ devices, and a sidebar title filter.
80
+
81
+ ### Added
82
+
83
+ - **Mid-turn steering** for Claude conversations: messages sent while a turn is
84
+ running are pushed into the live turn (any number of them — Claude Code
85
+ parity) instead of queueing behind it. Folded messages settle with a compact
86
+ *merged* marker; a message the CLI answers on its own turn gets its real
87
+ reply as before.
88
+ - A **conversation title filter** above the sidebar list. Case-insensitive,
89
+ clears with × or Escape, and automatically loads older pages while active so
90
+ it searches every conversation, not just the loaded ones.
91
+ - **Resend** on a steered message that was cut off by **Stop**: the marker
92
+ reads *Stopped with the turn — not answered* and one tap sends the original
93
+ text and attachments again. Each original can be resent once (again only if
94
+ that Resend was cancelled or failed), and the button reads **Resent** on
95
+ every device and after reloads.
96
+ - The running reply's **Stop** header stays pinned at the top of the chat
97
+ while a long reply scrolls, so it is always in reach on a phone.
98
+ - Background subagents in the task panel show the **model they actually run
99
+ on** (e.g. *Opus 5.5*) beside their kind pill, and the **command they are
100
+ running right now** with its tool emoji (e.g. *🔧 Tool (Bash): npm test*),
101
+ cropped to the row — instead of *using Bash · opus*.
102
+ - **Mid-turn steering for Copilot conversations** (SDK engine, hosted and
103
+ BYOK alike, every Copilot model): a message sent while a turn runs is
104
+ pushed into the live turn. The runtime folds it in at the next tool boundary
105
+ (the message settles with the same *merged* marker as on Claude, and the
106
+ reply continues in the running bubble) or, when the model is mid-sentence,
107
+ answers it right after on its own bubble. The composer reads **Steer** for
108
+ these conversations; while a question card, approval, elicitation or a
109
+ compaction is open it reads **Queue** and the message waits in the queue,
110
+ cancellable, then steers in one round-trip after the answer. **Stop** now
111
+ ends only the reply — background agents the turn started keep running under
112
+ their own Stop — and messages pushed just before the Stop settle as *Stopped
113
+ with the turn — not answered* with **Resend**, exactly as on Claude.
114
+ - Messages steered into a running turn (Claude and Copilot) carry a short
115
+ hidden note telling the model to handle them in addition to the request it
116
+ is working on. In testing, a model that received a steer before its first
117
+ output answered only the new message and dropped the original request.
118
+ - The newest message you steered into a running Copilot turn keeps its
119
+ **Cancel** until the runtime picks it up: cancelling pulls it back out of the
120
+ runtime, so it is never answered. (Copilot's queue can only give back its
121
+ newest waiting message, so older ones lose Cancel once pushed, as on Claude.)
122
+ - **Background tasks for Copilot conversations** now match Claude's: background
123
+ agents the model spawns and detached shells appear in the task panel as they
124
+ run, with the agent's kind, the model it runs on, the command it is running
125
+ right now, its token count and a **Stop** that works (the runtime's task
126
+ registry). A reply no longer waits for a background agent to finish; the
127
+ agent's result arrives afterwards as its own *continuation* reply, and the
128
+ relay's *Background task timeout* slider now governs Copilot tasks too
129
+ (expiry stops them instead of silently forgetting them).
130
+ - **Multi-select question cards.** A question that allows several answers
131
+ renders checkmarks instead of one-shot buttons, plus one **Reply with
132
+ selection** button; the reply lists every ticked choice and whatever you
133
+ typed in addition. Previously the first click answered the whole card.
134
+ Claude cards follow Claude's own `multiSelect` flag. Copilot models get the
135
+ relay's own `ask_user` tool, which adds a `multi_select` field to the
136
+ runtime's (the built-in has no way to say "pick several"); a question worded
137
+ "select all that apply" counts too, and every Copilot choice card has a
138
+ **Select several** switch for the cases a model forgets to flag.
139
+
140
+ ### Changed
141
+
142
+ - Copilot replies settle when the main agent goes idle rather than when the
143
+ whole session does, so a background agent the turn spawned no longer keeps
144
+ the reply's bubble spinning for as long as it runs; the agent's own
145
+ follow-up arrives as a *continuation* reply, as on Claude. A background agent
146
+ that asks a question, or in ask mode asks to run something, after the reply
147
+ settled gets its card on a row of its own; approvals follow the
148
+ conversation's current mode, not the one the session started in. A
149
+ follow-up turn that ends with nothing to show (no text, and no activity
150
+ beyond the background agent's own bookkeeping, such as its granted
151
+ permissions) no longer leaves an empty "completed without a text reply"
152
+ row. Messages
153
+ are sent to the Copilot runtime as `immediate` throughout — the only mode
154
+ that never strands a message behind background work.
155
+
156
+ - The background task panel scrolls when it holds more tasks than fit,
157
+ instead of growing past the window — in phone landscape too, where the
158
+ composer now always stays on screen.
159
+ - The **Background task timeout** setting defaults to **4 hours** instead of
160
+ no limit, for Claude and Copilot alike: long-running work still finishes,
161
+ but a forgotten shell or agent no longer keeps its session alive forever.
162
+ A value you already chose (including *No limit*) is kept.
163
+ - Rotating a phone to landscape keeps the portrait text size instead of
164
+ jumping to the larger desktop size, and opening the on-screen keyboard
165
+ no longer shrinks it.
166
+ - Rotating a phone (or resizing the window, or opening the keyboard) keeps
167
+ your place in the transcript: a reader at the end stays at the end, and
168
+ mid-history the same message stays at the top of the screen. Previously
169
+ the reflow left you screens above where you were, usually further back.
170
+ A question card's reply box you are typing in stays in view when the
171
+ keyboard opens.
172
+
173
+ - The composer's send button no longer turns into **Stop** while a turn runs.
174
+ It reads **Steer** when a Claude turn is live and text is drafted, and stays
175
+ disabled on an empty composer. While steering is held (open question card or
176
+ plan approval, compaction) it stays enabled and reads **Queue**, with the
177
+ reason in its tooltip: the message waits as an ordinary pending bubble (with
178
+ **Cancel**) and steers into the turn once the card is answered or the
179
+ compaction ends. Stopping lives where the work is: the running reply's bubble
180
+ carries **Stop**, and queued messages carry **Cancel** until they are picked
181
+ up. The label follows the worker within about a second, including right
182
+ after a worker starts or the relay restarts.
183
+ - Queued messages are claimed strictly in send order, including messages that
184
+ went through a delivery retry. Previously a retried message could starve
185
+ behind newer ones for minutes in an active conversation, or run after them
186
+ so replies arrived out of order.
187
+ - `@anthropic-ai/claude-agent-sdk` 0.3.283 (bundled Claude Code 2.1.283),
188
+ which makes **Claude Opus 5.5** (`claude-opus-5-5`, plus its 1M-context
189
+ variant) discoverable. Model discovery runs the SDK's bundled CLI, so
190
+ `claude update` alone never surfaces a new Claude model.
191
+ - `@github/copilot-sdk` 1.0.14: typed message provenance (human vs. system vs.
192
+ agent senders), a `fast` auto-routing tier, a `managedSettings.clearCache`
193
+ RPC, and streaming-throughput fixes.
194
+ - `@cursor/sdk` 1.0.32 (additive API changes only).
195
+ - The npm package no longer ships test fixtures and test harnesses.
196
+ - The README is reorganized around installing and using OAR; relay internals
197
+ and development notes moved to DEVELOPING.md.
198
+
199
+ ### Fixed
200
+
201
+ - **Deleting a conversation no longer leaves its session running.** Its
202
+ worker used to keep running (about 40 MB each, holding its workspace
203
+ folder) until the relay restarted, and Claude and Copilot SDK
204
+ conversations never had their CLI session removed. Delete now stops the
205
+ worker first, removes the CLI session (Copilot through the SDK, Claude by
206
+ deleting that session's transcript) and the conversation at once. A
207
+ Copilot session the relay only imported from the host, and never ran, is
208
+ hidden instead: its CLI session stays, and no process is stopped for it. A
209
+ conversation that is still working, with a running turn or live background
210
+ tasks, is not deleted: the sidebar says why, so you can stop it first (a task
211
+ without a Stop button: use **Kill session**). A worker that a failed turn
212
+ marked as errored counts as working for as long as its process runs.
213
+ - A message starting with "/" (other than the relay's own `/compact` and
214
+ `/preview`) that reached a Claude conversation between turns ran as a Claude
215
+ CLI command instead: Claude never saw the text, and the next reply was
216
+ pinned to that message. It now reaches Claude as the text you typed, as the
217
+ composer's "send again to send as text" promises.
218
+ - A Copilot turn that ended in `task_complete` after streaming a fragment
219
+ published the fragment as its final reply; the completion summary now takes
220
+ precedence in the saved reply too, not only in the live stream.
221
+ - A background subagent's own events could open a spurious *continuation*
222
+ reply after the Copilot turn that spawned it had settled, and its
223
+ session-level model notice could confuse a pending model switch.
224
+ - Switching the model, mode or effort between turns no longer strands the
225
+ next message: the Claude CLI re-initialises after such a change, and the
226
+ worker took that for a turn it opened on its own, so the reply landed on
227
+ a *continuation* row while the message itself stayed processing with an
228
+ empty Stop bubble (until the session was killed). A background task's reply
229
+ that arrives during such a switch still gets its own row instead of
230
+ trading places with the message's reply.
231
+ - A steered message could wedge its conversation indefinitely when a
232
+ long-running (or hung) background task was alive: the merge settle waited for
233
+ a full idle that never came, keeping every later message stuck behind it.
234
+ Background tasks no longer block the settle; delivery watchdogs share the
235
+ same rule.
236
+ - **Drafts no longer get overwritten across devices.** The draft version check
237
+ never actually ran, so an idle device re-saved its stale (usually empty)
238
+ composer over another device's draft every few seconds. Saves are now
239
+ version-checked and happen only on real edits; a device you are not typing
240
+ on picks up the other's draft, and when two devices edit at once the newest
241
+ keystroke wins and the other text is offered back with **Restore** (and
242
+ **Undo**).
243
+ - Other ways to lose a draft are fixed too: text typed or attachments added
244
+ while a send was in flight, switching conversations mid-send or before the
245
+ next draft loaded, reopening the open conversation (which dropped its draft
246
+ attachments), a failed send overwriting what was typed meanwhile, and drafts
247
+ over 20,000 characters re-saving in a loop (the saved part is now cut
248
+ cleanly, and the composer says the rest is not saved).
249
+ - A steered message could run twice: Claude had already answered it, but the
250
+ relay's recovery (or a worker crash or restart) delivered it again. Steered
251
+ messages are now handled at most once: if the outcome cannot be recorded, the
252
+ message ends with an error telling you to check the reply above rather than
253
+ being run again.
254
+ - A message sent while a question card was open or the conversation was
255
+ compacting could stall for many seconds, or be pushed past the open card.
256
+ Question cards are never bypassed or dropped by steering: such messages wait
257
+ and steer in once the card is answered.
258
+ - **Stop now really stops** when steered messages were already pushed into the
259
+ turn. The Claude CLI otherwise opened a follow-up turn for them that re-ran
260
+ the stopped tool call; those messages now show *Stopped with the turn — not
261
+ answered* with **Resend** instead of claiming they were handled. A stopped
262
+ message is also no longer re-run if its worker dies right after the Stop.
263
+ - After a reload, a message sent after a folded steer is no longer styled as
264
+ steered with its reply merged into the previous turn, and older replies stay
265
+ anchored under their own messages.
266
+ - The live reply bubble no longer carries over into another conversation or
267
+ drops below later steered messages, and the live bubble and **Stop** follow
268
+ the turn that is actually producing output. A finished background task's
269
+ answer is no longer lost when a queued message takes over the turn.
270
+ - A screenshot-only send no longer vanishes from the transcript before its
271
+ message lands.
272
+ - Sidebar filter: keeps searching older pages while the list is busy loading,
273
+ matches accented titles regardless of how they were typed, no longer zooms
274
+ the page on iOS, and announces the match count once to screen readers
275
+ (saying when only loaded conversations were searched).
276
+ - A background agent's failure no longer vanishes: a Copilot follow-up turn
277
+ that carries the failure line, but has nothing else to show, keeps its row.
278
+ - The **Background task timeout** slider no longer reads *No limit* while the
279
+ settings load; it starts at the 4-hour default.
280
+ - Settings calls the SDK engine the Copilot default (it still read
281
+ *experimental*, with Extension as *current*), and the background task
282
+ timeout help names Copilot's background agents and shells.
283
+ - `oar` probed a different port than the one it started the relay on when the
284
+ config's `port` was not 3333 or differed from `--port`, then killed the relay
285
+ it had just started after 20 seconds. It now uses one port: `--port` for that
286
+ run, else the config's. `--help` no longer lists `--token`, which the
287
+ launcher never read.
288
+ - Concurrent conversation deletes could start extra Copilot runtimes that were
289
+ never shut down.
290
+ - Question cards said *Copilot question* in every conversation; they now name
291
+ the runtime that asks (Claude, Copilot, Cursor, OpenAI).
292
+
293
+ ## [0.9.2] — 2026-09-13
294
+
295
+ ### Added
296
+
297
+ - **Opt-in self-update**: a settings toggle for automatic update checks (off by
298
+ default; the relay never contacts oar.sh unless you opt in), plus a manual
299
+ check button. `OAR_NO_UPDATE_CHECK=1` disables even manual checks.
300
+ - Agents can embed **images, video and audio inline** in replies by bare
301
+ absolute path, on every provider. Clicking an embedded image opens the file
302
+ viewer with zoom, download and copy.
303
+ - **Screenshot annotations**: mark up uploaded screenshots with highlighter
304
+ strokes before (or after) sending; the original upload is never modified.
305
+ - A **Features** settings tab; feature flags now live in the database (the
306
+ `config.json` `features` key is migrated once and removed; env vars still win).
307
+ - Windows **system-startup autostart** mode (a boot-triggered scheduled task,
308
+ one UAC confirmation) alongside the existing at-sign-in mode.
309
+ - `config.json` can carry the data directory.
310
+
311
+ ### Changed
312
+
313
+ - New Copilot conversations default to the **SDK engine** when the relay can
314
+ run it, falling back to the CLI extension otherwise; an explicitly stored
315
+ setting always wins.
316
+ - The Copilot model catalog shows each model's real reasoning efforts and
317
+ context window, in a canonical order.
318
+ - Model labels are compact enough for phone composers (`Fable 5.1`,
319
+ `Haiku 4.5`), and the composer placeholder names the selected model's family.
320
+ - Claude models stay in the picker when Anthropic rotates the advertised
321
+ lineup; `@anthropic-ai/claude-agent-sdk` 0.3.261 (Fable 5.1 model floor).
322
+ - Global installs run from the OAR state root, so self-update can replace the
323
+ package directory on Windows.
324
+ - Transient relay notices render as an opaque toast instead of bleeding
325
+ through modals.
326
+
327
+ ### Fixed
328
+
329
+ - A custom PWA app name no longer reverts on Android: the relay serves it in
330
+ the manifest instead of a browser-local override.
331
+ - Queue writes are fenced to one processing attempt, so a superseded delivery
332
+ can no longer overwrite a settled message.
333
+ - Live-bubble rendering: cross-conversation teardown, muted streams after
334
+ enqueue, a live-poll deadlock and anchor theft.
335
+ - A queued send's own text no longer reappears in the composer.
336
+ - Copilot turns ending in a bare `task_complete` tool call show the real
337
+ summary instead of a cut-off fragment; detached Copilot shells appear as
338
+ background-task cards.
339
+ - Copilot worker and server-side session ownership hardening (continuations,
340
+ questions, shutdown, rekeying, worker kill verification).
341
+
6
342
  ## [0.9.1] — 2026-09-02
7
343
 
8
344
  ### Fixed