@oar-sh/oar 0.9.8 → 0.9.10
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.
- package/CHANGELOG.md +478 -0
- package/README.md +129 -39
- package/bin/bat/oar.bat +46 -46
- package/bin/oar.js +814 -228
- package/package.json +5 -6
- package/server/README.md +306 -4
- package/server/claude-worker/claude-remote-relay-tool.mjs +10 -0
- package/server/claude-worker/claude-session-process.mjs +35 -1
- package/server/claude-worker/claude-turn-publisher.mjs +3 -0
- package/server/claude-worker/sdk-message-normalizer.mjs +31 -4
- package/server/claude-worker/subagent-timeline-recorder.mjs +572 -0
- package/server/copilot-worker/copilot-sdk-session-process.mjs +3 -1
- package/server/cursor-worker/cursor-sdk-adapter.mjs +56 -2
- package/server/db-schema.mjs +44 -0
- package/server/migrations/0008-message-pins.mjs +85 -0
- package/server/migrations/0009-subagent-timeline.mjs +135 -0
- package/server/migrations/0010-subagent-timeline-pruning.mjs +89 -0
- package/server/migrations/0011-relay-event-subscriptions.mjs +125 -0
- package/server/migrations/0012-cross-relay-event-subscriptions.mjs +116 -0
- package/server/public/app/agent-exchange-fold-view.mjs +248 -0
- package/server/public/app/api-client.js +91 -0
- package/server/public/app/archive-confirm.mjs +28 -0
- package/server/public/app/ask-user-view.js +3 -0
- package/server/public/app/attachments-view.js +1024 -131
- package/server/public/app/back-guard.mjs +58 -0
- package/server/public/app/background-tasks-view.mjs +18 -26
- package/server/public/app/bootstrap.js +230 -35
- package/server/public/app/chat-actions-menu-state.mjs +116 -0
- package/server/public/app/claude-cloud-conversation-ui.js +10 -22
- package/server/public/app/composer-control-state.mjs +16 -0
- package/server/public/app/composer-keyboard.mjs +54 -0
- package/server/public/app/conversation-view.js +471 -110
- package/server/public/app/explorer-edit-state.mjs +68 -0
- package/server/public/app/external-link-policy.mjs +4 -1
- package/server/public/app/file-version.mjs +58 -0
- package/server/public/app/host-suspend-ui.js +6 -3
- package/server/public/app/journal-view.js +161 -27
- package/server/public/app/no-conversation-state.mjs +58 -0
- package/server/public/app/pinned-messages-view.mjs +253 -0
- package/server/public/app/provider-readiness.mjs +120 -0
- package/server/public/app/pwa-install.js +1 -1
- package/server/public/app/relay-board-view.js +3 -0
- package/server/public/app/relay-dot-state.mjs +8 -1
- package/server/public/app/relay-event-subscriptions-ui.js +184 -0
- package/server/public/app/relay-event-view.mjs +234 -0
- package/server/public/app/router.js +8 -2
- package/server/public/app/settings-modal.js +53 -1
- package/server/public/app/socket-handlers.js +65 -28
- package/server/public/app/store.js +25 -2
- package/server/public/app/subagent-row.mjs +138 -0
- package/server/public/app/subagent-timeline-view.mjs +186 -0
- package/server/public/app/subagent-viewer-watch.mjs +50 -0
- package/server/public/app/subagent-viewer.mjs +534 -0
- package/server/public/app/task-row-format.mjs +29 -0
- package/server/public/app/thinking-follow.js +4 -0
- package/server/public/app/transcript-jump-bottom-view.mjs +290 -0
- package/server/public/app/transcript-jump-bottom.mjs +99 -0
- package/server/public/app/transcript-reload-freshness.mjs +25 -0
- package/server/public/app/update-ui.js +1 -1
- package/server/public/app/viewer-chat-gallery.mjs +59 -0
- package/server/public/app/viewer-controls-state.mjs +59 -0
- package/server/public/app/viewer-gallery.mjs +46 -0
- package/server/public/app/viewer-gestures.mjs +105 -0
- package/server/public/app/viewer-history.mjs +45 -0
- package/server/public/app/viewer-tap.mjs +112 -0
- package/server/public/index.html +700 -59
- package/server/repositories/message-pin-statements.mjs +39 -0
- package/server/repositories/message-repository.mjs +34 -3
- package/server/repositories/question-repository.mjs +9 -3
- package/server/repositories/remote-relay-repository.mjs +6 -2
- package/server/repositories/subagent-timeline-statements.mjs +170 -0
- package/server/routes/ask-user-routes.mjs +3 -0
- package/server/routes/messages-routes.mjs +548 -111
- package/server/routes/relay-event-routes.mjs +157 -0
- package/server/routes/remote-relay-routes.mjs +15 -0
- package/server/routes/sessions-routes.mjs +164 -24
- package/server/server-runtime.mjs +274 -62
- package/server/services/cloudflared-tunnel-service.mjs +139 -41
- package/server/services/explorer-entry-name.mjs +26 -0
- package/server/services/explorer-mutations.mjs +122 -0
- package/server/services/file-serving.mjs +155 -0
- package/server/services/message-pin-list.mjs +52 -0
- package/server/services/oar-cli-helpers.mjs +139 -4
- package/server/services/oar-cli-menu.mjs +240 -0
- package/server/services/oar-cli-wsl.mjs +129 -0
- package/server/services/preview-proxy-server.mjs +41 -23
- package/server/services/push-dispatch-service.mjs +18 -0
- package/server/services/relay-event-link-service.mjs +289 -0
- package/server/services/relay-event-service.mjs +1935 -0
- package/server/services/relay-shutdown-gate.mjs +31 -0
- package/server/services/remote-relay-caller-context.mjs +52 -7
- package/server/services/remote-relay-dispatcher.mjs +494 -2
- package/server/services/remote-relay-registry-service.mjs +11 -1
- package/server/services/safe-served-content.mjs +61 -11
- package/server/services/session-activity-tracker.mjs +487 -0
- package/server/services/session-history-refresh-service.mjs +31 -0
- package/server/services/stale-attempt-log-limiter.mjs +96 -0
- package/server/services/stale-recovery-clock-guard.mjs +57 -0
- package/server/services/stale-subagent-run-sweep.mjs +109 -0
- package/server/services/subagent-record-retention.mjs +164 -0
- package/server/services/subagent-timeline-service.mjs +328 -0
- package/server/services/subagent-viewer-rooms.mjs +119 -0
- package/server/services/upgrade-socket-guard.mjs +16 -0
- package/shared/relay-events.mjs +371 -0
- package/shared/remote-relay-contract.mjs +157 -5
- package/shared/subagent-record-retention.mjs +31 -0
- package/shared/subagent-timeline.mjs +79 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,484 @@ All notable changes to OAR are documented here. The format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.9.10] — 2026-10-12
|
|
9
|
+
|
|
10
|
+
Highlights: sessions watch other sessions. An agent subscribes to sessions on
|
|
11
|
+
its own relay or on a paired one and is woken by what happens there:
|
|
12
|
+
questions waiting for an answer, errors, replies, finished background work,
|
|
13
|
+
sessions gone quiet. A subscription only notifies; it approves nothing, and
|
|
14
|
+
the first one in a conversation asks you on a card. Also new: a sub-agent's
|
|
15
|
+
whole record opens in a viewer, a note between agents and its reply fold into
|
|
16
|
+
one row, `remote_relay` reports what the provider plans have left, the file
|
|
17
|
+
browser creates, renames and deletes folders and files, a ⌄ button jumps back
|
|
18
|
+
to the latest message, archiving an active session asks first, and Settings
|
|
19
|
+
opens with no conversation open.
|
|
20
|
+
|
|
21
|
+
### Upgrade notes
|
|
22
|
+
|
|
23
|
+
- **Running sessions pick up the new tools when their worker restarts.** A
|
|
24
|
+
session worker builds its `remote_relay` tool when it starts, so a worker
|
|
25
|
+
that was already running before the update offers neither `subscribe` nor
|
|
26
|
+
`usage`, and records no sub-agent timeline, until it is restarted: **☠️
|
|
27
|
+
Kill session** on an idle session is enough, the next message starts a new
|
|
28
|
+
worker. The database migrations run by themselves on the first start.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **A session can watch other sessions and be woken by what happens there.**
|
|
33
|
+
With `remote_relay`'s new `subscribe` an agent asks to be told about
|
|
34
|
+
questions waiting for an answer, errors (a failed turn, the usage limit, a
|
|
35
|
+
worker that died), replies, background agents and shell tasks that finish
|
|
36
|
+
or fail, sessions that went quiet while working, and the moment nothing
|
|
37
|
+
runs any more, for listed sessions or all of them. OAR writes a short event
|
|
38
|
+
message into the watcher's conversation: a new turn when it is idle, a
|
|
39
|
+
steer into the running turn where the provider steers, the next turn
|
|
40
|
+
otherwise. The message says it is automated and approves nothing, and shows
|
|
41
|
+
as a compact row, never as one of your messages. Events within 30 s come
|
|
42
|
+
together; past 20 wake-ups an hour the rest comes as a digest every ten
|
|
43
|
+
minutes, while questions and errors still come at once. An optional
|
|
44
|
+
heartbeat says the watching works while anything runs. Subscriptions
|
|
45
|
+
survive a restart and end on unsubscribe, when the watcher or the watched
|
|
46
|
+
session is archived, or after firing once. The first subscribe of a
|
|
47
|
+
conversation, and any to all sessions, asks you on an approval card;
|
|
48
|
+
`transfer_subscriptions` hands every subscription of an old watcher to a
|
|
49
|
+
new one after your approval. The watcher's header shows **watching N
|
|
50
|
+
sessions** with an Unsubscribe button per subscription, and a watched
|
|
51
|
+
session shows a **watched** badge.
|
|
52
|
+
|
|
53
|
+
- **Watch sessions on a paired relay.** `subscribe`, `unsubscribe`,
|
|
54
|
+
`list_subscriptions` and `transfer_subscriptions` also take a paired
|
|
55
|
+
relay, with the same events and options. The relay must be unlocked in
|
|
56
|
+
the conversation and allow at least reading, and the other relay must
|
|
57
|
+
accept other relays' agents; the approval card names that relay, and
|
|
58
|
+
nothing is asked there. This relay fetches the events every few seconds
|
|
59
|
+
over the pairing (a pairing in one direction is enough), and they arrive
|
|
60
|
+
like those of its own sessions, their lines starting with the relay's
|
|
61
|
+
name. If the link drops, the other relay keeps what happens (up to 500
|
|
62
|
+
events per subscription, for 7 days) and delivers it as one row tagged
|
|
63
|
+
**replayed** once it answers again, questions and errors first, between
|
|
64
|
+
"link lost" and "link restored". Subscriptions survive restarts of either
|
|
65
|
+
relay and end on unsubscribe (reaching the other relay later if it was
|
|
66
|
+
offline), when the watcher or the watched session is archived, or when
|
|
67
|
+
the pairing is removed. An older relay answers that it needs an update.
|
|
68
|
+
|
|
69
|
+
- **list_sessions and read_session say what a session is doing.** `state`
|
|
70
|
+
is `running_turn`, `background_only` or `idle`, and `tasks` lists its
|
|
71
|
+
background agents and shell tasks with when they started, when they last
|
|
72
|
+
did something, how they ended and the exit code where it is known. A
|
|
73
|
+
session with background agents running no longer reads as inactive.
|
|
74
|
+
|
|
75
|
+
- **Agents can check what the provider plans have left.** `remote_relay`'s
|
|
76
|
+
new `usage` action answers, for the relay an agent runs on or a paired
|
|
77
|
+
one, what each provider's plan has left: Cursor's spend against its
|
|
78
|
+
limit and when it resets, Claude's windows and whether a usage limit has
|
|
79
|
+
paused turns, Copilot and Grok where they are tracked; a provider that is
|
|
80
|
+
not set up says so. It needs only read permission and returns no account
|
|
81
|
+
names, addresses or tokens.
|
|
82
|
+
|
|
83
|
+
- **Notes between agents fold away.** A prompt another agent sent a
|
|
84
|
+
session, from a paired relay or from this one, and the session's reply to
|
|
85
|
+
it show as one row, "note from linux-test · report builder · replied",
|
|
86
|
+
which opens to both, on a desktop and on a phone. Your own messages and
|
|
87
|
+
the replies to them stay as they are, and a turn you steered a message
|
|
88
|
+
into, or one with a question or card open for you, is never folded. A
|
|
89
|
+
note still being worked on stays open and folds by itself once it is
|
|
90
|
+
answered; one you fold or open yourself stays as you left it.
|
|
91
|
+
|
|
92
|
+
- **Open a sub-agent's whole record.** A sub-agent's title in the composer's
|
|
93
|
+
background-task panel opens a viewer with everything the agent did, in
|
|
94
|
+
order: its thoughts, every tool call with its full input and its result,
|
|
95
|
+
its reply text and the agents it started itself, live while it runs. The
|
|
96
|
+
finished reply lists the turn's sub-agents as the same rows, with model,
|
|
97
|
+
type, tokens, time and how they ended, so the record stays one click away.
|
|
98
|
+
A child agent opens in place, and back returns to its parent. Stop is in
|
|
99
|
+
the viewer too. Claude records everything; Copilot and Cursor show what
|
|
100
|
+
they report. A tool result is kept to its first 4 KB, and a record to 2000
|
|
101
|
+
entries. Only a page with the viewer open receives the record as it grows.
|
|
102
|
+
|
|
103
|
+
- **Choose how long sub-agent tool output is kept.** Settings → General →
|
|
104
|
+
**Keep sub-agent tool output** offers 7, 14, 30 or 90 days (the default)
|
|
105
|
+
or always. Once a finished agent's record is older than that, its tool
|
|
106
|
+
inputs and results are removed, once a day; its thoughts, its reply, its
|
|
107
|
+
notes and the one-line summary of each tool call stay, and the viewer says
|
|
108
|
+
what was removed.
|
|
109
|
+
|
|
110
|
+
- **Folders can be created in the file browser.** A **📁+ New folder**
|
|
111
|
+
button in the toolbar opens a row at the top of the folder pane; the name
|
|
112
|
+
is typed there and Enter creates the folder inside the open one and opens
|
|
113
|
+
it. It works in the repository, on the drives and in the session folder,
|
|
114
|
+
and in the folder picker of **New chat** and **Change CWD**, so a new
|
|
115
|
+
project folder can be made and picked in one go. A taken name or a name
|
|
116
|
+
the system refuses is reported under the input.
|
|
117
|
+
|
|
118
|
+
- **Files and folders can be renamed and deleted in the file browser.** A
|
|
119
|
+
right-click, or a long press on a phone, on a folder in the tree or a file
|
|
120
|
+
in the folder pane opens a menu with **Rename** and **Delete**; a folder's
|
|
121
|
+
menu also offers **New folder here**. Rename edits the name in place: Enter
|
|
122
|
+
saves, Escape cancels, and an unchanged name is a cancel. Delete asks in a
|
|
123
|
+
dialog that says what goes: a file, an empty folder, or a folder with so
|
|
124
|
+
many files and folders in it; for a folder with content the folder's name
|
|
125
|
+
must be typed before the red button enables. Links are never followed or
|
|
126
|
+
changed; roots (the repository root, `/`, a drive, the session folder)
|
|
127
|
+
cannot be renamed or deleted, and the session folder is read-only apart
|
|
128
|
+
from new folders.
|
|
129
|
+
|
|
130
|
+
- **Jump back to the latest message.** A round ⌄ button above the composer
|
|
131
|
+
fades in once the transcript is about a screen above its end, and its
|
|
132
|
+
badge counts the rows that arrived since (an agent's folded exchange
|
|
133
|
+
counts once). A click goes back to the end, smoothly up to three screens
|
|
134
|
+
away and at once beyond, and the transcript follows new rows again; from
|
|
135
|
+
a search result or a pin deep in the history it loads the end.
|
|
136
|
+
|
|
137
|
+
### Changed
|
|
138
|
+
|
|
139
|
+
- **Archiving a session whose process is still active asks first.** A green
|
|
140
|
+
session, idle or working, now opens "Archive this session? Its process is
|
|
141
|
+
still active." from the row's 🗄, its menu and the chat menu alike; Cancel
|
|
142
|
+
or Escape keeps it. An inactive session archives at once, as before.
|
|
143
|
+
|
|
144
|
+
- **A message from another device no longer pulls a reader who scrolled up
|
|
145
|
+
down to the end.** Another client's message, an agent's prompt included,
|
|
146
|
+
is followed like any live row: only while the transcript is at its end.
|
|
147
|
+
|
|
148
|
+
- The npm package guard allows 6.75 MiB unpacked (was 6.5 MiB); the
|
|
149
|
+
sub-agent records and the event subscriptions, now across paired relays,
|
|
150
|
+
grew the server.
|
|
151
|
+
|
|
152
|
+
### Fixed
|
|
153
|
+
|
|
154
|
+
- **A dropped websocket can no longer take the relay down.** A browser that
|
|
155
|
+
reset its connection while a preview's websocket, or any websocket to the
|
|
156
|
+
relay, was still being set up ended the whole relay with `read ECONNRESET`.
|
|
157
|
+
|
|
158
|
+
- **A queued relay restart no longer waits forever on a busy relay.** The
|
|
159
|
+
restart used to wait for the whole queue to empty while new messages kept
|
|
160
|
+
being handed to workers, so each new turn postponed it again. It now waits
|
|
161
|
+
for the running turns only; queued messages are held until the relay is
|
|
162
|
+
back and run then. The banner says which.
|
|
163
|
+
|
|
164
|
+
- **Waking from sleep no longer restarts the running turns.** After the host
|
|
165
|
+
slept for hours, every worker heartbeat looked hours old the moment the
|
|
166
|
+
relay's timer fired again, so live turns were recovered as stale and their
|
|
167
|
+
messages delivered a second time while the workers' own replies were
|
|
168
|
+
refused, hundreds of warnings in minutes. The relay now notices the clock
|
|
169
|
+
jump and gives live workers a full inactivity window to report before
|
|
170
|
+
anything is recovered; a superseded attempt's refusals are logged once and
|
|
171
|
+
summarized, not line by line.
|
|
172
|
+
|
|
173
|
+
- **A Cursor turn waiting on a long command is no longer ended as stalled.**
|
|
174
|
+
When the agent started a command in the background and then waited for
|
|
175
|
+
it, the wait counted as a model request, and the turn was ended after five
|
|
176
|
+
minutes of quiet. The wait now gets the 30 minutes a running tool gets.
|
|
177
|
+
|
|
178
|
+
- **An event's fields cannot add lines to a watcher's event message.** A
|
|
179
|
+
line break in a session title, a task description or a field of a paired
|
|
180
|
+
relay's event could start a line without the `[relay event]` prefix. Every
|
|
181
|
+
field is now written as one line, and what a paired relay sends is stored
|
|
182
|
+
field by field from a fixed list, never as the payload it sent.
|
|
183
|
+
|
|
184
|
+
- **The relay event tables no longer grow without bound.** Delivered events
|
|
185
|
+
and ended subscriptions were never deleted, and the retention check for
|
|
186
|
+
paired relays scanned the whole event table every 30 seconds. Delivered
|
|
187
|
+
events now go after two hours (the wake-up count reads the last hour),
|
|
188
|
+
ended subscriptions after a week, and the check uses its index.
|
|
189
|
+
|
|
190
|
+
- **The "watching N sessions" list opens in full.** The header cut it off,
|
|
191
|
+
so on a phone it did not show at all. It now opens under the chip on top
|
|
192
|
+
of the page, and Escape or a tap elsewhere closes it.
|
|
193
|
+
|
|
194
|
+
- **Sub-agent runs a worker never closed no longer count as running work.**
|
|
195
|
+
A run whose worker went away without reporting its end stayed `running`
|
|
196
|
+
for good, so a subscription to all sessions never saw `all_idle` and
|
|
197
|
+
reported the session stalled over and over, and the suspend watchdog saw
|
|
198
|
+
work. Such runs are now closed shortly after the relay starts and every
|
|
199
|
+
ten minutes (never while their worker is alive), and a run row not touched
|
|
200
|
+
for a day is not counted meanwhile.
|
|
201
|
+
|
|
202
|
+
- **A background agent's output stays with the turn that started it.** Its
|
|
203
|
+
thoughts and tool calls no longer land in a later turn's reply, and between
|
|
204
|
+
turns they are no longer lost.
|
|
205
|
+
|
|
206
|
+
- **A background agent no longer shows as finished the moment it starts.**
|
|
207
|
+
Its row now ends when the agent does.
|
|
208
|
+
|
|
209
|
+
- **A sub-agent stopped by a failed turn or a worker crash settles on screen
|
|
210
|
+
without a reload.** The update used to arrive without its turn, and the
|
|
211
|
+
page ignored it.
|
|
212
|
+
|
|
213
|
+
- **The transcript keeps following the live reply when a background task
|
|
214
|
+
starts.** The task panel appearing, opening or growing shrank the
|
|
215
|
+
transcript from below and left it that much short of the end, so the next
|
|
216
|
+
live row stopped following. It now stays at the end.
|
|
217
|
+
|
|
218
|
+
- **The transcript follows the agent's reply as it streams in.** Each piece
|
|
219
|
+
of streamed text was scrolled to before it was shown, so the first new
|
|
220
|
+
line left the reader short of the end and the rest was not followed.
|
|
221
|
+
|
|
222
|
+
- **Going back to the end while a turn finishes sticks.** The reload at the
|
|
223
|
+
end of a turn put a reader back where they were when it was asked for, so
|
|
224
|
+
scrolling to the end, or pressing ⌄, during it was undone and the next
|
|
225
|
+
rows were not followed. It now keeps where the reader is when it lands.
|
|
226
|
+
|
|
227
|
+
- **⌄ no longer opens the keyboard on a phone.** The composer is no longer
|
|
228
|
+
focused when the page loads on a phone, and ⌄ keeps it focused only while
|
|
229
|
+
the keyboard is already open, so a tap on it just scrolls.
|
|
230
|
+
|
|
231
|
+
- **A message that arrives while the conversation reloads stays on screen.**
|
|
232
|
+
A reload asked for just before another client's message or a reply landed
|
|
233
|
+
rebuilt the transcript without it, until the next reload. The reload now
|
|
234
|
+
asks again when rows arrived on the way.
|
|
235
|
+
|
|
236
|
+
- **A message sent right after switching conversations shows up where it was
|
|
237
|
+
sent.** A quick run through the list, or a click back on the conversation
|
|
238
|
+
still open, could hand the view to an earlier click once the draft of the
|
|
239
|
+
conversation left behind had saved. The message then went to that
|
|
240
|
+
conversation, or its bubble and the live reply stayed hidden until the
|
|
241
|
+
conversation was opened again. A click now opens its conversation at once.
|
|
242
|
+
|
|
243
|
+
- **The live reply stays on screen right after a send.** A poll answer read
|
|
244
|
+
just before the agent took the message could end the turn on the page, and
|
|
245
|
+
a reply update lost on a busy connection left the bubble empty; both waited
|
|
246
|
+
for the conversation to be opened again.
|
|
247
|
+
|
|
248
|
+
- **Settings opens with no conversation open.** On a fresh install the ⋯
|
|
249
|
+
menu did nothing until a chat existed, so no provider could be turned on.
|
|
250
|
+
The menu now works on an empty screen, with the entries that need a
|
|
251
|
+
conversation left out, and the empty screen and New Chat point to
|
|
252
|
+
Settings → Providers until a provider is ready instead of starting a
|
|
253
|
+
Copilot chat that cannot run.
|
|
254
|
+
|
|
255
|
+
## [0.9.9] — 2026-10-07
|
|
256
|
+
|
|
257
|
+
Highlights: `oar` opens a menu and gains stop, restart, status, url and
|
|
258
|
+
service commands, and setup asks for access and port; files open in a
|
|
259
|
+
fullscreen viewer with galleries to swipe through, and a PDF opens in the
|
|
260
|
+
browser; messages can be pinned; a downloaded file is the file as it is now,
|
|
261
|
+
and no cache may keep an API answer; the built-in Cloudflare tunnel uses the
|
|
262
|
+
`cloudflared` you installed instead of a bundled copy; the three provider
|
|
263
|
+
SDKs are current.
|
|
264
|
+
|
|
265
|
+
### Upgrade notes
|
|
266
|
+
|
|
267
|
+
- **The built-in Cloudflare tunnel needs `cloudflared` installed on the relay
|
|
268
|
+
host.** An install that ran the tunnel (`cloudflaredTunnel` in the config)
|
|
269
|
+
on the copy that came with OAR loses the tunnel after this update until
|
|
270
|
+
`cloudflared` is installed: `winget install --id Cloudflare.cloudflared`
|
|
271
|
+
on Windows, `brew install cloudflared` on macOS, Cloudflare's package
|
|
272
|
+
repository on Linux (<https://pkg.cloudflare.com/>). The relay itself
|
|
273
|
+
starts and works locally in the meantime. Installs with
|
|
274
|
+
`cloudflaredTunnel.binary` set, with `cloudflared` on the `PATH`, or
|
|
275
|
+
with a tunnel that runs outside OAR are not affected.
|
|
276
|
+
|
|
277
|
+
### Added
|
|
278
|
+
|
|
279
|
+
- **The file viewer fills the screen.** A file opened from the file browser
|
|
280
|
+
or from a chat takes the whole screen: pictures, video and audio on black,
|
|
281
|
+
text in a column that reads well on a wide screen. The title, the toolbar
|
|
282
|
+
and the meta line float over the content; a tap on the content hides them
|
|
283
|
+
and brings them back, and while a video or an audio file plays they fade
|
|
284
|
+
by themselves after three seconds, as players do. Audio files play in the
|
|
285
|
+
viewer. A PDF, which the viewer cannot show, is a card with **Open in new
|
|
286
|
+
tab** (the browser's own viewer) next to **Download**.
|
|
287
|
+
- **Swipe through a folder, or through a chat's media.** A file opened from
|
|
288
|
+
the browser's folder pane carries its neighbours along: a sideways swipe
|
|
289
|
+
shows the next or the previous file, as do the arrow keys and the two
|
|
290
|
+
buttons below with the position between them. A picture or an attachment
|
|
291
|
+
tapped in a chat opens with every image, video and audio file of the
|
|
292
|
+
conversation to swipe through, oldest to newest. Neighbours are fetched
|
|
293
|
+
ahead, so a swipe lands on the file and not on "Fetching…".
|
|
294
|
+
- **The back gesture closes the viewer, and a swipe down dismisses it.** On
|
|
295
|
+
a phone the system back gesture closed the app; it now closes the viewer
|
|
296
|
+
and nothing else, and the address never changes. A swipe down on a picture,
|
|
297
|
+
a player or a card lets the content follow the finger and then go.
|
|
298
|
+
- **The back button never closes the installed app.** In the app installed
|
|
299
|
+
from the browser (Android, iOS, desktop), pressing back with nothing left
|
|
300
|
+
to go back to used to close it. From the first touch on, the app keeps
|
|
301
|
+
one entry beneath its own and steps back onto it: back closes the file
|
|
302
|
+
viewer and nothing more, and a back with nothing left to close shows
|
|
303
|
+
*Press back again to close the app* — a second press without a touch in
|
|
304
|
+
between closes it, as phone apps do. (Before any touch the browser lets
|
|
305
|
+
back leave on purpose; a page cannot change that.) In a browser tab the
|
|
306
|
+
back button is the browser's as before.
|
|
307
|
+
- **`oar` opens a menu.** In a terminal, `oar` shows whether the relay runs,
|
|
308
|
+
its version, URL, port, access and service, and offers Start, Stop, Restart,
|
|
309
|
+
Show URL and QR code, Settings, Install or remove the service, Update,
|
|
310
|
+
Doctor and Copilot session (arrow keys or digits, Enter, `q`). Without a
|
|
311
|
+
terminal it prints the status and the usage.
|
|
312
|
+
- **`oar stop`, `oar restart`, `oar status`, `oar url`, `oar service`.** Stop
|
|
313
|
+
and restart wait for running turns (the systemd service is stopped and
|
|
314
|
+
restarted by systemd). `oar status` exits 0 when the relay runs and 3 when
|
|
315
|
+
it does not. `oar url` prints the relay URL again. `oar service install`,
|
|
316
|
+
`remove` and `status` manage the start at login: the systemd user service
|
|
317
|
+
on Linux, the sign-in autostart on Windows; on macOS, and on a Linux
|
|
318
|
+
without a systemd user session, the command says so and names `oar start`.
|
|
319
|
+
- **`oar setup` asks for access and port, with options for each.** In a
|
|
320
|
+
terminal, setup asks for a new token, the access (this machine only, or
|
|
321
|
+
LAN) and the port, each with its current value; Enter keeps it. `--port`,
|
|
322
|
+
`--lan`, `--local` and `--new-token` answer without a question, and
|
|
323
|
+
`--defaults` keeps everything of an existing config.
|
|
324
|
+
- **WSL: a port has to be free on Windows too.** In WSL's default networking
|
|
325
|
+
a port can be free in the distro while a Windows program listens on it, and
|
|
326
|
+
a Windows browser's `localhost` then reaches that program. A new config now
|
|
327
|
+
takes the first port that is free on both sides, setup refuses a port
|
|
328
|
+
Windows holds, and `oar start`, `oar status`, `oar doctor` and `oar setup`
|
|
329
|
+
warn when Windows holds the relay's port. Where Windows cannot be asked,
|
|
330
|
+
nothing changes.
|
|
331
|
+
- **Pin messages in a conversation.** Every message has a **Pin** button
|
|
332
|
+
beside **Hide**. A conversation with pins shows a **📍** button with their
|
|
333
|
+
count in its header; it opens the list of pinned messages, each with a short
|
|
334
|
+
preview. A row scrolls to its message, also one far up in the history, and
|
|
335
|
+
**🗑** unpins it. Pins are stored on the relay, appear on every open device
|
|
336
|
+
at once, and are not part of the shared view. At most 100 per conversation.
|
|
337
|
+
|
|
338
|
+
### Changed
|
|
339
|
+
|
|
340
|
+
- **The Copilot terminal session is `oar copilot`.** `oar` alone no longer
|
|
341
|
+
starts the relay and `gh copilot`. `oar copilot [-- <args>]` does; the old
|
|
342
|
+
forms with an option and no command (`oar -- <args>`,
|
|
343
|
+
`oar --install-extension`, `oar --no-install-extension`,
|
|
344
|
+
`oar --migrate-from <dir>`) still run it.
|
|
345
|
+
- **Setup no longer asks about the tunnel or the service.** The service is
|
|
346
|
+
`oar service install`; the tunnel is set in the config
|
|
347
|
+
([Remote access](README.md#remote-access)). `oar setup --start` still
|
|
348
|
+
installs the service where a systemd user session exists.
|
|
349
|
+
- **A QR code only for an address a phone can reach.** With LAN access the
|
|
350
|
+
URL and the QR code carry the LAN address. With access for this machine
|
|
351
|
+
only, setup prints the localhost URL and one line on how to get phone
|
|
352
|
+
access, and no QR code. In WSL a LAN address is internal to WSL: setup says
|
|
353
|
+
so and names the two ways out, a tunnel or WSL's mirrored networking.
|
|
354
|
+
- The npm package guard allows 6.5 MiB unpacked (was 6 MiB); the command line
|
|
355
|
+
grew by the menu and the new commands, and the app by the pinned messages
|
|
356
|
+
and the file viewer.
|
|
357
|
+
- **A tunnel without `cloudflared` says so.** When the built-in Cloudflare
|
|
358
|
+
tunnel is switched on and no `cloudflared` is found, the relay starts as
|
|
359
|
+
usual, the tunnel stays off, and the status dot's tooltip, `oar doctor` and
|
|
360
|
+
the server log name the install command for the system. The relay looks
|
|
361
|
+
again every minute and starts the tunnel once `cloudflared` is there. With
|
|
362
|
+
`cloudflaredTunnel.required` the queue stays paused until the tunnel is
|
|
363
|
+
connected, as before.
|
|
364
|
+
- **`--allow-scripts` lists two packages.** A manual install on npm 12 is now
|
|
365
|
+
`npm install -g --allow-scripts=better-sqlite3,koffi @oar-sh/oar`;
|
|
366
|
+
`oar update` and the **Update** button pass the same list.
|
|
367
|
+
- `@github/copilot-sdk` 1.0.17 (bundled Copilot CLI 1.0.93): typed structured
|
|
368
|
+
outputs for `sendAndWait`, structured JSON-RPC error data, an experimental
|
|
369
|
+
`setTools()` to replace a session's client tools mid-session, sub-agent
|
|
370
|
+
start/stop hooks, and the new session events `tool.shell_output` and
|
|
371
|
+
`human_response.recorded`. The extension entry renames `factories` to
|
|
372
|
+
`workflows`; OAR passes neither. The package now pins its own dependencies
|
|
373
|
+
exactly, so it carries its own `zod` 4.3.6.
|
|
374
|
+
- `@anthropic-ai/claude-agent-sdk` 0.3.292 (bundled Claude Code 2.1.292).
|
|
375
|
+
Of the nine releases since 0.3.283 these touch the relay's worker: a
|
|
376
|
+
cut-short or interrupted streaming reply now always ends with its
|
|
377
|
+
`message_stop` (0.3.287, 0.3.290), so the turn-liveness watchdog sees the
|
|
378
|
+
end of the model's output; a follow-up turn woken by a finished background
|
|
379
|
+
agent no longer fails its hooks, `canUseTool` and SDK MCP calls with
|
|
380
|
+
"Stream closed" (0.3.284); `commands_changed` no longer arrives before
|
|
381
|
+
`init` (0.3.287); a user message replayed under the same `uuid` while the
|
|
382
|
+
first copy still waits is no longer replayed twice (0.3.290); subagent
|
|
383
|
+
`assistant`/`user` messages carry `agent_id`, task events carry
|
|
384
|
+
`parent_task_id` and `run_id`, and `background_tasks_changed` for a
|
|
385
|
+
finishing task now arrives after its `task_updated` and `task_notification`
|
|
386
|
+
(0.3.292). An omitted `permissionMode` is now left to Claude Code (0.3.286);
|
|
387
|
+
the worker always passes one.
|
|
388
|
+
- `@cursor/sdk` 1.0.36 (additive API changes only): an opt-in
|
|
389
|
+
`subagentInherit` for `Task` child sessions, `sessionId` in the custom-tool
|
|
390
|
+
context, and the SDK's *default* agent-store path now derives from SHA-256
|
|
391
|
+
with a one-time rename of the old MD5-named store. OAR passes its own
|
|
392
|
+
`stateRoot`, so its stores under `data/cursor-agents` keep their layout.
|
|
393
|
+
The package dropped its `@connectrpc/connect-node` dependency.
|
|
394
|
+
|
|
395
|
+
### Removed
|
|
396
|
+
|
|
397
|
+
- **The bundled `cloudflared`.** OAR no longer installs the `cloudflared`
|
|
398
|
+
npm package, which downloaded the binary during the install. The tunnel
|
|
399
|
+
runs the `cloudflared` named in `cloudflaredTunnel.binary` (or
|
|
400
|
+
`COPILOT_CLOUDFLARED_BINARY`), or else the one on the `PATH`.
|
|
401
|
+
|
|
402
|
+
### Fixed
|
|
403
|
+
|
|
404
|
+
- **A jump into the history stays where it landed.** After opening a search
|
|
405
|
+
result in an older part of a conversation, the page put the end of the
|
|
406
|
+
conversation back as soon as it refreshed itself: within a second while a
|
|
407
|
+
turn was running, and whenever a turn ended or the connection came back.
|
|
408
|
+
The window you jumped to now stays until you scroll down to the newest
|
|
409
|
+
message. The same holds for a jump to a pinned message.
|
|
410
|
+
- **On a phone, the Pin button of a message can be tapped.** A tap on a bubble
|
|
411
|
+
reveals its Pin and Hide buttons, but the reveal hung on the hover state the
|
|
412
|
+
tap leaves behind, which the browser drops at the next scroll or layout
|
|
413
|
+
change (seen on Linux), and the share-visibility line then took the tap
|
|
414
|
+
instead of the button. The tap now holds the reveal until a tap lands
|
|
415
|
+
outside the bubble.
|
|
416
|
+
- **A PDF opens in the browser's viewer again.** Served files carry a
|
|
417
|
+
`Content-Security-Policy` with `sandbox` so a worker-written file can never
|
|
418
|
+
run as script on the relay's origin; browsers refuse to show a sandboxed
|
|
419
|
+
PDF in their built-in viewer, so "open in a new tab" turned into a
|
|
420
|
+
download. A PDF alone is now served without the `sandbox` directive (PDF
|
|
421
|
+
viewers run no document script) and may be framed by the relay's own
|
|
422
|
+
origin; HTML, SVG and every other type keep the full lock-down.
|
|
423
|
+
- **Audio files were an opaque download.** No audio type was known, so an
|
|
424
|
+
`.mp3` or `.wav` went out as `application/octet-stream` with a download
|
|
425
|
+
disposition. Audio types are served as such, previewed as audio, and
|
|
426
|
+
`.bmp` and `.avif` are served as images.
|
|
427
|
+
- **An SVG can be previewed.** It was served as plain text so that a
|
|
428
|
+
worker-written SVG could never run as a page on the relay's origin, and
|
|
429
|
+
an `<img>` refuses plain text: the viewer and the file browser's
|
|
430
|
+
thumbnails showed a broken picture. An SVG now keeps its type; the
|
|
431
|
+
`sandbox` in every served file's `Content-Security-Policy` still denies
|
|
432
|
+
it script and an origin if it is opened as a page.
|
|
433
|
+
- **Uploaded video can seek.** A video attachment in a chat was streamed
|
|
434
|
+
without range support, so the player could not jump; it now goes through
|
|
435
|
+
the same range-aware file serving as a workspace file, and so does a
|
|
436
|
+
shared conversation's attachment. Their long-lived caching stays.
|
|
437
|
+
- **A file with a Japanese, Chinese or emoji name can be served again.** Its
|
|
438
|
+
name went into `Content-Disposition` as is, which the HTTP layer refuses
|
|
439
|
+
for characters outside Latin-1, so such a workspace file, upload or shared
|
|
440
|
+
attachment failed with a server error and left a file handle open each
|
|
441
|
+
time. The header now carries an ASCII fallback and the full name in the
|
|
442
|
+
`filename*` form, so the file opens and is saved under its own name.
|
|
443
|
+
- **A changed file is downloaded as it is now.** The **Download** button of
|
|
444
|
+
the file viewer handed back an older copy of a file that had been written
|
|
445
|
+
again under the same name. Three things were wrong:
|
|
446
|
+
- A click on Download did not download: since 0.9.0 every link click was
|
|
447
|
+
turned into "open in a new tab", so the link's own file name was dropped
|
|
448
|
+
and a phone showed or saved the file under its plain name, next to (or
|
|
449
|
+
instead of) the copy it already had. Download links are left to the
|
|
450
|
+
browser again.
|
|
451
|
+
- A file fetched within two seconds of a change arrived cut to its
|
|
452
|
+
previous length, because the announced length came from an older read
|
|
453
|
+
than the bytes. Length, validators and bytes now come from one open file.
|
|
454
|
+
- The address and the saved name never changed. The link now carries the
|
|
455
|
+
file's version (`v=…`), and the file is saved with the time it was last
|
|
456
|
+
changed, `report.pdf` as `report (1005-0719).pdf`.
|
|
457
|
+
- **No cache between the relay and you may keep an answer.** Every API
|
|
458
|
+
answer, errors included, now says `Cache-Control: no-store` unless the route
|
|
459
|
+
sets its own value, and every answer carries `CDN-Cache-Control: no-store`
|
|
460
|
+
and `Cloudflare-CDN-Cache-Control: no-store`, which bind a CDN in front of
|
|
461
|
+
a tunnel whatever cache rule its zone has. Served files also carry an
|
|
462
|
+
`ETag` and `Last-Modified`.
|
|
463
|
+
- **A file preview could stop the relay.** The preview and file routes used a
|
|
464
|
+
cache they were never given, so dropping a stale entry threw; in the
|
|
465
|
+
preview route that happened outside any handler and ended the process.
|
|
466
|
+
- **A changed token, port or access reaches the running relay.** `oar setup`
|
|
467
|
+
wrote the new value while the relay kept the old one, so the relay refused
|
|
468
|
+
the token in its own config. Setup now restarts a running relay after such
|
|
469
|
+
a change (in a terminal it asks first), with the old token on the old port,
|
|
470
|
+
and waits until the relay answers with the new settings. If you decline, or
|
|
471
|
+
the relay is still busy with a turn, `oar status`, `oar stop` and
|
|
472
|
+
`oar restart` still find it.
|
|
473
|
+
- **The command may follow its options, and an unknown one starts nothing.**
|
|
474
|
+
`oar --port 3339 setup` and `oar --setup` used to start the relay and
|
|
475
|
+
`gh copilot` instead of running setup. The command is now found wherever it
|
|
476
|
+
stands, `--setup`, `--start`, `--stop` and `--status` name it too, and an
|
|
477
|
+
unknown command or option prints the usage and exits with code 2.
|
|
478
|
+
- **`oar copilot` checks the relay on its port, and says when `gh` is
|
|
479
|
+
missing.** A lock file alone no longer counts as a running relay: the relay
|
|
480
|
+
has to answer on the port. Without the GitHub CLI the command says so and
|
|
481
|
+
starts nothing, instead of *spawn gh ENOENT*.
|
|
482
|
+
- **Ctrl+C in a prompt** ends the command quietly with exit code 130 instead
|
|
483
|
+
of *Unhandled error*.
|
|
484
|
+
|
|
485
|
+
|
|
8
486
|
## [0.9.8] — 2026-10-04
|
|
9
487
|
|
|
10
488
|
Highlights: OAR has a logo, an otter, with icons that follow Day and Night
|