@oar-sh/oar 0.9.2 → 0.9.4
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/.github/extensions/web-relay/extension.mjs +6 -1
- package/.github/extensions/web-relay/skills/tool-activity.mjs +7 -0
- package/CHANGELOG.md +336 -0
- package/README.md +455 -423
- package/bin/oar.js +25 -10
- package/package.json +11 -5
- package/server/README.md +44 -4
- package/server/claude-worker/claude-remote-relay-tool.mjs +99 -0
- package/server/claude-worker/claude-sdk-adapter.mjs +26 -4
- package/server/claude-worker/claude-session-process.mjs +1073 -69
- package/server/claude-worker/claude-session-worker.mjs +45 -4
- package/server/claude-worker/claude-turn-publisher.mjs +81 -20
- package/server/claude-worker/claude-worker-link-wiring.mjs +9 -0
- package/server/claude-worker/sdk-message-normalizer.mjs +7 -0
- package/server/copilot-worker/copilot-ask-user-tool.mjs +89 -0
- package/server/copilot-worker/copilot-continuation-signals.mjs +55 -0
- package/server/copilot-worker/copilot-question-bridge.mjs +34 -12
- package/server/copilot-worker/copilot-remote-relay-tool.mjs +59 -0
- package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +113 -14
- package/server/copilot-worker/copilot-sdk-session-process.mjs +2349 -498
- package/server/copilot-worker/copilot-sdk-session-worker.mjs +82 -20
- package/server/cursor-worker/cursor-remote-relay-tool.mjs +65 -0
- package/server/cursor-worker/cursor-turn-runner.mjs +45 -2
- package/server/cursor-worker/sdk-message-normalizer.mjs +25 -0
- package/server/db-schema.mjs +28 -0
- package/server/grok-worker/acp-client.mjs +92 -6
- package/server/grok-worker/grok-turn-runner.mjs +59 -8
- package/server/grok-worker/sdk-message-normalizer.mjs +13 -0
- package/server/mcp/oar-mcp-server.mjs +473 -0
- package/server/migrations/0004-steer-settle-markers.mjs +119 -0
- package/server/migrations/0005-remote-relays.mjs +90 -0
- package/server/public/app/action-confirmations.js +141 -13
- package/server/public/app/api-client.js +125 -2
- package/server/public/app/ask-user-view.js +102 -13
- package/server/public/app/attachments-view.js +37 -2
- package/server/public/app/background-task-timeout-options.mjs +9 -0
- package/server/public/app/background-tasks-view.mjs +33 -18
- package/server/public/app/bootstrap.js +55 -14
- package/server/public/app/composer-control-state.mjs +52 -34
- package/server/public/app/conversation-deep-link.mjs +42 -0
- package/server/public/app/conversation-draft-sync.mjs +187 -0
- package/server/public/app/conversation-list-filter.mjs +86 -0
- package/server/public/app/conversation-provider-indicator.mjs +8 -0
- package/server/public/app/conversation-view.js +1016 -200
- package/server/public/app/emoji-view.js +2 -0
- package/server/public/app/font-scaling.js +4 -37
- package/server/public/app/host-suspend-ui.js +272 -0
- package/server/public/app/journal-view.js +149 -6
- package/server/public/app/mention-autocomplete.mjs +298 -0
- package/server/public/app/model-selector-options.mjs +1 -1
- package/server/public/app/push-settings.js +1 -0
- package/server/public/app/pwa-install.js +8 -3
- package/server/public/app/question-multi-select.mjs +52 -0
- package/server/public/app/relay-prompt-sanitizer.mjs +61 -3
- package/server/public/app/remote-relay-origin-view.mjs +76 -0
- package/server/public/app/remote-relay-shared.mjs +85 -0
- package/server/public/app/remote-relays-settings.js +618 -0
- package/server/public/app/remote-relays-store.mjs +126 -0
- package/server/public/app/settings-modal.js +12 -8
- package/server/public/app/settings-tabs.js +1 -1
- package/server/public/app/socket-handlers.js +37 -1
- package/server/public/app/steered-turn-merge.mjs +100 -0
- package/server/public/app/store.js +126 -15
- package/server/public/app/sync-outbox.mjs +16 -8
- package/server/public/app/thinking-follow.js +260 -0
- package/server/public/app/tool-activity-emoji.mjs +40 -0
- package/server/public/app/transcript-viewport-anchor.mjs +210 -0
- package/server/public/index.html +463 -17
- package/server/public/sw.js +7 -0
- package/server/relay-tools.md +7 -1
- package/server/repositories/message-repository.mjs +68 -18
- package/server/repositories/question-repository.mjs +18 -0
- package/server/repositories/remote-relay-repository.mjs +75 -0
- package/server/repositories/session-repository.mjs +13 -32
- package/server/routes/ask-user-routes.mjs +42 -2
- package/server/routes/messages-routes.mjs +572 -56
- package/server/routes/remote-relay-routes.mjs +133 -0
- package/server/routes/sessions-routes.mjs +378 -84
- package/server/server-runtime.mjs +303 -25
- package/server/services/github-ci-activity-service.mjs +199 -0
- package/server/services/host-suspend-command.mjs +38 -0
- package/server/services/host-suspend-service.mjs +254 -0
- package/server/services/live-turn-picker.mjs +47 -0
- package/server/services/push-dispatch-service.mjs +37 -4
- package/server/services/relay-prompt-sanitizer.mjs +17 -2
- package/server/services/relay-question-context.mjs +46 -0
- package/server/services/remote-relay-caller-context.mjs +341 -0
- package/server/services/remote-relay-client.mjs +332 -0
- package/server/services/remote-relay-dispatcher.mjs +1500 -0
- package/server/services/remote-relay-inbound.mjs +187 -0
- package/server/services/remote-relay-pairing.mjs +189 -0
- package/server/services/remote-relay-registry-service.mjs +581 -0
- package/server/services/sdk-session-import-service.mjs +49 -7
- package/server/services/session-worker-launch-service.mjs +131 -12
- package/server/services/session-worker-registry-service.mjs +75 -0
- package/server/services/session-worker-supervisor-service.mjs +7 -1
- package/server/services/session-worker-websocket-service.mjs +23 -1
- package/server/services/steer-resend.mjs +45 -0
- package/shared/background-task-timeout.mjs +12 -4
- package/shared/control-poller.mjs +4 -1
- package/shared/question-multi-select.mjs +16 -0
- package/shared/relay-stop-failure.mjs +15 -0
- package/shared/remote-relay-contract.mjs +539 -0
- package/shared/remote-relay-mentions.mjs +129 -0
- package/shared/remote-relay-tool-core.mjs +314 -0
- package/shared/slash-command-guard.mjs +76 -0
- package/shared/steer-note.mjs +66 -0
- package/shared/steer-settle-failure.mjs +89 -0
- package/shared/steer-settle-markers.mjs +18 -0
- package/shared/worker-crash-guard.mjs +40 -25
- package/shared/worker-runtime/api-client.mjs +112 -22
- package/shared/worker-runtime/heartbeat.mjs +55 -1
- package/shared/worker-runtime/runner-link-wiring.mjs +44 -0
- package/shared/worker-runtime/worker-websocket-link.mjs +119 -8
- package/server/claude-worker/claude-session-test-harness.mjs +0 -250
- package/server/claude-worker/fixtures/workflow-run-record.json +0 -1
- package/server/copilot-worker/copilot-sdk-test-harness.mjs +0 -382
- package/server/copilot-worker/fixtures/abort-turn.json +0 -588
- package/server/copilot-worker/fixtures/ask-user-turn.json +0 -1154
- package/server/copilot-worker/fixtures/background-timer-continuation.json +0 -445
- package/server/copilot-worker/fixtures/background-timer-turn.json +0 -497
- package/server/copilot-worker/fixtures/happy-turn.json +0 -566
- package/server/copilot-worker/fixtures/quota-turn.json +0 -647
- package/server/copilot-worker/fixtures/reasoning-turn.json +0 -625
- package/server/copilot-worker/fixtures/resume-turn.json +0 -700
- package/server/copilot-worker/fixtures/subagent-turn.json +0 -1378
- package/server/copilot-worker/fixtures/tool-permission-turn.json +0 -1293
- package/server/routes/messages-routes-test-harness.mjs +0 -111
- package/server/services/fixtures/claude-auth-stub.cmd +0 -1
- package/server/services/fixtures/claude-auth-stub.mjs +0 -76
- package/server/services/fixtures/claude-auth-stub.sh +0 -4
- package/server/services/fixtures/cli-install-stub.cmd +0 -1
- package/server/services/fixtures/cli-install-stub.mjs +0 -98
- package/server/services/fixtures/cli-install-stub.sh +0 -4
- package/server/services/fixtures/grok-stub.cmd +0 -1
- package/server/services/fixtures/grok-stub.mjs +0 -89
- package/server/services/fixtures/grok-stub.sh +0 -4
- package/shared/fixtures/copilot-catalog-raw-2026-09-07.json +0 -2363
package/README.md
CHANGED
|
@@ -1,287 +1,290 @@
|
|
|
1
1
|
# OAR — Open Agent Relay
|
|
2
2
|
|
|
3
|
-
Drive
|
|
3
|
+
Drive the coding agents on your machine from any browser (phone, tablet, or a second computer) through a self-hosted web relay.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
curl -fsSL oar.sh/install | sh
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
Landing page
|
|
9
|
+
On Windows, run `irm oar.sh/install.ps1 | iex` in PowerShell. Landing page: [oar.sh](https://oar.sh). npm and git-checkout installs are covered under [Install](#install).
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
OAR relays six runtimes: **GitHub Copilot**, **OpenAI (BYOK)**, **OpenAI Image (BYOK)**, **Claude (Agent SDK)**, **Cursor (Agent SDK)**, and **Grok (CLI ACP)**. You pick one per conversation, and all of them share the same chat UI, queue, history, file browser, and question cards.
|
|
12
12
|
|
|
13
13
|
```text
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
┌── Copilot SDK worker, or Copilot CLI + extension
|
|
15
|
+
├── OpenAI Copilot runtime, your API key
|
|
16
|
+
[Browser] <--WebSocket--> [OAR relay :3333] ┼── OpenAI Image Images API, called by the relay
|
|
17
|
+
├── Claude Agent SDK worker, host's Claude login
|
|
18
|
+
├── Cursor Agent SDK worker, your Cursor API key
|
|
19
|
+
└── Grok Grok CLI over ACP, host's Grok login
|
|
17
20
|
```
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
The relay runs on your hardware, listens on localhost only until you change that, and collects no telemetry (see [Security notes](#security-notes)).
|
|
23
|
+
|
|
24
|
+
OAR is under active development, so expect occasional rough edges and some provider SDK features that are missing or incomplete.
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
## In action
|
|
22
27
|
|
|
23
|
-
<
|
|
24
|
-
<table width="100%" cellspacing="0" cellpadding="0">
|
|
28
|
+
<table>
|
|
25
29
|
<tr>
|
|
26
|
-
<td
|
|
27
|
-
<a href="
|
|
28
|
-
<
|
|
29
|
-
</a>
|
|
30
|
-
<div align="center"><small>Mobile session view with the composer and a PWA-style fullscreen layout.</small></div>
|
|
30
|
+
<td width="42%" rowspan="3" valign="top">
|
|
31
|
+
<a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.3"><img src="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.3" alt="OAR on a phone: a finished agent turn in a conversation, with the composer below"></a>
|
|
32
|
+
<br><sub>On a phone: the same conversations, installable as an app.</sub>
|
|
31
33
|
</td>
|
|
32
|
-
<td width="
|
|
33
|
-
<a href="
|
|
34
|
-
<
|
|
35
|
-
</a>
|
|
36
|
-
<div align="center"><small>Desktop PWA App (Chrome), chat view with background agents running in parallel.</small></div>
|
|
34
|
+
<td width="58%" valign="top">
|
|
35
|
+
<a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.3"><img src="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.3" alt="Background subagents running in parallel in the task panel, each with live token counts, elapsed time, and its own Stop button"></a>
|
|
36
|
+
<br><sub>Agents working in parallel, each with live token counts and its own Stop.</sub>
|
|
37
37
|
</td>
|
|
38
38
|
</tr>
|
|
39
39
|
<tr>
|
|
40
|
-
<td
|
|
41
|
-
<a href="
|
|
42
|
-
<
|
|
43
|
-
</
|
|
44
|
-
|
|
40
|
+
<td valign="top">
|
|
41
|
+
<a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.3"><img src="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.3" alt="A question card in the middle of a turn: the agent offers several choices and a free-text reply"></a>
|
|
42
|
+
<br><sub>When the agent needs a decision, it asks, and waits for your answer.</sub>
|
|
43
|
+
</td>
|
|
44
|
+
</tr>
|
|
45
|
+
<tr>
|
|
46
|
+
<td valign="top">
|
|
47
|
+
<a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.3"><img src="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.3" alt="An expanded Thoughts block showing the agent's reasoning above its reply"></a>
|
|
48
|
+
<br><sub>Read the reasoning, not just the answer.</sub>
|
|
45
49
|
</td>
|
|
46
50
|
</tr>
|
|
47
51
|
</table>
|
|
48
|
-
</div>
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
<a id="quick-start"></a>
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
## Install
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
2. **Copilot CLI extension** (`.github/extensions/web-relay/`): polls the relay, executes turns, streams activity, bridges `ask_user` questions into web question cards.
|
|
56
|
-
3. **Claude worker** (`server/claude-worker/`): a per-conversation Node process that runs turns through the Claude Agent SDK and speaks the same relay contracts as the Copilot workers.
|
|
57
|
+
### Prerequisites
|
|
57
58
|
|
|
59
|
+
You need Node.js plus whatever the runtimes you actually use need. Nothing else is required.
|
|
58
60
|
|
|
61
|
+
| Requirement | Needed for | Notes |
|
|
62
|
+
| ----------- | ---------- | ----- |
|
|
63
|
+
| Node.js 22.13 or newer | always | Both installers check this. Running the development test suite needs Node 24 (see [DEVELOPING.md](DEVELOPING.md#node-version)) |
|
|
64
|
+
| GitHub Copilot CLI (`copilot`), signed in | Copilot and OpenAI (BYOK) chats | Install it with `npm install -g @github/copilot`, then run `copilot` once on the relay host and sign in. The relay looks for the CLI's runtime when it starts, so restart the relay after installing or upgrading the CLI. Needs a GitHub Copilot plan that includes Copilot CLI |
|
|
65
|
+
| GitHub CLI (`gh`), signed in | the `oar` launcher command; Extension-engine sessions on Windows; the Copilot card in **Check Usage** | `gh copilot` is built into current GitHub CLI releases, so there is no extension to install. The usage card also accepts a token in `GH_TOKEN` or `GITHUB_TOKEN` |
|
|
66
|
+
| OpenAI API key | OpenAI and OpenAI Image chats | Entered in **⚙️ Settings**, stored in the relay database |
|
|
67
|
+
| A Claude login on the relay host | Claude chats | Log in from **⚙️ Settings → Providers → Claude → Relogin** (the panel can install the Claude Code CLI first), or run `claude` once on the host. The relay stores no Claude key |
|
|
68
|
+
| Cursor API key | Cursor chats | Entered in **⚙️ Settings**, stored in the relay database |
|
|
69
|
+
| Grok CLI, signed in | Grok chats | Install it and sign in from **⚙️ Settings → Providers → Grok**, or set `XAI_API_KEY` in the relay's environment |
|
|
70
|
+
| tmux (optional) | Linux and macOS | Session workers run in detached tmux sessions you can watch from the browser (**🖥️ Inspect tmux console**). Without tmux they run as plain background processes |
|
|
59
71
|
|
|
60
|
-
|
|
72
|
+
### macOS and Linux
|
|
61
73
|
|
|
62
|
-
|
|
74
|
+
```bash
|
|
75
|
+
curl -fsSL oar.sh/install | sh
|
|
76
|
+
```
|
|
63
77
|
|
|
78
|
+
The script checks for Node.js 22.13 or newer, runs `npm install -g @oar-sh/oar`, and then hands off to `oar setup` (with `--defaults` when there is no terminal to ask questions in). It refuses to run as root, because OAR installs per user. `OAR_VERSION=x.y.z` pins a version, `OAR_CHANNEL=beta` follows the beta channel, and `OAR_DRY_RUN=1` only prints what it would do:
|
|
64
79
|
|
|
65
|
-
|
|
80
|
+
```bash
|
|
81
|
+
curl -fsSL oar.sh/install | OAR_VERSION=0.9.4 sh
|
|
82
|
+
```
|
|
66
83
|
|
|
67
|
-
|
|
68
|
-
- Per-message **mode** picker: `plan`, `ask`, `agent`, `autopilot`
|
|
69
|
-
- Per-message **model** and **reasoning effort** pickers (live model discovery + fallback catalog)
|
|
70
|
-
- Streaming tool/activity updates *and* live assistant reply text while a turn runs
|
|
71
|
-
- Nested **subagent bubbles**: each subagent gets its own live bubble with its own thoughts, activity, and streamed text, kept as collapsible sections after the turn finishes
|
|
72
|
-
- **Background task panel** with live per-task state, model, and token use; Claude workflow tasks fold out into a progress tree of phases and agents, and leave a *Finished background task* card in the transcript when they complete
|
|
73
|
-
- Mathematical and scientific notation rendering for TeX/LaTeX equations and chemical formulas
|
|
74
|
-
- Web question cards for `ask_user` clarification flows (single-field text and multi-field structured forms)
|
|
75
|
-
- Structured answer support: multi-field elicitation with JSON schema validation and UI-rendered forms
|
|
76
|
-
- **Context usage** modal with a per-category token breakdown of the model's context window, plus a per-conversation **auto-compact window** slider for Claude sessions
|
|
77
|
-
- **Transcript breaks**: day separators, a marker where a Claude session auto-compacted its context, and matching dots beside the scrollbar
|
|
78
|
-
- **Plan usage** modal with subscription credits, rate-limit windows and reset countdowns for Copilot, Claude, Cursor, and Grok
|
|
79
|
-
- **Image conversations** (OpenAI BYOK): generate images in chat and iterate on a generated image with *Edit this image*
|
|
80
|
-
- **Share** a conversation by link, with per-message *Hide from shares* control
|
|
81
|
-
- Conversation history stored in local SQLite
|
|
82
|
-
- Conversation delete requests are relayed to Copilot CLI SDK `deleteSession()` so web deletes can remove resumable CLI sessions
|
|
83
|
-
- Conversation **compact** workflow (`/compact`) to continue with summary carry-over
|
|
84
|
-
- Workspace + drives browser with file preview, raw file access, and sticky hidden/heavy filters
|
|
85
|
-
- **Git changes** modal: branch + ahead/behind info, pull, and a per-file diff viewer with *Changes only* / *Full file* modes
|
|
86
|
-
- `@file:` and `@folder:` reference tokens with copy-to-clipboard helpers
|
|
87
|
-
- Uploads and image attachment relay support
|
|
88
|
-
- Optional SSH reverse tunnel support for internet access
|
|
89
|
-
- PWA install support with installed-app fullscreen preference and browser-mode fallbacks
|
|
84
|
+
Read it before you run it: [oar.sh/install](https://oar.sh/install).
|
|
90
85
|
|
|
91
|
-
|
|
86
|
+
### Windows
|
|
92
87
|
|
|
93
|
-
|
|
88
|
+
```powershell
|
|
89
|
+
irm oar.sh/install.ps1 | iex
|
|
90
|
+
```
|
|
94
91
|
|
|
92
|
+
The PowerShell installer (PowerShell 5 or newer) does the same: 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.4'` before the command.
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
| ---------------------------- | ----------------------------- | ----------------------------------------- |
|
|
98
|
-
| Node.js 24+ | always | Runs the relay server. Verified on 24; the unit suite requires 24+ (20 and 22 fail — see DEVELOPING.md) |
|
|
99
|
-
| GitHub CLI (`gh`) | Copilot provider | Must be available in PATH |
|
|
100
|
-
| GitHub Copilot CLI extension | Copilot provider | `gh extension install github/gh-copilot` |
|
|
101
|
-
| Copilot subscription | Copilot provider | Individual, Business, or Enterprise |
|
|
102
|
-
| OpenAI API key | OpenAI + OpenAI Image chats | Entered in **⚙️ Settings**, stored locally |
|
|
103
|
-
| Claude Code CLI, logged in | Claude chats | Log in from **⚙️ Settings → Providers → Claude → Relogin**, or run `claude` once on the relay host; no API key is stored by the relay |
|
|
94
|
+
### npm
|
|
104
95
|
|
|
96
|
+
```bash
|
|
97
|
+
npm install -g @oar-sh/oar
|
|
98
|
+
oar setup
|
|
99
|
+
```
|
|
105
100
|
|
|
106
|
-
|
|
101
|
+
### From a git checkout
|
|
107
102
|
|
|
108
103
|
```bash
|
|
109
104
|
git clone https://github.com/oar-sh/oar
|
|
110
105
|
cd oar
|
|
111
106
|
npm install
|
|
107
|
+
node bin/oar.js setup # writes server/config.json with a fresh auth token
|
|
108
|
+
npm start # the same as: node server/server.js
|
|
112
109
|
```
|
|
113
110
|
|
|
114
|
-
|
|
111
|
+
A checkout keeps all of its state inside the repository (`server/config.json`, `server/data/`, `server/uploads/`, `server/logs/`) and never updates itself. `node server/server.js --port <port> --token <token>` overrides the config for one run without saving it.
|
|
112
|
+
|
|
113
|
+
You can also write `server/config.json` by hand; every key you leave out takes its default (see the [configuration reference](#configuration-reference-serverconfigjson)):
|
|
115
114
|
|
|
116
115
|
```json
|
|
117
116
|
{
|
|
118
|
-
"authToken": "change-me",
|
|
117
|
+
"authToken": "change-me-to-a-long-random-secret",
|
|
119
118
|
"port": 3333,
|
|
120
|
-
"localhostOnly": true
|
|
121
|
-
"pollIntervalMs": 3000,
|
|
122
|
-
"processingTimeoutMs": 600000,
|
|
123
|
-
"conversationSessionMode": "isolated"
|
|
119
|
+
"localhostOnly": true
|
|
124
120
|
}
|
|
125
121
|
```
|
|
126
122
|
|
|
127
|
-
|
|
123
|
+
Development workflows, tests, and relay internals are in [DEVELOPING.md](DEVELOPING.md).
|
|
128
124
|
|
|
129
|
-
|
|
130
|
-
npm run copilot:relay
|
|
131
|
-
```
|
|
125
|
+
### Start the relay
|
|
132
126
|
|
|
133
|
-
|
|
127
|
+
`oar setup` creates the config and prints the relay URL with a QR code, but it does not start the relay. Pick one:
|
|
128
|
+
|
|
129
|
+
- **Linux (global install):** accept the systemd user service that `oar setup` offers, then run `systemctl --user enable --now oar`. The relay then starts when you log in; run `loginctl enable-linger "$USER"` once if it should keep running while you are logged out.
|
|
130
|
+
- **Windows:** start the relay once with `oar`, then choose **⚙️ Settings → General → Autostart (Windows)**: *At sign-in* opens a visible terminal after you log on, and *At system startup* runs it headless before anyone signs in, after one admin confirmation on the PC itself.
|
|
131
|
+
- **Any platform:** `oar` starts the relay in the background if it is not already running, then opens the Copilot CLI (`gh copilot`) in the same shell. A relay that `oar` started stops again when that Copilot session ends, and `oar` needs the GitHub CLI.
|
|
132
|
+
|
|
133
|
+
To run only the relay from a global install, without a Copilot session, start the server the way the systemd unit does:
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
|
-
|
|
136
|
+
COPILOT_WEB_RELAY_CONFIG="$HOME/.oar/config.json" \
|
|
137
|
+
COPILOT_WEB_RELAY_DATA_DIR="$HOME/.oar/data" \
|
|
138
|
+
COPILOT_WEB_RELAY_LOG_DIR="$HOME/.oar/logs" \
|
|
139
|
+
node "$(npm root -g)/@oar-sh/oar/server/server.js"
|
|
137
140
|
```
|
|
138
141
|
|
|
139
|
-
|
|
142
|
+
On Windows, point the same three variables at `%APPDATA%\oar\config.json`, `%APPDATA%\oar\data`, and `%APPDATA%\oar\logs`. The directory you start it from becomes the default working directory for new sessions.
|
|
140
143
|
|
|
141
|
-
|
|
144
|
+
Then open the URL that `oar setup` printed, or scan its QR code. With `localhostOnly` on (the default) that is `http://localhost:3333/` on the relay host; reach it from other devices through a [tunnel](#remote-access), or set `localhostOnly` to `false` in the config file and restart the relay for LAN access. The URL carries your token once (`?token=…`); after that, the relay keeps the browser signed in with an HttpOnly cookie.
|
|
142
145
|
|
|
143
|
-
|
|
146
|
+
### The `oar` command
|
|
144
147
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
| Command | What it does |
|
|
149
|
+
| ------- | ------------ |
|
|
150
|
+
| `oar` | Starts the relay in the background unless one is already running, then runs `gh copilot` in the same shell. The relay's output goes to its log directory, not your terminal |
|
|
151
|
+
| `oar -- <args>` | The same, passing `<args>` through to the Copilot CLI (`gh copilot -- <args>`) |
|
|
152
|
+
| `oar --port <port>` | Finds or starts the relay on `<port>` for this run, overriding the config's `port` without changing the file; a config this command creates saves it. Without it, `oar` uses the config's `port` (default `3333`) |
|
|
153
|
+
| `oar --migrate-from <dir>` | Global installs: copies the relay state of a pre-rename git checkout at `<dir>` into the OAR state root, once. The source is never moved or changed, and a relay still running from it blocks the copy. `oar setup` accepts the same option |
|
|
154
|
+
| `oar --install-extension` | Writes or refreshes the user-global Copilot CLI extension wrapper in `~/.copilot/extensions/web-relay/`, then exits. Plain `oar` does this on every run; `--no-install-extension` skips it |
|
|
155
|
+
| `oar setup [--defaults]` | Creates or updates the config: generates an auth token (with a config already present it offers a new one, which signs every device out), asks whether a new config should allow LAN access and whether to enable the managed Cloudflare tunnel, offers the systemd user service on Linux global installs, then prints the relay URL and a QR code. An existing config keeps its `localhostOnly` value. `--defaults` accepts every default without asking |
|
|
156
|
+
| `oar doctor` | Prints the version, Node.js, install mode, state root, config path, port, whether an auth token is set, the tunnel mode, the database path and size, and which provider CLIs (`gh`, `claude`, `grok`) answer. It changes nothing |
|
|
157
|
+
| `oar update [--beta] [--to X.Y.Z]` | Global installs: looks up the newest release of your channel in `https://oar.sh/latest.json` (`--beta` for the beta channel), installs it with `npm install -g`, and asks a running relay to restart once no turn is running. `--to X.Y.Z` installs that version straight from npm, which works even when `OAR_NO_UPDATE_CHECK=1` blocks the lookup |
|
|
158
|
+
| `oar --version`, `oar --help` | Prints the version, or the usage |
|
|
148
159
|
|
|
149
|
-
|
|
160
|
+
### Where OAR keeps its files
|
|
150
161
|
|
|
151
|
-
|
|
162
|
+
A global install keeps its state in `~/.oar` (`%APPDATA%\oar` on Windows; `OAR_STATE_ROOT` moves it): `config.json`, `data/` (the database and uploads), and `logs/` (`server.log` when `oar` started the relay, and a `worker-<session>.log` per Node session worker). `npm install -g` updates never touch it. A git checkout uses `server/config.json`, `server/data/`, `server/uploads/`, and `server/logs/` instead.
|
|
152
163
|
|
|
153
|
-
|
|
164
|
+
`COPILOT_WEB_RELAY_CONFIG`, `COPILOT_WEB_RELAY_DATA_DIR`, and `COPILOT_WEB_RELAY_LOG_DIR` override the config file, the data directory, and the log directory. `oar doctor` prints the paths in use.
|
|
154
165
|
|
|
155
|
-
|
|
166
|
+
### Updating
|
|
156
167
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
| `oar` | Global npm command (`npm i -g @oar-sh/oar`, or `npm link` from a checkout) that starts the web relay if needed, then runs `gh copilot` in the same shell |
|
|
160
|
-
| `oar --install-extension` | Installs/updates a user-global `web-relay` wrapper entrypoint and exits |
|
|
161
|
-
| `npm run copilot:relay` | Starts Copilot CLI with an initial prompt so the extension loads and the relay worker link comes online |
|
|
162
|
-
| `node server/server.js` | The one way to start the relay server. `npm start` is an alias for exactly this command |
|
|
168
|
+
- **Global install:** **⚙️ Settings → General → Check for updates** shows a newer release with an **Update** button, which installs it with npm and restarts the relay once no turn is running. `oar update` does the same from a terminal. Automatic checks are opt-in: **Check for updates automatically** asks oar.sh about twice a day. `OAR_NO_UPDATE_CHECK=1` in the relay's environment blocks every check, manual ones included.
|
|
169
|
+
- **Git checkout:** `git pull`, `npm install`, then restart the relay (**🌄 Restart web relay** in the conversation's `⋯` menu).
|
|
163
170
|
|
|
164
|
-
|
|
171
|
+
<a id="providers"></a>
|
|
165
172
|
|
|
166
|
-
|
|
167
|
-
extension, `oar`, Windows autostart, and the e2e runner all start it.
|
|
168
|
-
Its role is chosen by argv, never by the environment:
|
|
173
|
+
## Runtimes
|
|
169
174
|
|
|
170
|
-
|
|
171
|
-
supervisor and runs the real server (`server-runtime.mjs`) in a worker child it
|
|
172
|
-
marks with `--relay-runtime`. Exit code 75 relaunches the worker; any other
|
|
173
|
-
non-zero exit is retried up to 3 times before the supervisor gives up.
|
|
174
|
-
- `node server/server.js --supervised` — runs the server in this process and
|
|
175
|
-
exits 75 on restart, leaving restarts to whoever spawned it. The CLI extension
|
|
176
|
-
passes this so its own bounded-backoff supervision is the only one in play.
|
|
175
|
+
The runtime (called the *provider* in the UI) is chosen in **New Chat** and then fixed for that conversation: once a conversation has sent its first message it keeps that provider, and its composer is locked to that provider's models. The composer states this above the input — `🔒 Session locked to GitHub Copilot / OpenAI / OpenAI Image / Claude SDK / Cursor SDK / Grok models.`, colour-coded per provider. OpenAI sessions also pin one exact model, which the note names in parentheses and the model dropdown shows as a disabled `🔒` entry.
|
|
177
176
|
|
|
178
|
-
|
|
179
|
-
worker sessions and by the Copilot CLI it launches — an env-based flag would be
|
|
180
|
-
read by unrelated servers started further down that tree.
|
|
177
|
+
Turning a provider **off** (or removing the OpenAI key) rebinds conversations that have not sent a message yet back to Copilot, so you are never left with a conversation pointing at a runtime that can no longer start. Conversations already in flight, and conversations belonging to a different provider, are left alone.
|
|
181
178
|
|
|
182
|
-
|
|
179
|
+
| Runtime | Enable via | Auth | Notes |
|
|
180
|
+
| ------- | ---------- | ---- | ----- |
|
|
181
|
+
| **Copilot** (default) | always available | the relay host's Copilot CLI login | Two engines: the headless **SDK** worker (default) or the Copilot CLI with OAR's **Extension**. The only runtime that reports Copilot usage |
|
|
182
|
+
| **OpenAI (BYOK)** | ⚙️ Settings → Providers → OpenAI | your API key, stored in the relay database | Runs the Copilot runtime, on either engine, against an OpenAI-compatible endpoint |
|
|
183
|
+
| **OpenAI Image (BYOK)** | ⚙️ Settings → Providers → OpenAI | the same key | The relay calls the OpenAI Images API itself; a chat whose replies are images |
|
|
184
|
+
| **Claude (Agent SDK)** | ⚙️ Settings → Providers → Claude | the relay host's Claude login, **switchable from the panel** | A dedicated Node worker per conversation. The Claude Code CLI can be installed and updated from the panel |
|
|
185
|
+
| **Cursor (Agent SDK)** | ⚙️ Settings → Providers → Cursor | your Cursor API key, stored in the relay database | A dedicated Node worker per conversation through the Cursor Agent SDK |
|
|
186
|
+
| **Grok (CLI ACP)** | ⚙️ Settings → Providers → Grok | the relay host's Grok CLI login, **sign in and out from the panel** | Drives the Grok CLI's `grok agent stdio` over ACP. The CLI can be installed and updated from the panel |
|
|
183
187
|
|
|
184
|
-
|
|
188
|
+
### GitHub Copilot — engine choice
|
|
185
189
|
|
|
186
|
-
|
|
187
|
-
2. **Standalone mode** (manual escape hatch): start the server, then run `node server/relay.mjs` by hand. It spawns its own Copilot CLI process and polls `GET /api/pending` over HTTP. Use it only when the extension transport is unavailable — there is no npm script for it.
|
|
190
|
+
Copilot conversations, and OpenAI (BYOK) ones, run on one of two engines, chosen in **⚙️ Settings → Providers → Copilot → Copilot engine**. The setting is per relay and is read whenever a conversation's worker starts; sessions already running keep their engine until their worker restarts.
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
Unit tests (`npm test`) and e2e tests (`npm run test:e2e`) are isolated from a live relay and may run beside it; see `DEVELOPING.md` → Tests.
|
|
192
|
+
| Engine | What runs | Notes |
|
|
193
|
+
| ------ | --------- | ----- |
|
|
194
|
+
| **SDK** (default) | A headless Node worker per conversation, driving the Copilot CLI's bundled SDK runtime | Needs only a Copilot CLI that is installed and signed in; there is no extension to install or keep in sync. This engine has [mid-turn steering](#while-a-turn-runs), the [background task panel](#background-tasks) with a working **Stop**, **Cancel** on a steered message the runtime has not picked up yet, and multi-select question cards, for every Copilot model, hosted or BYOK. There is no TUI: the tmux console shows the worker's log |
|
|
195
|
+
| **Extension** | The Copilot CLI in a terminal session with OAR's web-relay extension loaded | The engine OAR started with. **🖥️ Inspect tmux console** shows the live Copilot TUI (Linux and macOS, with tmux). Messages sent during a turn queue behind it |
|
|
194
196
|
|
|
195
|
-
|
|
196
|
-
The extension now supervises managed `server.js` restarts (bounded backoff) while the CLI session is alive, and stops restart attempts on session shutdown.
|
|
197
|
-
When the CLI extension connects, it also prints the relay info window (local/network/remote/auth URLs) directly in the Copilot CLI client.
|
|
197
|
+
The SDK engine has been the default since 0.9.2. A relay where nobody has saved a choice uses it whenever it can and falls back to **Extension** when it cannot; a saved choice always wins. Saving **SDK** is refused, with the reason shown in place of the engine description, when:
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
- *"The Copilot SDK was not found when the relay started (COPILOT_SDK_PATH did not resolve)…"*: the relay found no Copilot CLI runtime at startup. It looks in the Copilot CLI's package cache, newest version first (`~/.cache/copilot/pkg` on Linux, `~/Library/Application Support/copilot/pkg` on macOS, `%LOCALAPPDATA%\copilot\pkg` on Windows; `COPILOT_PKG_DIR` adds a location), unless `COPILOT_SDK_PATH` names the runtime's `copilot-sdk` directory directly. Install or upgrade the Copilot CLI, run it once, **and restart the relay**: the launch environment is snapshotted at startup, so a CLI installed since then is not visible yet.
|
|
200
|
+
- *"The SDK engine requires session worker routing, which is disabled on this relay (SESSION_WORKER_ROUTING_ENABLED)…"*: with routing off no SDK worker is ever spawned. Turn **Session worker routing** back on in **⚙️ Settings → Features** (it is on by default) and restart the relay.
|
|
200
201
|
|
|
201
|
-
|
|
202
|
+
A refusal never changes the stored engine; the select snaps back to the engine the relay actually runs.
|
|
202
203
|
|
|
203
|
-
|
|
204
|
+
SDK-engine turns report their own per-turn billing to the relay, which appears as the **Last SDK worker turn** section on the Copilot card in **Check Usage**. The card's meters come from the account-level quota API and are correct for both engines.
|
|
204
205
|
|
|
205
|
-
|
|
206
|
-
- `POST /api/relay/shutdown` with `restart: true` queues an intentional self-restart.
|
|
207
|
-
- Requests are localhost-only and still require relay auth.
|
|
208
|
-
- The relay waits until the queue is idle before acting; this endpoint does not interrupt an in-flight turn.
|
|
209
|
-
- `/api/status` exposes the queued relay exit state as `relayShutdown` so the UI/logs can distinguish idle, queued, and shutting-down restart/shutdown flows.
|
|
210
|
-
- Restart ownership follows the argv role, so exactly one supervisor acts on exit code 75:
|
|
211
|
-
- `node server/server.js` — the attached supervisor respawns its worker child in the same terminal session
|
|
212
|
-
- `node server/server.js --supervised` — the server exits 75 and the CLI extension relaunches it (bounded backoff, and it stops trying once the CLI session shuts down)
|
|
206
|
+
### OpenAI (BYOK) and OpenAI Image
|
|
213
207
|
|
|
214
|
-
|
|
208
|
+
Save your key in **⚙️ Settings → Providers → OpenAI**, together with the **OpenAI model ID** (default `gpt-4o`) and an optional **Base URL** (default `https://api.openai.com/v1`). **Enable OpenAI API key for New Chat model selection** is on once a key is saved, and saving discovers the endpoint's models from `/v1/models`.
|
|
215
209
|
|
|
216
|
-
|
|
210
|
+
- **OpenAI (BYOK)** conversations run the Copilot runtime against that endpoint with your key, on whichever Copilot engine is selected.
|
|
211
|
+
- **OpenAI Image (BYOK)** conversations are chats whose replies are images. **New Chat** asks for **Quality** and **Size** instead of reasoning effort, and **Edit this image** under a generated image iterates on it.
|
|
217
212
|
|
|
218
|
-
|
|
219
|
-
npm link
|
|
220
|
-
# or
|
|
221
|
-
npm install -g .
|
|
222
|
-
```
|
|
213
|
+
### Claude (Agent SDK)
|
|
223
214
|
|
|
224
|
-
|
|
215
|
+
Turn on **⚙️ Settings → Providers → Claude → Enable Claude for New Chat model selection**. The relay authenticates through the Claude credentials on the host machine (`~/.claude`), so there is no key to enter.
|
|
225
216
|
|
|
226
|
-
|
|
217
|
+
The same panel manages the account itself. The row at the top names the signed-in account and plan, and:
|
|
227
218
|
|
|
228
|
-
|
|
219
|
+
- **Relogin** runs the Claude CLI's login on the relay host and brings the flow to the browser: the authorize link appears inline with a **Copy link** button — open it on any device, authorize on claude.ai, then paste the returned code back into the field. The relay holds no Claude secret; the CLI rewrites the host credentials, and a fresh model discovery runs straight after, so switching accounts needs no relay restart. The flow is pushed over the socket, so you can start it on one device and finish it on another, and closing the modal does not lose it.
|
|
220
|
+
- **Logout** asks for confirmation first and tells you how many Claude workers are running. Running Claude sessions keep the previous account's token until their worker exits; new sessions use the new account.
|
|
229
221
|
|
|
230
|
-
|
|
222
|
+
If Claude has never been logged in on the host, running `claude` there once works too.
|
|
231
223
|
|
|
232
|
-
|
|
224
|
+
Enabling Claude also runs model discovery against the Agent SDK and adds the discovered `claude-*` model IDs to the pickers (default model `claude-sonnet-5`). Use **🤗 Select Models → Claude SDK** to choose which of them appear in the composer; the configured default model always stays enabled. Discovery runs the Claude Code build bundled with the Agent SDK, so `claude update` alone never surfaces a new Claude model: that arrives with an OAR update.
|
|
233
225
|
|
|
234
|
-
|
|
235
|
-
2. **Option 3**: support `oar -- [gh copilot args]` pass-through.
|
|
236
|
-
3. **Session resume**: add `--session-id=<...>` handoff once the session orchestration contract is defined.
|
|
226
|
+
What Claude conversations support:
|
|
237
227
|
|
|
238
|
-
|
|
228
|
+
- Per-message model and reasoning effort (`none`, `low`, `medium`, `high`, `xhigh`, `max`), changeable between turns, plus **Ultracode** on models that support `xhigh` — `xhigh` effort *and* multi-agent workflow orchestration, at a matching jump in token use
|
|
229
|
+
- All four relay modes — `plan` maps to the SDK's plan permission mode and produces a **Plan ready** board, `ask` and `autopilot` adjust the system prompt
|
|
230
|
+
- Image and file attachments (images up to 5 MB are inlined; larger files are passed as paths for Claude to read)
|
|
231
|
+
- Question cards (including multi-select), thinking/thought streams, live reply streaming, and nested subagent bubbles
|
|
232
|
+
- **Mid-turn steering** with **Steer** / **Queue**, **Stop** on the reply bubble, and **Resend** for steered messages a Stop cut off (see [While a turn runs](#while-a-turn-runs))
|
|
233
|
+
- Background tasks that outlive the reply that started them, in the composer's task panel, including **Ultracode** workflow trees (see [Background tasks](#background-tasks))
|
|
234
|
+
- Session continuity across worker restarts — the native Agent SDK session id is stored and resumed
|
|
235
|
+
- Real context-window metrics, reported after each turn, and a per-conversation auto-compact window (see [Usage and context](#usage-and-context))
|
|
239
236
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
- Start a chat with **New Chat**, which asks for the **working directory**, **provider**, **model**, and **reasoning effort** (or **quality** and **size**, for image chats) before the conversation exists. The working directory picker lists the known CWDs (current session, relay workspace, browser folder, recent roots) plus a **Custom path…** entry, defaults to the last directory you picked, and the chosen directory is applied before the session worker first launches. With Copilot as the only provider the provider row is hidden.
|
|
243
|
-
- Choose **mode** and **model** per message in the composer.
|
|
244
|
-
- Use **Compact** to branch to a fresh conversation seeded with summary context.
|
|
245
|
-
- Type **`/preview`** to publish a local dev server or directory on the public preview host without involving the agent: `/preview 5173 [label]`, `/preview ./dist [label]`, `/preview list`, `/preview close`. Agents can do the same via the `preview` tool (Claude/Cursor) or the documented API (see `docs/preview-servers.md`).
|
|
246
|
-
- Use **Browse files** to inspect workspace/drives and open previews. The **Hidden** and **Heavy** toolbar filters are remembered per browser, and a refresh re-opens the folders you had expanded.
|
|
247
|
-
- Click file/folder copy controls to insert `@file:...` / `@folder:...` tokens.
|
|
248
|
-
- 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 only** and **Full file** modes; closing it returns to the still-open list.
|
|
249
|
-
- Answer clarification prompts in relay question cards (from `ask_user`, or Claude's `AskUserQuestion`).
|
|
250
|
-
- Watch the reply arrive: assistant text streams into the pending bubble as it is generated, and any subagent the turn spawns gets its own nested bubble with its own thoughts, activity, and text.
|
|
251
|
-
- Use **Check Usage** (`📊`) in the conversation menu for plan usage across every configured provider: remaining credits, rate-limit windows, reset countdowns, and collapsible cost/token detail. Each provider gets its own tab, opening on the conversation's own provider, and the card names the signed-in account (email and plan) beneath its title. Sources differ per provider:
|
|
252
|
-
- **Copilot** — live quota (AI credits or premium requests, chat, plan), plus per-model/product billed cost when your GitHub token can read personal billing. Conversations running on the experimental SDK engine add a **Last SDK worker turn** section (AI credits actually spent, tokens, model calls, overage) with the model and how long ago it was captured; it is one turn's numbers rather than a running total, and it stops being shown once it is more than seven days old.
|
|
253
|
-
- **Claude** — subscription limit windows (5-hour, weekly, per-model), extra-usage credits, session cost, and local usage attribution. Read from the live session at the end of a turn; the relay never starts a hidden turn to refresh it, so the newest reading is from your last Claude turn.
|
|
254
|
-
- **Cursor** — spend from the Cursor SDK measured against the monthly allowances you enter in Settings, split into the Cursor Models and Other Models pools. Cursor exposes no account API for included pools, so these figures are estimates and the Spending dashboard remains authoritative.
|
|
255
|
-
- **Grok** — per-turn tokens and estimated cost from the agent prompt result (no live plan-quota API over ACP). Optional monthly USD allowance in Settings for an estimated remaining meter; card is hidden when Grok is disabled. Billing: [console.x.ai](https://console.x.ai).
|
|
256
|
-
- Per-reply usage lines are recorded only for Copilot turns — OpenAI, Claude, Cursor, and Grok turns do not consume Copilot premium requests, and no usage line is attached to them.
|
|
257
|
-
- Use the **Context** button for a per-category breakdown of the conversation's context window: a usage bar, a token/percentage table, and free space. Claude sessions report exact SDK categories; Copilot sessions show the coarser system/tools + messages + buffer split, labelled as a lower-bound estimate when the runtime no longer emits full buckets.
|
|
258
|
-
- Claude conversations additionally get an **auto-compact window** slider in that modal. Claude Code compacts a session once it approaches a model-tuned window (around 967k tokens on Opus 5), which is why long conversations rarely compact at all; setting a smaller window makes it happen sooner and keeps turns cheaper. *Auto* hands the choice back to the CLI. The smallest window is 100k, because the CLI silently ignores anything below that and falls back to its own default. The line beneath the slider reports the window actually in force and where it came from — your setting, the model default, or the `CLAUDE_CODE_AUTO_COMPACT_WINDOW` environment override — and fills in once the conversation's first turn completes. The change reaches a running session on its next message.
|
|
259
|
-
- The transcript marks day boundaries, and marks the point where a Claude session compacted its context with the tokens before and after. Both appear as dots beside the scrollbar for the messages currently loaded.
|
|
260
|
-
- Use **Share** in the conversation menu to publish a read-only link. Hover any message and choose **Hide from shares** to keep it out of the shared view without deleting it; hidden messages stay fully visible to you and are marked as hidden.
|
|
261
|
-
- External links in chat open in a new tab with `noopener`/`noreferrer`; workspace file mentions stay in the in-app preview.
|
|
262
|
-
- Workspace browsing follows the selected CLI session's effective CWD. Running sessions keep their learned runtime CWD, menu changes update the next-launch CWD, and chat `cd ...` commands do not retarget the browser.
|
|
237
|
+
Differences from Copilot conversations:
|
|
263
238
|
|
|
264
|
-
|
|
239
|
+
- Cancelling one individual subagent works only for backgrounded ones; a subagent running inside the current turn can be stopped only by stopping the whole turn
|
|
240
|
+
- Claude turns are not included in the Copilot usage line, and no usage line is attached to their replies (Claude's own plan limits appear in **Check Usage**)
|
|
241
|
+
- The browsable **Session** root points at the Agent SDK's project directory rather than a Copilot session-state folder
|
|
265
242
|
|
|
266
|
-
|
|
243
|
+
### Cursor (Agent SDK)
|
|
267
244
|
|
|
268
|
-
|
|
245
|
+
Turn on **⚙️ Settings → Providers → Cursor**, paste your Cursor API key, and enable it for New Chat model selection. Saving the key runs model discovery and also discovers each model's supported reasoning-effort tiers; use **🤗 Select Models → Cursor SDK** to choose which models appear in the composer (the configured default model, `composer-2.5` unless you change it, always stays enabled).
|
|
269
246
|
|
|
247
|
+
What Cursor conversations support:
|
|
270
248
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
249
|
+
- Per-message model and reasoning effort — effort tiers come from per-model discovery, and `none` means the model's default behavior
|
|
250
|
+
- All four relay modes — `plan` uses the SDK's native plan mode and produces a **Plan ready** board; `ask` and `autopilot` ride as instructions on the message text, injected only when the mode changes
|
|
251
|
+
- Live reply streaming, question cards, and **Stop** to abort the running turn
|
|
252
|
+
- The browsable **Session** root points at the worker's per-session agent store, created on the session's first turn
|
|
253
|
+
- Expired cached agent handles are recreated and retried automatically once — a second auth failure means the API key itself is invalid
|
|
254
|
+
|
|
255
|
+
Like Claude, Cursor turns are not included in the Copilot usage line and no usage line is attached to their replies. Cursor spend is tracked separately in **Check Usage**; set your monthly pool allowances and billing reset day under **⚙️ Settings → Providers → Cursor → Cursor monthly plan allowance**. The optional **Dashboard session token** in the same panel unlocks live *Included in plan* bars; it is detected automatically from the host's Cursor IDE login when there is one, and headless hosts can set `CURSOR_SESSION_TOKEN` instead.
|
|
256
|
+
|
|
257
|
+
### Grok (CLI ACP)
|
|
258
|
+
|
|
259
|
+
Turn on **⚙️ Settings → Providers → Grok**. There is no key to enter: the relay drives the Grok CLI on the host and uses whatever account that CLI is signed in to (or `XAI_API_KEY` in the host environment).
|
|
260
|
+
|
|
261
|
+
The same panel manages both the CLI and the account:
|
|
262
|
+
|
|
263
|
+
- The **Grok CLI** row installs or updates the CLI itself — see
|
|
264
|
+
[Installing a provider CLI from the relay](#installing-a-provider-cli-from-the-relay).
|
|
265
|
+
- **Sign in** starts the CLI's device-code login on the relay host and brings it to the browser: the
|
|
266
|
+
x.ai authorization link appears inline with a **Copy link** button, and the `XXXX-XXXX` code is
|
|
267
|
+
shown next to it so you can check it against what the browser displays. Open the link on any
|
|
268
|
+
device, confirm, and the panel flips to signed-in **by itself** — nothing is pasted back through
|
|
269
|
+
the relay, because the CLI polls x.ai and finishes on its own. Model discovery re-runs straight
|
|
270
|
+
after, so switching accounts needs no relay restart. The flow is pushed over the socket: start it
|
|
271
|
+
on one device, finish it on another, and closing the modal does not lose it.
|
|
272
|
+
- **Sign out** asks for confirmation first and tells you how many Grok workers are running. Running
|
|
273
|
+
sessions keep the previous token until their worker exits.
|
|
274
|
+
|
|
275
|
+
The relay holds no Grok secret: the device code is public by design, and the token is written by the
|
|
276
|
+
CLI straight into `~/.grok/auth.json`. Running `grok login` on the host still works as before.
|
|
277
|
+
|
|
278
|
+
Grok conversations support live reply streaming, thoughts, plan boards, subagent lifecycle chips,
|
|
279
|
+
**Stop**, session resume across worker restarts, per-turn context metrics, and the live weekly quota
|
|
280
|
+
bar in **Check Usage**. The model is fixed per conversation (ACP has no mid-session switch; the default
|
|
281
|
+
is `grok-4.5`), and question cards are not available — the protocol has no ask-user surface.
|
|
279
282
|
|
|
280
283
|
### Installing a provider CLI from the relay
|
|
281
284
|
|
|
282
285
|
Grok and Claude both run as CLIs on the relay host, and the failure mode used to be a dead end: a
|
|
283
286
|
turn fails with *"Grok CLI was not found on PATH"* and the only fix is a shell on the host — the one
|
|
284
|
-
thing the relay exists to avoid.
|
|
287
|
+
thing the relay exists to avoid. The Copilot, Claude, and Grok sub-tabs therefore carry a CLI row at the top:
|
|
285
288
|
|
|
286
289
|
```
|
|
287
290
|
Grok CLI not installed [ Install ]
|
|
@@ -302,141 +305,172 @@ Grok CLI 1.0.13 · ~/.grok/bin/grok · native · up to date [ Update ]
|
|
|
302
305
|
- If your Claude was installed with npm into a folder the relay user cannot write, `claude doctor`
|
|
303
306
|
says so and the button becomes **Switch to native installer** — Anthropic's own recommended fix.
|
|
304
307
|
The npm copy stays where it is; the native build takes precedence on PATH.
|
|
305
|
-
- The **Copilot** row is read-only: that CLI is managed with npm on
|
|
308
|
+
- The **Copilot** row is read-only: that CLI is managed with npm on the host, so there is nothing
|
|
306
309
|
the relay could usefully run.
|
|
307
310
|
|
|
308
311
|
When a turn fails because a CLI is missing or signed out, the failed reply itself carries the fix as
|
|
309
312
|
a button — **Install Grok CLI**, **Sign in to Grok**, **Claude settings** — which opens the right
|
|
310
313
|
panel and, for an install, the same confirmation sheet.
|
|
311
314
|
|
|
312
|
-
###
|
|
313
|
-
|
|
314
|
-
Copilot conversations can run on either of two engines, chosen in **⚙️ Settings → Providers → Copilot → Copilot engine**. The setting is per relay and applies to newly started conversations; sessions already running keep the engine they started on until their worker restarts.
|
|
315
|
-
|
|
316
|
-
| Engine | What runs | Trade-offs |
|
|
317
|
-
| ------ | --------- | ---------- |
|
|
318
|
-
| **Extension** (default) | The Copilot CLI in a terminal session with the web-relay extension loaded | The engine everything has shipped on. Attach to a live session with the tmux inspector (`tmux attach -t <sdk-session-id>`) |
|
|
319
|
-
| **SDK** (experimental) | A headless Node worker per conversation, driving the CLI's bundled SDK runtime over JSON-RPC | No CLI extension to install or keep in sync — first run needs only a Copilot CLI that is installed and logged in. **No tmux inspector** for those sessions: there is no TUI to attach to |
|
|
315
|
+
### Models
|
|
320
316
|
|
|
321
|
-
|
|
317
|
+
The composer's model picker is the union of every enabled provider's catalog, filtered to the models the active conversation's provider can actually serve:
|
|
322
318
|
|
|
323
|
-
-
|
|
324
|
-
-
|
|
319
|
+
- **Copilot** models are discovered from the installed Copilot CLI runtime a few seconds after the relay starts, and refreshed by the sessions that run on it. Each model shows the reasoning efforts and context window the runtime reports. The list starts with `auto`, then groups models by vendor (OpenAI, Anthropic, Google, xAI, Microsoft, Azure OpenAI, Moonshot AI, then any others alphabetically), newest version first. Before the first discovery, a fresh relay offers a curated set: `gpt-5.4`, `gpt-5.4-mini` (the default), `gpt-5.3-codex`, `claude-sonnet-4.6`, and `claude-haiku-4.5`.
|
|
320
|
+
- **OpenAI (BYOK)** models are discovered from `/v1/models` when the key is saved or re-enabled.
|
|
321
|
+
- **Claude** models are discovered from the Agent SDK when the provider is enabled. Bracketed `[1m]` long-context variants (such as `claude-opus-5[1m]`) do not appear as separate entries; they surface as a 1M option in the composer's context-size dropdown for the base model.
|
|
322
|
+
- **Cursor** models (and their per-model reasoning-effort tiers) are discovered when the API key is saved or the provider is re-enabled.
|
|
323
|
+
- **Grok** models are discovered from the Grok CLI over ACP when the provider is enabled, after a sign-in, and after a CLI install from the panel.
|
|
325
324
|
|
|
326
|
-
|
|
325
|
+
Use **🤗 Select Models** to choose which variants show up in the composer, then **💾 Save enabled models**; **Refresh** reruns discovery for every enabled runtime. The modal has one tab per runtime — **Copilot**, **OpenAI**, **Claude SDK**, **Cursor SDK**, **Grok** — and each tab lists only the models that runtime serves; there is no cross-runtime switching inside a conversation.
|
|
327
326
|
|
|
328
|
-
|
|
327
|
+
## Highlights
|
|
329
328
|
|
|
330
|
-
###
|
|
329
|
+
### New in 0.9.4
|
|
331
330
|
|
|
332
|
-
|
|
331
|
+
- **Remote relays**: pair two OAR relays in **Settings → Relays** by pasting the other relay's web address; the pairing works both ways. An agent in any session can then work on the other relay when you ask it to: list and read its sessions, prompt one, start a new one, wait for the reply, and pass a question the remote agent asks back to you. See [Remote relays](#remote-relays).
|
|
332
|
+
- **You decide when a relay is in reach**: a paired relay stays locked in a conversation until you mention it (`@` picks it from a list). Per relay, **Agents may** limits agents to *read only*, *read and prompt*, or *full*, and in ask and plan mode every write action asks you first. The receiving relay marks such messages with a **↗ from …** badge.
|
|
333
|
+
- **Every provider gets the same tools**: Grok and the Copilot extension engine now get `remote_relay` and `preview` as real tools through OAR's MCP server, instead of instruction text.
|
|
334
|
+
- **Follow the live reply**: three toggles in the running reply's header keep its thoughts, its answer, or its tool list pinned just above the composer as the reply grows. Scrolling the transcript by hand turns it off; typing in the composer does not.
|
|
335
|
+
- **Suspend host waits for the work to finish** (Windows): the relay queues the suspend and fires it after 2 minutes with no turn, background task, or GitHub Actions run active, or after a 30-second countdown when nothing was running. Every device shows a banner with what is still active and a **Cancel** button; a queued relay restart shows the same banner.
|
|
336
|
+
- **Stop** asks for confirmation before it cancels the running turn.
|
|
337
|
+
- **Fewer stuck and failed sends**: switching the Claude model mid-conversation no longer strands the next message, and the relay keeps idle connections open for 65 seconds, so a request sent after a short pause is no longer reset (behind a proxy or tunnel: an occasional 502).
|
|
333
338
|
|
|
334
|
-
|
|
339
|
+
### Everything else
|
|
335
340
|
|
|
336
|
-
-
|
|
337
|
-
- **
|
|
341
|
+
- Remote chat UI for local coding agents, with the runtime chosen per conversation
|
|
342
|
+
- Per-message **mode** (`plan`, `ask`, `agent`, `autopilot`), **model**, and **reasoning effort** pickers, backed by live model discovery
|
|
343
|
+
- Streaming tool activity, thoughts, and live assistant reply text while a turn runs
|
|
344
|
+
- Nested **subagent bubbles**: each subagent gets its own live bubble with its own thoughts, activity, and streamed text, kept as collapsible sections after the turn finishes
|
|
345
|
+
- **Background task panel** with live per-task state, model, and token use; Claude workflow tasks fold out into a progress tree of phases and agents, and leave a *Finished background task* card in the transcript when they complete
|
|
346
|
+
- Question cards for clarification: one-click choices, multi-select checkmarks, free text, and multi-field structured forms validated against their JSON schema
|
|
347
|
+
- Mathematical and scientific notation rendering for TeX/LaTeX equations and chemical formulas
|
|
348
|
+
- **Context usage** modal with a per-category token breakdown of the model's context window, plus a per-conversation **auto-compact window** slider for Claude sessions
|
|
349
|
+
- **Transcript breaks**: day separators, a marker where a Claude session auto-compacted its context, and matching dots beside the scrollbar
|
|
350
|
+
- **Plan usage** modal with subscription credits, rate-limit windows, and reset countdowns for Copilot, Claude, Cursor, and Grok
|
|
351
|
+
- **Image conversations** (OpenAI Image): generate images in chat and iterate on a generated image with **Edit this image**
|
|
352
|
+
- Agents can embed **images, video, and audio** in a reply by their absolute path, on every runtime; clicking an embedded image opens the file viewer with zoom, download, and copy
|
|
353
|
+
- **Screenshot annotations**: mark up an uploaded screenshot with highlighter strokes before or after sending; the original upload is never modified
|
|
354
|
+
- **Share** a conversation by read-only link, and hide individual messages from the shared view
|
|
355
|
+
- Conversation history in local SQLite, including the Copilot sessions already stored on the host
|
|
356
|
+
- `/compact` continues in a fresh conversation seeded with a summary; `/preview` publishes a local dev server or folder on a public preview host
|
|
357
|
+
- Workspace and drive browser with file previews, **Git changes** with a diff viewer, and `@file:` / `@folder:` reference tokens
|
|
358
|
+
- **Push notifications** for questions, finished or failed turns, plan boards, and a CLI going offline
|
|
359
|
+
- Opt-in **self-update** from Settings or `oar update`
|
|
360
|
+
- Remote access through a managed **SSH** reverse tunnel or **Cloudflare Tunnel**
|
|
361
|
+
- Installable **PWA** with an installed-app fullscreen preference and browser-mode fallbacks
|
|
338
362
|
|
|
339
|
-
|
|
363
|
+
## Using the web UI
|
|
340
364
|
|
|
341
|
-
|
|
365
|
+
### Starting a conversation
|
|
342
366
|
|
|
343
|
-
|
|
367
|
+
- Start a chat with **+ New Chat**, which asks for the **Working directory**, **Provider**, **Model**, and **Reasoning effort** (or **Quality** and **Size**, for image chats; **Context window** where a model offers several) before the conversation exists. The working-directory list offers the known directories (current session, relay workspace, browser folder, recent roots), a **Custom path…** entry, and a 📁 folder picker; it defaults to the directory you picked last, and the chosen directory is applied before the session worker first launches. With Copilot as the only provider the provider row is hidden.
|
|
368
|
+
- Choose the **mode** and **model** per message in the composer.
|
|
369
|
+
- The composer knows two slash commands, with autocomplete: **`/compact`** branches to a fresh conversation seeded with summary context, and **`/preview`** publishes a local dev server or directory on the public preview host without involving the agent (`/preview 5173 [label]`, `/preview ./dist [label]`, `/preview list`, `/preview close`). Agents can do the same through the `preview` tool (Claude and Cursor) or the documented API (see [docs/preview-servers.md](docs/preview-servers.md)). Any other single-line text starting with `/` is held back once with an *Unknown command* notice; press send again to send it as text.
|
|
344
370
|
|
|
345
|
-
|
|
346
|
-
- All four relay modes — `plan` maps to the SDK's plan permission mode and produces a **Plan ready** board, `ask` and `autopilot` adjust the system prompt
|
|
347
|
-
- Image and file attachments (images up to ~5 MB are inlined; larger files are passed as paths for Claude to read)
|
|
348
|
-
- Question cards, thinking/thought streams, live reply streaming, and nested subagent bubbles
|
|
349
|
-
- **Stop** to abort the running turn
|
|
350
|
-
- Background tasks that outlive the reply that started them: the composer's task panel lists them live with their own **Stop**, and an **Ultracode** workflow folds out into a tree of its phases and agents (state, model, tokens). When the workflow finishes, the summarizing reply keeps a collapsed *Finished background task* card holding the final tree, which survives reloads
|
|
351
|
-
- Session continuity across worker restarts — the native Agent SDK session id is stored and resumed
|
|
352
|
-
- Real context-window metrics, reported after each turn
|
|
371
|
+
### Relay modes
|
|
353
372
|
|
|
354
|
-
|
|
373
|
+
| Mode | Behavior |
|
|
374
|
+
| ----------- | ------------------------------------------------------------ |
|
|
375
|
+
| `ask` | Clarification-first behavior before implementation |
|
|
376
|
+
| `plan` | Planning response style (no implementation unless requested) |
|
|
377
|
+
| `agent` | Interactive coding agent behavior |
|
|
378
|
+
| `autopilot` | Action-first behavior; asks only when truly blocking |
|
|
355
379
|
|
|
356
|
-
|
|
357
|
-
- Claude turns are not included in the Copilot usage line, and no usage line is attached to their replies (Claude's own plan limits appear in **Check Usage**)
|
|
358
|
-
- The browsable **Session** root points at the Agent SDK's project directory rather than a Copilot session-state folder
|
|
380
|
+
### While a turn runs
|
|
359
381
|
|
|
360
|
-
|
|
382
|
+
- Assistant text streams into the pending bubble as it is generated, next to the turn's tool activity and thoughts. Any subagent the turn spawns gets its own nested bubble with its own thoughts, activity, and text.
|
|
383
|
+
- **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**.
|
|
384
|
+
- **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.
|
|
385
|
+
- On Copilot, **Stop** ends only the reply: background agents the turn started keep running under their own **Stop** in the task panel.
|
|
361
386
|
|
|
362
|
-
|
|
387
|
+
### Question cards
|
|
363
388
|
|
|
364
|
-
|
|
389
|
+
- Agents ask clarifying questions through question cards: `ask_user` on Copilot and Cursor, `AskUserQuestion` on Claude. A choice answers with one click; a question that allows several answers shows checkmarks and one **Reply with selection** button, whose reply lists every ticked choice plus anything you typed. Structured requests render as multi-field forms.
|
|
390
|
+
- On Copilot's SDK engine, the model gets OAR's own `ask_user` tool, which can mark a question as multi-select; a question worded "select all that apply" counts too, and every Copilot choice card that accepts free text has a **Select several** switch.
|
|
391
|
+
- A card waits 8 hours for an answer (2 hours on Copilot's Extension engine). After that the agent is told nobody answered and continues according to the conversation's mode. A turn that is waiting on a card is never treated as stuck.
|
|
392
|
+
- Grok has no question cards: ACP has no ask-user surface.
|
|
365
393
|
|
|
366
|
-
|
|
367
|
-
- All four relay modes — `plan` uses the SDK's native plan mode and produces a **Plan ready** board; `ask` and `autopilot` ride as instructions on the message text, injected only when the mode changes
|
|
368
|
-
- Live reply streaming, question cards, and **Stop** to abort the running turn
|
|
369
|
-
- The browsable **Session** root points at the worker's per-session agent store, created on the session's first turn
|
|
370
|
-
- Expired cached agent handles are recreated and retried automatically once — a second auth failure means the API key itself is invalid
|
|
394
|
+
### Background tasks
|
|
371
395
|
|
|
372
|
-
|
|
396
|
+
- 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.
|
|
397
|
+
- 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.
|
|
398
|
+
- A background task's result arrives as its own *continuation* reply.
|
|
399
|
+
- **⚙️ 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.
|
|
373
400
|
|
|
374
|
-
###
|
|
401
|
+
### Drafts across devices
|
|
375
402
|
|
|
376
|
-
|
|
377
|
-
on the host and uses whatever account that CLI is signed in to (or `XAI_API_KEY` in the host
|
|
378
|
-
environment).
|
|
403
|
+
The composer draft, attachments included, is saved per conversation on the relay and follows you to your other devices. Saves happen only on real edits and are version-checked, so an idle device never overwrites the draft you are typing on another one, and a device you are not typing on picks up the other's draft. When two devices edit at once, the newest keystroke wins and the replaced text is offered back with **Restore** (and **Undo**). A draft longer than 20,000 characters is saved up to that length, and the composer says the rest is not saved.
|
|
379
404
|
|
|
380
|
-
The
|
|
405
|
+
### The conversation list
|
|
381
406
|
|
|
382
|
-
-
|
|
383
|
-
|
|
384
|
-
-
|
|
385
|
-
|
|
386
|
-
shown next to it so you can check it against what the browser displays. Open the link on any
|
|
387
|
-
device, confirm, and the panel flips to signed-in **by itself** — nothing is pasted back through
|
|
388
|
-
the relay, because the CLI polls x.ai and finishes on its own. Model discovery re-runs straight
|
|
389
|
-
after, so switching accounts needs no relay restart. The flow is pushed over the socket: start it
|
|
390
|
-
on one device, finish it on another, and closing the modal does not lose it.
|
|
391
|
-
- **Sign out** asks for confirmation first and tells you how many Grok workers are running. Running
|
|
392
|
-
sessions keep the previous token until their worker exits.
|
|
407
|
+
- **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.
|
|
408
|
+
- **🔍** in the conversation header searches message text across all conversations.
|
|
409
|
+
- 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.
|
|
410
|
+
- Deleting a conversation stops its session worker and removes its CLI session too (Copilot through the runtime, Claude by deleting that session's transcript). 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.
|
|
393
411
|
|
|
394
|
-
|
|
395
|
-
CLI straight into `~/.grok/auth.json`. Running `grok login` on the host still works as before.
|
|
412
|
+
### Files, git, and previews
|
|
396
413
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
414
|
+
- 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.
|
|
415
|
+
- Click the file and folder copy controls to insert `@file:...` / `@folder:...` tokens.
|
|
416
|
+
- 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.
|
|
417
|
+
- 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.
|
|
418
|
+
- Agents can embed images, video, and audio in a reply by absolute path; they render inline.
|
|
419
|
+
- Tap an image attachment in the composer (it carries a 🖍️ badge), or use **🖍️ Annotate** in the file viewer, to mark it up with highlighter strokes. The annotated copy is uploaded; the original stays untouched.
|
|
420
|
+
- External links in chat open in a new tab with `noopener`/`noreferrer`; workspace file mentions stay in the in-app preview.
|
|
401
421
|
|
|
402
|
-
|
|
422
|
+
### Usage and context
|
|
403
423
|
|
|
424
|
+
- Use **📊 Check Usage** in the conversation `⋯` menu for plan usage across every configured provider: remaining credits, rate-limit windows, reset countdowns, and collapsible cost/token detail. Each provider gets its own tab, opening on the conversation's own provider, and the card names the signed-in account (email and plan) beneath its title. Sources differ per provider:
|
|
425
|
+
- **Copilot** — live quota (AI credits or premium requests, chat, plan), plus per-model/product billed cost when your GitHub token can read personal billing. Conversations on the SDK engine add a **Last SDK worker turn** section (AI credits actually spent, tokens, model calls, overage) with the model and how long ago it was captured; it is one turn's numbers rather than a running total, and it stops being shown once it is more than seven days old.
|
|
426
|
+
- **Claude** — subscription limit windows (5-hour, weekly, per-model), extra-usage credits, session cost, and local usage attribution. Read from the live session at the end of a turn; the relay never starts a hidden turn to refresh it, so the newest reading is from your last Claude turn.
|
|
427
|
+
- **Cursor** — spend from the Cursor SDK measured against the monthly allowances you enter in Settings, split into the Cursor Models and Other Models pools; these figures are estimates, and Cursor's Spending dashboard remains authoritative. With a dashboard session token, the card adds live *Included in plan* bars.
|
|
428
|
+
- **Grok** — the live weekly subscription quota, read with the host's Grok CLI login, plus per-turn tokens and estimated cost from the agent's prompt result. An optional monthly USD allowance in Settings adds an estimated remaining meter; the card is hidden when Grok is disabled. Billing: [console.x.ai](https://console.x.ai).
|
|
429
|
+
- Per-reply usage lines are recorded only for Copilot turns — OpenAI, Claude, Cursor, and Grok turns do not consume Copilot premium requests, and no usage line is attached to them.
|
|
430
|
+
- Use the **🧠** context button for a per-category breakdown of the conversation's context window: a usage bar, a token/percentage table, and free space. Claude sessions report exact SDK categories; Copilot sessions show the coarser system/tools + messages + buffer split, labelled as a lower-bound estimate when the runtime no longer emits full buckets.
|
|
431
|
+
- Claude conversations additionally get an **auto-compact window** slider in that modal. Claude Code compacts a session once it approaches a model-tuned window (around 967k tokens on a 1M-context model), which is why long conversations rarely compact at all; setting a smaller window makes it happen sooner and keeps turns cheaper. *Auto* hands the choice back to the CLI. The smallest window is 100k, because the CLI silently ignores anything below that and falls back to its own default. The line beneath the slider reports the window actually in force and where it came from — your setting, the model default, or the `CLAUDE_CODE_AUTO_COMPACT_WINDOW` environment override — and fills in once the conversation's first turn completes. The change reaches a running session on its next message.
|
|
432
|
+
- The transcript marks day boundaries, and marks the point where a Claude session compacted its context with the tokens before and after. Both appear as dots beside the scrollbar for the messages currently loaded.
|
|
404
433
|
|
|
405
|
-
|
|
406
|
-
| ----------- | ------------------------------------------------------------ |
|
|
407
|
-
| `ask` | Clarification-first behavior before implementation |
|
|
408
|
-
| `plan` | Planning response style (no implementation unless requested) |
|
|
409
|
-
| `agent` | Interactive coding agent behavior |
|
|
410
|
-
| `autopilot` | Action-first behavior; asks only when truly blocking |
|
|
434
|
+
### Sharing
|
|
411
435
|
|
|
436
|
+
Use **➡️ Share conversation** in the `⋯` menu to publish a read-only link. Each message has a **Hide** button that keeps it out of the shared view without deleting it; hidden messages stay fully visible to you, marked *Hidden from shared viewers*, with an **Unhide** button. Anyone with the link can read the shared view, without signing in.
|
|
412
437
|
|
|
413
|
-
|
|
438
|
+
### Notifications and the app
|
|
414
439
|
|
|
415
|
-
|
|
440
|
+
- **⚙️ Settings → Notifications** turns on push notifications per device: for questions from the agent, completed or failed turns, plan boards, and the CLI going offline. They need a secure context (HTTPS, or `localhost`), are sent only while no device has the app in the foreground, and show generic text unless you opt in to message previews on that device.
|
|
441
|
+
- The **⬇** button in the sidebar installs OAR as an app (PWA). **Relay name** in Settings sets its label on every device that connects to this relay, and is the name [paired relays](#remote-relays) know it by.
|
|
416
442
|
|
|
417
|
-
|
|
418
|
-
- **OpenAI (BYOK)** models are discovered from `/v1/models` when the key is saved or re-enabled.
|
|
419
|
-
- **Claude** models are discovered from the Agent SDK when the provider is enabled. Bracketed `[1m]` long-context variants (such as `claude-opus-5[1m]`) do not appear as separate entries; they surface as a 1M option in the composer's context-size dropdown for the base model.
|
|
420
|
-
- **Cursor** models (and their per-model reasoning-effort tiers) are discovered when the API key is saved or the provider is re-enabled.
|
|
443
|
+
### The conversation menu
|
|
421
444
|
|
|
422
|
-
|
|
445
|
+
Besides the entries above, the `⋯` menu holds **✍️ Edit conversation title**, **🖥️ Inspect tmux console** (a read-only view of the session's tmux pane), **🤗 Select Models**, **⚙️ Settings**, **🌄 Restart web relay** (queued until the current turn is idle), **💤 Suspend host**, and **☠️ Kill session** (stops the conversation's worker; an active turn then needs a retry or a new message). Its header shows the queue counts with **🚮 Empty queue**.
|
|
423
446
|
|
|
424
|
-
|
|
447
|
+
## Settings and configuration reference
|
|
425
448
|
|
|
426
|
-
|
|
449
|
+
### Settings (⚙️ in the web UI)
|
|
427
450
|
|
|
428
|
-
The modal is organised into
|
|
451
|
+
The modal is organised into six tabs — **General**, **Providers** (with a **Copilot**, **OpenAI**, **Claude**, **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:
|
|
429
452
|
|
|
430
|
-
| Setting
|
|
431
|
-
|
|
|
432
|
-
|
|
|
433
|
-
|
|
|
434
|
-
|
|
|
435
|
-
|
|
|
436
|
-
|
|
|
437
|
-
|
|
|
438
|
-
|
|
|
439
|
-
|
|
|
453
|
+
| Tab | Setting | Default | What it does |
|
|
454
|
+
| --- | ------- | ------- | ------------ |
|
|
455
|
+
| General | Theme, Text scaling | dark theme | Per browser |
|
|
456
|
+
| General | Show Suspend host action | on | Per browser: shows **💤 Suspend host** in the conversation menu |
|
|
457
|
+
| 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) |
|
|
458
|
+
| 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) |
|
|
459
|
+
| 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) |
|
|
460
|
+
| 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) |
|
|
461
|
+
| 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 |
|
|
462
|
+
| General | Default CWD for new sessions | relay workspace root | Working directory for newly created sessions that have none of their own |
|
|
463
|
+
| Providers | Copilot engine | SDK | Extension when this relay cannot run the SDK engine (see [engine choice](#github-copilot--engine-choice)) |
|
|
464
|
+
| Providers | OpenAI API key / model / base URL | — / `gpt-4o` / `https://api.openai.com/v1` | Enables the OpenAI and OpenAI Image providers |
|
|
465
|
+
| Providers | Claude (Agent SDK) | off | Enables Claude as a New Chat provider and runs model discovery; default model `claude-sonnet-5` |
|
|
466
|
+
| Providers | Claude account (Relogin / Logout) | host login | Switches the Claude account the relay host's CLI uses, from the browser (see [Claude (Agent SDK)](#claude-agent-sdk)) |
|
|
467
|
+
| Providers | Cursor API key / model | — / `composer-2.5` | Enables Cursor; plus monthly plan allowances and the optional dashboard session token |
|
|
468
|
+
| Providers | Grok | off | Enables Grok; default model `grok-4.5`; **Sign in** / **Sign out**; optional monthly allowance |
|
|
469
|
+
| Relays | Public address, Accept prompts from other relays' agents | browser address / on | How paired relays reach this one, and whether their agents may prompt sessions here (see [Remote relays](#remote-relays)) |
|
|
470
|
+
| Relays | Remote relays | — | Pair a relay by pasting its web address; per relay **Agents may** read only, read and prompt, or do everything |
|
|
471
|
+
| Previews | Live previews | — | Lists the published previews, each of which you can close |
|
|
472
|
+
| Notifications | Push notifications | off | Per device: which events notify, and whether titles and message previews are included |
|
|
473
|
+
| Features | Feature flags | see below | **Session worker routing** (on), **Continuation answer routing** (on), **Generated-image continuity** (on), and a reserved **Worker fallback restart** (off, no effect yet). Changes apply after a relay restart; a `COPILOT_REMOTE_<FLAG>` environment variable pins a flag, e.g. `COPILOT_REMOTE_SESSION_WORKER_ROUTING_ENABLED=0` |
|
|
440
474
|
|
|
441
475
|
### How a stuck turn is detected
|
|
442
476
|
|
|
@@ -447,26 +481,24 @@ Two independent guards, both of which exempt a turn that is waiting on an unansw
|
|
|
447
481
|
|
|
448
482
|
When either trips, the turn is returned to the queue rather than lost.
|
|
449
483
|
|
|
450
|
-
|
|
484
|
+
### Configuration reference (`server/config.json`)
|
|
451
485
|
|
|
486
|
+
The config file is `server/config.json` in a git checkout and `~/.oar/config.json` (`%APPDATA%\oar\config.json`) for a global install; `COPILOT_WEB_RELAY_CONFIG` points the relay at another file. It holds the auth token, so the relay keeps it owner-only. The relay reads it at startup: restart the relay after editing it.
|
|
452
487
|
|
|
453
488
|
| Key | Default | Description |
|
|
454
489
|
| -------------------------- | -------------------- | ------------------------------------------------------------------------- |
|
|
455
|
-
| `authToken` | generated if
|
|
490
|
+
| `authToken` | generated if empty | Required for API/UI auth. If empty, the relay generates a random token on every start and prints it to its log without saving it; `oar setup` writes a stable one |
|
|
456
491
|
| `port` | `3333` | HTTP + WebSocket port |
|
|
457
492
|
| `localhostOnly` | `true` | Bind only to loopback (`127.0.0.1`) and disable LAN/WAN access |
|
|
458
|
-
| `
|
|
493
|
+
| `dataDir` | `data/` beside the server | Database and uploads; a relative path resolves against the config file's directory. `COPILOT_WEB_RELAY_DATA_DIR` wins, and global installs started by `oar` use `~/.oar/data` |
|
|
459
494
|
| `processingTimeoutMs` | `600000` | Inactivity window before a turn is treated as stale (not a cap on turn length) |
|
|
460
|
-
| `
|
|
461
|
-
| `conversationSessionMode` | `isolated` | Configured strategy (`isolated` / `shared`) exposed in status |
|
|
462
|
-
| `restartGracefulTimeoutMs` | `8000` | Graceful restart wait before force fallback |
|
|
463
|
-
| `restartShutdownTimeoutMs` | `45000` | Drain timeout while waiting for active queue job completion |
|
|
464
|
-
| `restartSpawnTimeoutMs` | `18000` | Max wait for resume/restart phase per attempt |
|
|
465
|
-
| `restartRebindTimeoutMs` | `20000` | Max wait for rebind/session-sync completion per attempt |
|
|
466
|
-
| `restartMaxAttempts` | `3` | Bounded restart attempts before terminal exhaustion |
|
|
467
|
-
| `restartRetryBackoffMs` | `[1000,3000,7000]` | Deterministic retry backoff schedule in milliseconds |
|
|
495
|
+
| `conversationSessionMode` | `isolated` | Configured strategy (`isolated` / `shared`) recorded on sessions and exposed in status |
|
|
468
496
|
| `maxRequeueRetries` | `5` | Queue retry limit for failed processing |
|
|
469
497
|
| `remotePath` | `""` | URL base path when reverse-proxied under a subpath; also drives PWA URLs and socket.io path |
|
|
498
|
+
| `trustProxy` | `"loopback"` | Which proxies may set `X-Forwarded-*` headers (Express `trust proxy`); widen it with a hop count or subnet when a proxy on another host forwards to the relay |
|
|
499
|
+
| `workspaceRootAllowList` | unset | Directories that conversations may start in (they and their subdirectories); unset allows any. `COPILOT_WORKSPACE_ROOT_ALLOW_LIST` sets it too, separated by the platform's path delimiter |
|
|
500
|
+
| `publicHostnames` | `[]` | Hostnames the relay itself answers on; the preview lane refuses to share one |
|
|
501
|
+
| `tunnelMarkerHeaders` | `[]` | Extra edge-injected headers that mark tunnel traffic (see [Session-worker path guard](#session-worker-path-guard)) |
|
|
470
502
|
| `sshTunnel.mode` | `disabled` | Tunnel mode (`disabled` or `managed`) |
|
|
471
503
|
| `sshTunnel.enabled` | `false` | Legacy alias (`true` => `managed`) |
|
|
472
504
|
| `sshTunnel.required` | `false` | Pause dequeue while managed tunnel is disconnected |
|
|
@@ -476,24 +508,59 @@ When either trips, the turn is returned to the queue rather than lost.
|
|
|
476
508
|
| `sshTunnel.host` | — | SSH host |
|
|
477
509
|
| `sshTunnel.remotePort` | — | Remote forwarded port |
|
|
478
510
|
| `sshTunnel.identityFile` | optional | SSH key path (falls back to default agent/key) |
|
|
479
|
-
| `cloudflaredTunnel.mode` | `disabled` | Cloudflare tunnel mode (`disabled` or `managed`)
|
|
511
|
+
| `cloudflaredTunnel.mode` | `disabled` | Cloudflare tunnel mode (`disabled` or `managed`; `enabled: true` is a legacy alias for `managed`) |
|
|
480
512
|
| `cloudflaredTunnel.required` | `false` | Pause dequeue while the managed Cloudflare tunnel is disconnected |
|
|
481
|
-
| `cloudflaredTunnel.token` | — |
|
|
513
|
+
| `cloudflaredTunnel.token` | — | The tunnel token from your own Cloudflare Zero Trust tunnel |
|
|
482
514
|
| `cloudflaredTunnel.binary` | *(auto)* | `cloudflared` path; defaults to the npm package, then `PATH` |
|
|
483
515
|
| `cloudflaredTunnel.extraArgs` | `[]` | Extra arguments appended to `cloudflared tunnel run` |
|
|
484
|
-
| `
|
|
485
|
-
| `publicHostnames` | `[]` | Hostnames the relay itself answers on; the preview lane refuses to share one |
|
|
486
|
-
| `previews.enabled` | `false` | Publish local dev servers on a separate listener (see `docs/preview-servers.md`) |
|
|
516
|
+
| `previews.enabled` | `false` | Publish local dev servers on a separate listener (see [docs/preview-servers.md](docs/preview-servers.md)) |
|
|
487
517
|
| `previews.port` | `port + 1` | Loopback port for the preview listener; `0` picks an ephemeral port |
|
|
488
518
|
| `previews.bindHost` | `127.0.0.1` | Bind address; non-loopback needs `previews.allowPublicBind` |
|
|
489
519
|
| `previews.publicBaseUrl` | — | Public base URL on a hostname **different** from the relay's |
|
|
490
520
|
| `previews.allowedTargetHosts` | `[]` | Upstreams allowed beyond loopback (container/VM IPs) |
|
|
491
521
|
| `previews.maxLive` | `8` | Maximum simultaneously published previews |
|
|
522
|
+
| `pushVapidSubject` | `mailto:copilot-remote@example.com` | Contact URI the relay sends to push services with Web Push requests |
|
|
523
|
+
| `sharedPresenceMaxPerConversation` | `200` | Most live viewers tracked per shared conversation |
|
|
524
|
+
| `sharedPresenceMaxGlobal` | `5000` | Most live shared-link viewers tracked in total |
|
|
525
|
+
| `cliBinaries` | *(written by the relay)* | Provider CLI paths resolved after an install from Settings, restored at startup |
|
|
526
|
+
| `restartGracefulTimeoutMs` | `8000` | Graceful restart wait before force fallback |
|
|
527
|
+
| `restartShutdownTimeoutMs` | `45000` | Drain timeout while waiting for active queue job completion |
|
|
528
|
+
| `restartSpawnTimeoutMs` | `18000` | Max wait for resume/restart phase per attempt |
|
|
529
|
+
| `restartRebindTimeoutMs` | `20000` | Max wait for rebind/session-sync completion per attempt |
|
|
530
|
+
| `restartMaxAttempts` | `3` | Bounded restart attempts before terminal exhaustion |
|
|
531
|
+
| `restartRetryBackoffMs` | `[1000,3000,7000]` | Deterministic retry backoff schedule in milliseconds |
|
|
492
532
|
|
|
533
|
+
### Environment variables
|
|
493
534
|
|
|
494
|
-
|
|
535
|
+
| Variable | Effect |
|
|
536
|
+
| -------- | ------ |
|
|
537
|
+
| `COPILOT_WEB_RELAY_CONFIG` | Config file to use |
|
|
538
|
+
| `COPILOT_WEB_RELAY_DATA_DIR` | Data directory (database and uploads) |
|
|
539
|
+
| `COPILOT_WEB_RELAY_LOG_DIR` | Log directory |
|
|
540
|
+
| `OAR_STATE_ROOT` | State root of a global install, instead of `~/.oar` / `%APPDATA%\oar` |
|
|
541
|
+
| `OAR_NO_UPDATE_CHECK=1` | Never contact oar.sh, not even for a manual update check |
|
|
542
|
+
| `COPILOT_SDK_PATH` | The Copilot CLI runtime's `copilot-sdk` directory, instead of the newest one in the CLI's package cache |
|
|
543
|
+
| `COPILOT_PKG_DIR` | An additional Copilot CLI package cache to search |
|
|
544
|
+
| `GH_TOKEN`, `GITHUB_TOKEN` | GitHub token for the Copilot usage card, instead of `gh auth token` |
|
|
545
|
+
| `XAI_API_KEY` | Grok authentication instead of the Grok CLI login |
|
|
546
|
+
| `CURSOR_SESSION_TOKEN` | Cursor dashboard session token for live plan bars |
|
|
547
|
+
| `CLAUDE_CONFIG_DIR` | Claude configuration directory, instead of `~/.claude` |
|
|
548
|
+
| `CLAUDE_CODE_AUTO_COMPACT_WINDOW` | Overrides the Claude auto-compact window |
|
|
549
|
+
| `COPILOT_CLOUDFLARED_MODE`, `COPILOT_CLOUDFLARED_TOKEN`, `COPILOT_CLOUDFLARED_BINARY` | Override `cloudflaredTunnel.mode`, `.token`, and `.binary` |
|
|
550
|
+
| `COPILOT_WORKSPACE_ROOT_ALLOW_LIST` | Same as `workspaceRootAllowList` |
|
|
551
|
+
| `COPILOT_REMOTE_<FLAG>` | Pins a feature flag from the **Features** tab (`1`/`0`, `true`/`false`) |
|
|
495
552
|
|
|
496
|
-
|
|
553
|
+
Many internal names still say "copilot" (`COPILOT_WEB_RELAY_*`, `copilot.db`) from the project's earlier life; they apply to every runtime.
|
|
554
|
+
|
|
555
|
+
### API overview
|
|
556
|
+
|
|
557
|
+
Every authenticated HTTP route accepts `Authorization: Bearer <token>` or the login cookie; the routes are documented in [server/README.md](server/README.md#api-reference).
|
|
558
|
+
|
|
559
|
+
## Remote access
|
|
560
|
+
|
|
561
|
+
`localhostOnly` controls only the relay's own listener (`127.0.0.1` vs `0.0.0.0`). To reach the relay from outside your network, prefer one of the two managed tunnels below over opening a port. They are independent and can run at the same time.
|
|
562
|
+
|
|
563
|
+
### Optional remote internet access (SSH tunnel)
|
|
497
564
|
|
|
498
565
|
Configure:
|
|
499
566
|
|
|
@@ -509,8 +576,7 @@ Configure:
|
|
|
509
576
|
}
|
|
510
577
|
```
|
|
511
578
|
|
|
512
|
-
|
|
513
|
-
SSH tunnel exposure is controlled independently by `sshTunnel.remoteBind`.
|
|
579
|
+
SSH tunnel exposure is controlled by `sshTunnel.remoteBind`, independently of `localhostOnly`.
|
|
514
580
|
|
|
515
581
|
Then reverse proxy on the VPS (example Caddy):
|
|
516
582
|
|
|
@@ -522,11 +588,17 @@ relay.example.com {
|
|
|
522
588
|
|
|
523
589
|
The relay auto-reconnects tunnel drops with exponential backoff.
|
|
524
590
|
|
|
525
|
-
|
|
591
|
+
### Optional remote internet access (Cloudflare Tunnel)
|
|
526
592
|
|
|
527
593
|
An alternative to the SSH tunnel that needs no VPS and no inbound port: the relay
|
|
528
594
|
supervises Cloudflare's `cloudflared` binary, and Cloudflare carries your hostname down
|
|
529
|
-
to `127.0.0.1:3333`.
|
|
595
|
+
to `127.0.0.1:3333`.
|
|
596
|
+
|
|
597
|
+
1. In your own Cloudflare Zero Trust account, create a tunnel and route a public hostname on
|
|
598
|
+
your zone to `http://localhost:3333` (or your `port`). That routing lives in Cloudflare, not
|
|
599
|
+
on this machine.
|
|
600
|
+
2. Put the tunnel's token into the config and restart the relay. (`oar setup` can switch the
|
|
601
|
+
tunnel on for you; it still needs the token.)
|
|
530
602
|
|
|
531
603
|
```json
|
|
532
604
|
"cloudflaredTunnel": {
|
|
@@ -538,10 +610,8 @@ to `127.0.0.1:3333`. Both modes are independent and may run at the same time.
|
|
|
538
610
|
}
|
|
539
611
|
```
|
|
540
612
|
|
|
541
|
-
|
|
542
|
-
`
|
|
543
|
-
start the relay. Environment overrides: `COPILOT_CLOUDFLARED_MODE`,
|
|
544
|
-
`COPILOT_CLOUDFLARED_TOKEN`, `COPILOT_CLOUDFLARED_BINARY`.
|
|
613
|
+
Environment overrides: `COPILOT_CLOUDFLARED_MODE`, `COPILOT_CLOUDFLARED_TOKEN`,
|
|
614
|
+
`COPILOT_CLOUDFLARED_BINARY`.
|
|
545
615
|
|
|
546
616
|
`localhostOnly` stays `true`: `cloudflared` connects outbound and nothing binds publicly.
|
|
547
617
|
The binary resolves from `cloudflaredTunnel.binary`, then the optional `cloudflared` npm
|
|
@@ -570,148 +640,110 @@ covering `/api/shared/*` — shared views poll for liveness and an edge-cached r
|
|
|
570
640
|
would pin viewers to a stale snapshot. Likewise, a Cloudflare Access application over the
|
|
571
641
|
bound hostname breaks share links unless it bypasses `/shared/*` and `/api/shared/*`.
|
|
572
642
|
|
|
573
|
-
##
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
643
|
+
## Remote relays
|
|
644
|
+
|
|
645
|
+
Several OAR relays can be paired, for example the one on your workstation and the one on a
|
|
646
|
+
server. An agent in any session can then work on the other relay when you ask it to: list and
|
|
647
|
+
read its sessions, prompt one, start a new one, wait for the reply, and pass a question the
|
|
648
|
+
remote agent asks back to you.
|
|
649
|
+
|
|
650
|
+
**Pairing.** Open **Settings → Relays** and paste the other relay's web address (the URL you
|
|
651
|
+
open it with in the browser). OAR checks the address with its own token, or with a
|
|
652
|
+
`?token=` the pasted link carries, and asks for a token only when that fails. It reads the
|
|
653
|
+
other relay's name from its API. With **Also add this relay there** checked, it introduces
|
|
654
|
+
itself to the other relay using the address in **Public address**, so the pairing works in both
|
|
655
|
+
directions. When the two relays use different tokens, that introduction hands over this relay's
|
|
656
|
+
token, so the other relay's agents can then work here. Each side can remove the other on its own,
|
|
657
|
+
but removing a relay does not revoke a token it already holds: rotate the token for that. A relay
|
|
658
|
+
that is already paired keeps the address and token stored for it when it introduces itself again;
|
|
659
|
+
change those in **Settings → Relays**.
|
|
660
|
+
|
|
661
|
+
A relay's name is the **Relay name** under **General** (it also names the installed app); when it
|
|
662
|
+
is empty, the machine's hostname is used.
|
|
663
|
+
|
|
664
|
+
**Mention a relay to unlock it.** Agents can see which relays are paired, but a relay stays
|
|
665
|
+
locked in a conversation until you mention it in one of your messages: type `@` and pick it
|
|
666
|
+
from the list, or write its name or its host name. An IP address or `localhost` never counts as a
|
|
667
|
+
host name; a relay whose name is itself an address is unlocked only with a leading `@`. The
|
|
668
|
+
mention unlocks that relay for the rest of
|
|
669
|
+
the conversation. Prompts that arrive from another relay's agent never unlock anything, and a
|
|
670
|
+
chain of relays prompting relays stops after two hops.
|
|
671
|
+
|
|
672
|
+
**What agents can do.** Every provider gets the same `remote_relay` tool (Claude, Cursor and
|
|
673
|
+
Copilot directly; Grok and the Copilot extension engine through OAR's MCP server). Per paired
|
|
674
|
+
relay, **Agents may** limits it to *read only*, *read and prompt* (existing sessions) or *full*
|
|
675
|
+
(also create sessions, answer questions, stop turns, archive). In the *ask* and *plan* relay
|
|
676
|
+
modes, every write action first asks you with a question card.
|
|
677
|
+
|
|
678
|
+
When an agent prompts a session on another relay, that relay shows a **↗ from …** badge with the
|
|
679
|
+
sending relay, session and model on the message, and marks sessions an agent created in the
|
|
680
|
+
conversation list. The remote agent sees a one-line header saying that another agent is talking
|
|
681
|
+
to it. Turns started this way do not send "reply ready" notifications on the remote relay;
|
|
682
|
+
question cards still do. **Accept prompts from other relays' agents** in **Settings → Relays**
|
|
683
|
+
switches incoming agent prompts off.
|
|
684
|
+
|
|
685
|
+
The calls go through your own relay, which holds the other relay's token: agents never see a
|
|
686
|
+
token. Remote addresses must use `https://`; plain `http://` is accepted only for loopback (an
|
|
687
|
+
SSH port forward, for example) and private network ranges.
|
|
604
688
|
|
|
605
|
-
##
|
|
606
|
-
|
|
607
|
-
Common routes:
|
|
608
|
-
|
|
609
|
-
- Browser/API: `/api/message`, `/api/conversations`, `/api/conversation/:id`, `/api/status`, `/api/models`, `/api/usage`, `/api/context/:conversationId`
|
|
610
|
-
- Settings: `/api/settings/openai`, `/api/settings/claude`, `/api/settings/grok`, `/api/settings/cursor`, `/api/settings/copilot`, `/api/settings/turn-ceiling`, `/api/settings/windows-autostart`
|
|
611
|
-
- Relay control: `/api/relay/shutdown`, `/api/relay/pause`, `/api/relay/resume`
|
|
612
|
-
- Worker bridge: `/api/pending`, `/api/response`, `/api/activity`, `/api/stream`, `/api/thought`, `/api/heartbeat`
|
|
613
|
-
- Claude worker: `/api/claude-native-session`, `/api/claude-context-usage`, `/api/claude-plan-usage`
|
|
614
|
-
- Claude account auth: `/api/claude/auth/status`, `/api/claude/auth/login/start`, `/api/claude/auth/login/code`, `/api/claude/auth/login/cancel`, `/api/claude/auth/logout`
|
|
615
|
-
- Grok account auth: `/api/grok/auth/status`, `/api/grok/auth/login/start`, `/api/grok/auth/login/cancel`, `/api/grok/auth/logout`
|
|
616
|
-
- Provider CLI install: `/api/cli/status`, `/api/cli/install`, `/api/cli/install/cancel`
|
|
617
|
-
- Cursor worker: `/api/cursor-agent-id`, `/api/cursor-context-usage`, `/api/cursor-plan-usage`
|
|
618
|
-
- Questions: `/api/relay-question`, `/api/relay-question/:id`, `/api/relay-question/:id/answer`
|
|
619
|
-
- Sharing: `/api/conversation/:id/share`, `/api/conversation/:id/message/:messageId/share-visibility`, `/api/shared/:token`
|
|
620
|
-
- Images: `/api/openai/images/generate`, `/api/image-operations/:operationId/execute`, `/api/generated-image/:conversationId/:messageId/:imageId/content`
|
|
621
|
-
- File access: `/api/files/*`, `/api/files-preview/*`, `/api/repo/tree`, `/api/drives/*`
|
|
622
|
-
- Git: `/api/git/status`, `/api/git/diff`, `/api/git/pull`
|
|
623
|
-
- Previews: `/api/previews`, `/api/previews/:token` (publish a local dev server; see `docs/preview-servers.md`)
|
|
624
|
-
- Uploads: `/api/upload`, `/api/upload/:sha256/content`
|
|
625
|
-
|
|
626
|
-
All authenticated routes accept either:
|
|
627
|
-
|
|
628
|
-
- `Authorization: Bearer <token>`
|
|
629
|
-
- auth cookie from prior login
|
|
689
|
+
## Security notes
|
|
630
690
|
|
|
631
|
-
|
|
691
|
+
- 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` prints and puts in its QR code), 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.
|
|
692
|
+
- Keep the config file private (the relay keeps it owner-only) and rotate `authToken` if it is exposed: `oar setup` offers a new token, which signs every device out.
|
|
693
|
+
- `localhostOnly` (default `true`) keeps the relay on loopback. Beyond your LAN, use HTTPS through one of the [tunnels](#remote-access) rather than an open port.
|
|
694
|
+
- Agents act with the permissions of the user the relay runs as. OAR adds reach, not a sandbox: whoever holds the token can have them run commands on the host. `workspaceRootAllowList` limits the directories conversations may start in.
|
|
695
|
+
- Shared conversation links and preview links are public by design: anyone with the URL can read the shared transcript or reach the previewed app, without signing in.
|
|
696
|
+
- A [paired relay](#remote-relays) holds a token that opens the other relay completely. The per-relay "Agents may" limit and the "Accept prompts from other relays' agents" switch are honoured by OAR's own traffic, but anyone holding the token can still use the full API: the token is the real boundary. A token entered for a paired relay is stored like the API keys below and never sent to browsers or agents.
|
|
697
|
+
- API keys you enter (OpenAI, Cursor) are stored in the relay database on the host and never sent to browsers. Claude and Grok credentials stay with their CLIs on the host.
|
|
698
|
+
- OAR has no telemetry. Its only request to oar.sh is the update check, which is off until you enable it.
|
|
632
699
|
|
|
633
700
|
## Troubleshooting
|
|
634
701
|
|
|
635
|
-
|
|
636
702
|
| Symptom | What to check |
|
|
637
703
|
| ---------------------------------- | -------------------------------------------------------------------------------- |
|
|
638
|
-
|
|
|
639
|
-
|
|
|
640
|
-
|
|
|
641
|
-
|
|
|
704
|
+
| Banner says *CLI is offline* | No session worker has reported in during the last 10 seconds, which can be normal on an idle relay. Send a message: the relay starts that conversation's worker, and the banner clears once it connects. If it stays, check `/api/status` and the worker's `worker-<session>.log` in the log directory |
|
|
705
|
+
| `oar` exits with *Copilot CLI process error: spawn gh ENOENT* | `oar` runs `gh copilot` after starting the relay, and stops the relay again when that fails. Install the GitHub CLI, or [run only the relay](#start-the-relay) |
|
|
706
|
+
| Settings refuses the **SDK** engine | Install or upgrade the Copilot CLI, run `copilot` once, and restart the relay (see [engine choice](#github-copilot--engine-choice)) |
|
|
707
|
+
| Messages stuck pending | Only one relay may run per data directory (a second one exits on the singleton lock), and only one process may own port `3333`. `oar doctor` shows the config and database in use |
|
|
708
|
+
| Wrong or old model list | **🤗 Select Models → Refresh** reruns discovery; `/api/model-variants` shows the Copilot catalog's `source` and `refreshedAt` |
|
|
709
|
+
| Clarification card not progressing | Answer via the web card; the turn resumes once the question status becomes `answered` |
|
|
642
710
|
| File links fail | Verify auth token/cookie and that paths are inside allowed workspace/drive roots |
|
|
643
711
|
| Claude missing from New Chat | Enable it in **⚙️ Settings → Providers → Claude**; the toggle is off by default |
|
|
644
712
|
| Claude reply says it cannot authenticate | Press **Claude settings** on the failed reply, then **Relogin** (or run `claude` on the relay host), and retry the turn |
|
|
645
713
|
| Grok reply says the CLI was not found | Press **Install Grok CLI** on the failed reply, or install it from **⚙️ Settings → Providers → Grok**; no relay restart is needed afterwards |
|
|
646
714
|
| Grok reply says authentication failed | Press **Sign in to Grok** on the failed reply and confirm the device code in a browser |
|
|
647
|
-
| Claude model list empty or stale | Re-save the Claude settings, or use **Select Models → Refresh** to rerun discovery |
|
|
648
715
|
| Long turn requeued unexpectedly | Raise or clear **Max turn duration** in Settings (0 = no limit) |
|
|
649
|
-
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
- Successful auth sets an HttpOnly cookie for browser sessions.
|
|
656
|
-
- Keep `server/config.json` private and rotate `authToken` if exposed.
|
|
657
|
-
- Set `localhostOnly` to `true` to force local-only access (no LAN/WAN listener).
|
|
658
|
-
- If exposed beyond LAN, use HTTPS and a strong token.
|
|
716
|
+
| Background agent or shell stopped on its own | It reached the **Background task timeout** (default 4 h); raise it or set 0 for no limit |
|
|
717
|
+
| A conversation will not delete | It is still working: a running turn, one waiting on a question card or approval, or live background tasks. Stop it, then delete |
|
|
718
|
+
| A conversation seems wedged | **☠️ Kill session** in the `⋯` menu stops its worker; retry the turn or send a new message |
|
|
719
|
+
| **🌄 Restart web relay** fails with *localhost-only* | The restart endpoint accepts loopback connections only: use it on the relay host or through a tunnel, not over a direct LAN connection |
|
|
720
|
+
| `npm install -g @oar-sh/oar` fails with node-gyp or prebuild errors | `better-sqlite3` has no prebuilt binary for your platform; install a C/C++ build toolchain and Python, then retry |
|
|
721
|
+
| No usage line under a reply | Expected for OpenAI, Claude, Cursor, and Grok turns; only Copilot turns record plan usage |
|
|
659
722
|
|
|
660
723
|
## Repository layout
|
|
661
724
|
|
|
662
725
|
```text
|
|
663
726
|
oar/
|
|
664
|
-
├── .github/extensions/web-relay/ # Copilot CLI extension (worker
|
|
727
|
+
├── .github/extensions/web-relay/ # Copilot CLI extension for the Extension engine (worker link, ask_user bridge, model snapshots)
|
|
728
|
+
├── bin/ # The `oar` command (oar.js) and a Windows cmd shim for checkouts (bat/oar.bat)
|
|
729
|
+
├── docs/ # Preview-server guide, Copilot BYOK notes, SDK feature tracker
|
|
665
730
|
├── server/
|
|
666
731
|
│ ├── claude-worker/ # Claude Agent SDK session worker (turn runner, ask-user bridge, attachments)
|
|
732
|
+
│ ├── copilot-worker/ # Copilot SDK engine session worker (steering, background tasks, questions)
|
|
667
733
|
│ ├── cursor-worker/ # Cursor Agent SDK session worker (turn runner, mode nudges, auth retry)
|
|
668
|
-
│ ├──
|
|
734
|
+
│ ├── grok-worker/ # Grok CLI session worker over ACP
|
|
735
|
+
│ ├── migrations/ # Database migrations
|
|
736
|
+
│ ├── public/ # Browser app (index.html, app/ modules, PWA shell)
|
|
737
|
+
│ ├── repositories/ # SQLite data access
|
|
669
738
|
│ ├── routes/ # Express route registration
|
|
670
|
-
│ ├── services/ # Relay services (workers,
|
|
671
|
-
│
|
|
672
|
-
├──
|
|
673
|
-
|
|
739
|
+
│ ├── services/ # Relay services (workers, usage, images, tunnels, previews, updates)
|
|
740
|
+
│ ├── tools/ # Diagnostic tools (OpenAI BYOK capture proxy)
|
|
741
|
+
│ ├── server.js # Entry point: supervisor plus relay runtime
|
|
742
|
+
│ └── server-runtime.mjs # Express + Socket.IO relay server
|
|
743
|
+
├── shared/ # Code shared by the server, the extension, and the workers
|
|
744
|
+
│ └── worker-runtime/ # Common worker plumbing (relay API client, heartbeat, worker link)
|
|
745
|
+
├── tests/ # Playwright end-to-end suite and its isolated relay harness
|
|
746
|
+
├── CHANGELOG.md
|
|
747
|
+
├── DEVELOPING.md # Development workflows, tests, relay internals
|
|
674
748
|
└── README.md
|
|
675
749
|
```
|
|
676
|
-
|
|
677
|
-
## Extra screenshots
|
|
678
|
-
|
|
679
|
-
More views from the same app experience:
|
|
680
|
-
|
|
681
|
-
<div align="center">
|
|
682
|
-
<table width="100%" cellspacing="0" cellpadding="0">
|
|
683
|
-
<tr>
|
|
684
|
-
<td rowspan="2" width="50%" align="center" valign="middle">
|
|
685
|
-
<a href="docs/screenshots/mobile_session_input.jpg" target="_blank" rel="noopener noreferrer">
|
|
686
|
-
<img src="docs/screenshots/mobile_session_input.jpg" alt="Mobile session composer">
|
|
687
|
-
</a>
|
|
688
|
-
<div align="center"><small>Mobile chat composer in portrait, with the keyboard open and the input ready to send.</small></div>
|
|
689
|
-
</td>
|
|
690
|
-
<td width="50%" align="right" valign="top">
|
|
691
|
-
<a href="docs/screenshots/desktop_pwa_chrome_portrait.png" target="_blank" rel="noopener noreferrer">
|
|
692
|
-
<img src="docs/screenshots/desktop_pwa_chrome_portrait.png" alt="Desktop PWA portrait view">
|
|
693
|
-
</a>
|
|
694
|
-
<div align="center"><small>Desktop PWA running in portrait mode, sized for a narrow browser window.</small></div>
|
|
695
|
-
</td>
|
|
696
|
-
</tr>
|
|
697
|
-
<tr>
|
|
698
|
-
<td width="50%" align="right" valign="bottom">
|
|
699
|
-
<a href="docs/screenshots/desktop_pwa_workspace_file_explorer.png" target="_blank" rel="noopener noreferrer">
|
|
700
|
-
<img src="docs/screenshots/desktop_pwa_workspace_file_explorer.png" alt="Workspace file explorer">
|
|
701
|
-
</a>
|
|
702
|
-
<div align="center"><small>Workspace file explorer with folder browsing and file previews.</small></div>
|
|
703
|
-
</td>
|
|
704
|
-
</tr>
|
|
705
|
-
</table>
|
|
706
|
-
|
|
707
|
-
<table width="50%" cellspacing="0" cellpadding="0">
|
|
708
|
-
<tr>
|
|
709
|
-
<td align="center">
|
|
710
|
-
<a href="docs/screenshots/mobile_file_viewer.jpg" target="_blank" rel="noopener noreferrer">
|
|
711
|
-
<img src="docs/screenshots/mobile_file_viewer.jpg" alt="Mobile file viewer">
|
|
712
|
-
</a>
|
|
713
|
-
<div align="center"><small>Mobile (PWA/Browser) file viewer showing an image preview and download actions.</small></div>
|
|
714
|
-
</td>
|
|
715
|
-
</tr>
|
|
716
|
-
</table>
|
|
717
|
-
</div>
|