@oar-sh/oar 0.9.10 โ†’ 0.9.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/CHANGELOG.md +440 -1
  2. package/README.md +156 -972
  3. package/package.json +13 -13
  4. package/server/README.md +112 -25
  5. package/server/claude-worker/claude-remote-relay-tool.mjs +2 -0
  6. package/server/claude-worker/claude-sdk-adapter.mjs +57 -4
  7. package/server/claude-worker/claude-session-process.mjs +217 -11
  8. package/server/claude-worker/claude-session-worker.mjs +26 -1
  9. package/server/claude-worker/task-output-discovery.mjs +126 -0
  10. package/server/copilot-worker/copilot-sdk-session-process.mjs +235 -14
  11. package/server/copilot-worker/copilot-sdk-session-worker.mjs +8 -0
  12. package/server/copilot-worker/copilot-shell-output-mirror.mjs +94 -0
  13. package/server/copilot-worker/copilot-task-settle.mjs +96 -0
  14. package/server/db-schema.mjs +68 -2
  15. package/server/grok-worker/acp-client.mjs +2 -1
  16. package/server/grok-worker/acp-host-services.mjs +46 -6
  17. package/server/grok-worker/grok-background-tasks.mjs +241 -0
  18. package/server/grok-worker/grok-sdk-adapter.mjs +3 -1
  19. package/server/grok-worker/grok-session-worker.mjs +33 -1
  20. package/server/grok-worker/grok-turn-runner.mjs +11 -0
  21. package/server/migrations/0013-previews.mjs +130 -0
  22. package/server/migrations/0014-conversation-groups.mjs +89 -0
  23. package/server/migrations/0015-conversation-group-position.mjs +76 -0
  24. package/server/migrations/0016-share-access-log.mjs +110 -0
  25. package/server/migrations/0017-conversation-history-indexes.mjs +89 -0
  26. package/server/public/app/about-ui.js +73 -0
  27. package/server/public/app/about-view.mjs +234 -0
  28. package/server/public/app/action-confirmations.js +31 -14
  29. package/server/public/app/agent-exchange-fold-view.mjs +40 -101
  30. package/server/public/app/api-client.js +264 -16
  31. package/server/public/app/ask-user-view.js +17 -15
  32. package/server/public/app/background-tasks-view.mjs +285 -16
  33. package/server/public/app/bootstrap.js +253 -37
  34. package/server/public/app/chat-actions-menu-state.mjs +20 -10
  35. package/server/public/app/claude-background-command-limit-options.mjs +21 -0
  36. package/server/public/app/context-menu.mjs +90 -6
  37. package/server/public/app/conversation-group-drag.mjs +191 -0
  38. package/server/public/app/conversation-group-order.mjs +265 -0
  39. package/server/public/app/conversation-group-state.mjs +100 -0
  40. package/server/public/app/conversation-list-drag.mjs +449 -0
  41. package/server/public/app/conversation-payload-cache.mjs +357 -0
  42. package/server/public/app/conversation-turn-state.mjs +81 -0
  43. package/server/public/app/conversation-view.js +567 -94
  44. package/server/public/app/conversation-window-retention.mjs +96 -0
  45. package/server/public/app/cwd-picker.js +1 -1
  46. package/server/public/app/hidden-messages-view.mjs +312 -0
  47. package/server/public/app/host-suspend-ui.js +7 -1
  48. package/server/public/app/journal-view.js +1033 -82
  49. package/server/public/app/preview-cards.mjs +117 -31
  50. package/server/public/app/preview-command.mjs +10 -1
  51. package/server/public/app/relay-board-view.js +19 -6
  52. package/server/public/app/relay-prompt-sanitizer.mjs +11 -4
  53. package/server/public/app/relay-restart-error.mjs +16 -0
  54. package/server/public/app/settings-modal.js +150 -10
  55. package/server/public/app/settings-tabs.js +1 -1
  56. package/server/public/app/share-format.mjs +49 -0
  57. package/server/public/app/share-preview-marks.mjs +167 -0
  58. package/server/public/app/share-state.js +109 -0
  59. package/server/public/app/shared-conversations-settings.js +480 -0
  60. package/server/public/app/socket-handlers.js +138 -20
  61. package/server/public/app/store.js +121 -30
  62. package/server/public/app/subagent-row.mjs +3 -3
  63. package/server/public/app/subagent-viewer.mjs +37 -7
  64. package/server/public/app/task-console-render.mjs +237 -0
  65. package/server/public/app/task-console-watch.mjs +42 -0
  66. package/server/public/app/task-console.mjs +630 -0
  67. package/server/public/app/task-output-text.mjs +234 -0
  68. package/server/public/app/thinking-follow.js +0 -4
  69. package/server/public/app/transcript-island.mjs +73 -0
  70. package/server/public/app/transcript-jump-bottom-view.mjs +230 -26
  71. package/server/public/app/transcript-jump-bottom.mjs +170 -22
  72. package/server/public/app/transcript-reading-anchor.mjs +256 -0
  73. package/server/public/app/transcript-reload-freshness.mjs +17 -1
  74. package/server/public/app/transcript-viewport-anchor.mjs +159 -0
  75. package/server/public/index.html +625 -21
  76. package/server/relay-tools.md +3 -1
  77. package/server/relay.mjs +2 -1
  78. package/server/repositories/conversation-group-repository.mjs +444 -0
  79. package/server/repositories/message-repository.mjs +9 -0
  80. package/server/repositories/question-repository.mjs +4 -0
  81. package/server/repositories/session-repository.mjs +12 -37
  82. package/server/repositories/share-repository.mjs +322 -0
  83. package/server/repositories/share-visibility-statements.mjs +10 -0
  84. package/server/routes/about-routes.mjs +15 -0
  85. package/server/routes/cache-routes.mjs +4 -0
  86. package/server/routes/conversation-group-routes.mjs +171 -0
  87. package/server/routes/messages-routes.mjs +433 -68
  88. package/server/routes/preview-routes.mjs +9 -1
  89. package/server/routes/sessions-routes.mjs +901 -253
  90. package/server/server-runtime.mjs +392 -99
  91. package/server/services/about-service.mjs +461 -0
  92. package/server/services/background-task-store.mjs +365 -0
  93. package/server/services/background-work-view.mjs +228 -0
  94. package/server/services/cache-rebuild-service.mjs +30 -0
  95. package/server/services/cli-process-runner.mjs +4 -21
  96. package/server/services/conversation-history-page.mjs +199 -0
  97. package/server/services/delete-archive-service.mjs +4 -1
  98. package/server/services/detached-worker-watch-service.mjs +150 -0
  99. package/server/services/hidden-message-list.mjs +21 -0
  100. package/server/services/host-suspend-command.mjs +2 -1
  101. package/server/services/message-pin-list.mjs +1 -1
  102. package/server/services/preview-health-probe.mjs +8 -1
  103. package/server/services/preview-proxy-server.mjs +8 -4
  104. package/server/services/preview-registry-service.mjs +676 -26
  105. package/server/services/preview-static-handler.mjs +40 -7
  106. package/server/services/preview-store.mjs +274 -0
  107. package/server/services/rate-limit-service.mjs +12 -1
  108. package/server/services/relay-auth.mjs +183 -0
  109. package/server/services/relay-cli-launcher-service.mjs +3 -1
  110. package/server/services/relay-event-service.mjs +17 -0
  111. package/server/services/relay-prompt-sanitizer.mjs +11 -4
  112. package/server/services/remote-relay-dispatcher.mjs +187 -7
  113. package/server/services/request-boundary.mjs +183 -0
  114. package/server/services/session-activity-tracker.mjs +231 -19
  115. package/server/services/session-worker-launch-service.mjs +190 -6
  116. package/server/services/session-worker-process-service.mjs +10 -6
  117. package/server/services/share-access-log-service.mjs +123 -0
  118. package/server/services/share-path-redaction.mjs +424 -0
  119. package/server/services/status-event-service.mjs +7 -3
  120. package/server/services/task-output-rooms.mjs +259 -0
  121. package/server/services/task-output-service.mjs +562 -0
  122. package/server/services/tunnel-worker-path-guard.mjs +24 -41
  123. package/server/services/windows-autostart-service.mjs +6 -1
  124. package/server/services/windows-boot-autostart-service.mjs +207 -23
  125. package/shared/claude-background-command-limit.mjs +83 -0
  126. package/shared/preview-tool-core.mjs +15 -5
  127. package/shared/remote-relay-contract.mjs +35 -3
  128. package/shared/share-path-privacy.mjs +30 -0
  129. package/shared/task-output-text.mjs +737 -0
  130. package/shared/windows-system-tools.mjs +106 -0
  131. package/shared/worker-runtime/background-task-digest.mjs +30 -0
  132. package/shared/worker-runtime/heartbeat.mjs +24 -0
  133. package/shared/worker-runtime/kept-posts.mjs +83 -0
  134. package/shared/worker-runtime/task-output-spool.mjs +181 -0
  135. package/shared/worker-runtime/worker-websocket-link.mjs +47 -15
package/README.md CHANGED
@@ -7,1044 +7,228 @@
7
7
 
8
8
  # OAR โ€” Open Agent Relay
9
9
 
10
- Drive the coding agents on your machine from any browser (phone, tablet, or a second computer) through a self-hosted web relay.
10
+ Drive the coding agents on your machine from any browser (phone, tablet, or a second computer) through a web relay you host yourself.
11
11
 
12
12
  ```bash
13
13
  curl -fsSL oar.sh/install | sh
14
14
  ```
15
15
 
