@oar-sh/oar 0.9.9 → 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 +247 -0
- package/README.md +78 -9
- package/bin/bat/oar.bat +46 -46
- package/package.json +2 -2
- package/server/README.md +286 -3
- 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 +36 -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 +61 -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 +516 -6
- package/server/public/app/background-tasks-view.mjs +18 -26
- package/server/public/app/bootstrap.js +186 -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 +361 -85
- package/server/public/app/explorer-edit-state.mjs +68 -0
- package/server/public/app/host-suspend-ui.js +6 -3
- package/server/public/app/journal-view.js +146 -26
- package/server/public/app/no-conversation-state.mjs +58 -0
- package/server/public/app/provider-readiness.mjs +120 -0
- package/server/public/app/relay-board-view.js +3 -0
- 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/settings-modal.js +53 -1
- package/server/public/app/socket-handlers.js +59 -28
- package/server/public/app/store.js +19 -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-history.mjs +5 -3
- package/server/public/index.html +396 -5
- package/server/repositories/message-repository.mjs +29 -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 +515 -50
- package/server/routes/relay-event-routes.mjs +157 -0
- package/server/routes/remote-relay-routes.mjs +15 -0
- package/server/routes/sessions-routes.mjs +72 -0
- package/server/server-runtime.mjs +245 -62
- package/server/services/explorer-entry-name.mjs +26 -0
- package/server/services/explorer-mutations.mjs +122 -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/session-activity-tracker.mjs +487 -0
- package/server/services/session-history-refresh-service.mjs +7 -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,253 @@ 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
|
+
|
|
8
255
|
## [0.9.9] — 2026-10-07
|
|
9
256
|
|
|
10
257
|
Highlights: `oar` opens a menu and gains stop, restart, status, url and
|
package/README.md
CHANGED
|
@@ -35,27 +35,33 @@ OAR is under active development, so expect occasional rough edges and some provi
|
|
|
35
35
|
|
|
36
36
|
<table>
|
|
37
37
|
<tr>
|
|
38
|
-
<td width="42%" rowspan="
|
|
39
|
-
<a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.
|
|
38
|
+
<td width="42%" rowspan="4" valign="top">
|
|
39
|
+
<a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.10"><img src="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.10" alt="OAR on a phone: a finished agent turn in a conversation, with the composer below"></a>
|
|
40
40
|
<br><sub>On a phone: the same conversations, installable as an app.</sub>
|
|
41
41
|
</td>
|
|
42
42
|
<td width="58%" valign="top">
|
|
43
|
-
<a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.
|
|
43
|
+
<a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.10"><img src="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.10" alt="Background subagents running in parallel in the task panel, each with live token counts, elapsed time, and its own Stop button"></a>
|
|
44
44
|
<br><sub>Agents working in parallel, each with live token counts and its own Stop.</sub>
|
|
45
45
|
</td>
|
|
46
46
|
</tr>
|
|
47
47
|
<tr>
|
|
48
48
|
<td valign="top">
|
|
49
|
-
<a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.
|
|
49
|
+
<a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.10"><img src="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.10" alt="A question card in the middle of a turn: the agent offers several choices and a free-text reply"></a>
|
|
50
50
|
<br><sub>When the agent needs a decision, it asks, and waits for your answer.</sub>
|
|
51
51
|
</td>
|
|
52
52
|
</tr>
|
|
53
53
|
<tr>
|
|
54
54
|
<td valign="top">
|
|
55
|
-
<a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.
|
|
55
|
+
<a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.10"><img src="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.10" alt="An expanded Thoughts block showing the agent's reasoning above its reply"></a>
|
|
56
56
|
<br><sub>Read the reasoning, not just the answer.</sub>
|
|
57
57
|
</td>
|
|
58
58
|
</tr>
|
|
59
|
+
<tr>
|
|
60
|
+
<td valign="top">
|
|
61
|
+
<a href="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.10"><img src="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.10" 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"></a>
|
|
62
|
+
<br><sub>Agents that watch agents: woken by events, approving nothing.</sub>
|
|
63
|
+
</td>
|
|
64
|
+
</tr>
|
|
59
65
|
</table>
|
|
60
66
|
|
|
61
67
|
<a id="quick-start"></a>
|
|
@@ -101,7 +107,7 @@ The relay is started as a systemd user service where a systemd user session exis
|
|
|
101
107
|
|
|
102
108
|
```bash
|
|
103
109
|
curl -fsSL oar.sh/install | sh -s -- --yes
|
|
104
|
-
curl -fsSL oar.sh/install | OAR_VERSION=0.9.
|
|
110
|
+
curl -fsSL oar.sh/install | OAR_VERSION=0.9.10 OAR_PORT=3400 sh
|
|
105
111
|
```
|
|
106
112
|
|
|
107
113
|
The installer needs `curl` or `wget`, and `tar` when it fetches Node.js. It has no Node.js build for musl systems (Alpine) or for CPUs other than x64 and arm64; there, install Node.js yourself first. Releases before 0.9.8 are installed the same way, but their `oar setup` cannot start the relay: see [Start the relay](#start-the-relay).
|
|
@@ -114,7 +120,7 @@ Read it before you run it: [oar.sh/install](https://oar.sh/install).
|
|
|
114
120
|
irm oar.sh/install.ps1 | iex
|
|
115
121
|
```
|
|
116
122
|
|
|
117
|
-
The PowerShell installer (PowerShell 5 or newer) expects Node.js 22.13 or newer to be installed: it checks Node.js, runs `npm install -g @oar-sh/oar`, then `oar setup`. It reads the same `OAR_VERSION`, `OAR_CHANNEL`, and `OAR_DRY_RUN` variables, for example `$env:OAR_VERSION = '0.9.
|
|
123
|
+
The PowerShell installer (PowerShell 5 or newer) expects Node.js 22.13 or newer to be installed: it checks Node.js, runs `npm install -g @oar-sh/oar`, then `oar setup`. It reads the same `OAR_VERSION`, `OAR_CHANNEL`, and `OAR_DRY_RUN` variables, for example `$env:OAR_VERSION = '0.9.10'` before the command.
|
|
118
124
|
|
|
119
125
|
### npm
|
|
120
126
|
|
|
@@ -423,6 +429,16 @@ Use **🤗 Select Models** to choose which variants show up in the composer, the
|
|
|
423
429
|
|
|
424
430
|
## Highlights
|
|
425
431
|
|
|
432
|
+
### New in 0.9.10
|
|
433
|
+
|
|
434
|
+
- **Watching sessions**: an agent subscribes to other sessions, on its own relay or on a paired one, and is woken by what happens there: a question waiting for an answer, an error, a reply, a background task that finished or failed, a session gone quiet, nothing running any more. Each wake-up is a short automated message in its conversation, shown as a compact row. Subscriptions only notify; they approve nothing. The first subscribe of a conversation asks you on a card, and the header shows **watching N sessions** with an Unsubscribe per subscription. See [Watching sessions](#watching-sessions).
|
|
435
|
+
- **Notes between agents fold away**: a prompt another agent sent a session and the session's reply show as one row that opens to both.
|
|
436
|
+
- **A sub-agent's whole record**: its title in the task panel, or its row under the finished reply, opens a viewer with its thoughts, every tool call with input and result, and its reply, live while it runs. **Keep sub-agent tool output** under Settings → General decides how long tool inputs and results are kept.
|
|
437
|
+
- **What the plans have left**: `remote_relay`'s `usage` action tells an agent what each provider's plan has left on its own relay or a paired one, before it starts work that costs; `list_sessions` and `read_session` say whether a session is running a turn, only background work, or nothing.
|
|
438
|
+
- **Folders in the file browser**: **📁+ New folder**, and a right-click (a long press on a phone) on a file or folder to rename or delete it.
|
|
439
|
+
- **Back to the latest message**: a ⌄ button above the composer, with a count of the rows that arrived since, jumps back to the end, and the transcript follows new rows again.
|
|
440
|
+
- **Fewer surprises**: archiving a session whose process is still active asks first, Settings opens with no conversation open (so a fresh install can turn on a provider), a queued relay restart no longer waits forever on a busy relay, and waking the host from sleep no longer restarts the running turns.
|
|
441
|
+
|
|
426
442
|
### New in 0.9.7
|
|
427
443
|
|
|
428
444
|
- **Claude Cloud (experimental, off by default)**: a chat that runs Claude Code in a sandbox at Anthropic, on its own clone of a GitHub repository. Pick the repository and branch from a list, follow the turn live, answer its questions, stop it, change the model between turns; the results come back as pushed branches. See [Claude Cloud](#claude-cloud) for what it needs and what it does with the Claude login.
|
|
@@ -506,6 +522,7 @@ Use **🤗 Select Models** to choose which variants show up in the composer, the
|
|
|
506
522
|
- **Steer or queue.** On Claude, and on Copilot's SDK engine, a message you send during a live turn is pushed into that turn; the send button reads **Steer**. A folded message keeps a compact *merged* marker in the transcript, and one the agent answers separately gets its own reply. While the turn waits on a question card or plan approval, or is compacting, the button reads **Queue** and its tooltip says why: the message waits as a pending bubble (with **Cancel**) and steers in once the card is answered or the compaction ends. On the other runtimes, and on Copilot's Extension engine, messages sent during a turn queue behind it, and the button reads **Queue**.
|
|
507
523
|
- **Stop** is on the running reply's bubble, whose header stays pinned at the top while a long reply scrolls; the send button never turns into Stop. Queued messages carry their own **Cancel** until they are picked up. A steered message that a Stop cut off is marked *Stopped with the turn — not answered* and offers **Resend**; the button then reads **Resent** on every device.
|
|
508
524
|
- On Copilot, **Stop** ends only the reply: background agents the turn started keep running under their own **Stop** in the task panel.
|
|
525
|
+
- The transcript follows new rows while you are at its end. Once you scroll about a screen up, a round **⌄** button fades in above the composer, with a badge counting the rows that arrived since; it takes you back to the end and the transcript follows again. From a search result or a pin deep in the history it loads the end first.
|
|
509
526
|
|
|
510
527
|
### Question cards
|
|
511
528
|
|
|
@@ -519,6 +536,7 @@ Use **🤗 Select Models** to choose which variants show up in the composer, the
|
|
|
519
536
|
- The composer's task panel lists background work that outlives the reply that started it: backgrounded commands, subagents, monitors, and workflows on Claude; background agents and detached shells on Copilot's SDK engine. Each row shows its state, the model it runs on, the command it is running right now, its token count, and its own **Stop**. The panel scrolls when it holds more tasks than fit.
|
|
520
537
|
- An **Ultracode** workflow folds out into a tree of its phases and agents (state, model, tokens). When it finishes, the summarizing reply keeps a collapsed *Finished background task* card holding the final tree, which survives reloads.
|
|
521
538
|
- A background task's result arrives as its own *continuation* reply.
|
|
539
|
+
- A sub-agent's title in the panel opens its whole record: its thoughts, every tool call with its full input and its result, its reply and the agents it started itself, in order and live while it runs, with its own **Stop**. The finished reply lists the turn's sub-agents as the same rows (model, type, tokens, time, how they ended), so the record stays one click away; a child agent opens in place and back returns to its parent. Claude records everything; Copilot and Cursor show what they report. A tool result is kept to its first 4 KB and a record to 2000 entries; **Keep sub-agent tool output** in Settings → General decides how long tool inputs and results are kept.
|
|
522
540
|
- **⚙️ Settings → General → Background task timeout** (default 4 h; 0 = no limit, up to 10 h) caps how long background tasks alone may keep a Claude or Copilot session running after its reply. When the limit is reached, the tasks are stopped.
|
|
523
541
|
|
|
524
542
|
### Drafts across devices
|
|
@@ -530,13 +548,14 @@ The composer draft, attachments included, is saved per conversation on the relay
|
|
|
530
548
|
- **Filter conversations…** above the list matches titles, ignoring case. It clears with **×** or Escape, and while it is active it loads older pages, so it searches every conversation, not just the loaded ones.
|
|
531
549
|
- **🔍** in the conversation header searches message text across all conversations.
|
|
532
550
|
- On startup, the relay imports the Copilot sessions stored on the host (through the installed Copilot runtime) into its database, so they appear in the list with their history. If the runtime is unavailable, the import is reported as failed rather than guessed from the filesystem.
|
|
533
|
-
- **Archive** (🗄 on the row, or **Archive conversation** in the `⋯` menu) takes a conversation out of the list without deleting anything: its session worker is stopped, and a Claude Cloud chat's session at Anthropic is archived with it. The 🗄 toggle above the list shows the archived conversations; **Unarchive** (📂) brings one back, cloud session included. An archived chat that is opened cannot be sent to until it is unarchived; a message that reaches it anyway (an agent's through a paired relay, say) unarchives it. A conversation that is still working is not archived — stop it first.
|
|
551
|
+
- **Archive** (🗄 on the row, or **Archive conversation** in the `⋯` menu) takes a conversation out of the list without deleting anything: its session worker is stopped, and a Claude Cloud chat's session at Anthropic is archived with it. The 🗄 toggle above the list shows the archived conversations; **Unarchive** (📂) brings one back, cloud session included. An archived chat that is opened cannot be sent to until it is unarchived; a message that reaches it anyway (an agent's through a paired relay, say) unarchives it. Archiving a conversation whose session process is still active (green in the list, idle or working) asks first, because archiving stops it. A conversation that is still working is not archived — stop it first.
|
|
534
552
|
- A **right-click on a row** (a long press on a phone) opens a menu with Open, Edit title, Stop turn, Kill session, Archive or Unarchive, and Delete, so a session can be stopped or killed without opening it.
|
|
535
553
|
- Deleting a conversation stops its session worker and removes its CLI session too (Copilot through the runtime, Claude by deleting that session's transcript). A Claude Cloud conversation's session at Anthropic is archived, not deleted. An imported Copilot session the relay never ran is only hidden: its CLI session stays, and nothing is stopped for it. A conversation that is still working — a running turn, one waiting on a question card or approval, or live background tasks — is not deleted, and the sidebar says why so you can stop it first.
|
|
536
554
|
|
|
537
555
|
### Files, git, and previews
|
|
538
556
|
|
|
539
557
|
- Use **📁 Browse files** to inspect the workspace (💼), drives (📀), or the session's own folder (🕵️), and to open previews. The **Hidden** and **Heavy** toolbar filters are remembered per browser, and a refresh re-opens the folders you had expanded.
|
|
558
|
+
- **📁+ New folder** in the browser's toolbar opens a name field at the top of the folder pane; Enter creates the folder inside the open one and opens it. It works in the repository, on the drives, in the session folder, and in the folder picker of **New chat** and **Change CWD**. A right-click on a folder in the tree or a file in the folder pane (a long press on a phone) offers **Rename** and **Delete**, and on a folder **New folder here**. Delete says what goes, and a folder with content asks for its name to be typed first. Links are never followed or changed; roots (the repository root, `/`, a drive, the session folder) cannot be renamed or deleted, and the session folder is read-only apart from new folders.
|
|
540
559
|
- Click the file and folder copy controls to insert `@file:...` / `@folder:...` tokens.
|
|
541
560
|
- Workspace browsing follows the selected session's effective working directory. Running sessions keep their learned runtime directory, **🗂️ Change CWD** in the `⋯` menu changes the directory for the next launch, and `cd ...` typed in chat does not retarget the browser. A Claude Cloud chat has no **Change CWD**: it works on a clone at Anthropic.
|
|
542
561
|
- Use **🌿 Git changes** in the conversation `⋯` menu to review the workspace repository: the header shows the branch with ahead/behind counts and a **⬇ Pull** button, and the list shows every staged, unstaged, and untracked file (deleted files struck through). Clicking a file opens a diff viewer with **Changes** and **Full file** modes; closing it returns to the still-open list.
|
|
@@ -580,7 +599,7 @@ Besides the entries above, the `⋯` menu holds **✍️ Edit conversation title
|
|
|
580
599
|
|
|
581
600
|
### Settings (⚙️ in the web UI)
|
|
582
601
|
|
|
583
|
-
The modal is organised into six tabs — **General**, **Providers** (with a **Copilot**, **OpenAI**, **Claude**, **Claude Cloud**, **Grok**, and **Cursor** sub-tab), **Relays**, **Previews**, **Notifications**, and **Features** — and reopens on the tab you used last. Unless noted as per browser, these settings live in the relay database rather than the config file, and apply to every browser that connects:
|
|
602
|
+
The modal is organised into six tabs — **General**, **Providers** (with a **Copilot**, **OpenAI**, **Claude**, **Claude Cloud**, **Grok**, and **Cursor** sub-tab), **Relays**, **Previews**, **Notifications**, and **Features** — and reopens on the tab you used last. The `⋯` menu opens it with no conversation open too, so a fresh install can turn on a provider before the first chat; until a provider is ready, the empty screen and **New Chat** point to Settings → Providers. Unless noted as per browser, these settings live in the relay database rather than the config file, and apply to every browser that connects:
|
|
584
603
|
|
|
585
604
|
| Tab | Setting | Default | What it does |
|
|
586
605
|
| --- | ------- | ------- | ------------ |
|
|
@@ -588,6 +607,7 @@ The modal is organised into six tabs — **General**, **Providers** (with a **Co
|
|
|
588
607
|
| General | Show Suspend host action | on | Per browser: shows **💤 Suspend host** in the conversation menu |
|
|
589
608
|
| General | Max turn duration | `60 min` | Hard cap on how long one turn may run before the relay requeues it (0 = no limit, up to 10 h; see below) |
|
|
590
609
|
| General | Background task timeout | `4 h` | How long background tasks alone may keep a Claude or Copilot session running after its reply; then they are stopped (0 = no limit, up to 10 h) |
|
|
610
|
+
| General | Keep sub-agent tool output | `90 days` | 7, 14, 30 or 90 days, or always: once a finished sub-agent's record is older, its tool inputs and results are removed (once a day); its thoughts, reply and the one-line summary of each tool call stay |
|
|
591
611
|
| General | Autostart (Windows) | Off | *At sign-in* (a visible terminal after you log on) or *At system startup* (headless, no logon needed, one admin confirmation) |
|
|
592
612
|
| General | Relay name | `OAR` (the hostname for paired relays) | Label of the installed PWA on every device, and this relay's name for [paired relays](#remote-relays) |
|
|
593
613
|
| General | Check for updates automatically | off | Opt-in: the relay asks oar.sh for the latest version about twice a day. **Check for updates** works regardless; `OAR_NO_UPDATE_CHECK=1` blocks both |
|
|
@@ -839,6 +859,12 @@ is started with `provider: "claude-cloud"`, a `repo` (a GitHub URL or `owner/rep
|
|
|
839
859
|
the default model of the other relay's Claude Cloud tab. Claude Cloud must be switched on there.
|
|
840
860
|
A cloud session that exists can be read, prompted, waited for and stopped like any other.
|
|
841
861
|
|
|
862
|
+
Two read actions help an agent plan: `usage` says what each provider's plan has left on a relay
|
|
863
|
+
(Cursor's spend against its limit, Claude's windows and whether a usage limit has paused turns,
|
|
864
|
+
Copilot and Grok where they are tracked; never account names, addresses or tokens), and
|
|
865
|
+
`list_sessions` / `read_session` give each session's `state` (`running_turn`, `background_only` or
|
|
866
|
+
`idle`) with its background agents and shell tasks.
|
|
867
|
+
|
|
842
868
|
A `send`, `create_session` or `wait` call waits for the reply for as long as the agent asks, up to
|
|
843
869
|
**Longest wait per tool call** of the relay the agent runs on (10 minutes unless you changed it,
|
|
844
870
|
see [Agent sessions](#agent-sessions)). When the time is up the agent gets what there is so far
|
|
@@ -907,6 +933,49 @@ to this relay's agents when they wait for a session on a paired relay, so the sl
|
|
|
907
933
|
whether or not the switch above it is on. A turn that waits is still subject to **Max turn duration**
|
|
908
934
|
under **General** (60 minutes by default), which does not know about the wait.
|
|
909
935
|
|
|
936
|
+
### Watching sessions
|
|
937
|
+
|
|
938
|
+
An agent can watch other sessions instead of polling them: with `remote_relay`'s `subscribe` it
|
|
939
|
+
asks to be woken when something happens there. It works for every provider, on this relay (with
|
|
940
|
+
[agent sessions](#agent-sessions) switched on) and on a paired one.
|
|
941
|
+
|
|
942
|
+
**What wakes it.** A question waiting for an answer (with its options), an error (a failed turn,
|
|
943
|
+
the usage limit, a worker that died), a reply (with a preview), a background agent or shell task
|
|
944
|
+
that finished or failed, a session that went quiet while working (15 minutes by default), and the
|
|
945
|
+
moment nothing runs any more in the watched sessions. A subscription names sessions, or `all`
|
|
946
|
+
for every session, later ones included; the watcher's own conversation is never watched. It can
|
|
947
|
+
filter replies and task results by a regular expression, end after it fired once, and add a
|
|
948
|
+
heartbeat while work runs.
|
|
949
|
+
|
|
950
|
+
**What it looks like.** OAR writes a short message into the watcher's conversation: a new turn
|
|
951
|
+
when the watcher is idle, a steer into the running turn on Claude and Copilot's SDK engine, the
|
|
952
|
+
next turn otherwise. The message says it is automated, not from you, and that it approves
|
|
953
|
+
nothing; it shows as a compact row, never as one of your messages. Events within 30 seconds come
|
|
954
|
+
as one message; past 20 wake-ups an hour the rest comes as a digest every ten minutes, while
|
|
955
|
+
questions and errors still come at once.
|
|
956
|
+
|
|
957
|
+
**What it may do.** A subscription only notifies. It approves nothing and answers
|
|
958
|
+
nothing; what an event asks for is decided as before, by you or by the agent within what
|
|
959
|
+
it may do anyway. The first subscribe of a conversation, and any to `all`, asks you on an approval
|
|
960
|
+
card naming the sessions (and the relay); `transfer_subscriptions`, which hands every subscription
|
|
961
|
+
of an old watcher to a new one, always asks.
|
|
962
|
+
|
|
963
|
+
**Where you see it.** The watcher's header shows **watching N sessions**; the chip opens the list
|
|
964
|
+
of its subscriptions with an **Unsubscribe** button each. A watched session shows a **watched**
|
|
965
|
+
badge.
|
|
966
|
+
|
|
967
|
+
**Lifetime.** Subscriptions survive a restart and end on unsubscribe, when the watcher or the
|
|
968
|
+
watched session is archived, after firing once (`once`), or, for a paired relay, when the pairing
|
|
969
|
+
is removed.
|
|
970
|
+
|
|
971
|
+
**On a paired relay** the relay must be unlocked in the conversation and allow at least reading,
|
|
972
|
+
and the other relay must accept other relays' agents; nothing is asked there. This relay fetches
|
|
973
|
+
the events every few seconds over the pairing (a pairing in one direction is enough), and their
|
|
974
|
+
lines start with the other relay's name. If the link drops, the other relay keeps what happens
|
|
975
|
+
(up to 500 events per subscription, for 7 days) and delivers it as one row tagged **replayed** once
|
|
976
|
+
it answers again, questions and errors first, between "link lost" and "link restored". A relay
|
|
977
|
+
that runs an older OAR answers that it needs an update.
|
|
978
|
+
|
|
910
979
|
## Security notes
|
|
911
980
|
|
|
912
981
|
- The auth token guards the API and the Socket.IO channel. A browser signs in once, with the token or with a URL carrying `?token=` (such as the one `oar setup` and `oar url` print), and then holds an HttpOnly cookie for 30 days, marked `Secure` when the relay is reached over HTTPS. Treat the token, and that URL, like a password.
|
package/bin/bat/oar.bat
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
setlocal EnableExtensions
|
|
3
|
-
|
|
4
|
-
set "SCRIPT_DIR=%~dp0"
|
|
5
|
-
if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
|
|
6
|
-
|
|
7
|
-
for %%I in ("%SCRIPT_DIR%\..\..") do set "REPO_ROOT=%%~fI"
|
|
8
|
-
|
|
9
|
-
if not exist "%REPO_ROOT%\package.json" (
|
|
10
|
-
echo [oar] Unable to locate the repository root from "%SCRIPT_DIR%".
|
|
11
|
-
exit /b 1
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
echo(;%PATH%; | findstr /I /C:";%SCRIPT_DIR%;" >nul
|
|
15
|
-
if errorlevel 1 (
|
|
16
|
-
call :AddToUserPath "%SCRIPT_DIR%"
|
|
17
|
-
if errorlevel 1 (
|
|
18
|
-
echo [oar] Warning: could not update your user PATH.
|
|
19
|
-
) else (
|
|
20
|
-
echo [oar] Ensured "%SCRIPT_DIR%" is in your user PATH.
|
|
21
|
-
echo [oar] Open a new cmd.exe window to use oar by name.
|
|
22
|
-
echo.
|
|
23
|
-
)
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
set "COPILOT_WORKSPACE_ROOT=%REPO_ROOT%"
|
|
27
|
-
set "COPILOT_WEB_RELAY_ROOT=%REPO_ROOT%"
|
|
28
|
-
set "COPILOT_WEB_RELAY_SERVER_DIR=%REPO_ROOT%\server"
|
|
29
|
-
|
|
30
|
-
pushd "%REPO_ROOT%" >nul
|
|
31
|
-
if errorlevel 1 (
|
|
32
|
-
echo [oar] Failed to change to repo root: "%REPO_ROOT%".
|
|
33
|
-
exit /b 1
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
echo [oar] Starting Copilot from "%REPO_ROOT%"...
|
|
37
|
-
gh copilot -- --allow-all
|
|
38
|
-
set "EXIT_CODE=%errorlevel%"
|
|
39
|
-
popd >nul
|
|
40
|
-
exit /b %EXIT_CODE%
|
|
41
|
-
|
|
42
|
-
:AddToUserPath
|
|
43
|
-
set "TARGET_DIR=%~1"
|
|
44
|
-
powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "$target = [System.IO.Path]::GetFullPath($env:TARGET_DIR.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar)); $current = [Environment]::GetEnvironmentVariable('Path', 'User'); $parts = @(); if ($current) { $parts = $current -split ';' | ForEach-Object { $_.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) } | Where-Object { $_ } }; if ($parts -contains $target) { exit 0 }; $next = ($parts + $target) -join ';'; [Environment]::SetEnvironmentVariable('Path', $next, 'User')" >nul
|
|
45
|
-
if errorlevel 1 exit /b 1
|
|
46
|
-
exit /b 0
|
|
1
|
+
@echo off
|
|
2
|
+
setlocal EnableExtensions
|
|
3
|
+
|
|
4
|
+
set "SCRIPT_DIR=%~dp0"
|
|
5
|
+
if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
|
|
6
|
+
|
|
7
|
+
for %%I in ("%SCRIPT_DIR%\..\..") do set "REPO_ROOT=%%~fI"
|
|
8
|
+
|
|
9
|
+
if not exist "%REPO_ROOT%\package.json" (
|
|
10
|
+
echo [oar] Unable to locate the repository root from "%SCRIPT_DIR%".
|
|
11
|
+
exit /b 1
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
echo(;%PATH%; | findstr /I /C:";%SCRIPT_DIR%;" >nul
|
|
15
|
+
if errorlevel 1 (
|
|
16
|
+
call :AddToUserPath "%SCRIPT_DIR%"
|
|
17
|
+
if errorlevel 1 (
|
|
18
|
+
echo [oar] Warning: could not update your user PATH.
|
|
19
|
+
) else (
|
|
20
|
+
echo [oar] Ensured "%SCRIPT_DIR%" is in your user PATH.
|
|
21
|
+
echo [oar] Open a new cmd.exe window to use oar by name.
|
|
22
|
+
echo.
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
set "COPILOT_WORKSPACE_ROOT=%REPO_ROOT%"
|
|
27
|
+
set "COPILOT_WEB_RELAY_ROOT=%REPO_ROOT%"
|
|
28
|
+
set "COPILOT_WEB_RELAY_SERVER_DIR=%REPO_ROOT%\server"
|
|
29
|
+
|
|
30
|
+
pushd "%REPO_ROOT%" >nul
|
|
31
|
+
if errorlevel 1 (
|
|
32
|
+
echo [oar] Failed to change to repo root: "%REPO_ROOT%".
|
|
33
|
+
exit /b 1
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
echo [oar] Starting Copilot from "%REPO_ROOT%"...
|
|
37
|
+
gh copilot -- --allow-all
|
|
38
|
+
set "EXIT_CODE=%errorlevel%"
|
|
39
|
+
popd >nul
|
|
40
|
+
exit /b %EXIT_CODE%
|
|
41
|
+
|
|
42
|
+
:AddToUserPath
|
|
43
|
+
set "TARGET_DIR=%~1"
|
|
44
|
+
powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "$target = [System.IO.Path]::GetFullPath($env:TARGET_DIR.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar)); $current = [Environment]::GetEnvironmentVariable('Path', 'User'); $parts = @(); if ($current) { $parts = $current -split ';' | ForEach-Object { $_.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) } | Where-Object { $_ } }; if ($parts -contains $target) { exit 0 }; $next = ($parts + $target) -join ';'; [Environment]::SetEnvironmentVariable('Path', $next, 'User')" >nul
|
|
45
|
+
if errorlevel 1 exit /b 1
|
|
46
|
+
exit /b 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oar-sh/oar",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OAR — Open Agent Relay: drive your local coding agents from any browser",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"scripts": {
|
|
42
42
|
"start": "node server/server.js",
|
|
43
43
|
"copilot:relay": "gh copilot -- --allow-all",
|
|
44
|
-
"test": "node --test \"server/**/*.test.mjs\" \"shared/**/*.test.mjs\" \".github/extensions/web-relay/**/*.test.mjs\" \"scripts/**/*.test.mjs\"",
|
|
44
|
+
"test": "node --import ./scripts/node-test-keep-alive.mjs --test \"server/**/*.test.mjs\" \"shared/**/*.test.mjs\" \".github/extensions/web-relay/**/*.test.mjs\" \"scripts/**/*.test.mjs\"",
|
|
45
45
|
"test:agents:smoke": "node --test \"tests/agents/**/*.test.mjs\"",
|
|
46
46
|
"test:e2e": "node tests/run-e2e.mjs",
|
|
47
47
|
"setup:git": "node scripts/setup-git.mjs",
|