@oar-sh/oar 0.9.9 → 0.9.11

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 (154) hide show
  1. package/CHANGELOG.md +561 -1
  2. package/README.md +162 -909
  3. package/bin/bat/oar.bat +46 -46
  4. package/package.json +14 -14
  5. package/server/README.md +369 -13
  6. package/server/claude-worker/claude-remote-relay-tool.mjs +10 -0
  7. package/server/claude-worker/claude-session-process.mjs +121 -9
  8. package/server/claude-worker/claude-session-worker.mjs +9 -1
  9. package/server/claude-worker/claude-turn-publisher.mjs +3 -0
  10. package/server/claude-worker/sdk-message-normalizer.mjs +31 -4
  11. package/server/claude-worker/subagent-timeline-recorder.mjs +572 -0
  12. package/server/copilot-worker/copilot-sdk-session-process.mjs +79 -12
  13. package/server/copilot-worker/copilot-sdk-session-worker.mjs +6 -0
  14. package/server/cursor-worker/cursor-sdk-adapter.mjs +56 -2
  15. package/server/db-schema.mjs +95 -2
  16. package/server/grok-worker/acp-client.mjs +2 -1
  17. package/server/grok-worker/acp-host-services.mjs +4 -3
  18. package/server/migrations/0009-subagent-timeline.mjs +135 -0
  19. package/server/migrations/0010-subagent-timeline-pruning.mjs +89 -0
  20. package/server/migrations/0011-relay-event-subscriptions.mjs +125 -0
  21. package/server/migrations/0012-cross-relay-event-subscriptions.mjs +116 -0
  22. package/server/migrations/0013-previews.mjs +130 -0
  23. package/server/migrations/0014-conversation-groups.mjs +89 -0
  24. package/server/migrations/0015-conversation-group-position.mjs +76 -0
  25. package/server/migrations/0016-share-access-log.mjs +110 -0
  26. package/server/public/app/about-ui.js +73 -0
  27. package/server/public/app/about-view.mjs +234 -0
  28. package/server/public/app/action-confirmations.js +31 -14
  29. package/server/public/app/agent-exchange-fold-view.mjs +187 -0
  30. package/server/public/app/api-client.js +312 -16
  31. package/server/public/app/archive-confirm.mjs +28 -0
  32. package/server/public/app/ask-user-view.js +17 -12
  33. package/server/public/app/attachments-view.js +516 -6
  34. package/server/public/app/background-tasks-view.mjs +18 -26
  35. package/server/public/app/bootstrap.js +336 -59
  36. package/server/public/app/chat-actions-menu-state.mjs +126 -0
  37. package/server/public/app/claude-cloud-conversation-ui.js +10 -22
  38. package/server/public/app/composer-control-state.mjs +16 -0
  39. package/server/public/app/composer-keyboard.mjs +54 -0
  40. package/server/public/app/context-menu.mjs +90 -6
  41. package/server/public/app/conversation-group-drag.mjs +191 -0
  42. package/server/public/app/conversation-group-order.mjs +265 -0
  43. package/server/public/app/conversation-group-state.mjs +100 -0
  44. package/server/public/app/conversation-list-drag.mjs +449 -0
  45. package/server/public/app/conversation-turn-state.mjs +81 -0
  46. package/server/public/app/conversation-view.js +436 -117
  47. package/server/public/app/cwd-picker.js +1 -1
  48. package/server/public/app/explorer-edit-state.mjs +68 -0
  49. package/server/public/app/hidden-messages-view.mjs +312 -0
  50. package/server/public/app/host-suspend-ui.js +13 -4
  51. package/server/public/app/journal-view.js +930 -45
  52. package/server/public/app/no-conversation-state.mjs +58 -0
  53. package/server/public/app/preview-cards.mjs +117 -31
  54. package/server/public/app/preview-command.mjs +10 -1
  55. package/server/public/app/provider-readiness.mjs +120 -0
  56. package/server/public/app/relay-board-view.js +19 -3
  57. package/server/public/app/relay-event-subscriptions-ui.js +184 -0
  58. package/server/public/app/relay-event-view.mjs +234 -0
  59. package/server/public/app/relay-restart-error.mjs +16 -0
  60. package/server/public/app/settings-modal.js +93 -9
  61. package/server/public/app/settings-tabs.js +1 -1
  62. package/server/public/app/share-format.mjs +49 -0
  63. package/server/public/app/share-preview-marks.mjs +167 -0
  64. package/server/public/app/share-state.js +109 -0
  65. package/server/public/app/shared-conversations-settings.js +480 -0
  66. package/server/public/app/socket-handlers.js +137 -40
  67. package/server/public/app/store.js +115 -32
  68. package/server/public/app/subagent-row.mjs +138 -0
  69. package/server/public/app/subagent-timeline-view.mjs +186 -0
  70. package/server/public/app/subagent-viewer-watch.mjs +50 -0
  71. package/server/public/app/subagent-viewer.mjs +558 -0
  72. package/server/public/app/task-row-format.mjs +29 -0
  73. package/server/public/app/transcript-jump-bottom-view.mjs +464 -0
  74. package/server/public/app/transcript-jump-bottom.mjs +210 -0
  75. package/server/public/app/transcript-reload-freshness.mjs +25 -0
  76. package/server/public/app/transcript-viewport-anchor.mjs +140 -0
  77. package/server/public/app/update-ui.js +1 -1
  78. package/server/public/app/viewer-history.mjs +5 -3
  79. package/server/public/index.html +774 -12
  80. package/server/relay-tools.md +2 -1
  81. package/server/relay.mjs +2 -1
  82. package/server/repositories/conversation-group-repository.mjs +444 -0
  83. package/server/repositories/message-repository.mjs +30 -3
  84. package/server/repositories/question-repository.mjs +9 -3
  85. package/server/repositories/remote-relay-repository.mjs +6 -2
  86. package/server/repositories/session-repository.mjs +12 -37
  87. package/server/repositories/share-repository.mjs +322 -0
  88. package/server/repositories/share-visibility-statements.mjs +10 -0
  89. package/server/repositories/subagent-timeline-statements.mjs +170 -0
  90. package/server/routes/about-routes.mjs +15 -0
  91. package/server/routes/ask-user-routes.mjs +3 -0
  92. package/server/routes/cache-routes.mjs +4 -0
  93. package/server/routes/conversation-group-routes.mjs +171 -0
  94. package/server/routes/messages-routes.mjs +698 -114
  95. package/server/routes/preview-routes.mjs +9 -1
  96. package/server/routes/relay-event-routes.mjs +157 -0
  97. package/server/routes/remote-relay-routes.mjs +15 -0
  98. package/server/routes/sessions-routes.mjs +843 -193
  99. package/server/server-runtime.mjs +559 -160
  100. package/server/services/about-service.mjs +461 -0
  101. package/server/services/background-task-store.mjs +128 -0
  102. package/server/services/background-work-view.mjs +228 -0
  103. package/server/services/cache-rebuild-service.mjs +30 -0
  104. package/server/services/delete-archive-service.mjs +4 -1
  105. package/server/services/detached-worker-watch-service.mjs +150 -0
  106. package/server/services/explorer-entry-name.mjs +26 -0
  107. package/server/services/explorer-mutations.mjs +122 -0
  108. package/server/services/hidden-message-list.mjs +21 -0
  109. package/server/services/host-suspend-command.mjs +2 -1
  110. package/server/services/message-pin-list.mjs +1 -1
  111. package/server/services/preview-health-probe.mjs +8 -1
  112. package/server/services/preview-proxy-server.mjs +49 -27
  113. package/server/services/preview-registry-service.mjs +676 -26
  114. package/server/services/preview-static-handler.mjs +40 -7
  115. package/server/services/preview-store.mjs +274 -0
  116. package/server/services/push-dispatch-service.mjs +18 -0
  117. package/server/services/rate-limit-service.mjs +12 -1
  118. package/server/services/relay-auth.mjs +183 -0
  119. package/server/services/relay-cli-launcher-service.mjs +3 -1
  120. package/server/services/relay-event-link-service.mjs +289 -0
  121. package/server/services/relay-event-service.mjs +1952 -0
  122. package/server/services/relay-shutdown-gate.mjs +31 -0
  123. package/server/services/remote-relay-caller-context.mjs +52 -7
  124. package/server/services/remote-relay-dispatcher.mjs +551 -7
  125. package/server/services/remote-relay-registry-service.mjs +11 -1
  126. package/server/services/request-boundary.mjs +183 -0
  127. package/server/services/session-activity-tracker.mjs +695 -0
  128. package/server/services/session-history-refresh-service.mjs +7 -0
  129. package/server/services/session-worker-launch-service.mjs +190 -6
  130. package/server/services/session-worker-process-service.mjs +10 -6
  131. package/server/services/share-access-log-service.mjs +123 -0
  132. package/server/services/share-path-redaction.mjs +424 -0
  133. package/server/services/stale-attempt-log-limiter.mjs +96 -0
  134. package/server/services/stale-recovery-clock-guard.mjs +57 -0
  135. package/server/services/stale-subagent-run-sweep.mjs +109 -0
  136. package/server/services/status-event-service.mjs +7 -3
  137. package/server/services/subagent-record-retention.mjs +164 -0
  138. package/server/services/subagent-timeline-service.mjs +328 -0
  139. package/server/services/subagent-viewer-rooms.mjs +119 -0
  140. package/server/services/tunnel-worker-path-guard.mjs +24 -41
  141. package/server/services/upgrade-socket-guard.mjs +16 -0
  142. package/server/services/windows-autostart-service.mjs +6 -1
  143. package/server/services/windows-boot-autostart-service.mjs +207 -23
  144. package/shared/preview-tool-core.mjs +15 -5
  145. package/shared/relay-events.mjs +371 -0
  146. package/shared/remote-relay-contract.mjs +160 -6
  147. package/shared/share-path-privacy.mjs +30 -0
  148. package/shared/subagent-record-retention.mjs +31 -0
  149. package/shared/subagent-timeline.mjs +79 -0
  150. package/shared/windows-system-tools.mjs +106 -0
  151. package/shared/worker-runtime/background-task-digest.mjs +30 -0
  152. package/shared/worker-runtime/heartbeat.mjs +24 -0
  153. package/shared/worker-runtime/kept-posts.mjs +83 -0
  154. package/shared/worker-runtime/worker-websocket-link.mjs +47 -15
