@oar-sh/oar 0.9.10 → 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 (104) hide show
  1. package/CHANGELOG.md +314 -1
  2. package/README.md +156 -972
  3. package/package.json +13 -13
  4. package/server/README.md +95 -22
  5. package/server/claude-worker/claude-session-process.mjs +89 -11
  6. package/server/claude-worker/claude-session-worker.mjs +9 -1
  7. package/server/copilot-worker/copilot-sdk-session-process.mjs +76 -11
  8. package/server/copilot-worker/copilot-sdk-session-worker.mjs +6 -0
  9. package/server/db-schema.mjs +59 -2
  10. package/server/grok-worker/acp-client.mjs +2 -1
  11. package/server/grok-worker/acp-host-services.mjs +4 -3
  12. package/server/migrations/0013-previews.mjs +130 -0
  13. package/server/migrations/0014-conversation-groups.mjs +89 -0
  14. package/server/migrations/0015-conversation-group-position.mjs +76 -0
  15. package/server/migrations/0016-share-access-log.mjs +110 -0
  16. package/server/public/app/about-ui.js +73 -0
  17. package/server/public/app/about-view.mjs +234 -0
  18. package/server/public/app/action-confirmations.js +31 -14
  19. package/server/public/app/agent-exchange-fold-view.mjs +40 -101
  20. package/server/public/app/api-client.js +251 -16
  21. package/server/public/app/ask-user-view.js +17 -15
  22. package/server/public/app/bootstrap.js +158 -32
  23. package/server/public/app/chat-actions-menu-state.mjs +20 -10
  24. package/server/public/app/context-menu.mjs +90 -6
  25. package/server/public/app/conversation-group-drag.mjs +191 -0
  26. package/server/public/app/conversation-group-order.mjs +265 -0
  27. package/server/public/app/conversation-group-state.mjs +100 -0
  28. package/server/public/app/conversation-list-drag.mjs +449 -0
  29. package/server/public/app/conversation-turn-state.mjs +81 -0
  30. package/server/public/app/conversation-view.js +136 -93
  31. package/server/public/app/cwd-picker.js +1 -1
  32. package/server/public/app/hidden-messages-view.mjs +312 -0
  33. package/server/public/app/host-suspend-ui.js +7 -1
  34. package/server/public/app/journal-view.js +787 -22
  35. package/server/public/app/preview-cards.mjs +117 -31
  36. package/server/public/app/preview-command.mjs +10 -1
  37. package/server/public/app/relay-board-view.js +19 -6
  38. package/server/public/app/relay-restart-error.mjs +16 -0
  39. package/server/public/app/settings-modal.js +40 -8
  40. package/server/public/app/settings-tabs.js +1 -1
  41. package/server/public/app/share-format.mjs +49 -0
  42. package/server/public/app/share-preview-marks.mjs +167 -0
  43. package/server/public/app/share-state.js +109 -0
  44. package/server/public/app/shared-conversations-settings.js +480 -0
  45. package/server/public/app/socket-handlers.js +80 -14
  46. package/server/public/app/store.js +96 -30
  47. package/server/public/app/subagent-row.mjs +3 -3
  48. package/server/public/app/subagent-viewer.mjs +30 -6
  49. package/server/public/app/thinking-follow.js +0 -4
  50. package/server/public/app/transcript-jump-bottom-view.mjs +194 -20
  51. package/server/public/app/transcript-jump-bottom.mjs +132 -21
  52. package/server/public/app/transcript-viewport-anchor.mjs +140 -0
  53. package/server/public/index.html +386 -15
  54. package/server/relay-tools.md +2 -1
  55. package/server/relay.mjs +2 -1
  56. package/server/repositories/conversation-group-repository.mjs +444 -0
  57. package/server/repositories/message-repository.mjs +1 -0
  58. package/server/repositories/session-repository.mjs +12 -37
  59. package/server/repositories/share-repository.mjs +322 -0
  60. package/server/repositories/share-visibility-statements.mjs +10 -0
  61. package/server/routes/about-routes.mjs +15 -0
  62. package/server/routes/cache-routes.mjs +4 -0
  63. package/server/routes/conversation-group-routes.mjs +171 -0
  64. package/server/routes/messages-routes.mjs +184 -65
  65. package/server/routes/preview-routes.mjs +9 -1
  66. package/server/routes/sessions-routes.mjs +771 -193
  67. package/server/server-runtime.mjs +316 -100
  68. package/server/services/about-service.mjs +461 -0
  69. package/server/services/background-task-store.mjs +128 -0
  70. package/server/services/background-work-view.mjs +228 -0
  71. package/server/services/cache-rebuild-service.mjs +30 -0
  72. package/server/services/delete-archive-service.mjs +4 -1
  73. package/server/services/detached-worker-watch-service.mjs +150 -0
  74. package/server/services/hidden-message-list.mjs +21 -0
  75. package/server/services/host-suspend-command.mjs +2 -1
  76. package/server/services/message-pin-list.mjs +1 -1
  77. package/server/services/preview-health-probe.mjs +8 -1
  78. package/server/services/preview-proxy-server.mjs +8 -4
  79. package/server/services/preview-registry-service.mjs +676 -26
  80. package/server/services/preview-static-handler.mjs +40 -7
  81. package/server/services/preview-store.mjs +274 -0
  82. package/server/services/rate-limit-service.mjs +12 -1
  83. package/server/services/relay-auth.mjs +183 -0
  84. package/server/services/relay-cli-launcher-service.mjs +3 -1
  85. package/server/services/relay-event-service.mjs +17 -0
  86. package/server/services/remote-relay-dispatcher.mjs +57 -5
  87. package/server/services/request-boundary.mjs +183 -0
  88. package/server/services/session-activity-tracker.mjs +227 -19
  89. package/server/services/session-worker-launch-service.mjs +190 -6
  90. package/server/services/session-worker-process-service.mjs +10 -6
  91. package/server/services/share-access-log-service.mjs +123 -0
  92. package/server/services/share-path-redaction.mjs +424 -0
  93. package/server/services/status-event-service.mjs +7 -3
  94. package/server/services/tunnel-worker-path-guard.mjs +24 -41
  95. package/server/services/windows-autostart-service.mjs +6 -1
  96. package/server/services/windows-boot-autostart-service.mjs +207 -23
  97. package/shared/preview-tool-core.mjs +15 -5
  98. package/shared/remote-relay-contract.mjs +3 -1
  99. package/shared/share-path-privacy.mjs +30 -0
  100. package/shared/windows-system-tools.mjs +106 -0
  101. package/shared/worker-runtime/background-task-digest.mjs +30 -0
  102. package/shared/worker-runtime/heartbeat.mjs +24 -0
  103. package/shared/worker-runtime/kept-posts.mjs +83 -0
  104. package/shared/worker-runtime/worker-websocket-link.mjs +47 -15
package/CHANGELOG.md CHANGED
@@ -3,7 +3,320 @@
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.
7
320
 
8
321
  ## [0.9.10] — 2026-10-12
9
322