@oar-sh/oar 0.9.20 → 0.9.21

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 (95) hide show
  1. package/CHANGELOG.md +212 -0
  2. package/README.md +13 -13
  3. package/bin/oar.js +2 -2
  4. package/package.json +1 -1
  5. package/server/claude-cloud-worker/claude-cloud-session-process.mjs +231 -1
  6. package/server/claude-cloud-worker/claude-cloud-session-worker.mjs +22 -1
  7. package/server/claude-cloud-worker/claude-cloud-worker-steering.mjs +34 -0
  8. package/server/claude-worker/claude-session-process.mjs +7 -0
  9. package/server/claude-worker/claude-session-worker-main.mjs +8 -1
  10. package/server/copilot-worker/copilot-sdk-session-process.mjs +74 -1
  11. package/server/copilot-worker/copilot-sdk-session-worker.mjs +8 -1
  12. package/server/cursor-worker/cursor-session-worker.mjs +12 -1
  13. package/server/cursor-worker/cursor-worker-steering.mjs +20 -0
  14. package/server/grok-worker/grok-background-tasks.mjs +9 -0
  15. package/server/grok-worker/grok-session-worker.mjs +12 -1
  16. package/server/grok-worker/grok-turn-runner.mjs +3 -0
  17. package/server/grok-worker/grok-worker-steering.mjs +23 -0
  18. package/server/public/app/about-ui.js +1 -1
  19. package/server/public/app/about-view.mjs +19 -0
  20. package/server/public/app/action-confirmations.js +1 -1
  21. package/server/public/app/api-client.js +4 -2
  22. package/server/public/app/ask-user-view.js +20 -6
  23. package/server/public/app/attachments-view.js +1 -1
  24. package/server/public/app/back-guard.mjs +31 -5
  25. package/server/public/app/bootstrap.js +556 -68
  26. package/server/public/app/chat-actions-menu-state.mjs +139 -4
  27. package/server/public/app/chat-header-meta.mjs +27 -0
  28. package/server/public/app/claude-billing-toggle-ui.js +2 -1
  29. package/server/public/app/claude-cloud-conversation-ui.js +2 -2
  30. package/server/public/app/composer-settings-label.mjs +90 -0
  31. package/server/public/app/composer-settings-ui.js +442 -0
  32. package/server/public/app/context-menu.mjs +36 -5
  33. package/server/public/app/conversation-deep-link.mjs +15 -11
  34. package/server/public/app/conversation-group-drag.mjs +12 -2
  35. package/server/public/app/conversation-group-order.mjs +9 -2
  36. package/server/public/app/conversation-group-state.mjs +47 -10
  37. package/server/public/app/conversation-origin-ui.js +17 -1
  38. package/server/public/app/conversation-view.js +3 -0
  39. package/server/public/app/cwd-menu-placement.mjs +18 -6
  40. package/server/public/app/cwd-picker.js +36 -6
  41. package/server/public/app/emoji-view.js +1 -1
  42. package/server/public/app/font-scaling.js +26 -0
  43. package/server/public/app/header-priority-actions.mjs +234 -0
  44. package/server/public/app/hidden-messages-view.mjs +4 -0
  45. package/server/public/app/inbox-ui.js +18 -2
  46. package/server/public/app/journal-view.js +311 -58
  47. package/server/public/app/phone-landscape-ui.js +225 -0
  48. package/server/public/app/phone-landscape.mjs +99 -0
  49. package/server/public/app/phone-typing-state.mjs +104 -0
  50. package/server/public/app/pinned-messages-view.mjs +5 -0
  51. package/server/public/app/push-settings.js +2 -2
  52. package/server/public/app/pwa-install.js +24 -3
  53. package/server/public/app/relay-event-subscriptions-ui.js +71 -22
  54. package/server/public/app/relay-event-view.mjs +38 -0
  55. package/server/public/app/relay-restart-ui.js +2 -2
  56. package/server/public/app/relay-schedule-view.mjs +32 -6
  57. package/server/public/app/relay-schedules-ui.js +69 -20
  58. package/server/public/app/remote-relay-origin-view.mjs +41 -23
  59. package/server/public/app/session-history-ui.js +250 -0
  60. package/server/public/app/session-history.mjs +599 -0
  61. package/server/public/app/settings-modal.js +61 -6
  62. package/server/public/app/settings-tabs.js +12 -0
  63. package/server/public/app/share-state.js +1 -1
  64. package/server/public/app/shared-conversations-settings.js +2 -2
  65. package/server/public/app/socket-handlers.js +5 -2
  66. package/server/public/app/status-view.mjs +18 -2
  67. package/server/public/app/store.js +87 -16
  68. package/server/public/app/subagent-viewer.mjs +27 -2
  69. package/server/public/app/tab-strip-fade.mjs +101 -0
  70. package/server/public/app/tap-actions.js +66 -0
  71. package/server/public/app/usage-limit-ui.js +63 -2
  72. package/server/public/app/viewer-history.mjs +13 -2
  73. package/server/public/index.html +2249 -1590
  74. package/server/relay-tools.md +1 -0
  75. package/server/repositories/message-repository.mjs +23 -0
  76. package/server/repositories/session-repository.mjs +19 -0
  77. package/server/routes/claude-cloud-routes.mjs +19 -0
  78. package/server/routes/messages-routes.mjs +57 -1
  79. package/server/routes/sessions-routes.mjs +41 -7
  80. package/server/server-runtime.mjs +190 -40
  81. package/server/services/about-service.mjs +22 -53
  82. package/server/services/claude-billing-swap-service.mjs +7 -20
  83. package/server/services/claude-cloud-session-service.mjs +47 -5
  84. package/server/services/remote-relay-dispatcher.mjs +61 -14
  85. package/server/services/session-activity-tracker.mjs +1 -1
  86. package/server/services/session-launch-mutex-service.mjs +23 -0
  87. package/server/services/session-worker-registry-service.mjs +36 -0
  88. package/server/services/session-worker-websocket-service.mjs +40 -16
  89. package/server/services/worker-recycle-service.mjs +772 -0
  90. package/server/services/worker-replacement.mjs +197 -0
  91. package/shared/build-id.mjs +128 -0
  92. package/shared/worker-runtime/heartbeat.mjs +5 -0
  93. package/shared/worker-runtime/simple-steering-state.mjs +38 -0
  94. package/shared/worker-runtime/worker-websocket-link.mjs +23 -0
  95. package/shared/worker-runtime/working-directory.mjs +1 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,218 @@