package/CHANGELOG.md CHANGED
@@ -3,7 +3,567 @@
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]
6
+ ## [0.9.11] — 2026-10-17
7
+
8
+ Highlights: sessions an agent starts now sit in a group under the session
9
+ that started it, with a pill that sums up what runs and what waits for you,
10
+ and rows drag into, out of and within a group. Settings › Sharing lists every
11
+ shared link with Copy, Renew and Stop and an access log of each open (the IP
12
+ address optional), shared views hide the host's paths in one of three modes,
13
+ **Preview as shared** shows what a viewer gets, and a 🙈 list gathers the
14
+ messages hidden from viewers. The relay is harder to reach from outside: ten
15
+ wrong tokens lock an address out for a minute, the live channel accepts only
16
+ the relay's own origin, forwarded headers count only from a trusted proxy, and
17
+ `?token=` signs in the page only. Day/Night is back in the ⋯ menu, next to the
18
+ new **About OAR**.
19
+
20
+ ### Upgrade notes
21
+
22
+ - **Behind a reverse proxy on another host or origin, set `trustProxy` and
23
+ `allowedOrigins`.** This is the first release that checks the origin of the
24
+ live Socket.IO channel and that reads the client address, host and scheme
25
+ from `X-Forwarded-*` headers only when the proxy is trusted. A proxy on the
26
+ relay host itself is trusted already (`trustProxy` defaults to
27
+ `"loopback"`). For one on another machine, or one that rewrites the `Host`
28
+ header, add to the config file (`~/.oar/config.json`, or `server/config.json`
29
+ in a git checkout) and restart the relay:
30
+
31
+ ```json
32
+ "trustProxy": "192.0.2.10",
33
+ "allowedOrigins": ["https://relay.example.com"]
34
+ ```
35
+
36
+ `trustProxy` takes what Express's `trust proxy` takes: the proxy's address,
37
+ a subnet such as `"10.0.0.0/8"`, or a hop count such as `1`.
38
+ `allowedOrigins` lists the public origins exactly as the browser shows them
39
+ (scheme, host, a port other than the default). Without them, the page loads
40
+ but its live connection is refused (the relay logs `Refused a handshake from
41
+ origin …`), and every client shares the proxy's address, so one client's
42
+ ten wrong tokens lock the rest out for a minute.
43
+ - **Ten wrong tokens from one address within a minute answer `429`** with
44
+ `Retry-After` until the minute is over. Requests from the relay host itself
45
+ that no proxy forwarded are never counted.
46
+ - **`?token=` works on the page address only.** Scripts that fetch API, file
47
+ or asset URLs with `?token=` must send `Authorization: Bearer <token>`
48
+ instead (or the cookie); a Socket.IO client passes it as `auth.token` (or
49
+ the cookie), not in the query.
50
+ - **Restart web relay, cancelling a queued restart and Empty queue need only
51
+ the token**, from any signed-in device. The session-worker socket and the
52
+ image-operation run answer only requests from the relay host that no tunnel
53
+ or proxy forwarded.
54
+ - Four database migrations (0013 previews, 0014 session groups, 0015 the place
55
+ in a group, 0016 the share access log) run by themselves on the first
56
+ start. The Node.js requirement is unchanged (22.13 or newer).
57
+
58
+ ### Added
59
+
60
+ - **Session groups in the sidebar.** A session that an agent starts with the
61
+ `remote_relay` tool now sits under the session that started it. The head
62
+ row carries a fold button and a pill that sums up the members ("3 · 1 waiting
63
+ for you, 2 running"); the members are indented under it, folded away once
64
+ everything in the group has finished unless you fold or unfold it yourself.
65
+ A question or approval waiting in a folded member shows on the head row,
66
+ and a tap on it opens that member. A member's activity lifts the whole
67
+ group to the top of the list. The head row's menu archives the whole group
68
+ (members still working are kept and named) and stops everything running in
69
+ it, and dissolves it; any row's menu adds a conversation to a group,
70
+ moves it, or takes it out, and groups can be named by hand. The `remote_relay` tool's
71
+ `list_sessions` and `read_session` show the group a session is in (agents
72
+ only read it). Migration 0014 adds the `conversation_groups` table and the
73
+ `group_id` column; nothing is backfilled.
74
+ - **Drag and drop for session groups.** Drag a session into a group, out of
75
+ it, or to another place inside it; the order you set stays, and later
76
+ arrivals join at the end. Drop one loose session onto the middle of another
77
+ and the two make a new group. On a phone a long press lifts the row and a
78
+ move drags it; with the keyboard, a member's menu has "Move up" and "Move
79
+ down". Migration 0015 adds the `group_position` column. Rolling back needs
80
+ no data change: older releases ignore the column, and a stale place they
81
+ leave behind is cleared when this release starts again.
82
+ - **A 🙈 list of the messages hidden from shared viewers.** Next to 📍, a 🙈
83
+ button with a count appears while the open conversation hides messages
84
+ from its shared link (never in the shared view itself). It opens a list
85
+ like the pinned one: each row jumps to its message, also one far up in the
86
+ history, and has Unhide; "Unhide all" asks first and leaves only a message
87
+ whose turn is still running hidden, naming it. The count and the list cover
88
+ messages outside the loaded page, and other open tabs and devices follow a
89
+ change without a reload.
90
+ - **Manage your shared links in Settings › Sharing.** Every shared
91
+ conversation is listed with when it was shared and last opened, how often
92
+ and by how many viewers. **Copy link** copies it, **Renew link** gives the
93
+ conversation a new link (the old one stops working at once), and **Stop
94
+ sharing** ends it; the `⋯` menu has **Stop sharing** too while the open
95
+ conversation is shared, and asks first. Each row unfolds into an access
96
+ log: one entry per open of the page (not per refresh) with time, IP
97
+ address, country and browser. The log carries over to a renewed link and
98
+ is deleted when you stop sharing or delete the conversation. Switch off
99
+ **Store viewer IP addresses** to log new opens without the address (the
100
+ status log of shared reads follows it); switching it off also offers to
101
+ remove the addresses already stored, from both logs. Distinct viewers are
102
+ still told apart. Migration 0016 adds the share lineage and the
103
+ `conversation_share_opens` table.
104
+ - **Preview as shared.** The `⋯` menu's new entry opens the conversation as
105
+ a share link shows it, without creating one: what viewers get instead of a
106
+ path is marked green, whatever still looks like a path amber.
107
+ - **Sub-agent records in shared views.** A sub-agent's row in a shared
108
+ conversation now opens its record, read-only and without Stop, filtered
109
+ like the rest of the share.
110
+
111
+ - **Wrong tokens lock an address out for a minute.** Ten wrong tokens from
112
+ one address within a minute (a Bearer header, the cookie, the sign-in field
113
+ or a Socket.IO handshake) make the relay answer that address `429` with
114
+ `Retry-After` until the minute is over, the right token included, and the
115
+ sign-in page says how long to wait. The same stale value repeated counts
116
+ once, so a tab with an old cookie does not lock you out. Requests from the
117
+ relay host itself that no proxy forwarded are never locked out, so the
118
+ relay's own workers and agent tools keep working.
119
+ - **About OAR in the ⋯ menu.** A new entry, "ℹ️ About OAR · <version>",
120
+ opens what this relay is and has done: version, commit (for a git
121
+ checkout) and when the relay started; each provider that is on, with its
122
+ CLI and SDK version and whether it is online; conversations, turns and
123
+ messages since the first conversation, turns per provider, and the usage
124
+ the providers report (Copilot premium requests, Cursor and Grok tokens and
125
+ cost, Claude Cloud cost); uptime, database size and running workers. With
126
+ the opt-in update check on it names the latest version, otherwise it says
127
+ where to turn the check on. Shared viewers do not get the entry. The data
128
+ comes from the new `GET /api/about`, which counts large databases in small
129
+ steps and keeps the totals for a minute.
130
+
131
+ ### Changed
132
+
133
+ - **A stopped or renewed share link answers a plain 404**, the page included,
134
+ exactly like a link that never existed, and a shared page that is still open
135
+ stops at its next refresh instead of polling on. The address in the access
136
+ log and in the status log of shared reads is now the one the relay trusts
137
+ through its `trustProxy` setting; a forwarded header from an untrusted peer
138
+ no longer sets it.
139
+
140
+ - **A share link no longer carries the relay host's paths.** The relay
141
+ filters what it serves to shared viewers, by **Paths and file names** in
142
+ Settings › Sharing: show every path, hide the path and keep the file name
143
+ (the default), or hide both. Tool lines, thoughts, replies, attachments and sub-agent records all
144
+ pass through it; before, only the viewer's browser shortened the paths in
145
+ tool lines, and attachments carried their full path on the host. Free text
146
+ is filtered on a best-effort basis: relative paths are left as they are.
147
+
148
+ - **A long press on a sidebar row opens its menu when the finger lifts**, no
149
+ longer while it is still down: a long press that then moves drags the row
150
+ instead. Where rows cannot move (the Archived view, under a filter, on a
151
+ group's own line) the menu still opens during the press.
152
+
153
+ - **A reply to another agent's note is no longer folded away.** Only the
154
+ note itself folds into its row now, "note from linux-test · report builder ·
155
+ answered below"; the session's reply to it always shows, like a reply to
156
+ you, because it may hold a line for you. A turn you steered a message into
157
+ folds its note too, since nothing of yours is behind the row any more. The
158
+ ⌄ button's badge counts the note and the reply as two rows.
159
+
160
+ - **A conversation no longer scrolls down by itself while you read further
161
+ up.** It only moves when you are at its end, or when you have just sent a
162
+ message yourself. Anywhere else it stays exactly where it is, whatever
163
+ lands: a reply, a relay event, another agent's note, a question, an approval
164
+ or a plan board. The ⌄ button then tells you: it turns blue with a count
165
+ (or a dot while a reply is still being written) when something new is
166
+ below, and amber with a "?" when a question or an approval waits for your
167
+ answer; a screen reader hears both. The text you are reading also keeps its
168
+ place when the reply you are in the middle of is finished and when the
169
+ conversation reloads. New in Settings › General: "Jump to the end when I
170
+ send a message", on by default and saved in the browser; switch it off and
171
+ your own messages leave you where you are as well.
172
+
173
+ - The npm package guard allows 7.25 MiB unpacked and 490 entries (was 6.75
174
+ MiB and 450); session groups, the stored previews, the background-task
175
+ resync, the transcript anchor, share management, path privacy in shared
176
+ views and About OAR grew the server and the app to about 7.05 MiB and 485
177
+ entries.
178
+
179
+ - **Restart web relay, cancelling a queued restart and Empty queue work from
180
+ every signed-in device.** They were meant for the relay host only, but the
181
+ check let everything through a tunnel or a proxy on the same machine and
182
+ refused only a direct LAN connection; they now need the token and nothing
183
+ else. The other way round, the session-worker socket and the image-operation
184
+ run now answer only requests from the relay host itself: a loopback peer
185
+ with no `cf-ray` or other tunnel marker and no `X-Forwarded-For`,
186
+ `Forwarded` or `X-Real-IP`. A same-host proxy that adds no marker no longer
187
+ reaches them.
188
+
189
+ - **A `?token=` signs in the page only.** The page takes it out of the address
190
+ and signs in with it as before; API, file and asset URLs now accept the
191
+ token in the `Authorization` header or the cookie only, and a Socket.IO
192
+ handshake no longer reads a `token` query.
193
+
194
+ - **The live Socket.IO channel accepts the relay's own origin only** (it
195
+ accepted any). The host compared is the one the browser addressed,
196
+ `X-Forwarded-Host` included when a trusted proxy sends it; for a proxy that
197
+ rewrites the `Host` header, list the public origin in the new
198
+ `allowedOrigins` setting.
199
+
200
+ ### Fixed
201
+
202
+ - **Day/Night is back in the ⋯ menu.** Since 0.9.10 the entry showed only in
203
+ a shared view; everywhere else, on every screen size, it was gone from the
204
+ menu. It is there again whenever you are signed in, with or without a
205
+ conversation open, and stays the one entry of a shared view.
206
+
207
+ - **A shared conversation no longer marks every reply "ran on" its own
208
+ provider.** In a shared link to a Claude, Cursor, Grok or OpenAI
209
+ conversation, each reply carried the red `ran on claude` (etc.) chip, as if
210
+ the turn had run on the wrong provider. The shared view now knows which
211
+ provider the conversation is bound to, and shows the chip only when a reply
212
+ really ran on another one; with no provider bound it shows none.
213
+
214
+ - **A wide table in the file reader scrolls sideways.** A markdown table wider
215
+ than the reader made the whole page scroll sideways, and a sideways swipe to
216
+ read the rest of it turned to the next file instead. The table now scrolls
217
+ in its own box, as code blocks do; a swipe that starts on it stays in the
218
+ file.
219
+
220
+ - **A worker no longer stops reconnecting after a relay restart.** On Node
221
+ 22, a worker whose reconnect reached a relay that was not ready yet gave up
222
+ for good: its process kept running without a connection, the relay started
223
+ no new one while that process lived, and every message to the session
224
+ stayed queued until the worker was killed and launched by hand. A failed
225
+ connect now always leads to the next attempt, with the usual backoff of up
226
+ to 8 seconds. As a safety net, the relay replaces a worker that has had no
227
+ connection for 60 seconds while a message waits for it: it stops the old
228
+ process and starts a new one, which then takes the message. It never does
229
+ so while a turn or background work of that session is running, and logs a
230
+ `WORKER DETACHED` line when it does.
231
+
232
+ - **A relay restart no longer forgets running background tasks.** A session
233
+ with a background shell or sub-agent still running read as idle after the
234
+ relay restarted: its task panel was empty, **Suspend host** saw nothing
235
+ to wait for, and a watching session could be told `all_idle`. The relay
236
+ now asks each worker for its tasks again on the worker's next heartbeat,
237
+ and the tasks come back with their original start time. A session whose
238
+ worker was started before this update shows "background work, details not
239
+ reported since the relay restarted" until it reports. For the first 45
240
+ seconds after a relay start a suspend waits for sessions that are still
241
+ reporting, and a task that ended while the relay was away still reports
242
+ its end.
243
+
244
+ - **Workers start when no `node` is on the relay's PATH.** A relay started
245
+ by the Windows system-startup task, before anyone signed in, ran fine, but
246
+ none of its workers ever started: they were launched with a bare `node`
247
+ that the task's PATH did not have, Windows reported the failure in a
248
+ dialog no one could see, and the log showed only a heartbeat timeout
249
+ every 90 seconds. Workers now run on the relay's own Node by its full path
250
+ on every platform, tmux included; `COPILOT_WEB_RELAY_NODE` still overrides
251
+ it. That Node's folder is added at the end of a worker's PATH when the PATH
252
+ lacks it, so the CLIs and commands a worker starts find a `node` too; a
253
+ PATH that has one keeps it.
254
+
255
+ - **A worker that cannot be started says why.** On Windows, a launch whose
256
+ `start` command fails or never returns now fails at once, naming the
257
+ program, and the conversation's launch note shows it; before, the relay
258
+ waited for a heartbeat that never came. The relay log names the command
259
+ and the PATH, SystemRoot and ComSpec it was started with, also when a
260
+ launch saw no worker process yet, and a failed process-list read logs its
261
+ exit code and error output. Windows' own programs (PowerShell, schtasks,
262
+ taskkill and others) are called by their full path. Changing the launch
263
+ folder and the Windows autostart setting show the relay's reason when they
264
+ fail, not a bare "Failed to …", and an autostart read that fails is logged.
265
+
266
+ - **The Windows system-startup launcher copes with an early boot.** The
267
+ launcher OAR generates adds Node's folder at the end of PATH and waits up
268
+ to two minutes for the `%TEMP%` folder to exist before it starts the relay
269
+ (a temp folder on a drive that a startup script creates later no longer
270
+ makes the relay and its CLIs fail); a wait that runs out is logged next to
271
+ the launcher. Settings now names how a boot task or launcher differs from
272
+ the generated one (a missing boot delay, an edited launcher) instead of
273
+ treating it as a failure; selecting "At system startup" again regenerates
274
+ both and keeps a changed launcher as a `.bak` copy. A boot task that cannot
275
+ be read no longer breaks the setting.
276
+
277
+ - **Preview links survive a relay restart.** A preview lived only in the
278
+ relay's memory, so an update, a queued restart or a crash turned every
279
+ preview link into a 404, and publishing again gave a new link. Previews are
280
+ now kept in the relay's database and restored when it starts, each checked
281
+ again: a folder preview while its folder is still inside the workspace root,
282
+ a dev-server preview until the machine reboots (on Windows with fast
283
+ startup, until a Restart: Shut down and power-on is not seen as one).
284
+ Publishing the same port or folder again in a conversation returns the same
285
+ link. Deleting a conversation, also through a cache rebuild, closes its
286
+ previews; archiving keeps them. Settings → Previews shows when each preview
287
+ was published and last opened, and the limit of 8 names the oldest ones and
288
+ how to close them. Relay logs no longer carry a preview's token; a publicly
289
+ shared transcript shows a preview card with its label only and removes every
290
+ preview link quoted in its text, also one cut short by a reply still being
291
+ written. A preview the relay cannot check at a start (a busy database, an
292
+ unreadable folder) is not served, shows as such in Settings, and is checked
293
+ again every 15 s for ten minutes. The table comes with migration 0013, which
294
+ runs by itself on the first start; the npm package gains two modules for it.
295
+
296
+ - **An install gets the dependency versions the release was tested with.**
297
+ The package asked for each of its dependencies "this version or newer", so
298
+ an install or update fetched whatever was newest that day, e.g. a newer
299
+ Copilot SDK than any release was tested with. The packages OAR depends on
300
+ directly (the Claude, Copilot and Cursor SDKs among them) are now pinned to
301
+ exact versions; a newer one arrives with an OAR update. Packages those
302
+ depend on still follow their own version ranges.
303
+
304
+ - **Restart web relay no longer reports success when the relay refuses it.**
305
+ A refused request said "Relay restart requested"; it now shows the relay's
306
+ reason, and a request that got no answer says so instead of claiming
307
+ success. The restart after a feature-flag change and cancelling a queued
308
+ restart report their errors the same way, and Empty queue names the reason
309
+ it failed.
310
+
311
+ - **Forwarded headers count only from a trusted proxy everywhere.** The
312
+ shared-view presence limit read `X-Forwarded-For` and `X-Real-IP` and the
313
+ share links read `X-Forwarded-Host` and `X-Forwarded-Proto` straight from the
314
+ request, so a direct client could pick its own rate-limit bucket or the
315
+ host of a new share link. Both now follow the `trustProxy` setting.
316
+
317
+ - **`tunnelMarkerHeaders: []` in the config no longer switches off
318
+ `COPILOT_TUNNEL_MARKER_HEADERS`.** The two lists are merged, with `cf-ray`
319
+ always included.
320
+
321
+ ## [0.9.10] — 2026-10-12
322
+
323
+ Highlights: sessions watch other sessions. An agent subscribes to sessions on
324
+ its own relay or on a paired one and is woken by what happens there:
325
+ questions waiting for an answer, errors, replies, finished background work,
326
+ sessions gone quiet. A subscription only notifies; it approves nothing, and
327
+ the first one in a conversation asks you on a card. Also new: a sub-agent's
328
+ whole record opens in a viewer, a note between agents and its reply fold into
329
+ one row, `remote_relay` reports what the provider plans have left, the file
330
+ browser creates, renames and deletes folders and files, a ⌄ button jumps back
331
+ to the latest message, archiving an active session asks first, and Settings
332
+ opens with no conversation open.
333
+
334
+ ### Upgrade notes
335
+
336
+ - **Running sessions pick up the new tools when their worker restarts.** A
337
+ session worker builds its `remote_relay` tool when it starts, so a worker
338
+ that was already running before the update offers neither `subscribe` nor
339
+ `usage`, and records no sub-agent timeline, until it is restarted: **☠️
340
+ Kill session** on an idle session is enough, the next message starts a new
341
+ worker. The database migrations run by themselves on the first start.
342
+
343
+ ### Added
344
+
345
+ - **A session can watch other sessions and be woken by what happens there.**
346
+ With `remote_relay`'s new `subscribe` an agent asks to be told about
347
+ questions waiting for an answer, errors (a failed turn, the usage limit, a
348
+ worker that died), replies, background agents and shell tasks that finish
349
+ or fail, sessions that went quiet while working, and the moment nothing
350
+ runs any more, for listed sessions or all of them. OAR writes a short event
351
+ message into the watcher's conversation: a new turn when it is idle, a
352
+ steer into the running turn where the provider steers, the next turn
353
+ otherwise. The message says it is automated and approves nothing, and shows
354
+ as a compact row, never as one of your messages. Events within 30 s come
355
+ together; past 20 wake-ups an hour the rest comes as a digest every ten
356
+ minutes, while questions and errors still come at once. An optional
357
+ heartbeat says the watching works while anything runs. Subscriptions
358
+ survive a restart and end on unsubscribe, when the watcher or the watched
359
+ session is archived, or after firing once. The first subscribe of a
360
+ conversation, and any to all sessions, asks you on an approval card;
361
+ `transfer_subscriptions` hands every subscription of an old watcher to a
362
+ new one after your approval. The watcher's header shows **watching N
363
+ sessions** with an Unsubscribe button per subscription, and a watched
364
+ session shows a **watched** badge.
365
+
366
+ - **Watch sessions on a paired relay.** `subscribe`, `unsubscribe`,
367
+ `list_subscriptions` and `transfer_subscriptions` also take a paired
368
+ relay, with the same events and options. The relay must be unlocked in
369
+ the conversation and allow at least reading, and the other relay must
370
+ accept other relays' agents; the approval card names that relay, and
371
+ nothing is asked there. This relay fetches the events every few seconds
372
+ over the pairing (a pairing in one direction is enough), and they arrive
373
+ like those of its own sessions, their lines starting with the relay's
374
+ name. If the link drops, the other relay keeps what happens (up to 500
375
+ events per subscription, for 7 days) and delivers it as one row tagged
376
+ **replayed** once it answers again, questions and errors first, between
377
+ "link lost" and "link restored". Subscriptions survive restarts of either
378
+ relay and end on unsubscribe (reaching the other relay later if it was
379
+ offline), when the watcher or the watched session is archived, or when
380
+ the pairing is removed. An older relay answers that it needs an update.
381
+
382
+ - **list_sessions and read_session say what a session is doing.** `state`
383
+ is `running_turn`, `background_only` or `idle`, and `tasks` lists its
384
+ background agents and shell tasks with when they started, when they last
385
+ did something, how they ended and the exit code where it is known. A
386
+ session with background agents running no longer reads as inactive.
387
+
388
+ - **Agents can check what the provider plans have left.** `remote_relay`'s
389
+ new `usage` action answers, for the relay an agent runs on or a paired
390
+ one, what each provider's plan has left: Cursor's spend against its
391
+ limit and when it resets, Claude's windows and whether a usage limit has
392
+ paused turns, Copilot and Grok where they are tracked; a provider that is
393
+ not set up says so. It needs only read permission and returns no account
394
+ names, addresses or tokens.
395
+
396
+ - **Notes between agents fold away.** A prompt another agent sent a
397
+ session, from a paired relay or from this one, and the session's reply to
398
+ it show as one row, "note from linux-test · report builder · replied",
399
+ which opens to both, on a desktop and on a phone. Your own messages and
400
+ the replies to them stay as they are, and a turn you steered a message
401
+ into, or one with a question or card open for you, is never folded. A
402
+ note still being worked on stays open and folds by itself once it is
403
+ answered; one you fold or open yourself stays as you left it.
404
+
405
+ - **Open a sub-agent's whole record.** A sub-agent's title in the composer's
406
+ background-task panel opens a viewer with everything the agent did, in
407
+ order: its thoughts, every tool call with its full input and its result,
408
+ its reply text and the agents it started itself, live while it runs. The
409
+ finished reply lists the turn's sub-agents as the same rows, with model,
410
+ type, tokens, time and how they ended, so the record stays one click away.
411
+ A child agent opens in place, and back returns to its parent. Stop is in
412
+ the viewer too. Claude records everything; Copilot and Cursor show what
413
+ they report. A tool result is kept to its first 4 KB, and a record to 2000
414
+ entries. Only a page with the viewer open receives the record as it grows.
415
+
416
+ - **Choose how long sub-agent tool output is kept.** Settings → General →
417
+ **Keep sub-agent tool output** offers 7, 14, 30 or 90 days (the default)
418
+ or always. Once a finished agent's record is older than that, its tool
419
+ inputs and results are removed, once a day; its thoughts, its reply, its
420
+ notes and the one-line summary of each tool call stay, and the viewer says
421
+ what was removed.
422
+
423
+ - **Folders can be created in the file browser.** A **📁+ New folder**
424
+ button in the toolbar opens a row at the top of the folder pane; the name
425
+ is typed there and Enter creates the folder inside the open one and opens
426
+ it. It works in the repository, on the drives and in the session folder,
427
+ and in the folder picker of **New chat** and **Change CWD**, so a new
428
+ project folder can be made and picked in one go. A taken name or a name
429
+ the system refuses is reported under the input.
430
+
431
+ - **Files and folders can be renamed and deleted in the file browser.** A
432
+ right-click, or a long press on a phone, on a folder in the tree or a file
433
+ in the folder pane opens a menu with **Rename** and **Delete**; a folder's
434
+ menu also offers **New folder here**. Rename edits the name in place: Enter
435
+ saves, Escape cancels, and an unchanged name is a cancel. Delete asks in a
436
+ dialog that says what goes: a file, an empty folder, or a folder with so
437
+ many files and folders in it; for a folder with content the folder's name
438
+ must be typed before the red button enables. Links are never followed or
439
+ changed; roots (the repository root, `/`, a drive, the session folder)
440
+ cannot be renamed or deleted, and the session folder is read-only apart
441
+ from new folders.
442
+
443
+ - **Jump back to the latest message.** A round ⌄ button above the composer
444
+ fades in once the transcript is about a screen above its end, and its
445
+ badge counts the rows that arrived since (an agent's folded exchange
446
+ counts once). A click goes back to the end, smoothly up to three screens
447
+ away and at once beyond, and the transcript follows new rows again; from
448
+ a search result or a pin deep in the history it loads the end.
449
+
450
+ ### Changed
451
+
452
+ - **Archiving a session whose process is still active asks first.** A green
453
+ session, idle or working, now opens "Archive this session? Its process is
454
+ still active." from the row's 🗄, its menu and the chat menu alike; Cancel
455
+ or Escape keeps it. An inactive session archives at once, as before.
456
+
457
+ - **A message from another device no longer pulls a reader who scrolled up
458
+ down to the end.** Another client's message, an agent's prompt included,
459
+ is followed like any live row: only while the transcript is at its end.
460
+
461
+ - The npm package guard allows 6.75 MiB unpacked (was 6.5 MiB); the
462
+ sub-agent records and the event subscriptions, now across paired relays,
463
+ grew the server.
464
+
465
+ ### Fixed
466
+
467
+ - **A dropped websocket can no longer take the relay down.** A browser that
468
+ reset its connection while a preview's websocket, or any websocket to the
469
+ relay, was still being set up ended the whole relay with `read ECONNRESET`.
470
+
471
+ - **A queued relay restart no longer waits forever on a busy relay.** The
472
+ restart used to wait for the whole queue to empty while new messages kept
473
+ being handed to workers, so each new turn postponed it again. It now waits
474
+ for the running turns only; queued messages are held until the relay is
475
+ back and run then. The banner says which.
476
+
477
+ - **Waking from sleep no longer restarts the running turns.** After the host
478
+ slept for hours, every worker heartbeat looked hours old the moment the
479
+ relay's timer fired again, so live turns were recovered as stale and their
480
+ messages delivered a second time while the workers' own replies were
481
+ refused, hundreds of warnings in minutes. The relay now notices the clock
482
+ jump and gives live workers a full inactivity window to report before
483
+ anything is recovered; a superseded attempt's refusals are logged once and
484
+ summarized, not line by line.
485
+
486
+ - **A Cursor turn waiting on a long command is no longer ended as stalled.**
487
+ When the agent started a command in the background and then waited for
488
+ it, the wait counted as a model request, and the turn was ended after five
489
+ minutes of quiet. The wait now gets the 30 minutes a running tool gets.
490
+
491
+ - **An event's fields cannot add lines to a watcher's event message.** A
492
+ line break in a session title, a task description or a field of a paired
493
+ relay's event could start a line without the `[relay event]` prefix. Every
494
+ field is now written as one line, and what a paired relay sends is stored
495
+ field by field from a fixed list, never as the payload it sent.
496
+
497
+ - **The relay event tables no longer grow without bound.** Delivered events
498
+ and ended subscriptions were never deleted, and the retention check for
499
+ paired relays scanned the whole event table every 30 seconds. Delivered
500
+ events now go after two hours (the wake-up count reads the last hour),
501
+ ended subscriptions after a week, and the check uses its index.
502
+
503
+ - **The "watching N sessions" list opens in full.** The header cut it off,
504
+ so on a phone it did not show at all. It now opens under the chip on top
505
+ of the page, and Escape or a tap elsewhere closes it.
506
+
507
+ - **Sub-agent runs a worker never closed no longer count as running work.**
508
+ A run whose worker went away without reporting its end stayed `running`
509
+ for good, so a subscription to all sessions never saw `all_idle` and
510
+ reported the session stalled over and over, and the suspend watchdog saw
511
+ work. Such runs are now closed shortly after the relay starts and every
512
+ ten minutes (never while their worker is alive), and a run row not touched
513
+ for a day is not counted meanwhile.
514
+
515
+ - **A background agent's output stays with the turn that started it.** Its
516
+ thoughts and tool calls no longer land in a later turn's reply, and between
517
+ turns they are no longer lost.
518
+
519
+ - **A background agent no longer shows as finished the moment it starts.**
520
+ Its row now ends when the agent does.
521
+
522
+ - **A sub-agent stopped by a failed turn or a worker crash settles on screen
523
+ without a reload.** The update used to arrive without its turn, and the
524
+ page ignored it.
525
+
526
+ - **The transcript keeps following the live reply when a background task
527
+ starts.** The task panel appearing, opening or growing shrank the
528
+ transcript from below and left it that much short of the end, so the next
529
+ live row stopped following. It now stays at the end.
530
+
531
+ - **The transcript follows the agent's reply as it streams in.** Each piece
532
+ of streamed text was scrolled to before it was shown, so the first new
533
+ line left the reader short of the end and the rest was not followed.
534
+
535
+ - **Going back to the end while a turn finishes sticks.** The reload at the
536
+ end of a turn put a reader back where they were when it was asked for, so
537
+ scrolling to the end, or pressing ⌄, during it was undone and the next
538
+ rows were not followed. It now keeps where the reader is when it lands.
539
+
540
+ - **⌄ no longer opens the keyboard on a phone.** The composer is no longer
541
+ focused when the page loads on a phone, and ⌄ keeps it focused only while
542
+ the keyboard is already open, so a tap on it just scrolls.
543
+
544
+ - **A message that arrives while the conversation reloads stays on screen.**
545
+ A reload asked for just before another client's message or a reply landed
546
+ rebuilt the transcript without it, until the next reload. The reload now
547
+ asks again when rows arrived on the way.
548
+
549
+ - **A message sent right after switching conversations shows up where it was
550
+ sent.** A quick run through the list, or a click back on the conversation
551
+ still open, could hand the view to an earlier click once the draft of the
552
+ conversation left behind had saved. The message then went to that
553
+ conversation, or its bubble and the live reply stayed hidden until the
554
+ conversation was opened again. A click now opens its conversation at once.
555
+
556
+ - **The live reply stays on screen right after a send.** A poll answer read
557
+ just before the agent took the message could end the turn on the page, and
558
+ a reply update lost on a busy connection left the bubble empty; both waited
559
+ for the conversation to be opened again.
560
+
561
+ - **Settings opens with no conversation open.** On a fresh install the ⋯
562
+ menu did nothing until a chat existed, so no provider could be turned on.
563
+ The menu now works on an empty screen, with the entries that need a
564
+ conversation left out, and the empty screen and New Chat point to
565
+ Settings → Providers until a provider is ready instead of starting a
566
+ Copilot chat that cannot run.
7
567
 
8
568
  ## [0.9.9] — 2026-10-07
9
569