16
- 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).
17
-
18
- OAR relays seven runtimes: **GitHub Copilot**, **OpenAI (BYOK)**, **OpenAI Image (BYOK)**, **Claude (Agent SDK)**, **Claude Cloud**, **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.
19
-
20
- ```text
21
- โ”Œโ”€โ”€ Copilot SDK worker, or Copilot CLI + extension
22
- โ”œโ”€โ”€ OpenAI Copilot runtime, your API key
23
- โ”œโ”€โ”€ OpenAI Image Images API, called by the relay
24
- [Browser] <--WebSocket--> [OAR relay :3333] โ”ผโ”€โ”€ Claude Agent SDK worker, host's Claude login
25
- โ”œโ”€โ”€ Claude Cloud sandbox at Anthropic, host's Claude login
26
- โ”œโ”€โ”€ Cursor Agent SDK worker, your Cursor API key
27
- โ””โ”€โ”€ Grok Grok CLI over ACP, host's Grok login
16
+ On Windows, run `irm oar.sh/install.ps1 | iex` in PowerShell. Other ways to install are under [Install](#install). Landing page: [oar.sh](https://oar.sh); the complete guide: [oar.sh/docs](https://oar.sh/docs/).
17
+
18
+ ## What OAR does
19
+
20
+ Coding agents such as GitHub Copilot, Claude Code, Cursor and Grok usually live in a terminal. OAR puts them into a chat you can open anywhere: start a task at your desk, follow it from the sofa, answer the agent's question from your phone. The agents keep running on your own computer, with your files and your logins; OAR passes the conversation back and forth.
21
+
22
+ ```mermaid
23
+ flowchart LR
24
+ subgraph devices["Your devices"]
25
+ phone["๐Ÿ“ฑ Phone"]
26
+ browser["๐Ÿ’ป Browser"]
27
+ end
28
+ subgraph host["Your computer"]
29
+ relay["OAR relay<br/>chats, queue, history, files"]
30
+ agents["Copilot ยท OpenAI ยท Claude<br/>Cursor ยท Grok"]
31
+ end
32
+ cloud["โ˜๏ธ Claude Cloud<br/>sandbox at Anthropic"]
33
+ phone <--> relay
34
+ browser <--> relay
35
+ relay <--> agents
36
+ relay <--> cloud
28
37
  ```
29
38
 
30
- The relay runs on your hardware, listens on localhost only until you change that, and collects no telemetry (see [Security notes](#security-notes)).
31
-
32
- OAR is under active development, so expect occasional rough edges and some provider SDK features that are missing or incomplete.
39
+ - **Seven runtimes, one chat**: you pick the agent per conversation, and all of them share the same chat, queue, history, file browser, and question cards ([Runtimes](#runtimes)).
40
+ - **Yours**: the relay runs on your hardware, answers only on this machine until you change that, and collects no telemetry ([Security notes](#security-notes)).
41
+ - **Under active development**: expect occasional rough edges, and some provider SDK features that are missing or incomplete.
33
42
 
34
43
  ## In action
35
44
 
36
45
  <table>
37
46
  <tr>
38
47
  <td width="42%" rowspan="4" valign="top">
39
- <a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.10"><img src="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.10" alt="OAR on a phone: a finished agent turn in a conversation, with the composer below"></a>
48
+ <a href="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.12"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-mobile.light.png?v=0.9.12"><img src="https://oar.sh/assets/shots/cfmail-mobile.png?v=0.9.12" alt="OAR on a phone: a finished agent turn in a conversation, with the composer below"></picture></a>
40
49
  <br><sub>On a phone: the same conversations, installable as an app.</sub>
41
50
  </td>
42
51
  <td width="58%" valign="top">
43
- <a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.10"><img src="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.10" alt="Background subagents running in parallel in the task panel, each with live token counts, elapsed time, and its own Stop button"></a>
52
+ <a href="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.12"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-agents.light.png?v=0.9.12"><img src="https://oar.sh/assets/shots/cfmail-agents.png?v=0.9.12" alt="Background subagents running in parallel in the task panel, each with live token counts, elapsed time, and its own Stop button"></picture></a>
44
53
  <br><sub>Agents working in parallel, each with live token counts and its own Stop.</sub>
45
54
  </td>
46
55
  </tr>
47
56
  <tr>
48
57
  <td valign="top">
49
- <a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.10"><img src="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.10" alt="A question card in the middle of a turn: the agent offers several choices and a free-text reply"></a>
58
+ <a href="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.12"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-question.light.png?v=0.9.12"><img src="https://oar.sh/assets/shots/cfmail-question.png?v=0.9.12" alt="A question card in the middle of a turn: the agent offers several choices and a free-text reply"></picture></a>
50
59
  <br><sub>When the agent needs a decision, it asks, and waits for your answer.</sub>
51
60
  </td>
52
61
  </tr>
53
62
  <tr>
54
63
  <td valign="top">
55
- <a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.10"><img src="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.10" alt="An expanded Thoughts block showing the agent's reasoning above its reply"></a>
64
+ <a href="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.12"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pingcf-thoughts.light.png?v=0.9.12"><img src="https://oar.sh/assets/shots/pingcf-thoughts.png?v=0.9.12" alt="An expanded Thoughts block showing the agent's reasoning above its reply"></picture></a>
56
65
  <br><sub>Read the reasoning, not just the answer.</sub>
57
66
  </td>
58
67
  </tr>
59
68
  <tr>
60
69
  <td valign="top">
61
- <a href="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.10"><img src="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.10" alt="A session watching two other sessions: a finished reply arrives as a compact relay-event row, the watching 2 sessions list is open with Unsubscribe, and a request to also watch a session on a paired relay waits on an Allow / Deny card"></a>
70
+ <a href="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.12"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-watching.light.png?v=0.9.12"><img src="https://oar.sh/assets/shots/pagewright-watching.png?v=0.9.12" alt="A session watching two other sessions: a finished reply arrives as a compact relay-event row, the watching 2 sessions list is open with Unsubscribe, and a request to also watch a session on a paired relay waits on an Allow / Deny card"></picture></a>
62
71
  <br><sub>Agents that watch agents: woken by events, approving nothing.</sub>
63
72
  </td>
64
73
  </tr>
65
74
  </table>
66
75
 
67
76
  <a id="quick-start"></a>
77
+ <a id="prerequisites"></a>
78
+ <a id="start-the-relay"></a>
79
+ <a id="the-oar-command"></a>
68
80
 
69
81
  ## Install
70
82
 
71
- ### Prerequisites
72
-
73
- You need Node.js plus whatever the runtimes you actually use need. Nothing else is required.
74
-
75
- | Requirement | Needed for | Notes |
76
- | ----------- | ---------- | ----- |
77
- | Node.js 22.13 or newer | always | The macOS and Linux installer fetches its own copy when yours is missing or too old; the Windows installer only checks. Running the development test suite needs Node 24 (see [DEVELOPING.md](DEVELOPING.md#node-version)) |
78
- | 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 |
79
- | GitHub CLI (`gh`), signed in | `oar copilot`; 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` |
80
- | OpenAI API key | OpenAI and OpenAI Image chats | Entered in **โš™๏ธ Settings**, stored in the relay database |
81
- | 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 |
82
- | The same Claude login, from a claude.ai account with GitHub connected | Claude Cloud chats | The Claude GitHub app must be allowed on each repository a cloud chat works on (see [Claude Cloud](#claude-cloud)) |
83
- | Cursor API key | Cursor chats | Entered in **โš™๏ธ Settings**, stored in the relay database |
84
- | 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 |
85
- | `cloudflared` (optional) | the built-in Cloudflare tunnel | Not included; install it yourself (see [Cloudflare Tunnel](#optional-remote-internet-access-cloudflare-tunnel)) |
86
- | 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 |
87
-
88
- ### macOS and Linux
89
-
90
- ```bash
91
- curl -fsSL oar.sh/install | sh
92
- ```
93
-
94
- The script looks at what the machine has, prints what it is about to do, and asks once. After a yes it installs OAR, writes the config with a fresh auth token, starts the relay, and prints the relay URL with a QR code. Nothing needs root (it refuses to run as root, because OAR installs per user), and everything lands in your home directory:
95
-
96
- | The machine has | What the installer does |
97
- | --------------- | ----------------------- |
98
- | Node.js 22.13 or newer whose global npm folder you can write (nvm, fnm, Homebrew, a user prefix) | `npm install -g @oar-sh/oar`, as you would by hand |
99
- | Node.js 22.13 or newer whose global npm folder needs root (a distribution or NodeSource package) | Installs the package into `~/.oar/npm` with your Node.js and puts an `oar` command into `~/.local/bin`. Your npm setup is not changed |
100
- | No Node.js, or an older one | Downloads the current Node.js 24 from nodejs.org into `~/.oar/runtime/node` (checksum verified, about 30 MB), installs the package into `~/.oar/npm` with it, and puts an `oar` command into `~/.local/bin`. Only OAR uses that Node.js; one of your own stays first on `PATH` |
101
-
102
- When it puts the command into `~/.local/bin` and that folder is not on your `PATH`, it adds one line to your shell's startup file (`~/.bashrc`, `~/.zshrc`, or `~/.profile`) and says so.
103
-
104
- The relay is started as a systemd user service where a systemd user session exists, with lingering enabled, so it is back after a reboot and keeps running while you are logged out. Elsewhere (macOS, containers, WSL without systemd) it is started in the background; after a reboot, `oar start` starts it again.
105
-
106
- `--yes` skips the question, for scripts and CI; without a terminal to ask on, the installer stops and says so. `OAR_VERSION=x.y.z` pins a version, `OAR_CHANNEL=beta` follows the beta channel, `OAR_PORT=n` sets the relay port, `OAR_NODE=private` uses OAR's own Node.js even when yours would do, and `OAR_DRY_RUN=1` only prints the plan:
107
-
108
- ```bash
109
- curl -fsSL oar.sh/install | sh -s -- --yes
110
- curl -fsSL oar.sh/install | OAR_VERSION=0.9.10 OAR_PORT=3400 sh
111
- ```
112
-
113
- The installer needs `curl` or `wget`, and `tar` when it fetches Node.js. It has no Node.js build for musl systems (Alpine) or for CPUs other than x64 and arm64; there, install Node.js yourself first. Releases before 0.9.8 are installed the same way, but their `oar setup` cannot start the relay: see [Start the relay](#start-the-relay).
114
-
115
- Read it before you run it: [oar.sh/install](https://oar.sh/install).
116
-
117
- ### Windows
118
-
119
- ```powershell
120
- irm oar.sh/install.ps1 | iex
121
- ```
122
-
123
- The PowerShell installer (PowerShell 5 or newer) expects Node.js 22.13 or newer to be installed: it checks Node.js, runs `npm install -g @oar-sh/oar`, then `oar setup`. It reads the same `OAR_VERSION`, `OAR_CHANNEL`, and `OAR_DRY_RUN` variables, for example `$env:OAR_VERSION = '0.9.10'` before the command.
124
-
125
- ### npm
126
-
127
- ```bash
128
- npm install -g --allow-scripts=better-sqlite3,koffi @oar-sh/oar
129
- oar setup
130
- ```
83
+ 1. **Install.** Run `curl -fsSL oar.sh/install | sh` (macOS, Linux) or `irm oar.sh/install.ps1 | iex` (Windows PowerShell). On macOS and Linux the installer says what it is about to do and asks once, then installs OAR into your home directory, writes a config with a fresh access token, starts the relay, and prints its address with a QR code. On Windows, run `oar start` afterwards.
84
+ 2. **Open the address** it printed; `oar url` prints it again. On the relay's own machine that is `http://localhost:3333/`. For your phone, switch on LAN access with `oar setup --lan`, or use a [tunnel](#remote-access).
85
+ 3. **Turn on an agent** under **โš™๏ธ Settings โ†’ Providers**, then start a conversation with **+ New Chat**.
131
86
 
132
- npm 12 runs no install script it was not told to allow, and two of OAR's dependencies fetch their native binary in one; without `--allow-scripts` the install succeeds there and the relay then fails to start. npm 10 and 11 accept the flag and need none. The installers and `oar update` pass it themselves.
87
+ You need Node.js 22.13 or newer (the macOS and Linux installer brings its own when yours is missing or older) and an account with the agent you want to use; see [Prerequisites](docs/install.md#prerequisites). There are also installs [with npm](docs/install.md#npm) and [from a git checkout](docs/install.md#from-a-git-checkout).
133
88
 
134
- ### From a git checkout
135
-
136
- ```bash
137
- git clone https://github.com/oar-sh/oar
138
- cd oar
139
- npm install
140
- node bin/oar.js setup # writes server/config.json with a fresh auth token
141
- npm start # the same as: node server/server.js
142
- ```
143
-
144
- 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.
145
-
146
- You can also write `server/config.json` by hand; every key you leave out takes its default (see the [configuration reference](#configuration-reference-serverconfigjson)):
147
-
148
- ```json
149
- {
150
- "authToken": "change-me-to-a-long-random-secret",
151
- "port": 3333,
152
- "localhostOnly": true
153
- }
154
- ```
155
-
156
- Development workflows, tests, and relay internals are in [DEVELOPING.md](DEVELOPING.md).
157
-
158
- ### Start the relay
159
-
160
- The macOS and Linux installer starts the relay itself. After a manual install, `oar setup` creates the config and prints the relay URL, but it does not start the relay unless you pass `--start`. `oar` alone, in a terminal, opens a menu with everything below.
161
-
162
- - **Any global install:** `oar start` starts the relay in the background (through the systemd user service when one is installed) and prints the relay URL. `oar stop` stops it, `oar restart` restarts it, `oar status` says whether it runs; stop and restart wait for running turns.
163
- - **At every login:** `oar service install`. On Linux that writes and enables a systemd user service and enables lingering, so the relay is back after a reboot and keeps running while you are logged out; the unit carries the `PATH` of the shell that ran the command, plus `~/.local/bin`, so the relay's sessions find the tools you have (run it again after your `PATH` changed). On Windows it switches on the same autostart as **โš™๏ธ Settings โ†’ General โ†’ Autostart (Windows)** โ†’ *At sign-in*; *At system startup*, which runs the relay headless before anyone signs in, is set in the web UI because it needs one admin confirmation. `oar service remove` takes it out again. Not available on macOS yet, nor on a Linux without a systemd user session (containers, WSL without systemd): use `oar start` there.
164
- - **With a Copilot terminal session:** `oar copilot` 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 copilot` started stops again when that session ends. It needs the GitHub CLI.
165
-
166
- To run the relay of a global install in the foreground, start the server the way the systemd unit does:
167
-
168
- ```bash
169
- COPILOT_WEB_RELAY_CONFIG="$HOME/.oar/config.json" \
170
- COPILOT_WEB_RELAY_DATA_DIR="$HOME/.oar/data" \
171
- COPILOT_WEB_RELAY_LOG_DIR="$HOME/.oar/logs" \
172
- node "$(npm root -g)/@oar-sh/oar/server/server.js"
173
- ```
174
-
175
- 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.
176
-
177
- Then open the URL that `oar setup` printed (`oar url` prints it again). With access set to this machine only (the default, `localhostOnly` in the config) that is `http://localhost:3333/` on the relay host; reach it from other devices through a [tunnel](#remote-access), or switch on LAN access with `oar setup --lan`, which also prints a QR code for your phone. The URL carries your token once (`?token=โ€ฆ`); after that, the relay keeps the browser signed in with an HttpOnly cookie.
178
-
179
- ### The `oar` command
180
-
181
- | Command | What it does |
182
- | ------- | ------------ |
183
- | `oar` | In a terminal: a menu with the relay's status (running or stopped, version, URL, port, access, service) and an entry for each command below; arrow keys or digits, Enter, `q`. Without a terminal: the status and the usage |
184
- | `oar start` | Starts the relay in the background, through the systemd user service when one is installed, and prints the relay URL. A relay that is already running is left alone. Global installs only; a git checkout uses `npm start` |
185
- | `oar stop` | Stops the relay: the systemd service through `systemctl --user stop oar`, any other relay through its own API, once no turn is running |
186
- | `oar restart` | Restarts a running relay once no turn is running (the service through systemd, at once), or starts a stopped one |
187
- | `oar status` | Says whether the relay runs, on which port and with which access, and whether the service is installed. Exit code 0 when it runs, 3 when it does not |
188
- | `oar url` | Prints the relay URL. With LAN access it also prints a QR code; with access for this machine only it says how to get phone access instead. When the running relay reports a tunnel address, that address and its QR code follow |
189
- | `oar setup` | Creates or updates the config. In a terminal it asks, showing the current value (Enter keeps it): a new auth token (which signs every device out), access (this machine only, or LAN), and the port. Options answer without a question: `--port <port>`, `--lan` or `--local`, `--new-token`; `--defaults` keeps everything of an existing config and gives a new one access for this machine only and the first free port from 3333 on. A port must be free, unless the install's own running relay holds it. When the token, the port or the access changed while the relay runs, setup restarts it (in a terminal it asks first) and waits until it answers with the new settings. `--start` (global installs, used by the installer) also installs the service where a systemd user session exists and starts the relay. `--migrate-from <dir>` copies the relay state of a pre-rename git checkout at `<dir>` into the OAR state root, once; the source is never changed, and a relay still running from it blocks the copy |
190
- | `oar service install` \| `remove` \| `status` | Starts the relay at every login: a systemd user service on Linux, the sign-in autostart on Windows. See [Start the relay](#start-the-relay) |
191
- | `oar copilot [-- <args>]` | Starts the relay in the background unless one already answers on its port, then runs `gh copilot` in the same shell, passing `<args>` through (`gh copilot -- <args>`). `--port <port>` finds or starts the relay on that port for this run without changing the config; a config this command creates saves it. `--install-extension` writes or refreshes the user-global Copilot CLI extension wrapper in `~/.copilot/extensions/web-relay/`, then exits; every run does that unless `--no-install-extension` is given. The same options without the word `copilot` (`oar -- <args>`, `oar --install-extension`) still run this command |
192
- | `oar doctor` | Prints the version, Node.js, install mode, state root, config path, port, whether an auth token is set, the tunnel mode and whether the `cloudflared` it needs is installed, the database path and size, and which provider CLIs (`gh`, `claude`, `grok`) answer. It changes nothing |
193
- | `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` (the npm of the Node.js that runs `oar`, into the folder the package is installed in), 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 |
194
- | `oar --version`, `oar help` | Prints the version, or the usage. An unknown command or option prints the usage and exits with code 2 |
195
-
196
- ### Where OAR keeps its files
197
-
198
- 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. The macOS and Linux installer may add two folders there that are not state: `npm/` (the package, when npm's own global folder was not writable) and `runtime/node/` (OAR's own Node.js, when the machine had none new enough). A git checkout uses `server/config.json`, `server/data/`, `server/uploads/`, and `server/logs/` instead.
199
-
200
- `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.
201
-
202
- ### Updating
203
-
204
- - **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.
205
- - **Git checkout:** `git pull`, `npm install`, then restart the relay (**๐ŸŒ„ Restart web relay** in the conversation's `โ‹ฏ` menu).
89
+ `oar` alone opens a menu. `oar start`, `stop`, `restart` and `status` control the relay, `oar service install` starts it at every login, `oar doctor` shows what it uses, and `oar update` installs a new release (as does **โš™๏ธ Settings โ†’ General โ†’ Check for updates**). All the details are in [Installing and running OAR](docs/install.md): [the `oar` command](docs/install.md#the-oar-command), [where OAR keeps its files](docs/install.md#where-oar-keeps-its-files), and [updating](docs/install.md#updating).
206
90
 
207
91
  <a id="providers"></a>
92
+ <a id="claude-cloud"></a>
93
+ <a id="claude-agent-sdk"></a>
208
94
 
209
95
  ## Runtimes
210
96
 
211
- 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 / โ˜ Claude Cloud / 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.
212
-
213
- 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. Claude Cloud is the exception: switching it off only takes it out of **New Chat**, and its conversations stay as they are.
214
-
215
- | Runtime | Enable via | Auth | Notes |
216
- | ------- | ---------- | ---- | ----- |
217
- | **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 |
218
- | **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 |
219
- | **OpenAI Image (BYOK)** | โš™๏ธ Settings โ†’ Providers โ†’ OpenAI | the same key | The relay calls the OpenAI Images API itself; a chat whose replies are images |
220
- | **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 |
221
- | **Claude Cloud** | โš™๏ธ Settings โ†’ Providers โ†’ Claude Cloud (off by default) | the relay host's Claude login (a claude.ai account), read from the Claude CLI | Claude Code in a sandbox at Anthropic, on a clone of a GitHub repository. Results come back as pushed branches; billed to that Claude account. Uses API endpoints that are not a documented public API (see [Claude Cloud](#claude-cloud)) |
222
- | **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 |
223
- | **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 |
224
-
225
- ### GitHub Copilot โ€” engine choice
226
-
227
- 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.
228
-
229
- | Engine | What runs | Notes |
230
- | ------ | --------- | ----- |
231
- | **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 |
232
- | **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 |
233
-
234
- 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:
235
-
236
- - *"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.
237
- - *"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.
238
-
239
- A refusal never changes the stored engine; the select snaps back to the engine the relay actually runs.
240
-
241
- 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.
242
-
243
- ### OpenAI (BYOK) and OpenAI Image
244
-
245
- 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`.
246
-
247
- - **OpenAI (BYOK)** conversations run the Copilot runtime against that endpoint with your key, on whichever Copilot engine is selected.
248
- - **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.
249
-
250
- ### Claude (Agent SDK)
251
-
252
- 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.
253
-
254
- **Commit attribution.** Commits an agent makes in a Claude session end with `Co-authored-by: Open Agent Relay (<model>) <no-reply@oar.sh>` โ€” the model's name in the parentheses, e.g. `Claude Fable 5.1` โ€” and pull request bodies with `๐Ÿค– Generated with [Open Agent Relay](https://oar.sh)`. The same tab has the choice: **OAR** (default), **vanilla** (Claude Code's own `Co-Authored-By: Claude โ€ฆ` lines) or **off** (no attribution). A repo folder can override it for all of its Claude sessions from the **๐Ÿง ** context modal. A change reaches a running session with its next message (a switch back to vanilla restarts the session's CLI between turns). [Claude Cloud](#claude-cloud) chats follow the same setting. Other providers are not affected: their tools have no such setting.
255
-
256
- The same panel manages the account itself. The row at the top names the signed-in account and plan, and:
257
-
258
- - **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.
259
- - **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.
260
-
261
- If Claude has never been logged in on the host, running `claude` there once works too.
262
-
263
- 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.
264
-
265
- What Claude conversations support:
266
-
267
- - 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
268
- - 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
269
- - Image and file attachments (images up to 5 MB are inlined; larger files are passed as paths for Claude to read)
270
- - Question cards (including multi-select), thinking/thought streams, live reply streaming, and nested subagent bubbles
271
- - **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))
272
- - Background tasks that outlive the reply that started them, in the composer's task panel, including **Ultracode** workflow trees (see [Background tasks](#background-tasks))
273
- - Session continuity across worker restarts โ€” the native Agent SDK session id is stored and resumed
274
- - Real context-window metrics, reported after each turn, and a per-conversation auto-compact window (see [Usage and context](#usage-and-context))
275
-
276
- Differences from Copilot conversations:
277
-
278
- - 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
279
- - 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**)
280
- - The browsable **Session** root points at the Agent SDK's project directory rather than a Copilot session-state folder
281
-
282
- ### Claude Cloud
283
-
284
- **Experimental**, and off by default.
285
-
286
- A Claude Cloud chat runs Claude Code in a sandbox at Anthropic instead of on the relay host. The sandbox works on its own clone of a GitHub repository: it sees what is pushed to GitHub and nothing on the host, and its results come back as branches the agent pushes. The chat is billed to the Claude account the relay host's Claude CLI is logged in to. In OAR it is a conversation like the others: the reply streams in, questions arrive as cards, and it works from the phone.
287
-
288
- **What it needs.**
289
-
290
- - The Claude CLI on the relay host, logged in with a claude.ai account (**โš™๏ธ Settings โ†’ Providers โ†’ Claude โ†’ Relogin**, or `claude` on the host). The same login serves Claude chats.
291
- - GitHub connected to that Claude account: [claude.ai/connect-github](https://claude.ai/connect-github).
292
- - The Claude GitHub app allowed on every repository a cloud chat should work on (GitHub โ†’ Settings โ†’ Applications โ†’ Claude โ†’ Configure).
293
-
294
- **Enabling.** Turn on **โš™๏ธ Settings โ†’ Providers โ†’ Claude Cloud โ†’ Enable Claude Cloud for New Chat**. It is off by default, and it is refused while the host has no Claude login. The tab then shows:
295
-
296
- - the account the chats are billed to, and where the relay reads the login from and until when it is valid. The login itself is changed on the Claude tab (**Claude login** jumps there);
297
- - **Cloud environment**: the environments of the account. With none saved, the relay takes the first active one and saves it. An account without any gets its default one when you open [claude.ai/code](https://claude.ai/code) once;
298
- - **Default model** for new cloud chats (`claude-sonnet-5-5` until you change it). The list is the Claude provider's models without their `[1m]` variants, plus the default.
299
-
300
- Switching Claude Cloud off takes it out of **New Chat**. Cloud conversations that already exist keep working.
301
-
302
- **Starting a chat.** Choose **+ New Chat โ†’ Provider โ†’ Claude Cloud**. The folder picker stays, but here it only fills in two fields, which you can also pick from a list or type yourself:
303
-
304
- - **Repository**: read from the folder's `origin` remote (or the remote of its upstream branch). With the field focused, OAR lists the repositories the Claude GitHub app can reach for the account (read from Anthropic, kept for a minute; **โ†ป** next to the label reads it again), the ones used in earlier cloud chats on this relay first and marked "used here"; typing filters the list by `owner/name`, arrow keys and Enter pick, Escape closes. `owner/repo` or a GitHub URL, https or ssh, can still be typed or pasted; only GitHub repositories are accepted.
305
- - **Branch**: the folder's current branch; a repository picked from the list brings its default branch. With the field focused, OAR offers the repository's branches as the relay host's git sees them (`git ls-remote`, with the host's own GitHub login; the host's `OAR_CLAUDE_CLOUD_BRANCH_LOOKUP=off` turns this off). When the host cannot read them the field is a plain text field. Leave it empty for the repository's default branch.
306
-
307
- Under the fields OAR warns about what the cloud clone will not have: commits that are not pushed, a branch without an upstream, uncommitted changes. It also says when the folder is no git repository or has no GitHub remote, when the Claude GitHub app has no access to the repository in the field (the cloud would refuse the first message), and when no cloud environment is set. There is no reasoning effort to choose. The cloud session itself is created by the first message.
308
-
309
- **What works.**
310
-
311
- - The reply streams in as the agent writes it, with thoughts, tool activity, subagent bubbles and the steps of the sandbox starting up
312
- - Questions from the agent (`AskUserQuestion`) as question cards, multi-select included; a tool that asks for permission gets an **Allow** / **Deny** card. A card answered on claude.ai closes in OAR
313
- - **Stop** interrupts the cloud turn. If the cloud does not confirm within 30 seconds, the turn ends in OAR with a note that the agent may still be working there
314
- - Image attachments (JPEG, PNG, GIF or WebP, up to 5 MB each), sent inline
315
- - Follow-up messages go into the same cloud session, also after it has been idle for a long time
316
- - A restart of the worker or the relay in the middle of a turn does not send your message twice: the new worker finds the turn in the session's event log and follows it, and a turn that finished in the meantime is answered from the log. A dropped connection to the session is reopened where it broke off
317
- - The **cloud line** above the composer: the repository, the branch, a link to the session on claude.ai, one **โ‡ก branch** link per branch the agent pushed (it opens GitHub's comparison with the branch the chat started from) and what the session has cost so far. The `โ‹ฏ` menu has **โ˜๏ธ Open on claude.ai**
318
- - [Commit attribution](#claude-agent-sdk): commits and pull requests the agent makes in the sandbox end with what the Claude tab's setting says (**OAR**, **vanilla** or **off**), from the first message on, and a change reaches the sandbox with the next message. In OAR mode the `Claude-Session` link to claude.ai is left out too. A chat started from a folder follows that folder's override, and its **๐Ÿง ** modal has the select. The commit's author is still the sandbox's git identity (`Claude`); the setting covers the lines at the end
319
- - A turn the session starts by itself โ€” the agent's closing turn when its background work outlived the hold, or a message you sent on claude.ai โ€” appears in the chat as a continuation reply, with a line saying where it came from; the worker keeps the session's event stream open for that
320
- - A turn the cloud refuses at the Claude usage limit is paused like a local Claude turn (a note with the reset time, the banner, and the work carries on by itself after the reset); the limit warning before it appears in the tool activity
321
- - Deleting or archiving the conversation archives the cloud session at Anthropic; nothing is deleted there
322
- - A **Claude Cloud** card in **Check Usage** with the account's cloud credit and the cost Anthropic reports for this relay's cloud sessions, and the **๐Ÿง ** button for the session's context use (see [Usage and context](#usage-and-context))
323
- - A failure the relay can name says what to do: log in again, connect GitHub, give the Claude GitHub app access to the repository, choose an environment
324
-
325
- **What does not.**
326
-
327
- - No relay modes, no reasoning effort and no context size: the three selectors are hidden. The model is chosen in **New Chat** and can be changed between turns in the composer like a local Claude chat's (the sandbox switches before the next message)
328
- - No mid-turn steering: a message sent during a turn waits for the turn to end
329
- - Nothing from the relay host reaches the sandbox: attachments other than images are refused, there is no working directory to change and no **Session** folder to browse
330
- - No relay tools inside the sandbox: no `preview`, no `remote_relay`, no media embedded by host path
331
- - No background task panel
332
-
333
- **Started by an agent.** An agent in another session can start a cloud chat with the `remote_relay` tool, naming the repository and, if it wants one, the branch (`repo` and `branch`): an agent on a [paired relay](#remote-relays), under the rules for starting any session from there, or an agent on this relay once [agent sessions](#agent-sessions) are switched on. The same checks apply as in **New Chat** (the provider is on, an environment is set, the repository is on GitHub), and a chat without a requested model starts with the tab's default model. It can also read, prompt, wait for and stop a cloud chat you created.
334
-
335
- **What OAR does with the Claude login.** Claude Cloud uses the login the Claude CLI keeps on the relay host (`.credentials.json` in `~/.claude`, or in `CLAUDE_CONFIG_DIR`) to call the Anthropic API endpoints the Claude CLI itself uses for cloud sessions and account usage. **These endpoints are not a documented public API.** Anthropic can change them without notice; cloud chats and the live usage figures then fail with a note until OAR is updated. If you are not comfortable with a program other than the CLI using its login this way, leave the provider off.
336
-
337
- - The relay and its cloud workers only **read** the access token. It is sent as the bearer token to `https://api.anthropic.com` and to no other host. (OAR's own tests point the client at a fake API on the same machine; that setting accepts a loopback address and nothing else.)
338
- - It is held in memory only. It is never written to the database or to a log, never sent to the browser and never put into an error message. Each cloud worker reads the credentials file itself, so nothing is passed along at launch.
339
- - It is never refreshed by OAR, and the refresh token is never used. The CLI keeps its own login fresh; when the token has run out, the relay โ€” and a cloud chat's worker, through the relay โ€” reads the file again and at most once in five minutes has the CLI check its own login (`claude auth status`, which refreshes a login whose refresh token is still good), then retries. A login that is still expired ends the turn with a note to log in again.
340
- - While the switch is off, the relay asks Anthropic for nothing with this login: it lists no environments and reads no account usage. Only a cloud conversation from before still talks to its session, when you write to it, delete it or archive it.
341
- - The provider is off by default. Switching it on is the consent for all of the above.
342
-
343
- `CLAUDE_CODE_OAUTH_TOKEN` in the relay's environment is used instead of the credentials file when it is set. That is the one case in which the relay passes the login on: a cloud worker started under tmux gets the variable through a file only the relay's user can read, which the worker's shell reads once and deletes; workers of other providers are not given it that way (see [Environment variables](#environment-variables)).
344
-
345
- ### Cursor (Agent SDK)
346
-
347
- 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).
348
-
349
- What Cursor conversations support:
350
-
351
- - Per-message model and reasoning effort โ€” effort tiers come from per-model discovery, and `none` means the model's default behavior
352
- - 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
353
- - Live reply streaming, question cards, and **Stop** to abort the running turn
354
- - The browsable **Session** root points at the worker's per-session agent store, created on the session's first turn
355
- - Expired cached agent handles are recreated and retried automatically once โ€” a second auth failure means the API key itself is invalid
356
-
357
- 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.
358
-
359
- ### Grok (CLI ACP)
360
-
361
- 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).
362
-
363
- The same panel manages both the CLI and the account:
364
-
365
- - The **Grok CLI** row installs or updates the CLI itself โ€” see
366
- [Installing a provider CLI from the relay](#installing-a-provider-cli-from-the-relay).
367
- - **Sign in** starts the CLI's device-code login on the relay host and brings it to the browser: the
368
- x.ai authorization link appears inline with a **Copy link** button, and the `XXXX-XXXX` code is
369
- shown next to it so you can check it against what the browser displays. Open the link on any
370
- device, confirm, and the panel flips to signed-in **by itself** โ€” nothing is pasted back through
371
- the relay, because the CLI polls x.ai and finishes on its own. Model discovery re-runs straight
372
- after, so switching accounts needs no relay restart. The flow is pushed over the socket: start it
373
- on one device, finish it on another, and closing the modal does not lose it.
374
- - **Sign out** asks for confirmation first and tells you how many Grok workers are running. Running
375
- sessions keep the previous token until their worker exits.
376
-
377
- The relay holds no Grok secret: the device code is public by design, and the token is written by the
378
- CLI straight into `~/.grok/auth.json`. Running `grok login` on the host still works as before.
379
-
380
- Grok conversations support live reply streaming, thoughts, plan boards, subagent lifecycle chips,
381
- **Stop**, session resume across worker restarts, per-turn context metrics, and the live weekly quota
382
- bar in **Check Usage**. The model is fixed per conversation (ACP has no mid-session switch; the default
383
- is `grok-4.5`), and question cards are not available โ€” the protocol has no ask-user surface.
384
-
385
- ### Installing a provider CLI from the relay
386
-
387
- Grok and Claude both run as CLIs on the relay host, and the failure mode used to be a dead end: a
388
- turn fails with *"Grok CLI was not found on PATH"* and the only fix is a shell on the host โ€” the one
389
- thing the relay exists to avoid. The Copilot, Claude, and Grok sub-tabs therefore carry a CLI row at the top:
390
-
391
- ```
392
- Grok CLI not installed [ Install ]
393
- Grok CLI 1.0.13 ยท ~/.grok/bin/grok ยท native ยท up to date [ Update ]
394
- ```
97
+ The runtime (the *provider* in the UI) is chosen in **New Chat** and stays fixed for that conversation.
395
98
 
396
- - **Install** asks for confirmation first, naming the exact command and the directory it writes into.
397
- The commands are the vendors' own one-liners (`curl -fsSL https://x.ai/cli/install.sh | bash`,
398
- `curl -fsSL https://claude.ai/install.sh | bash`, or their PowerShell equivalents on Windows) and
399
- they are hardcoded in the relay โ€” nothing you type reaches a shell. They run as the relay user,
400
- into your home directory, never under sudo.
401
- - The output streams into the panel live, and the log survives closing the modal or watching from
402
- another device. One install at a time, relay-wide.
403
- - When it finishes, the relay resolves the binary, wires it into the environment it launches workers
404
- with, and remembers it across restarts โ€” **no relay restart is needed**. Sessions already running
405
- keep the binary they started with.
406
- - **Update** runs the CLI's own updater (`grok update`, `claude update`), not the install script again.
407
- - If your Claude was installed with npm into a folder the relay user cannot write, `claude doctor`
408
- says so and the button becomes **Switch to native installer** โ€” Anthropic's own recommended fix.
409
- The npm copy stays where it is; the native build takes precedence on PATH.
410
- - The **Copilot** row is read-only: that CLI is managed with npm on the host, so there is nothing
411
- the relay could usefully run.
99
+ | Runtime | What it is | What you need |
100
+ | ------- | ---------- | ------------- |
101
+ | [**GitHub Copilot**](docs/providers.md#github-copilot--engine-choice) (default) | GitHub's coding agent, with every model your Copilot plan offers | The Copilot CLI, signed in on the relay host |
102
+ | [**OpenAI (BYOK)**](docs/providers.md#openai-byok-and-openai-image) | Copilot's agent against OpenAI or a compatible endpoint | Your own API key |
103
+ | [**OpenAI Image**](docs/providers.md#openai-byok-and-openai-image) | A chat whose replies are images | The same key |
104
+ | [**Claude**](docs/providers.md#claude-agent-sdk) | Claude Code on your machine | A Claude login, made from **โš™๏ธ Settings** |
105
+ | [**Claude Cloud**](docs/providers.md#claude-cloud) (experimental) | Claude Code in a sandbox at Anthropic, on a GitHub repository | A claude.ai account with GitHub connected |
106
+ | [**Cursor**](docs/providers.md#cursor-agent-sdk) | Cursor's agent | A Cursor API key |
107
+ | [**Grok**](docs/providers.md#grok-cli-acp) | xAI's Grok CLI | A Grok sign-in, made from **โš™๏ธ Settings** |
412
108
 
413
- When a turn fails because a CLI is missing or signed out, the failed reply itself carries the fix as
414
- a button โ€” **Install Grok CLI**, **Sign in to Grok**, **Claude settings** โ€” which opens the right
415
- panel and, for an install, the same confirmation sheet.
109
+ API keys are entered in **โš™๏ธ Settings** and stay on the relay host. The Claude and Grok CLIs can be [installed and signed in to from the browser](docs/providers.md#installing-a-provider-cli-from-the-relay), so no shell on the host is needed. Each runtime discovers its own [models](docs/providers.md#models). The full picture of each runtime is in [Providers](docs/providers.md).
416
110
 
417
- ### Models
111
+ <a id="highlights"></a>
112
+ <a id="using-the-web-ui"></a>
418
113
 
419
- 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:
114
+ ## What you can do
420
115
 
421
- - **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`.
422
- - **OpenAI (BYOK)** models are discovered from `/v1/models` when the key is saved or re-enabled.
423
- - **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.
424
- - **Claude Cloud** has no discovery of its own: its picker offers the Claude provider's models under their plain ids, plus the tab's default model.
425
- - **Cursor** models (and their per-model reasoning-effort tiers) are discovered when the API key is saved or the provider is re-enabled.
426
- - **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.
427
-
428
- 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. Claude Cloud has no tab there.
429
-
430
- ## Highlights
431
-
432
- ### New in 0.9.10
433
-
434
- - **Watching sessions**: an agent subscribes to other sessions, on its own relay or on a paired one, and is woken by what happens there: a question waiting for an answer, an error, a reply, a background task that finished or failed, a session gone quiet, nothing running any more. Each wake-up is a short automated message in its conversation, shown as a compact row. Subscriptions only notify; they approve nothing. The first subscribe of a conversation asks you on a card, and the header shows **watching N sessions** with an Unsubscribe per subscription. See [Watching sessions](#watching-sessions).
435
- - **Notes between agents fold away**: a prompt another agent sent a session and the session's reply show as one row that opens to both.
436
- - **A sub-agent's whole record**: its title in the task panel, or its row under the finished reply, opens a viewer with its thoughts, every tool call with input and result, and its reply, live while it runs. **Keep sub-agent tool output** under Settings โ†’ General decides how long tool inputs and results are kept.
437
- - **What the plans have left**: `remote_relay`'s `usage` action tells an agent what each provider's plan has left on its own relay or a paired one, before it starts work that costs; `list_sessions` and `read_session` say whether a session is running a turn, only background work, or nothing.
438
- - **Folders in the file browser**: **๐Ÿ“+ New folder**, and a right-click (a long press on a phone) on a file or folder to rename or delete it.
439
- - **Back to the latest message**: a โŒ„ button above the composer, with a count of the rows that arrived since, jumps back to the end, and the transcript follows new rows again.
440
- - **Fewer surprises**: archiving a session whose process is still active asks first, Settings opens with no conversation open (so a fresh install can turn on a provider), a queued relay restart no longer waits forever on a busy relay, and waking the host from sleep no longer restarts the running turns.
441
-
442
- ### New in 0.9.7
443
-
444
- - **Claude Cloud (experimental, off by default)**: a chat that runs Claude Code in a sandbox at Anthropic, on its own clone of a GitHub repository. Pick the repository and branch from a list, follow the turn live, answer its questions, stop it, change the model between turns; the results come back as pushed branches. See [Claude Cloud](#claude-cloud) for what it needs and what it does with the Claude login.
445
- - **Archive, and a menu on every conversation**: archive a conversation instead of deleting it (๐Ÿ—„ shows the archived ones), and right-click a row, or long-press it on a phone, to open, rename, stop, kill, archive or delete it.
446
- - **Agent sessions**: an agent can start other sessions on the relay it runs on, wait for them and read their results, once you switch it on under Settings โ†’ Relays.
447
- - **Commits say Open Agent Relay**: commits and pull requests of Claude sessions carry `Co-authored-by: Open Agent Relay (<model>)`; choose OAR, Claude Code's own lines or none, per relay and per folder.
448
- - **Check Usage opens at once**: the last reading shows immediately and only the open tab is read live.
449
- - **Fixes**: two prices in one paragraph are no longer shown as a formula, a reply that names an error code is no longer stored as a failed turn, and on Windows the relay no longer stalls or fails to start workers while it reads the process list.
450
-
451
- ### New in 0.9.6
452
-
453
- - **Claude pauses at the usage limit**: a turn that runs into the subscription's usage limit is paused instead of failed. A banner above the composer says when it carries on, with **Resume now** and **Cancel**; after the reset the relay sends a message of its own that quotes what was refused. The pause survives a relay restart, and a reset more than six hours away waits for you. A second banner warns when the usage passes 90 % of a limit.
454
- - **A quiet command no longer fails its turn** (Copilot, Cursor, Grok): how long a turn may stay silent depends on what the agent is doing: 2 minutes with nothing in flight, 5 minutes during a model request, 30 minutes while a tool runs.
455
- - **Copilot turns that fail, fail well**: the failure shows at once, what the agent had written stays above the note, Stop works on a stuck turn, a finished reply survives a relay restart, and the commands of a runtime that died are stopped. A message you send while a command runs waits for the command instead of cutting it short.
456
- - **Agents on paired relays can answer each other**: a relay you mentioned stays in reach for the whole session, and an agent that was asked from another relay can answer, ask back or report later. See [Remote relays](#remote-relays).
457
- - **Less scrolling in a running turn**: subagents are folded to a one-line header that says what they are doing, and a plan board sits inside the reply it belongs to; its choice also sets the session's mode.
458
- - **Failure notes that help**: no more advice to restart the relay, and a Claude account problem (billing, sign-in, access) says what to check. The relay keeps its console in `relay-console.log`.
459
-
460
- ### New in 0.9.5
461
-
462
- - **A misplaced Claude reply no longer blocks the conversation**: when a reply is published as a *background continuation* instead of under your message, the message closes after about 45 seconds of silence with a small note naming that reply, and the next message runs normally. It used to hold every later message for up to 5 minutes.
463
- - **Effort for remote sessions**: an agent can set the reasoning effort of a session it starts or prompts on another relay; a new session takes the calling session's effort when the model there supports it. See [Remote relays](#remote-relays).
464
- - **Waiting on another relay is steadier**: the wait no longer stops at an interim answer while the remote agent goes on in a turn of its own, for example while its subagents work.
465
- - **Worker logs on Windows**: session workers now write the same `worker-<session>.log` as on Linux, in the relay's log directory, while the console window keeps showing the output.
466
-
467
- ### New in 0.9.4
468
-
469
- - **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).
470
- - **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.
471
- - **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.
472
- - **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.
473
- - **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.
474
- - **Stop** asks for confirmation before it cancels the running turn.
475
- - **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).
476
-
477
- ### Everything else
478
-
479
- - Remote chat UI for local coding agents, with the runtime chosen per conversation
480
- - Per-message **mode** (`plan`, `ask`, `agent`, `autopilot`), **model**, and **reasoning effort** pickers, backed by live model discovery
481
- - Streaming tool activity, thoughts, and live assistant reply text while a turn runs
482
- - 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
483
- - **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
484
- - Question cards for clarification: one-click choices, multi-select checkmarks, free text, and multi-field structured forms validated against their JSON schema
485
- - Mathematical and scientific notation rendering for TeX/LaTeX equations and chemical formulas, written as `$โ€ฆ$`, `\(โ€ฆ\)`, `$$โ€ฆ$$` or `\[โ€ฆ\]`. A single `$` opens a formula only when a non-space character follows it directly and the next `$` has a non-space character directly before it and no digit directly after it (a digit is allowed after something that looks like TeX, as in `$x^2$3`), so prices such as `$5 and $9` stay text
486
- - **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
487
- - **Transcript breaks**: day separators, a marker where a Claude session auto-compacted its context (shown while the compaction runs), and matching dots beside the scrollbar
488
- - **Plan usage** modal with subscription credits, rate-limit windows, and reset countdowns for Copilot, Claude, Claude Cloud, Cursor, and Grok
489
- - **Image conversations** (OpenAI Image): generate images in chat and iterate on a generated image with **Edit this image**
490
- - 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
491
- - **Screenshot annotations**: mark up an uploaded screenshot with highlighter strokes before or after sending; the original upload is never modified
492
- - **Share** a conversation by read-only link, and hide individual messages from the shared view
493
- - **Pin** messages in a conversation and get back to them from a list that scrolls to each one
494
- - Conversation history in local SQLite, including the Copilot sessions already stored on the host
495
- - `/compact` continues in a fresh conversation seeded with a summary; `/preview` publishes a local dev server or folder on a public preview host
496
- - Workspace and drive browser with file previews, **Git changes** with a diff viewer, and `@file:` / `@folder:` reference tokens
497
- - **Push notifications** for questions, finished or failed turns, plan boards, and a CLI going offline
498
- - Opt-in **self-update** from Settings or `oar update`
499
- - Remote access through a managed **SSH** reverse tunnel or **Cloudflare Tunnel**
500
- - Installable **PWA** with an installed-app fullscreen preference and browser-mode fallbacks
501
-
502
- ## Using the web UI
503
-
504
- ### Starting a conversation
505
-
506
- - 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. For **Claude Cloud** the modal asks for a **Repository** and a **Branch** instead of a reasoning effort, filled in from the chosen folder (see [Claude Cloud](#claude-cloud)).
507
- - Choose the **mode** and **model** per message in the composer.
508
- - 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.
509
-
510
- ### Relay modes
511
-
512
- | Mode | Behavior |
513
- | ----------- | ------------------------------------------------------------ |
514
- | `ask` | Clarification-first behavior before implementation |
515
- | `plan` | Planning response style (no implementation unless requested) |
516
- | `agent` | Interactive coding agent behavior |
517
- | `autopilot` | Action-first behavior; asks only when truly blocking |
518
-
519
- ### While a turn runs
520
-
521
- - 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.
522
- - **Steer or queue.** On Claude, and on Copilot's SDK engine, a message you send during a live turn is pushed into that turn; the send button reads **Steer**. A folded message keeps a compact *merged* marker in the transcript, and one the agent answers separately gets its own reply. While the turn waits on a question card or plan approval, or is compacting, the button reads **Queue** and its tooltip says why: the message waits as a pending bubble (with **Cancel**) and steers in once the card is answered or the compaction ends. On the other runtimes, and on Copilot's Extension engine, messages sent during a turn queue behind it, and the button reads **Queue**.
523
- - **Stop** is on the running reply's bubble, whose header stays pinned at the top while a long reply scrolls; the send button never turns into Stop. Queued messages carry their own **Cancel** until they are picked up. A steered message that a Stop cut off is marked *Stopped with the turn โ€” not answered* and offers **Resend**; the button then reads **Resent** on every device.
524
- - On Copilot, **Stop** ends only the reply: background agents the turn started keep running under their own **Stop** in the task panel.
525
- - The transcript follows new rows while you are at its end. Once you scroll about a screen up, a round **โŒ„** button fades in above the composer, with a badge counting the rows that arrived since; it takes you back to the end and the transcript follows again. From a search result or a pin deep in the history it loads the end first.
526
-
527
- ### Question cards
528
-
529
- - Agents ask clarifying questions through question cards: `ask_user` on Copilot and Cursor, `AskUserQuestion` on Claude and Claude Cloud. 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.
530
- - 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.
531
- - 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.
532
- - Grok has no question cards: ACP has no ask-user surface.
533
-
534
- ### Background tasks
535
-
536
- - The composer's task panel lists background work that outlives the reply that started it: backgrounded commands, subagents, monitors, and workflows on Claude; background agents and detached shells on Copilot's SDK engine. Each row shows its state, the model it runs on, the command it is running right now, its token count, and its own **Stop**. The panel scrolls when it holds more tasks than fit.
537
- - An **Ultracode** workflow folds out into a tree of its phases and agents (state, model, tokens). When it finishes, the summarizing reply keeps a collapsed *Finished background task* card holding the final tree, which survives reloads.
538
- - A background task's result arrives as its own *continuation* reply.
539
- - A sub-agent's title in the panel opens its whole record: its thoughts, every tool call with its full input and its result, its reply and the agents it started itself, in order and live while it runs, with its own **Stop**. The finished reply lists the turn's sub-agents as the same rows (model, type, tokens, time, how they ended), so the record stays one click away; a child agent opens in place and back returns to its parent. Claude records everything; Copilot and Cursor show what they report. A tool result is kept to its first 4 KB and a record to 2000 entries; **Keep sub-agent tool output** in Settings โ†’ General decides how long tool inputs and results are kept.
540
- - **โš™๏ธ Settings โ†’ General โ†’ Background task timeout** (default 4 h; 0 = no limit, up to 10 h) caps how long background tasks alone may keep a Claude or Copilot session running after its reply. When the limit is reached, the tasks are stopped.
541
-
542
- ### Drafts across devices
543
-
544
- 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.
545
-
546
- ### The conversation list
547
-
548
- - **Filter conversationsโ€ฆ** above the list matches titles, ignoring case. It clears with **ร—** or Escape, and while it is active it loads older pages, so it searches every conversation, not just the loaded ones.
549
- - **๐Ÿ”** in the conversation header searches message text across all conversations.
550
- - On startup, the relay imports the Copilot sessions stored on the host (through the installed Copilot runtime) into its database, so they appear in the list with their history. If the runtime is unavailable, the import is reported as failed rather than guessed from the filesystem.
551
- - **Archive** (๐Ÿ—„ on the row, or **Archive conversation** in the `โ‹ฏ` menu) takes a conversation out of the list without deleting anything: its session worker is stopped, and a Claude Cloud chat's session at Anthropic is archived with it. The ๐Ÿ—„ toggle above the list shows the archived conversations; **Unarchive** (๐Ÿ“‚) brings one back, cloud session included. An archived chat that is opened cannot be sent to until it is unarchived; a message that reaches it anyway (an agent's through a paired relay, say) unarchives it. Archiving a conversation whose session process is still active (green in the list, idle or working) asks first, because archiving stops it. A conversation that is still working is not archived โ€” stop it first.
552
- - A **right-click on a row** (a long press on a phone) opens a menu with Open, Edit title, Stop turn, Kill session, Archive or Unarchive, and Delete, so a session can be stopped or killed without opening it.
553
- - Deleting a conversation stops its session worker and removes its CLI session too (Copilot through the runtime, Claude by deleting that session's transcript). A Claude Cloud conversation's session at Anthropic is archived, not deleted. An imported Copilot session the relay never ran is only hidden: its CLI session stays, and nothing is stopped for it. A conversation that is still working โ€” a running turn, one waiting on a question card or approval, or live background tasks โ€” is not deleted, and the sidebar says why so you can stop it first.
554
-
555
- ### Files, git, and previews
556
-
557
- - Use **๐Ÿ“ Browse files** to inspect the workspace (๐Ÿ’ผ), drives (๐Ÿ“€), or the session's own folder (๐Ÿ•ต๏ธ), and to open previews. The **Hidden** and **Heavy** toolbar filters are remembered per browser, and a refresh re-opens the folders you had expanded.
558
- - **๐Ÿ“+ New folder** in the browser's toolbar opens a name field at the top of the folder pane; Enter creates the folder inside the open one and opens it. It works in the repository, on the drives, in the session folder, and in the folder picker of **New chat** and **Change CWD**. A right-click on a folder in the tree or a file in the folder pane (a long press on a phone) offers **Rename** and **Delete**, and on a folder **New folder here**. Delete says what goes, and a folder with content asks for its name to be typed first. Links are never followed or changed; roots (the repository root, `/`, a drive, the session folder) cannot be renamed or deleted, and the session folder is read-only apart from new folders.
559
- - Click the file and folder copy controls to insert `@file:...` / `@folder:...` tokens.
560
- - Workspace browsing follows the selected session's effective working directory. Running sessions keep their learned runtime directory, **๐Ÿ—‚๏ธ Change CWD** in the `โ‹ฏ` menu changes the directory for the next launch, and `cd ...` typed in chat does not retarget the browser. A Claude Cloud chat has no **Change CWD**: it works on a clone at Anthropic.
561
- - Use **๐ŸŒฟ Git changes** in the conversation `โ‹ฏ` menu to review the workspace repository: the header shows the branch with ahead/behind counts and a **โฌ‡ Pull** button, and the list shows every staged, unstaged, and untracked file (deleted files struck through). Clicking a file opens a diff viewer with **Changes** and **Full file** modes; closing it returns to the still-open list.
562
- - Agents can embed images, video, and audio in a reply by absolute path; they render inline.
563
- - 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.
564
- - External links in chat open in a new tab with `noopener`/`noreferrer`; workspace file mentions stay in the in-app preview.
565
-
566
- ### Usage and context
567
-
568
- - 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. The modal opens on the reading it showed last time and reads only the open tab's provider live (its card says "updatingโ€ฆ" until the answer is in); a tab you switch to is read live when its reading is older than a minute, **Refresh** reads the open tab, and the relay keeps the last live answer of every provider so the other cards are never empty. Sources differ per provider:
569
- - **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.
570
- - **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. With **Claude Cloud** switched on, the limits are read live from the account each time the modal opens (kept for a minute), with no turn involved: the card then carries a **Live** badge, a limit that is getting close carries Anthropic's own warning word, and it adds **This week by product** (Claude Code, chats and the other products as shares of the week) and the organisation's **Prepaid credits** when there are any. Limit resets (full and 5-hour) are shown on claude.ai only; the card links there. When the live read fails, the card falls back to the last turn's reading and says why.
571
- - **Claude Cloud** โ€” shown while the provider is on. A meter per dollar credit the account holds, with what is used, what is left and when it expires; **Cloud spend (OAR sessions)**, the cost Anthropic reports per cloud session, summed over the cloud conversations this relay still has, with this conversation's own cost when the modal was opened from a cloud chat; and the latest cloud session report (its cost, and cost and tokens by model). These figures are Anthropic's reported cost, not a billing statement, and sessions started elsewhere are not counted. A credit that is offered but not yet claimed is mentioned with a link to claude.ai. Cloud turns spend the cloud credit first; without a credit they count against the Claude limits above.
572
- - **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.
573
- - **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).
574
- - Per-reply usage lines are recorded only for Copilot turns โ€” OpenAI, Claude, Claude Cloud, Cursor, and Grok turns do not consume Copilot premium requests, and no usage line is attached to them.
575
- - Use the **๐Ÿง ** context button for a per-category breakdown of the conversation's context window (Claude sessions also show the auto-compact window, the thinking controls and the folder's commit attribution there): 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. A Claude Cloud session reports how much of its window is used after each turn, without categories.
576
- - 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 slider is saved when the modal closes. Claude Code reads the window only when a session starts, so a running session restarts once, before the next message, to pick up a new one; while a turn or background work is running the change waits, and the note under the slider says for what. A window below what the conversation already uses compacts it on the next message, which the slider warns about. The modal's headline shows the tokens in use, the compaction window and, when known, the model's own limit.
577
- - The transcript marks day boundaries, and marks the point where a Claude session compacted its context with the tokens before and after. While a compaction runs, the line reads *Compacting contextโ€ฆ* and the turn's bubble says so; it turns into the final line when the compaction ends. Both kinds appear as dots beside the scrollbar for the messages currently loaded.
578
-
579
- ### Sharing
580
-
581
- 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.
582
-
583
- ### Pinned messages
584
-
585
- Each message has a **Pin** button next to **Hide**; a pinned message is marked *๐Ÿ“ Pinned* and offers **Unpin**. Once a conversation has pins, a **๐Ÿ“** button with their count appears in its header. It opens the list of pinned messages in conversation order, each with a short preview. A row closes the list and scrolls to its message, also when that message is far up in the history; **๐Ÿ—‘** in a row unpins it. Pins are kept on the relay and show on every device at once, up to 100 per conversation. They are your own bookmarks: the shared view does not show them, and a conversation continued with `/compact` starts without them.
586
-
587
- ### Notifications and the app
588
-
589
- - **โš™๏ธ 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.
590
- - 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.
591
-
592
- ### The conversation menu
593
-
594
- Besides the entries above, the `โ‹ฏ` menu holds **โœ๏ธ Edit conversation title**, **๐Ÿ—„ Archive conversation** (**๐Ÿ“‚ Unarchive** on an archived chat), **๐Ÿ–ฅ๏ธ Inspect tmux console** (a read-only view of the session's tmux pane), **๐Ÿค— Select Models**, **โ˜๏ธ Open on claude.ai** (Claude Cloud chats, once their session exists), **โš™๏ธ Settings**, **๐ŸŒ„ Restart web relay** (queued until the current turn is idle), **๐Ÿ’ค Suspend host** (Windows; see below), 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**.
595
-
596
- **๐Ÿ’ค Suspend host** puts the machine the relay runs on to sleep, once the work is done. The confirmation lists what is still active. The relay then queues the suspend and fires it after 2 minutes with nothing active: no queued, running or parked turn, no background task of a live worker, and no open GitHub Actions run in the repository of a conversation that was busy since the request (read through `gh`; a repository whose state cannot be read blocks for 15 minutes, then is ignored). With nothing running when you confirm, it fires after a 30-second countdown. While a suspend is queued, every device shows a banner with the blockers or the countdown and a **Cancel** button, and a push notification reports the suspend. A relay restart drops a queued suspend and says so. **Show Suspend host action** in Settings hides the entry per browser.
597
-
598
- ## Settings and configuration reference
599
-
600
- ### Settings (โš™๏ธ in the web UI)
601
-
602
- The modal is organised into six tabs โ€” **General**, **Providers** (with a **Copilot**, **OpenAI**, **Claude**, **Claude Cloud**, **Grok**, and **Cursor** sub-tab), **Relays**, **Previews**, **Notifications**, and **Features** โ€” and reopens on the tab you used last. The `โ‹ฏ` menu opens it with no conversation open too, so a fresh install can turn on a provider before the first chat; until a provider is ready, the empty screen and **New Chat** point to Settings โ†’ Providers. Unless noted as per browser, these settings live in the relay database rather than the config file, and apply to every browser that connects:
603
-
604
- | Tab | Setting | Default | What it does |
605
- | --- | ------- | ------- | ------------ |
606
- | General | Theme, Text scaling | dark theme | Per browser |
607
- | General | Show Suspend host action | on | Per browser: shows **๐Ÿ’ค Suspend host** in the conversation menu |
608
- | General | Max turn duration | `60 min` | Hard cap on how long one turn may run before the relay requeues it (0 = no limit, up to 10 h; see below) |
609
- | General | Background task timeout | `4 h` | How long background tasks alone may keep a Claude or Copilot session running after its reply; then they are stopped (0 = no limit, up to 10 h) |
610
- | General | Keep sub-agent tool output | `90 days` | 7, 14, 30 or 90 days, or always: once a finished sub-agent's record is older, its tool inputs and results are removed (once a day); its thoughts, reply and the one-line summary of each tool call stay |
611
- | General | Autostart (Windows) | Off | *At sign-in* (a visible terminal after you log on) or *At system startup* (headless, no logon needed, one admin confirmation) |
612
- | General | Relay name | `OAR` (the hostname for paired relays) | Label of the installed PWA on every device, and this relay's name for [paired relays](#remote-relays) |
613
- | General | Check for updates automatically | off | Opt-in: the relay asks oar.sh for the latest version about twice a day. **Check for updates** works regardless; `OAR_NO_UPDATE_CHECK=1` blocks both |
614
- | General | Default CWD for new sessions | relay workspace root | Working directory for newly created sessions that have none of their own |
615
- | Providers | Copilot engine | SDK | Extension when this relay cannot run the SDK engine (see [engine choice](#github-copilot--engine-choice)) |
616
- | Providers | OpenAI API key / model / base URL | โ€” / `gpt-4o` / `https://api.openai.com/v1` | Enables the OpenAI and OpenAI Image providers |
617
- | Providers | Claude (Agent SDK) | off | Enables Claude as a New Chat provider and runs model discovery; default model `claude-sonnet-5` |
618
- | 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)) |
619
- | Providers | Claude Cloud | off | Enables Claude Cloud as a New Chat provider, and the live account figures in **Check Usage**; the switch is the consent to use the Claude CLI's login (see [Claude Cloud](#claude-cloud)) |
620
- | Providers | Claude Cloud environment / default model | first active environment of the account / `claude-sonnet-5-5` | The cloud environment new cloud sessions run in, and the model a new cloud chat starts with |
621
- | Providers | Cursor API key / model | โ€” / `composer-2.5` | Enables Cursor; plus monthly plan allowances and the optional dashboard session token |
622
- | Providers | Grok | off | Enables Grok; default model `grok-4.5`; **Sign in** / **Sign out**; optional monthly allowance |
623
- | 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)) |
624
- | Relays | Agent sessions: Agents may start and use sessions on this relay | off | Lets agents on this relay start and use other sessions here with the `remote_relay` tool; the first session an agent starts in a conversation asks for your approval (see [Agent sessions](#agent-sessions)) |
625
- | Relays | Agent sessions: Longest wait per tool call | `10 min` | How long one `remote_relay` call may wait for a reply (2 to 60 minutes), on this relay and on paired ones; adjustable whether or not the switch above is on |
626
- | Relays | Remote relays | โ€” | Pair a relay by pasting its web address; per relay **Agents may** read only, read and prompt, or do everything |
627
- | Previews | Live previews | โ€” | Lists the published previews, each of which you can close |
628
- | Notifications | Push notifications | off | Per device: which events notify, and whether titles and message previews are included |
629
- | 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` |
630
-
631
- ### How a stuck turn is detected
632
-
633
- Two independent guards, both of which exempt a turn that is waiting on an unanswered question card:
634
-
635
- 1. **Inactivity** โ€” a turn is only considered stale after `processingTimeoutMs` with no sign of life from its worker. Worker heartbeats name the message they are working on, so a turn that legitimately runs for an hour of tool calls keeps resetting this window.
636
- 2. **Max turn duration** โ€” an absolute ceiling on elapsed time, set with the Settings slider (0 = no limit, up to 10 hours). This exists purely to catch a worker that has hung while still heartbeating.
637
-
638
- When either trips, the turn is returned to the queue rather than lost.
639
-
640
- ### Configuration reference (`server/config.json`)
641
-
642
- 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.
643
-
644
- | Key | Default | Description |
645
- | -------------------------- | -------------------- | ------------------------------------------------------------------------- |
646
- | `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 |
647
- | `port` | `3333` | HTTP + WebSocket port |
648
- | `localhostOnly` | `true` | Bind only to loopback (`127.0.0.1`) and disable LAN/WAN access |
649
- | `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` |
650
- | `processingTimeoutMs` | `600000` | Inactivity window before a turn is treated as stale (not a cap on turn length) |
651
- | `conversationSessionMode` | `isolated` | Configured strategy (`isolated` / `shared`) recorded on sessions and exposed in status |
652
- | `maxRequeueRetries` | `5` | Queue retry limit for failed processing |
653
- | `remotePath` | `""` | URL base path when reverse-proxied under a subpath; also drives PWA URLs and socket.io path |
654
- | `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 |
655
- | `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 |
656
- | `publicHostnames` | `[]` | Hostnames the relay itself answers on; the preview lane refuses to share one |
657
- | `tunnelMarkerHeaders` | `[]` | Extra edge-injected headers that mark tunnel traffic (see [Session-worker path guard](#session-worker-path-guard)) |
658
- | `sshTunnel.mode` | `disabled` | Tunnel mode (`disabled` or `managed`) |
659
- | `sshTunnel.enabled` | `false` | Legacy alias (`true` => `managed`) |
660
- | `sshTunnel.required` | `false` | Pause dequeue while managed tunnel is disconnected |
661
- | `sshTunnel.remoteBind` | `loopback` | Remote bind mode for SSH `-R` (`loopback` or `public`) |
662
- | `sshTunnel.command` | `ssh` | SSH executable path/command |
663
- | `sshTunnel.user` | โ€” | SSH user |
664
- | `sshTunnel.host` | โ€” | SSH host |
665
- | `sshTunnel.remotePort` | โ€” | Remote forwarded port |
666
- | `sshTunnel.identityFile` | optional | SSH key path (falls back to default agent/key) |
667
- | `cloudflaredTunnel.mode` | `disabled` | Cloudflare tunnel mode (`disabled` or `managed`; `enabled: true` is a legacy alias for `managed`) |
668
- | `cloudflaredTunnel.required` | `false` | Pause dequeue while the managed Cloudflare tunnel is disconnected |
669
- | `cloudflaredTunnel.token` | โ€” | The tunnel token from your own Cloudflare Zero Trust tunnel |
670
- | `cloudflaredTunnel.binary` | `cloudflared` | Path or command name of the `cloudflared` you installed; defaults to `cloudflared` on `PATH` |
671
- | `cloudflaredTunnel.extraArgs` | `[]` | Extra arguments appended to `cloudflared tunnel run` |
672
- | `previews.enabled` | `false` | Publish local dev servers on a separate listener (see [docs/preview-servers.md](docs/preview-servers.md)) |
673
- | `previews.port` | `port + 1` | Loopback port for the preview listener; `0` picks an ephemeral port |
674
- | `previews.bindHost` | `127.0.0.1` | Bind address; non-loopback needs `previews.allowPublicBind` |
675
- | `previews.publicBaseUrl` | โ€” | Public base URL on a hostname **different** from the relay's |
676
- | `previews.allowedTargetHosts` | `[]` | Upstreams allowed beyond loopback (container/VM IPs) |
677
- | `previews.maxLive` | `8` | Maximum simultaneously published previews |
678
- | `pushVapidSubject` | `mailto:copilot-remote@example.com` | Contact URI the relay sends to push services with Web Push requests |
679
- | `sharedPresenceMaxPerConversation` | `200` | Most live viewers tracked per shared conversation |
680
- | `sharedPresenceMaxGlobal` | `5000` | Most live shared-link viewers tracked in total |
681
- | `cliBinaries` | *(written by the relay)* | Provider CLI paths resolved after an install from Settings, restored at startup |
682
- | `restartGracefulTimeoutMs` | `8000` | Graceful restart wait before force fallback |
683
- | `restartShutdownTimeoutMs` | `45000` | Drain timeout while waiting for active queue job completion |
684
- | `restartSpawnTimeoutMs` | `18000` | Max wait for resume/restart phase per attempt |
685
- | `restartRebindTimeoutMs` | `20000` | Max wait for rebind/session-sync completion per attempt |
686
- | `restartMaxAttempts` | `3` | Bounded restart attempts before terminal exhaustion |
687
- | `restartRetryBackoffMs` | `[1000,3000,7000]` | Deterministic retry backoff schedule in milliseconds |
688
-
689
- ### Environment variables
690
-
691
- | Variable | Effect |
692
- | -------- | ------ |
693
- | `COPILOT_WEB_RELAY_CONFIG` | Config file to use |
694
- | `COPILOT_WEB_RELAY_DATA_DIR` | Data directory (database and uploads) |
695
- | `COPILOT_WEB_RELAY_LOG_DIR` | Log directory |
696
- | `OAR_STATE_ROOT` | State root of a global install, instead of `~/.oar` / `%APPDATA%\oar` |
697
- | `OAR_NO_UPDATE_CHECK=1` | Never contact oar.sh, not even for a manual update check |
698
- | `OAR_HOST_SUSPEND_DRY_RUN=1` | **Suspend host** logs the suspend instead of sleeping the machine |
699
- | `OAR_MCP_SERVER=0` | Do not attach OAR's MCP server to Grok and the Copilot extension engine; they get the preview instructions as text again, and no `remote_relay` tool |
700
- | `COPILOT_SDK_PATH` | The Copilot CLI runtime's `copilot-sdk` directory, instead of the newest one in the CLI's package cache |
701
- | `COPILOT_PKG_DIR` | An additional Copilot CLI package cache to search |
702
- | `GH_TOKEN`, `GITHUB_TOKEN` | GitHub token for the Copilot usage card, instead of `gh auth token` |
703
- | `XAI_API_KEY` | Grok authentication instead of the Grok CLI login |
704
- | `CURSOR_SESSION_TOKEN` | Cursor dashboard session token for live plan bars |
705
- | `CLAUDE_CONFIG_DIR` | Claude configuration directory, instead of `~/.claude`; Claude Cloud reads the CLI's login (`.credentials.json`) from it |
706
- | `CLAUDE_CODE_OAUTH_TOKEN` | A Claude login token for Claude Cloud, used instead of the CLI's credentials file, by the relay (settings tab, **Check Usage**, archiving) and by its cloud workers. Under tmux the relay hands it to cloud workers only, through an owner-only file; a worker started without tmux inherits the relay's environment as it is |
707
- | `CLAUDE_CODE_AUTO_COMPACT_WINDOW` | Overrides the Claude auto-compact window |
708
- | `COPILOT_CLOUDFLARED_MODE`, `COPILOT_CLOUDFLARED_TOKEN`, `COPILOT_CLOUDFLARED_BINARY` | Override `cloudflaredTunnel.mode`, `.token`, and `.binary` |
709
- | `COPILOT_WORKSPACE_ROOT_ALLOW_LIST` | Same as `workspaceRootAllowList` |
710
- | `COPILOT_REMOTE_<FLAG>` | Pins a feature flag from the **Features** tab (`1`/`0`, `true`/`false`) |
711
-
712
- Many internal names still say "copilot" (`COPILOT_WEB_RELAY_*`, `copilot.db`) from the project's earlier life; they apply to every runtime.
116
+ <table>
117
+ <tr>
118
+ <td width="62%" valign="top">
119
+ <a href="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.12"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/cfmail-steering.light.png?v=0.9.12"><img src="https://oar.sh/assets/shots/cfmail-steering.png?v=0.9.12" alt="A running Claude turn with a message steered into it mid-turn, the reply streaming with its tool calls, and the Steer button in the composer"></picture></a>
120
+ <br><sub>Send a message while the agent works, and it steers the running turn.</sub>
121
+ </td>
122
+ <td width="38%" valign="top">
123
+ <a href="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.12"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/usage-copilot-compact.light.png?v=0.9.12"><img src="https://oar.sh/assets/shots/usage-copilot-compact.png?v=0.9.12" alt="The GitHub Copilot card in Check Usage: premium requests and plan quota with reset dates, and the cost of the last turn"></picture></a>
124
+ <br><sub>What each plan has left, in one place.</sub>
125
+ </td>
126
+ </tr>
127
+ </table>
713
128
 
714
- ### API overview
129
+ - **Watch a turn live**: the reply, the agent's thoughts and every tool call appear as they happen, and each sub-agent gets a bubble of its own. **Stop** is always one tap away ([While a turn runs](docs/web-ui.md#while-a-turn-runs)).
130
+ - **Steer or queue**: a message sent during a turn goes into it (Claude, Copilot) or waits behind it ([While a turn runs](docs/web-ui.md#while-a-turn-runs)).
131
+ - **Answer questions**: the agent asks on a card with one-click choices, checkmarks or free text, and waits for you ([Question cards](docs/web-ui.md#question-cards)).
132
+ - **Background work**: a task panel lists the commands and sub-agents that outlive a reply, each with its own **Stop** and its full record; a command's console shows its output live, says when it has gone quiet and counts a test run's passes and failures ([Background tasks](docs/web-ui.md#background-tasks)).
133
+ - **Choose how the agent works**: `ask`, `plan`, `agent` or `autopilot`, plus model and reasoning effort, per message ([Relay modes](docs/web-ui.md#relay-modes)).
134
+ - **Files and git**: browse the workspace, preview files, review git changes in a diff viewer, and publish a dev server as a preview link ([Files, git, and previews](docs/web-ui.md#files-git-and-previews), [preview servers](docs/preview-servers.md)).
135
+ - **Usage and context**: plan limits and credits of every provider, and what fills the model's context window ([Usage and context](docs/web-ui.md#usage-and-context)).
136
+ - **Your conversations everywhere**: the history lives on the relay, drafts follow you from device to device, and conversations can be searched, archived, grouped and pinned ([The conversation list](docs/web-ui.md#the-conversation-list), [drafts](docs/web-ui.md#drafts-across-devices), [session groups](docs/web-ui.md#session-groups), [pins](docs/web-ui.md#pinned-messages)).
137
+ - **Share** a conversation as a read-only link, hide single messages and the host's paths from it, and see when it was opened ([Sharing](docs/web-ui.md#sharing)).
138
+ - **On your phone**: install OAR as an app and get push notifications ([Notifications and the app](docs/web-ui.md#notifications-and-the-app)).
715
139
 
716
- 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).
140
+ The full tour is in [Using the web UI](docs/web-ui.md); every setting and config key is in [Settings and configuration](docs/configuration.md). What changed in each release is in the [changelog](CHANGELOG.md), with [0.9.12](CHANGELOG.md#0912--2026-10-18) as the latest.
717
141
 
718
142
  ## Remote access
719
143
 
720
- `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.
721
-
722
- ### Optional remote internet access (SSH tunnel)
723
-
724
- Configure:
725
-
726
- ```json
727
- "sshTunnel": {
728
- "mode": "managed",
729
- "required": false,
730
- "remoteBind": "loopback",
731
- "user": "ubuntu",
732
- "host": "relay.example.com",
733
- "remotePort": 4444,
734
- "identityFile": "~/.ssh/id_rsa"
735
- }
144
+ Out of the box the relay answers only on its own machine. There are three ways to reach it from elsewhere:
145
+
146
+ - **Same network**: `oar setup --lan` opens the relay to your LAN and prints a QR code for your phone.
147
+ - **Cloudflare Tunnel**: no server of your own and no open port. You need a Cloudflare account and `cloudflared` on the relay host ([how](docs/remote-access.md#optional-remote-internet-access-cloudflare-tunnel)).
148
+ - **SSH tunnel**: through a server you already run, behind your own HTTPS proxy ([how](docs/remote-access.md#optional-remote-internet-access-ssh-tunnel)).
149
+
150
+ ```mermaid
151
+ flowchart LR
152
+ phone["๐Ÿ“ฑ Phone"]
153
+ subgraph host["Your computer"]
154
+ relay["OAR relay"]
155
+ cfd["cloudflared"]
156
+ end
157
+ cf["Cloudflare"]
158
+ vps["Your server<br/>HTTPS proxy"]
159
+ phone -- "same network<br/>oar setup --lan" --> relay
160
+ phone -- HTTPS --> cf --> cfd --> relay
161
+ phone -- HTTPS --> vps -- "SSH tunnel" --> relay
736
162
  ```
737
163
 
738
- SSH tunnel exposure is controlled by `sshTunnel.remoteBind`, independently of `localhostOnly`.
739
-
740
- Then reverse proxy on the VPS (example Caddy):
741
-
742
- ```text
743
- relay.example.com {
744
- reverse_proxy localhost:4444
745
- }
164
+ The relay keeps both tunnels running and reconnects them by itself. Beyond your own network, always go through HTTPS. Behind a reverse proxy of your own on another host, tell the relay to trust it ([how](docs/remote-access.md#behind-a-reverse-proxy)). The details are in [Remote access](docs/remote-access.md).
165
+
166
+ <a id="remote-relays"></a>
167
+ <a id="agent-sessions"></a>
168
+ <a id="watching-sessions"></a>
169
+
170
+ ## Agents working together
171
+
172
+ - **Paired relays**: connect the OAR on your workstation with the OAR on a server. An agent on one can then read, prompt and start sessions on the other, once you mention that relay with `@`.
173
+ - **Agent sessions**: an agent hands parts of its task to new sessions on its own relay, waits for them and reads their results. It is off until you switch it on, the first session asks for your approval, and at most 4 run at once.
174
+ - **Watching sessions**: an agent subscribes to other sessions and is woken when a question waits, an error happens, or a reply arrives, instead of polling.
175
+
176
+ ```mermaid
177
+ flowchart LR
178
+ you(("You")) --> A
179
+ subgraph r1["Relay on your workstation"]
180
+ A["Your agent's session"] -- "starts, waits for" --> B["A session it started"]
181
+ end
182
+ subgraph r2["Paired relay on a server"]
183
+ C["A session there"]
184
+ end
185
+ A -- "reads, prompts, waits" --> C
186
+ C -. "question, reply, error" .-> A
746
187
  ```
747
188
 
748
- The relay auto-reconnects tunnel drops with exponential backoff.
749
-
750
- ### Optional remote internet access (Cloudflare Tunnel)
751
-
752
- An alternative to the SSH tunnel that needs no VPS and no inbound port: the relay
753
- supervises Cloudflare's `cloudflared` binary, and Cloudflare carries your hostname down
754
- to `127.0.0.1:3333`.
755
-
756
- OAR does not include `cloudflared`; install it on the relay host first:
757
-
758
- | System | Install |
759
- | ------ | ------- |
760
- | Windows | `winget install --id Cloudflare.cloudflared` |
761
- | macOS | `brew install cloudflared` |
762
- | Linux | Cloudflare's package repository (Debian, Ubuntu, RHEL): [pkg.cloudflare.com](https://pkg.cloudflare.com/) |
763
-
764
- 1. In your own Cloudflare Zero Trust account, create a tunnel and route a public hostname on
765
- your zone to `http://localhost:3333` (or your `port`). That routing lives in Cloudflare, not
766
- on this machine.
767
- 2. Put the tunnel's token into the config and restart the relay (`oar restart`).
768
-
769
- ```json
770
- "cloudflaredTunnel": {
771
- "mode": "managed",
772
- "required": false,
773
- "token": "<tunnel token>",
774
- "binary": "",
775
- "extraArgs": []
776
- }
777
- ```
778
-
779
- Environment overrides: `COPILOT_CLOUDFLARED_MODE`, `COPILOT_CLOUDFLARED_TOKEN`,
780
- `COPILOT_CLOUDFLARED_BINARY`.
781
-
782
- `localhostOnly` stays `true`: `cloudflared` connects outbound and nothing binds publicly.
783
- The relay runs the `cloudflared` named in `cloudflaredTunnel.binary`, or else the one on
784
- its `PATH`. When there is none, the relay starts as usual and the tunnel stays off: the
785
- status dot's tooltip, `oar doctor` and the server log say that `cloudflared` is not
786
- installed and how to install it. The relay looks again every minute and starts the tunnel
787
- once `cloudflared` is there; restart the relay if it was installed into a folder that is
788
- not on the relay's `PATH`. With `required: true` the queue stays paused for as long as
789
- the tunnel is not connected, a missing `cloudflared` included. Connection drops reconnect with jittered exponential backoff,
790
- and repeated fast exits (deleted tunnel or bad token) are reported as `auth-or-config`
791
- instead of hammering Cloudflare.
792
-
793
- The relay status dot turns **amber** while the Cloudflare tunnel is connected, so it is
794
- obvious at a glance that the relay is reachable from the internet rather than only from
795
- this machine. It stays green when no tunnel is configured and grey when the relay itself
796
- is unreachable; a managed tunnel that has dropped, or could not start, keeps the dot green โ€” the relay
797
- still answers locally โ€” and gives the reason in the tooltip.
798
-
799
- ### Session-worker path guard
800
-
801
- Any public tunnel forwards *every* path on the bound hostname to port `3333`, including
802
- the internal session-worker WebSocket endpoints. Requests to those paths that carry an
803
- edge marker header (`cf-ray`, plus anything listed in `tunnelMarkerHeaders`) are rejected
804
- with `403` on both the request and upgrade paths. Local workers connect over `127.0.0.1`
805
- without such a header and are unaffected, and shared conversation links keep working
806
- anonymously over the tunnel.
807
-
808
- Shared links work unchanged through Cloudflare. Do not add a Cloudflare Cache Rule
809
- covering `/api/shared/*` โ€” shared views poll for liveness and an edge-cached response
810
- would pin viewers to a stale snapshot. Likewise, a Cloudflare Access application over the
811
- bound hostname breaks share links unless it bypasses `/shared/*` and `/api/shared/*`.
812
-
813
- ## Remote relays
814
-
815
- Several OAR relays can be paired, for example the one on your workstation and the one on a
816
- server. An agent in any session can then work on the other relay when you ask it to: list and
817
- read its sessions, prompt one, start a new one, wait for the reply, and pass a question the
818
- remote agent asks back to you.
189
+ You stay in charge: per paired relay you decide whether agents may only read, also prompt, or do everything, and in the `ask` and `plan` modes every write asks you first. A subscription only notifies; it approves nothing.
819
190
 
820
- **Pairing.** Open **Settings โ†’ Relays** and paste the other relay's web address (the URL you
821
- open it with in the browser). OAR checks the address with its own token, or with a
822
- `?token=` the pasted link carries, and asks for a token only when that fails. It reads the
823
- other relay's name from its API. With **Also add this relay there** checked, it introduces
824
- itself to the other relay using the address in **Public address**, so the pairing works in both
825
- directions. When the two relays use different tokens, that introduction hands over this relay's
826
- token, so the other relay's agents can then work here. Each side can remove the other on its own,
827
- but removing a relay does not revoke a token it already holds: rotate the token for that. A relay
828
- that is already paired keeps the address and token stored for it when it introduces itself again;
829
- change those in **Settings โ†’ Relays**.
191
+ <p><a href="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.12"><picture><source media="(prefers-color-scheme: light)" srcset="https://oar.sh/assets/shots/pagewright-cross-relay.light.png?v=0.9.12"><img src="https://oar.sh/assets/shots/pagewright-cross-relay.png?v=0.9.12" alt="A session watching two sessions on a paired relay: a reply arrives as a relay-event row, and after a short outage the events the other relay kept arrive as one replayed row" width="720"></picture></a></p>
830
192
 
831
- A relay's name is the **Relay name** under **General** (it also names the installed app); when it
832
- is empty, the machine's hostname is used.
833
-
834
- **Mention a relay to unlock it.** Agents can see which relays are paired, but a relay stays
835
- locked in a conversation until you mention it in one of your messages: type `@` and pick it
836
- from the list, or write its name or its host name. An IP address or `localhost` never counts as a
837
- host name; a relay whose name is itself an address is unlocked only with a leading `@`. The
838
- mention unlocks that relay for the rest of
839
- the conversation, whatever prompts arrive in it later.
840
-
841
- **Agents can answer each other.** When an agent on a paired relay writes to a conversation, that
842
- relay is open to the conversation from then on, within what **Agents may** allows, so the agent
843
- that was asked can answer, ask back or report later. A prompt from an agent never unlocks any
844
- other relay. A prompt that already crossed two relays is not passed on to a third one unless you
845
- unlocked that relay yourself; reading and waiting are never limited. When the agent of a
846
- conversation has sent 30 prompts to agents on other relays without a message from you, the next
847
- one asks you first with a question card; **Allow** starts the count again.
848
-
849
- **What agents can do.** Every provider gets the same `remote_relay` tool (Claude, Cursor and
850
- Copilot directly; Grok and the Copilot extension engine through OAR's MCP server). Sessions get it
851
- while at least one relay is paired or [agent sessions](#agent-sessions) are switched on. Per paired
852
- relay, **Agents may** limits it to *read only*, *read and prompt* (existing sessions) or *full*
853
- (also create sessions, answer questions, stop turns, archive). In the *ask* and *plan* relay
854
- modes, every write action first asks you with a question card. For a session it starts, an agent
855
- can set the provider, model, relay mode and reasoning effort; what it leaves out follows the
856
- session it works in, as far as the other relay offers it. A [Claude Cloud](#claude-cloud) session
857
- is started with `provider: "claude-cloud"`, a `repo` (a GitHub URL or `owner/repo`, for example
858
- `example-org/sample-repo`) and an optional `branch` instead of a folder; without a model it gets
859
- the default model of the other relay's Claude Cloud tab. Claude Cloud must be switched on there.
860
- A cloud session that exists can be read, prompted, waited for and stopped like any other.
861
-
862
- Two read actions help an agent plan: `usage` says what each provider's plan has left on a relay
863
- (Cursor's spend against its limit, Claude's windows and whether a usage limit has paused turns,
864
- Copilot and Grok where they are tracked; never account names, addresses or tokens), and
865
- `list_sessions` / `read_session` give each session's `state` (`running_turn`, `background_only` or
866
- `idle`) with its background agents and shell tasks.
867
-
868
- A `send`, `create_session` or `wait` call waits for the reply for as long as the agent asks, up to
869
- **Longest wait per tool call** of the relay the agent runs on (10 minutes unless you changed it,
870
- see [Agent sessions](#agent-sessions)). When the time is up the agent gets what there is so far
871
- and can call `wait` again.
872
-
873
- When an agent prompts a session on another relay, that relay shows a **โ†— from โ€ฆ** badge with the
874
- sending relay, session and model on the message, and marks sessions an agent created in the
875
- conversation list. The remote agent sees a one-line header saying that another agent is talking
876
- to it. Turns started this way do not send "reply ready" notifications on the remote relay;
877
- question cards still do. **Accept prompts from other relays' agents** in **Settings โ†’ Relays**
878
- switches incoming agent prompts off.
879
-
880
- The calls go through your own relay, which holds the other relay's token: agents never see a
881
- token. Remote addresses must use `https://`; plain `http://` is accepted only for loopback (an
882
- SSH port forward, for example) and private network ranges.
883
-
884
- ### Agent sessions
885
-
886
- The same `remote_relay` tool can also work on the relay the agent itself runs on. An agent can
887
- then hand parts of its task to other sessions: it starts them, waits for them and reads their
888
- replies. Every session it starts is an ordinary conversation in your list. No relay has to be
889
- paired for this.
890
-
891
- **Switching it on.** It is off by default. Turn on **Settings โ†’ Relays โ†’ Agent sessions โ†’
892
- Agents may start and use sessions on this relay**. From then on this relay is the first entry of
893
- the relay list an agent sees, marked as its own; the agent names it by its **Relay name** or as
894
- `this`. It needs no mention, and **Agents may** and **Accept prompts from other relays' agents**
895
- do not apply to it. On this relay an agent can do what *full* allows on a paired one: list and
896
- read every session here, prompt one, start one, wait for it, answer its question, stop its turn
897
- and archive it. Switching the setting off closes this relay to the tool again.
898
-
899
- **The rules.**
900
-
901
- - **Approval once per conversation.** The first time the agent of a conversation wants to start
902
- a session here, a question card (**Agent sessions**) shows the provider, the folder or
903
- repository and the prompt. **Allow** covers every session that conversation's agent starts
904
- later, also after a relay restart; **Deny** refuses that one call. In the *ask* and *plan*
905
- relay modes, every other write action still asks, as it does for a paired relay.
906
- - **At most 4 at work.** Of the sessions one conversation's agent started, 4 may have a turn
907
- queued or running at the same time. A fifth is refused until one of them has finished.
908
- - **One level only.** A session that an agent created, here or from a paired relay, cannot start
909
- sessions on this relay.
910
- - **Not on itself.** An agent cannot prompt, stop, archive, wait for or answer the conversation
911
- it runs in.
912
- - The count of 30 prompts without a message from you (see above) includes prompts to sessions
913
- on this relay.
914
-
915
- **What it can start.** A session with any provider that is enabled on this relay (Copilot,
916
- OpenAI, Claude, Cursor, Grok or Claude Cloud; not an OpenAI Image chat): in a folder
917
- (the relay's default folder when the agent names none), or a [Claude Cloud](#claude-cloud) chat
918
- with `provider: "claude-cloud"`, `repo` and an optional `branch`. A new session knows nothing of
919
- the conversation it was started from. Sessions an agent starts use this relay's accounts and
920
- count against their usage.
921
-
922
- **In the conversation list.** A session an agent on this relay started carries **via agent ยท
923
- โ€œtitleโ€** in the sidebar and under the title in the header, with the title of the conversation
924
- the agent runs in; clicking it opens that conversation while it is still in the list. The
925
- agent's prompts carry the same marker, with its model, as a badge on the message. The relay never
926
- archives these sessions by itself: they stay in the list until you archive or delete them, or the
927
- agent archives one with the tool.
928
-
929
- **Longest wait per tool call.** The slider under the switch sets how long one `send`,
930
- `create_session` or `wait` call may wait for a reply: 10 minutes by default, from 2 to 60 minutes
931
- in steps of one minute. The agent can call `wait` again to keep waiting. The limit also applies
932
- to this relay's agents when they wait for a session on a paired relay, so the slider can be moved
933
- whether or not the switch above it is on. A turn that waits is still subject to **Max turn duration**
934
- under **General** (60 minutes by default), which does not know about the wait.
935
-
936
- ### Watching sessions
937
-
938
- An agent can watch other sessions instead of polling them: with `remote_relay`'s `subscribe` it
939
- asks to be woken when something happens there. It works for every provider, on this relay (with
940
- [agent sessions](#agent-sessions) switched on) and on a paired one.
941
-
942
- **What wakes it.** A question waiting for an answer (with its options), an error (a failed turn,
943
- the usage limit, a worker that died), a reply (with a preview), a background agent or shell task
944
- that finished or failed, a session that went quiet while working (15 minutes by default), and the
945
- moment nothing runs any more in the watched sessions. A subscription names sessions, or `all`
946
- for every session, later ones included; the watcher's own conversation is never watched. It can
947
- filter replies and task results by a regular expression, end after it fired once, and add a
948
- heartbeat while work runs.
949
-
950
- **What it looks like.** OAR writes a short message into the watcher's conversation: a new turn
951
- when the watcher is idle, a steer into the running turn on Claude and Copilot's SDK engine, the
952
- next turn otherwise. The message says it is automated, not from you, and that it approves
953
- nothing; it shows as a compact row, never as one of your messages. Events within 30 seconds come
954
- as one message; past 20 wake-ups an hour the rest comes as a digest every ten minutes, while
955
- questions and errors still come at once.
956
-
957
- **What it may do.** A subscription only notifies. It approves nothing and answers
958
- nothing; what an event asks for is decided as before, by you or by the agent within what
959
- it may do anyway. The first subscribe of a conversation, and any to `all`, asks you on an approval
960
- card naming the sessions (and the relay); `transfer_subscriptions`, which hands every subscription
961
- of an old watcher to a new one, always asks.
962
-
963
- **Where you see it.** The watcher's header shows **watching N sessions**; the chip opens the list
964
- of its subscriptions with an **Unsubscribe** button each. A watched session shows a **watched**
965
- badge.
966
-
967
- **Lifetime.** Subscriptions survive a restart and end on unsubscribe, when the watcher or the
968
- watched session is archived, after firing once (`once`), or, for a paired relay, when the pairing
969
- is removed.
970
-
971
- **On a paired relay** the relay must be unlocked in the conversation and allow at least reading,
972
- and the other relay must accept other relays' agents; nothing is asked there. This relay fetches
973
- the events every few seconds over the pairing (a pairing in one direction is enough), and their
974
- lines start with the other relay's name. If the link drops, the other relay keeps what happens
975
- (up to 500 events per subscription, for 7 days) and delivers it as one row tagged **replayed** once
976
- it answers again, questions and errors first, between "link lost" and "link restored". A relay
977
- that runs an older OAR answers that it needs an update.
193
+ How pairing, permissions and limits work is in [Agents working together](docs/agents-together.md).
978
194
 
979
195
  ## Security notes
980
196
 
981
- - The auth token guards the API and the Socket.IO channel. A browser signs in once, with the token or with a URL carrying `?token=` (such as the one `oar setup` and `oar url` print), and then holds an HttpOnly cookie for 30 days, marked `Secure` when the relay is reached over HTTPS. Treat the token, and that URL, like a password.
982
- - Keep the config file private (the relay keeps it owner-only) and rotate `authToken` if it is exposed: `oar setup --new-token` writes a new one and restarts the relay, which signs every device out.
983
- - `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.
984
- - 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.
985
- - 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.
986
- - Files the relay serves (workspace and drive files, attachments) are never run as pages on its origin: HTML and XML go out as plain text, every file carries `nosniff` and a `Content-Security-Policy` with `sandbox`. An SVG keeps its type so it can be previewed, and the sandbox denies it script and an origin should it be opened as a page. A PDF is the one exception, served without `sandbox` (browsers refuse to show a sandboxed PDF in their built-in viewer, and PDF viewers run no document script) and framable only by the relay's own origin.
987
- - 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.
988
- - 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.
989
- - Claude Cloud, when you switch it on, reads the Claude CLI's login and uses it against Anthropic API endpoints that are not a documented public API. The token is only read and held in memory, and goes to `api.anthropic.com` only (see [Claude Cloud](#claude-cloud)). A cloud agent works in Anthropic's sandbox, not on your host.
990
- - OAR has no telemetry. Its only request to oar.sh is the update check, which is off until you enable it.
991
-
992
- ## Troubleshooting
197
+ - **The access token is the key to everything.** Whoever holds it, or a URL carrying `?token=`, can have your agents run commands on the host. Treat it like a password; `oar setup --new-token` replaces it and signs every device out.
198
+ - **OAR adds reach, not a sandbox.** Agents act with the permissions of the user the relay runs as.
199
+ - **Local by default.** The relay answers only on its own machine until you change that. Beyond your network, use one of the [tunnels](#remote-access).
200
+ - **Public links are public.** Anyone with a shared-conversation or preview link can open it without signing in.
201
+ - **No telemetry.** The only request to oar.sh is the update check, and it is off until you turn it on.
993
202
 
994
- | Symptom | What to check |
995
- | ---------------------------------- | -------------------------------------------------------------------------------- |
996
- | 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 |
997
- | `oar copilot` says the GitHub CLI is missing | It runs `gh copilot` beside the relay. Install the GitHub CLI, or [run only the relay](#start-the-relay) |
998
- | WSL: the browser on Windows shows another relay, or refuses the token | In WSL's default networking a port can be free in the distro while a Windows program listens on it; `localhost:<port>` in a Windows browser then reaches that program. `oar setup` picks a port that is free on both sides, and `oar status`, `oar start` and `oar doctor` warn when Windows holds the relay's port: choose another with `oar setup --port <port>` |
999
- | 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)) |
1000
- | 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 |
1001
- | Wrong or old model list | **๐Ÿค— Select Models โ†’ Refresh** reruns discovery; `/api/model-variants` shows the Copilot catalog's `source` and `refreshedAt` |
1002
- | Clarification card not progressing | Answer via the web card; the turn resumes once the question status becomes `answered` |
1003
- | File links fail | Verify auth token/cookie and that paths are inside allowed workspace/drive roots |
1004
- | Claude missing from New Chat | Enable it in **โš™๏ธ Settings โ†’ Providers โ†’ Claude**; the toggle is off by default |
1005
- | 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 |
1006
- | Claude Cloud missing from New Chat | Enable it in **โš™๏ธ Settings โ†’ Providers โ†’ Claude Cloud**; it is off by default and needs a Claude login on the relay host |
1007
- | Claude Cloud reply says GitHub is not connected, or the Claude GitHub app has no access | Connect GitHub to the Claude account at [claude.ai/connect-github](https://claude.ai/connect-github), or add the repository to the Claude GitHub app's repository access on GitHub; then send the message again |
1008
- | New Chat says *No cloud environment is set* | Choose one in **โš™๏ธ Settings โ†’ Providers โ†’ Claude Cloud**; an account without any gets its default one when you open claude.ai/code once |
1009
- | 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 |
1010
- | Grok reply says authentication failed | Press **Sign in to Grok** on the failed reply and confirm the device code in a browser |
1011
- | Long turn requeued unexpectedly | Raise or clear **Max turn duration** in Settings (0 = no limit) |
1012
- | 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 |
1013
- | 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 |
1014
- | A conversation seems wedged | **โ˜ ๏ธ Kill session** in the `โ‹ฏ` menu stops its worker; retry the turn or send a new message |
1015
- | **๐ŸŒ„ 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 |
1016
- | `npm install -g @oar-sh/oar` fails with `EACCES` | npm's global folder needs root (a distribution or NodeSource Node.js). Do not use `sudo`; run the installer (`curl -fsSL oar.sh/install \| sh`), which installs into `~/.oar/npm` instead |
1017
- | The relay does not start after an install with npm 12 (a `better-sqlite3` error in `server-err.log`) | npm 12 skipped the install scripts of OAR's native dependencies. Reinstall with `npm install -g --allow-scripts=better-sqlite3,koffi @oar-sh/oar` |
1018
- | The Cloudflare tunnel stays off and the status dot's tooltip says `cloudflared is not installed` | OAR runs the `cloudflared` on the relay host and ships none. Install it (see [Cloudflare Tunnel](#optional-remote-internet-access-cloudflare-tunnel)); the tunnel starts within a minute |
1019
- | `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 |
1020
- | No usage line under a reply | Expected for OpenAI, Claude, Claude Cloud, Cursor, and Grok turns; only Copilot turns record plan usage |
203
+ How keys, cookies, served files, paired relays and Claude Cloud's use of the Claude login are handled: [Security notes](docs/security.md).
1021
204
 
1022
- ## Repository layout
205
+ ## Troubleshooting
1023
206
 
1024
- ```text
1025
- oar/
1026
- โ”œโ”€โ”€ .github/extensions/web-relay/ # Copilot CLI extension for the Extension engine (worker link, ask_user bridge, model snapshots)
1027
- โ”œโ”€โ”€ bin/ # The `oar` command (oar.js) and a Windows cmd shim for checkouts (bat/oar.bat)
1028
- โ”œโ”€โ”€ docs/ # Preview-server guide, Copilot BYOK notes, SDK feature tracker
1029
- โ”œโ”€โ”€ server/
1030
- โ”‚ โ”œโ”€โ”€ claude-cloud-worker/ # Claude Cloud session worker (follows a cloud session's event stream; runs no agent itself)
1031
- โ”‚ โ”œโ”€โ”€ claude-worker/ # Claude Agent SDK session worker (turn runner, ask-user bridge, attachments)
1032
- โ”‚ โ”œโ”€โ”€ copilot-worker/ # Copilot SDK engine session worker (steering, background tasks, questions)
1033
- โ”‚ โ”œโ”€โ”€ cursor-worker/ # Cursor Agent SDK session worker (turn runner, mode nudges, auth retry)
1034
- โ”‚ โ”œโ”€โ”€ grok-worker/ # Grok CLI session worker over ACP
1035
- โ”‚ โ”œโ”€โ”€ migrations/ # Database migrations
1036
- โ”‚ โ”œโ”€โ”€ public/ # Browser app (index.html, app/ modules, PWA shell)
1037
- โ”‚ โ”œโ”€โ”€ repositories/ # SQLite data access
1038
- โ”‚ โ”œโ”€โ”€ routes/ # Express route registration
1039
- โ”‚ โ”œโ”€โ”€ services/ # Relay services (workers, usage, images, tunnels, previews, updates)
1040
- โ”‚ โ”œโ”€โ”€ tools/ # Diagnostic tools (OpenAI BYOK capture proxy)
1041
- โ”‚ โ”œโ”€โ”€ server.js # Entry point: supervisor plus relay runtime
1042
- โ”‚ โ””โ”€โ”€ server-runtime.mjs # Express + Socket.IO relay server
1043
- โ”œโ”€โ”€ shared/ # Code shared by the server, the extension, and the workers
1044
- โ”‚ โ”œโ”€โ”€ claude-cloud/ # Claude Cloud API client, credentials reader, repository and branch rules
1045
- โ”‚ โ””โ”€โ”€ worker-runtime/ # Common worker plumbing (relay API client, heartbeat, worker link)
1046
- โ”œโ”€โ”€ tests/ # Playwright end-to-end suite and its isolated relay harness
1047
- โ”œโ”€โ”€ CHANGELOG.md
1048
- โ”œโ”€โ”€ DEVELOPING.md # Development workflows, tests, relay internals
1049
- โ””โ”€โ”€ README.md
1050
- ```
207
+ | Symptom | What to do |
208
+ | ------- | ---------- |
209
+ | A runtime is missing from **New Chat** | Turn it on in **โš™๏ธ Settings โ†’ Providers**; all of them except Copilot are off until you do |
210
+ | A reply says a CLI is missing or signed out | Press the button on the failed reply (**Install Grok CLI**, **Sign in to Grok**, **Claude settings**) |
211
+ | Banner says *CLI is offline* | Normal on an idle relay: send a message, and the banner clears once the conversation's worker connects |
212
+ | A conversation seems wedged | **โ˜ ๏ธ Kill session** in the `โ‹ฏ` menu, then retry or send a new message |
213
+ | `npm install -g @oar-sh/oar` fails with `EACCES` | Do not use `sudo`; run the installer, which installs into your home directory |
214
+
215
+ `oar doctor` prints the version, paths, port and provider CLIs in use. More symptoms are in [Troubleshooting](docs/troubleshooting.md).
216
+
217
+ <a id="settings-and-configuration-reference"></a>
218
+ <a id="repository-layout"></a>
219
+
220
+ ## Documentation
221
+
222
+ | Guide | What is in it |
223
+ | ----- | ------------- |
224
+ | [Installing and running OAR](docs/install.md) | Prerequisites, every install method, starting the relay, the `oar` command, files, updating |
225
+ | [Providers](docs/providers.md) | Each runtime in detail, installing provider CLIs from the relay, models |
226
+ | [Using the web UI](docs/web-ui.md) | Conversations, running turns, question cards, background tasks, files, usage, sharing, the menu |
227
+ | [Settings and configuration](docs/configuration.md) | Settings, `config.json`, environment variables, stuck-turn detection |
228
+ | [Remote access](docs/remote-access.md) | SSH and Cloudflare tunnels, a reverse proxy on another host, the session-worker path guard |
229
+ | [Agents working together](docs/agents-together.md) | Paired relays, agent sessions, watching sessions |
230
+ | [Security notes](docs/security.md) ยท [Troubleshooting](docs/troubleshooting.md) | The full lists |
231
+ | [Preview servers](docs/preview-servers.md) ยท [Copilot CLI with your own key](docs/copilot-cli-byok.md) | Publishing dev servers; BYOK notes |
232
+ | [DEVELOPING.md](DEVELOPING.md) ยท [server/README.md](server/README.md#api-reference) ยท [CHANGELOG.md](CHANGELOG.md) | Development, tests, relay internals and the repository layout; the HTTP API; release notes |
233
+
234
+ The most complete guide, in six languages, is [oar.sh/docs](https://oar.sh/docs/).