3
3
  All notable changes to OAR are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow semver.
5
5
 
6
+ ## [0.9.21] — 2026-11-01
7
+
8
+ Highlights: the chat screen is more compact. The mode, model, effort,
9
+ context window and billing of a chat move behind **⚙** in a slim label row
10
+ above the message box, such as `🔒 Opus 5.5 · high · Auto`. The header menu
11
+ is now **︙**, and the header's icon buttons stay there while there is room
12
+ and move into it when there is not. Spacing is about halved across the app,
13
+ with controls and hit areas at 28 px. Back and forward walk through the
14
+ conversations you opened, with `?conv=<id>` in the address bar, and back
15
+ closes what is open on top first. A pinch, Ctrl/⌘+wheel or Text scaling
16
+ zooms the whole app, not only the text. A phone held sideways keeps the
17
+ phone's layout. A session group folds while one of its chats is open, the
18
+ filter row folds or unfolds all groups at once, and a group's name takes
19
+ its sessions' colour. The origin, watched and scheduled pills of a sidebar
20
+ row become one **ⓘ**. After a deploy, the relay replaces each session worker
21
+ still on the old code once its session is idle, so a restart no longer
22
+ leaves old worker code running.
23
+
24
+ ### Added
25
+
26
+ - **Chat settings behind ⚙ in the composer.** A slim label row above the
27
+ message box holds **⚙**, a label that says what the next message runs
28
+ with (model · effort · mode, such as `🔒 Opus 5.5 · high · Auto`, with the
29
+ context window added when it is not the model's default), the billing pill
30
+ of a Claude chat, the file browser, **📎** and, on a desktop, **🙂**. **⚙**,
31
+ or a tap on the label, opens the settings: **Mode**, **Model** with the
32
+ lock note under it, **Effort**, **Context window**, **Billing** and
33
+ **🧠 Context usage**. A tap on one part of the label opens them at that
34
+ field, and the **Subscription** or **API key** pill at **Billing**. On a
35
+ desktop they open as a popover above the row, on a phone as a sheet from
36
+ the bottom; **✕**, Escape, a tap outside or back closes them. An image
37
+ chat shows **Quality** and **Image size** in place of Effort and Context
38
+ window; a Claude Cloud chat has only **Model**. The label reads **Auto**
39
+ for the autopilot mode (the settings keep the full name) and **Plan 🔒**
40
+ on a plan-only session.
41
+ - **Back and forward through conversations.** Each conversation you open is
42
+ one step in the browser's history, with `?conv=<id>` in the address bar,
43
+ so back and forward walk through them, and a reload or a copied address
44
+ opens that conversation. The tab's title is the conversation's title.
45
+ Back first closes what is open on top: the session list on a phone, the
46
+ Inbox or Status view, the composer's settings, the **︙** menu, a row's
47
+ menu, dialogs, the git, tmux and annotate views, the watch and schedule
48
+ panels, the emoji picker and the Change CWD folder list. On a phone, back
49
+ from the first conversation you opened shows the session list, then the
50
+ installed app's *Press back again to close the app* as before.
51
+ - A link to a conversation the relay does not know (deleted, or a wrong
52
+ id) clears the view with a notice and keeps the last conversation; back
53
+ and forward step over it from then on, and nothing opens by itself until
54
+ you pick a conversation. A link to an archived conversation shows its
55
+ title.
56
+ - No address is written before the first conversation shows, so a link
57
+ survives an early tap. A jump from the Inbox to a question replaces the
58
+ history entry instead of adding one.
59
+ - A notification tap still opens `?push_conv=<id>`; the address then
60
+ reads `?conv=<id>`.
61
+ - **Zoom scales the whole app.** A pinch, Ctrl+wheel (⌘+wheel on a Mac) or
62
+ **⚙️ Settings → General → Text scaling** now zooms text, buttons,
63
+ spacing, the sidebar, menus and dialogs alike, so the layout keeps its
64
+ proportions; at 100 % nothing changes. Popovers and sheets are placed
65
+ anew on a change. The new-chat dialog's selects and inputs follow the
66
+ zoom too (they stayed at the browser's fixed 13.33 px).
67
+ - **A phone held sideways keeps the phone's layout.** The header is one
68
+ line. While the keyboard is up, the composer's label row folds into a
69
+ floating **⚙** that opens the settings and keeps the keyboard up, and the
70
+ background-task line into a floating **🤖**; both come back when the
71
+ keyboard goes down. Picking a conversation closes the session list and
72
+ does not bring up the keyboard (a long press does not pick). The **︙**
73
+ menu gains entries for the watch list, the schedules and the origin.
74
+ - **Fold a group with its chat open, and fold all.** A group folds even
75
+ while one of its sessions is open; that session stays under the folded
76
+ head until you open another one, and opening a member of a folded group
77
+ leaves it folded. A drop on that kept row is a drop on the folded head.
78
+ While the list has groups, the filter row has a fold-all button: **➖**
79
+ folds every group, **➕** unfolds them all once all are folded. It counts
80
+ as your own choice for each group and is disabled while the list is
81
+ filtered. The Archived view keeps its folds apart from the live list.
82
+ - **A group's name takes its sessions' colour:** red while one waits for
83
+ you, bright green while one works, dark green while one is idle with its
84
+ session ready, else the plain title colour. Archived heads stay muted.
85
+ - **ⓘ on sidebar rows.** The origin, watched and scheduled pills of a row
86
+ fold into one **ⓘ** in its second line. Its menu lists each with its
87
+ action: *Started by an agent in "…"* (or *on relay "…"*), which opens
88
+ that conversation or the session on the other relay, *Watched by …* with
89
+ the watchers' names, and *Scheduled · next …* (or *N schedules · next
90
+ …*). A member under the head row of the session that started it leaves
91
+ the origin out.
92
+ - **Session workers are recycled after a deploy.** Every worker states the
93
+ build id (version and commit) it started on; a worker on another build than
94
+ the relay, or one too old to say, is stopped and relaunched by the relay
95
+ once it is idle, the way **Change CWD → 🚀 Set new CWD and (re)launch**
96
+ does, so a restart no longer leaves old worker code running. An archived conversation's worker is
97
+ stopped for good. `/api/status` and the About modal show which build each
98
+ worker runs.
99
+ - Idle means no running turn, open question card, compaction, background
100
+ task or message on its way in, and the worker's CLI has ended. A CLI
101
+ ends by itself about 10 minutes after the last delivery or turn, so a
102
+ session is usually replaced some 10 minutes after its last turn. A
103
+ worker too old to say whether its CLI runs is replaced after 10 minutes
104
+ without a delivery or turn end. At most two are replaced at a time, one
105
+ start every 3 seconds, and each session once per relay build.
106
+ - Nothing is lost: the new worker resumes the native session, and a
107
+ message sent meanwhile waits and goes to the new worker. A Cursor
108
+ session is the exception for background shells and tasks, which its SDK
109
+ does not report: only the 10-minute quiet window protects them.
110
+ - **About OAR** shows *Workers: 14, all on this build* or *Workers: 14,
111
+ 3 on an older build*. `/api/status` gives the relay's `buildId` and,
112
+ per worker, `buildId`, `startedAt` and `stale`, and the recycle's
113
+ `staleCount`, replacements and the sessions it waits on with the reason.
114
+ relay_tool's session items carry `worker { kind, buildId, stale }`.
115
+ The relay log has `[worker-recycle]` lines for each replacement and,
116
+ at most once a minute per session, what it waits for.
117
+ - A Claude Cloud worker opens its event stream from the stored cursor at
118
+ start, so turns the cloud session starts by itself are followed after a
119
+ replacement too. New worker route `GET /api/claude-cloud-session`.
120
+ - The troubleshooting guide has a section, *Workers after a deploy*.
121
+
122
+ ### Changed
123
+
124
+ - **The header menu is ︙, and the icon buttons move into it when the
125
+ header is narrow.** **📍** pins and **🙈** hidden messages (while there
126
+ are any), **🔍** search, **🧠** context usage, **📊** Check Usage,
127
+ **☀️/🌙** Day/Night and **⛶** fullscreen stay in the header while there is
128
+ room and move into **︙** under their full names otherwise, the last ones
129
+ first. The menu stays inside the window, is capped in height and scrolls.
130
+ Fullscreen is disabled where the browser has none. Relay refusals, log
131
+ lines and help texts that named the `⋯` menu now name **︙**.
132
+ - **The open-question banner sits between the title and the header
133
+ buttons,** so **︙** always stays at the right edge. Where room is short
134
+ (a phone held sideways, a narrow desktop window) it shrinks to a **❓ N**
135
+ chip; on a wider header it takes at most 40 % of the row and ends in "…".
136
+ A long title ends in "…" too. When even the chip, **☰** and **︙** do not
137
+ fit next to the title, as at 150 % zoom with the sidebar open, the banner
138
+ takes a row of its own; on a phone held upright it always does.
139
+ - **About half the spacing, and 28 px controls.** Paddings, margins and
140
+ gaps in menus, the sidebar, dialogs, Settings, cards, bubbles and panels
141
+ are about halved, and controls that were 30 to 46 px high are 28 px, also
142
+ dialog buttons and tabs. Every hit area is at least 28 px at 100 %: the
143
+ row's **ⓘ**, fold, archive and delete, the live reply's **Stop** and
144
+ **💭 📃 🛠️** follow buttons, the Previews tab's **Open**, **Copy link**
145
+ and **Close**, the phone's header chips and the
146
+ question banner, the sidebar head buttons, Settings fields, selects,
147
+ checkboxes, radios and sliders, the repo browser's toolbar, the group
148
+ picker, the dialogs' **×** and **↻**, the Relays **Add**, the Sharing
149
+ row buttons, **Unsubscribe** and **Cancel**. Small labels and links get
150
+ an invisible 28 px hit area: the repo browser's Focus and crumbs, tree
151
+ rows, Sharing row titles, DeepSeek **Advanced**, the plan-only label, the
152
+ waiting pill, the agent-exchange and activity folds and the header chips.
153
+ - **On a phone the lines under the title are one row** that scrolls
154
+ sideways, with short labels (`↗ title`, `👁 3`, `⏰ 04:15`, the folder's
155
+ last name), and the open-question banner gets a row of its own.
156
+ - **Dialogs on a phone keep their title** with a compact **×**; the header
157
+ buttons no longer stretch across the width. Select Models puts its three
158
+ buttons under the title.
159
+ - **A group's head.** One chevron **›** that turns down while the group is
160
+ open replaces ▾/▸; focus returns to it only after a keyboard toggle, with
161
+ a focus ring in the accent colour. A head line without a head row reads
162
+ "<last active> · N sessions", and its pill shows only what waits, runs or
163
+ failed. A long head drops its message count whole, then its date, instead
164
+ of cutting them. A mouse click on **ⓘ** hands focus back without a ring.
165
+ - **Sidebar rows.** The title and the "1 open" pill share one line and only
166
+ the title is cut short; the title and meta line keep clear of the archive
167
+ and delete buttons. Archive and delete show on keyboard focus too. The
168
+ filter row's archive button reads **🗄️**, and the buttons close up to the
169
+ right when **×** or **➕** is hidden. The sidebar head follows the zoom and
170
+ wraps instead of cutting off **Inbox** or **Install**.
171
+ - **The Claude usage warning above the composer is one line,** such as
172
+ `⚠ Claude 94 % weekly · resets Tue 04:00`; a tap shows the full sentence.
173
+ - **A sub-agent's Stop is red and asks first** (**⏹ Stop agent**).
174
+ - **On a phone a background task's title has its own full-width line.**
175
+ - **Images in messages have no card around them.**
176
+ - **Editing a chat's title on a desktop takes the whole header row** while
177
+ you type; the title lines, the banner and the header buttons come back
178
+ when you are done.
179
+ - **Settings takes the focus when it opens, and Escape closes it.** The
180
+ Settings and Plan usage tab strips fade at an edge while more tabs lie
181
+ beyond it, and keep the selected tab in view. The Features help wraps
182
+ long identifiers.
183
+ - **Pack guard ceilings.** The npm tarball may hold 610 entries and 9.5 MiB
184
+ unpacked (590 and 9 MiB before); 0.9.20 had used 99.3 % of the bytes.
185
+
186
+ ### Fixed
187
+
188
+ - **The archive button on a sidebar row is visible on touch screens.** It
189
+ was invisible there but could still be tapped.
190
+ - **A ︙ entry tapped on a phone no longer closes what it opened.** The tap's
191
+ follow-up click landed on the menu's backdrop or the new dialog and closed
192
+ it again: About, Kill session, Check Usage, Context, Restart relay and
193
+ Search, and the watch and schedule panels; Git changes did not open a
194
+ diff.
195
+ - **Contrast in the light theme.** Primary buttons read at 5.19:1, red
196
+ buttons at 4.67:1, the sidebar's "1 open" pill at 6.47:1, Settings help
197
+ links at 6.85:1, the offline banner at 5.6:1 and the **☰** status tones
198
+ at 4.9:1 or more. A hovered accent button gets a darker fill (6.36:1)
199
+ instead of turning paler.
200
+ - **Status and the Inbox, closed with nothing to go back to,** show their
201
+ empty state instead of a blank pane.
202
+ - **The OpenAI, Grok and Cursor fields in Settings look like the others,**
203
+ and **Use a different token** and **Empty queue** are real buttons.
204
+ - **The ⚙ popover fits its width under zoom,** the full origin title shows,
205
+ and focus is kept when the list or header is drawn anew.
206
+
207
+ ### Upgrade notes
208
+
209
+ - **Every session worker is replaced once after the update.** Workers from
210
+ 0.9.20 state no build id, so 30 seconds after the relay first hears of
211
+ one it counts as on an older build and is replaced once its session is
212
+ idle and its CLI has ended, usually some 10 minutes after its last turn.
213
+ About shows *M on an older build* until then. Nothing needs to be done.
214
+ Each replacement costs one cold start on the session's next message.
215
+ - **No migration and no new dependency.** Going back to 0.9.20 needs no
216
+ database step.
217
+
6
218
  ## [0.9.20] — 2026-10-29
7
219
 
8
220
  Highlights: every plan card gets an "Approve plan" button that approves the
package/README.md CHANGED
@@ -45,29 +45,29 @@ flowchart LR
45
45
  <table>
46
46
  <tr>
47
47
  <td width="42%" rowspan="4" valign="top">
48
- <a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.20"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-mobile.light.png?v=0.9.20"><img src="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.20" alt="OAR on a phone: a finished agent turn in a conversation, with the composer below"></picture></a>
48
+ <a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.21"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-mobile.light.png?v=0.9.21"><img src="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.21" alt="OAR on a phone: a finished agent turn in a conversation, with the composer below"></picture></a>
49
49
  <br><sub>On a phone: the same conversations, installable as an app.</sub>
50
50
  </td>
51
51
  <td width="58%" valign="top">
52
- <a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.20"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-agents.light.png?v=0.9.20"><img src="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.20" alt="Background subagents running in parallel in the task panel, each with live token counts, elapsed time, and its own Stop button"></picture></a>
52
+ <a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.21"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-agents.light.png?v=0.9.21"><img src="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.21" alt="Background subagents running in parallel in the task panel, each with live token counts, elapsed time, and its own Stop button"></picture></a>
53
53
  <br><sub>Agents working in parallel, each with live token counts and its own Stop.</sub>
54
54
  </td>
55
55
  </tr>
56
56
  <tr>
57
57
  <td valign="top">
58
- <a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.20"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-question.light.png?v=0.9.20"><img src="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.20" alt="A question card in the middle of a turn: the agent offers several choices and a free-text reply"></picture></a>
58
+ <a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.21"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-question.light.png?v=0.9.21"><img src="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.21" alt="A question card in the middle of a turn: the agent offers several choices and a free-text reply"></picture></a>
59
59
  <br><sub>When the agent needs a decision, it asks, and waits for your answer.</sub>
60
60
  </td>
61
61
  </tr>
62
62
  <tr>
63
63
  <td valign="top">
64
- <a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.20"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pingcf-thoughts.light.png?v=0.9.20"><img src="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.20" alt="An expanded Thoughts block showing the agent's reasoning above its reply"></picture></a>
64
+ <a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.21"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pingcf-thoughts.light.png?v=0.9.21"><img src="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.21" alt="An expanded Thoughts block showing the agent's reasoning above its reply"></picture></a>
65
65
  <br><sub>Read the reasoning, not just the answer.</sub>
66
66
  </td>
67
67
  </tr>
68
68
  <tr>
69
69
  <td valign="top">
70
- <a href="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.20"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-watching.light.png?v=0.9.20"><img src="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.20" alt="A session watching two other sessions: a finished reply arrives as a compact relay-event row, the watching 2 sessions list is open with Unsubscribe, and a request to also watch a session on a paired relay waits on an Allow / Deny card"></picture></a>
70
+ <a href="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.21"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-watching.light.png?v=0.9.21"><img src="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.21" alt="A session watching two other sessions: a finished reply arrives as a compact relay-event row, the watching 2 sessions list is open with Unsubscribe, and a request to also watch a session on a paired relay waits on an Allow / Deny card"></picture></a>
71
71
  <br><sub>Agents that watch agents: woken by events, approving nothing.</sub>
72
72
  </td>
73
73
  </tr>
@@ -118,11 +118,11 @@ API keys are entered in **⚙️ Settings** and stay on the relay host. The Clau
118
118
  <table>
119
119
  <tr>
120
120
  <td width="62%" valign="top">
121
- <a href="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.20"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-steering.light.png?v=0.9.20"><img src="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.20" alt="A running Claude turn with a message steered into it mid-turn, the reply streaming with its tool calls, and the Steer button in the composer"></picture></a>
121
+ <a href="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.21"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-steering.light.png?v=0.9.21"><img src="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.21" alt="A running Claude turn with a message steered into it mid-turn, the reply streaming with its tool calls, and the Steer button in the composer"></picture></a>
122
122
  <br><sub>Send a message while the agent works, and it steers the running turn.</sub>
123
123
  </td>
124
124
  <td width="38%" valign="top">
125
- <a href="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.20"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/usage-copilot-compact.light.png?v=0.9.20"><img src="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.20" alt="The GitHub Copilot card in Check Usage: premium requests and plan quota with reset dates, and the cost of the last turn"></picture></a>
125
+ <a href="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.21"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/usage-copilot-compact.light.png?v=0.9.21"><img src="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.21" alt="The GitHub Copilot card in Check Usage: premium requests and plan quota with reset dates, and the cost of the last turn"></picture></a>
126
126
  <br><sub>What each plan has left, in one place.</sub>
127
127
  </td>
128
128
  </tr>
@@ -132,14 +132,14 @@ API keys are entered in **⚙️ Settings** and stay on the relay host. The Clau
132
132
  - **Steer or queue**: a message sent during a turn goes into it (Claude, Copilot) or waits behind it ([While a turn runs](docs/web-ui.md#while-a-turn-runs)).
133
133
  - **Answer questions**: the agent asks on a card with one-click choices, checkmarks or free text, and waits for you ([Question cards](docs/web-ui.md#question-cards)).
134
134
  - **Background work**: a task panel lists the commands and sub-agents that outlive a reply, each with its own **Stop** and its full record; a command's console shows its output live, says when it has gone quiet and counts a test run's passes and failures; the panel and its consoles come back after a relay restart, and a queued restart shows what it waits for and asks after a maximum wait ([Background tasks](docs/web-ui.md#background-tasks), [After a relay restart](docs/web-ui.md#after-a-relay-restart)).
135
- - **Choose how the agent works**: `ask`, `plan`, `agent` or `autopilot`, plus model and reasoning effort, per message ([Relay modes](docs/web-ui.md#relay-modes)).
135
+ - **Choose how the agent works**: `ask`, `plan`, `agent` or `autopilot`, plus model and reasoning effort, per message, from the composer's **⚙** ([The composer](docs/web-ui.md#the-composer), [Relay modes](docs/web-ui.md#relay-modes)).
136
136
  - **Files and git**: browse the workspace, preview files, review git changes in a diff viewer, and publish a dev server as a preview link ([Files, git, and previews](docs/web-ui.md#files-git-and-previews), [preview servers](docs/preview-servers.md)).
137
137
  - **Usage and context**: plan limits and credits of every provider, the estimated spend of DeepSeek and Claude API keys, and what fills the model's context window ([Usage and context](docs/web-ui.md#usage-and-context)).
138
138
  - **Your conversations everywhere**: the history lives on the relay, drafts follow you from device to device, and conversations can be searched, archived, grouped and pinned ([The conversation list](docs/web-ui.md#the-conversation-list), [drafts](docs/web-ui.md#drafts-across-devices), [session groups](docs/web-ui.md#session-groups), [pins](docs/web-ui.md#pinned-messages)).
139
139
  - **Share** a conversation as a read-only link, hide single messages and the host's paths from it, and see when it was opened ([Sharing](docs/web-ui.md#sharing)).
140
- - **On your phone**: install OAR as an app and get push notifications ([Notifications and the app](docs/web-ui.md#notifications-and-the-app)).
140
+ - **On your phone**: install OAR as an app and get push notifications; back walks through the conversations you opened, a pinch zooms the whole app, and held sideways it keeps a phone's layout ([Notifications and the app](docs/web-ui.md#notifications-and-the-app), [back and forward](docs/web-ui.md#back-and-forward), [zoom](docs/web-ui.md#zoom), [a phone held sideways](docs/web-ui.md#a-phone-held-sideways)).
141
141
 
142
- The full tour is in [Using the web UI](docs/web-ui.md); every setting and config key is in [Settings and configuration](docs/configuration.md). What changed in each release is in the [changelog](CHANGELOG.md), with [0.9.20](CHANGELOG.md#0920--2026-10-29) as the latest.
142
+ The full tour is in [Using the web UI](docs/web-ui.md); every setting and config key is in [Settings and configuration](docs/configuration.md). What changed in each release is in the [changelog](CHANGELOG.md), with [0.9.21](CHANGELOG.md#0921--2026-11-01) as the latest.
143
143
 
144
144
  ## Remote access
145
145
 
@@ -190,7 +190,7 @@ flowchart LR
190
190
 
191
191
  You stay in charge: per paired relay you decide whether agents may only read, also prompt, or do everything, and in the `ask` and `plan` modes every write asks you first. A subscription only notifies; it approves nothing.
192
192
 
193
- <p><a href="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.20"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-cross-relay.light.png?v=0.9.20"><img src="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.20" alt="A session watching two sessions on a paired relay: a reply arrives as a relay-event row, and after a short outage the events the other relay kept arrive as one replayed row" width="720"></picture></a></p>
193
+ <p><a href="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.21"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-cross-relay.light.png?v=0.9.21"><img src="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.21" alt="A session watching two sessions on a paired relay: a reply arrives as a relay-event row, and after a short outage the events the other relay kept arrive as one replayed row" width="720"></picture></a></p>
194
194
 
195
195
  How pairing, permissions and limits work is in [Agents working together](docs/agents-together.md).
196
196
 
@@ -211,7 +211,7 @@ How keys, cookies, served files, paired relays and Claude Cloud's use of the Cla
211
211
  | A runtime is missing from **New Chat** | Turn it on in **⚙️ Settings → Providers**; all of them except Copilot are off until you do |
212
212
  | A reply says a CLI is missing or signed out | Press the button on the failed reply (**Install Grok CLI**, **Sign in to Grok**, **Claude settings**) |
213
213
  | Banner says *CLI is offline* | Normal on an idle relay: send a message, and the banner clears once the conversation's worker connects |
214
- | A conversation seems wedged | **☠️ Kill session** in the `⋯` menu, then retry or send a new message |
214
+ | A conversation seems wedged | **☠️ Kill session** in the **︙** menu, then retry or send a new message |
215
215
  | `npm install -g @oar-sh/oar` fails with `EACCES` | Do not use `sudo`; run the installer, which installs into your home directory |
216
216
 
217
217
  `oar doctor` prints the version, paths, port and provider CLIs in use. More symptoms are in [Troubleshooting](docs/troubleshooting.md).
@@ -225,7 +225,7 @@ How keys, cookies, served files, paired relays and Claude Cloud's use of the Cla
225
225
  | ----- | ------------- |
226
226
  | [Installing and running OAR](docs/install.md) | Prerequisites, every install method, starting the relay, the `oar` command, files, updating |
227
227
  | [Providers](docs/providers.md) | Each runtime in detail, installing provider CLIs from the relay, models |
228
- | [Using the web UI](docs/web-ui.md) | Conversations, running turns, question cards, background tasks, files, usage, sharing, the menu |
228
+ | [Using the web UI](docs/web-ui.md) | Conversations, the composer, running turns, question cards, background tasks, files, usage, sharing, zoom and navigation, the menu |
229
229
  | [Settings and configuration](docs/configuration.md) | Settings, `config.json`, environment variables, stuck-turn detection |
230
230
  | [Remote access](docs/remote-access.md) | SSH and Cloudflare tunnels, a reverse proxy on another host, the session-worker path guard |
231
231
  | [Agents working together](docs/agents-together.md) | Paired relays, agent sessions, watching sessions |
package/bin/oar.js CHANGED
@@ -1005,7 +1005,7 @@ async function restartRelay(ctx, paths, { old, viaService, answered = true, now
1005
1005
  if (!restartService(ctx)) return { ok: false, reason: 'service' };
1006
1006
  } else if (!(await requestShutdown(ctx, old, true, { immediate: now }))) {
1007
1007
  if (!viaService) {
1008
- logger.error('[oar] The relay did not accept the restart request. Restart it from the web UI (⋯ → Restart web relay).');
1008
+ logger.error('[oar] The relay did not accept the restart request. Restart it from the web UI (︙ → Restart web relay).');
1009
1009
  return { ok: false, reason: 'restart-refused' };
1010
1010
  }
1011
1011
  if (!restartService(ctx)) return { ok: false, reason: 'service' };
@@ -1428,7 +1428,7 @@ function reportPortTaken(ctx, paths, port) {
1428
1428
  // cannot be asked to restart: only its lock tells that it is there.
1429
1429
  const pid = readRelayLock(paths.lockPath)?.pid;
1430
1430
  if (Number.isInteger(pid) && ctx.isProcessAliveImpl(pid)) {
1431
- ctx.logger.error(`[oar] A relay of this install seems to run (process ${pid}). If its token is an earlier one, restart it from its web UI (⋯ → Restart web relay), or end that process and run: oar start`);
1431
+ ctx.logger.error(`[oar] A relay of this install seems to run (process ${pid}). If its token is an earlier one, restart it from its web UI (︙ → Restart web relay), or end that process and run: oar start`);
1432
1432
  }
1433
1433
  return { code: 1, reason: 'port-taken' };
1434
1434
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oar-sh/oar",
3
- "version": "0.9.20",
3
+ "version": "0.9.21",
4
4
  "type": "module",
5
5
  "description": "OAR — Open Agent Relay: drive your local coding agents from any browser",
6
6
  "license": "MIT",