| 1 | <!-- English translation of adr/0117-remote-operator-surface-multidevice.md. Canonical Russian: ../../adr/0117-remote-operator-surface-multidevice.md --> |
| 2 | |
| 3 | # ADR 0117: Remote operator surface - multi-device operator (remote control, not mobile IDE) |
| 4 | |
| 5 | **Status:** Proposed |
| 6 | **Date:** 2026-05-16 |
| 7 | **Updated:** 2026-05-16 - remote surface client: **PWA** (canonical choice). |
| 8 | |
| 9 | ## Related ADRs |
| 10 | |
| 11 | | ADR | Role | |
| 12 | |-----|------| |
| 13 | | [0017](0017-multi-window-workspace-and-agent-surfaces.md) | Multi-window on one station (**not** remote) | |
| 14 | | [0108](0108-web-ai-portal-host-object-tools-bridge.md) | Web in MFD → `IdeCommands` (orthogonal) | |
| 15 | | [0035](0035-mfd-embedded-webview-external-llm-and-mcp-boundary.md) | Web Trust Boundary ↔ MCP | |
| 16 | | [0016](0016-agent-client-protocol-external-agent.md) | External ACP Agent | |
| 17 | | [0031](0031-agent-chat-clarification-batches-and-threading.md) | Clarifications Intercom vs PFD | |
| 18 | | [0030](0030-command-ids-hotkeys-and-ui-registry-layers.md) | `command_id`, confirmations | |
| 19 | | [0008](0008-mcp-contracts-and-testable-infrastructure.md) | `IdeCommands`, MCP | |
| 20 | | [0043](0043-mcp-transport-recovery-human-agent-parity.md) | Transport restoration | |
| 21 | | [0099](0099-ide-databus-typed-events-and-projections.md) | IDE Events → Projections | |
| 22 | | [0045](0045-agent-chat-persistence-event-log-and-projections.md) | Chat/event history | |
| 23 | |
| 24 | ## Summary |
| 25 | |
| 26 | - **Remote operator:** PWA remote control from a phone/other PC, Operator Gateway. |
| 27 | - Not mobile IDE; complements multi-window ([0017](0017-multi-window-workspace-and-agent-surfaces.md)). |
| 28 | - Communication with the web-portal bridge ([0108](0108-web-ai-portal-host-object-tools-bridge.md)). |
| 29 | |
| 30 | **Outside the repo:** vision **agent-forge** (worst case scenario, API + browser) - personal canon `agent-notes`. |
| 31 | |
| 32 | --- |
| 33 | |
| 34 | ## 1. Context |
| 35 | |
| 36 | **Powers:** |
| 37 | |
| 38 | - The operator (person) works not only at **one** desktop: a second PC, tablet, phone - for **monitoring**, **replying to the agent**, **confirmations**, changing the WORK/HUMAN mode, without transferring the full IDE. |
| 39 | - **[ADR 0017](0017-multi-window-workspace-and-agent-surfaces.md)** solves **multi-monitor on one station** (`MainWindow`, `MfdHostWindow`, `presentation` presets). This is **not** a “left the office - continued from the phone” session. |
| 40 | - **[ADR 0108](0108-web-ai-portal-host-object-tools-bridge.md)** gives **built-in** web in MFD with a bridge to IDE for **external** web model. This is not an **operator console** from another device and is not a replacement for native Intercom. |
| 41 | - Line **Friction / environment-first:** convenience is measured by **worst path** (weak network, browser only, first time). If it is impossible to **see the status** and **make a decision** there, and heavy task closure is possible only behind the desktop, this is acceptable **only** if the desktop is clearly in the loop; otherwise the operator on the second device is “second class”. |
| 42 | |
| 43 | **Problem:** without an explicit architecture it is easy to fall into (a) **mobile IDE** - unrealistic Roslyn/debugging parity; (b) **naked chat** without connection to the IDE session; (c) **unsecure** open port without pairing. |
| 44 | |
| 45 | --- |
| 46 | |
| 47 | ## 2. Decision (intention) |
| 48 | |
| 49 | ### 2.1. Three circuits (separation of responsibilities) |
| 50 | |
| 51 | | Contour | Where | Destination | |
| 52 | |--------|-----|-----------| |
| 53 | | **Heavy (cockpit)** | CascadeIDE on workstation | editor, Roslyn, build, debugging, full HUD, MCP in progress | |
| 54 | | **Lightweight (remote operator surface)** | **PWA** on another device (phone, tablet, second PC) | session status, Intercom (limited), approve/reject, pause/stop, “agent is waiting” notifications | |
| 55 | | **Machine** | git, MCP remote, CI, Issues | closing a task without UI; agent and automation | |
| 56 | |
| 57 | **Principle:** remote surface **does not** duplicate the cockpit. He **observes and decides**; execution of heavy tasks - on a station with IDE or through existing **MCP / `IdeCommands`** (with a policy on the gateway). |
| 58 | |
| 59 | ### 2.2. Remote operator surface (ROS) - scope v1 |
| 60 | |
| 61 | **Included (target phase 1-2):** |
| 62 | |
| 63 | - Subscription to **snapshot** of session: workspace id, active Intercom topic/thread (briefly), “waiting for person” flag, last N agent messages, IDE health / failed step (without leaking the full workspace tree). |
| 64 | - Operator actions with **explicit confirmation** on the desktop in case of risk: response to chat (follow-up), approve/reject for “human only” zones ([0030](0030-command-ids-hotkeys-and-ui-registry-layers.md), pre-flight — [0042](0042-pre-flight-planned-changes-and-review-before-apply.md) when it appears). |
| 65 | - **Push / pull notifications:** “agent is blocked on a question” (integration with an external channel - Telegram, email - **outside** the ADR core, via an adapter). |
| 66 | - **Pairing** of a remote client with an IDE instance (one-time code, TTL, recall). |
| 67 | |
| 68 | **Not included in v1:** |
| 69 | - Full-fledged code editor in the browser. |
| 70 | - Randomly call all `IdeCommands` from the phone without allowlist. |
| 71 | - Public cloud relay without E2E / without being tied to a pair of devices. |
| 72 | - **Native** companion applications (iOS/Android/desktop) - postponed; if necessary later as a wrapper over the same API gateway. |
| 73 | |
| 74 | ### 2.2.1. Client: PWA (canon) |
| 75 | |
| 76 | **Solution:** remote operator surface is implemented as a **Progressive Web App**, not as separate native applications and not as “just a tab without install”. |
| 77 | |
| 78 | **Why PWA:** |
| 79 | |
| 80 | | Criterion | PWA | |
| 81 | |----------|-----| |
| 82 | | One artifact | phone, tablet, second PC - one UI (HTML/CSS/TS), no store releases | |
| 83 | | Friction / worst way | “opened in browser → Add to Home Screen” without installing CIDE | |
| 84 | | Transport | the same **Operator Gateway** (SSE/WebSocket + REST); PWA - client only | |
| 85 | | Notifications (Phase 3) | **Web Push** + service worker with explicit consent; fallback - pull/SSE | |
| 86 | | Offline | service worker: UI shell cache + latest snapshot; **commands** when offline - queue or failure (see §7) | |
| 87 | | Security | console origin = origin gateway (loopback or VPN); **do not** mix with [0108](0108-web-ai-portal-host-object-tools-bridge.md) | |
| 88 | |
| 89 | **Delivery:** static PWA is distributed **from the same host** as the Operator Gateway (for example `Features/OperatorGateway/www/` or embedded `wwwroot`), so as not to produce CORS and a second port unnecessarily. After pairing, the user saves the “Cascade Operator” shortcut. |
| 90 | |
| 91 | **Minimum UX v1:** one “session” page (status + feed + response field); mobile-first layout; without complicated cockpit navigation. |
| 92 | |
| 93 | ### 2.3. Transport and border (Operator Gateway) |
| 94 | |
| 95 | Between **CascadeIDE (host)** and **remote client** there is a logical **Operator Gateway** (process or built-in module): |
| 96 | |
| 97 | 1. **Outgoing Events** - subset of [0099](0099-ide-databus-typed-events-and-projections.md) / projections ([0045](0045-agent-chat-persistence-event-log-and-projections.md)): serialized DTO, no secrets and no full paths to `.env` / `ai-keys.toml`. |
| 98 | 2. **Incoming commands** - narrow allowlist (`operator.reply`, `operator.approve`, `operator.reject`, `operator.pause_agent`, ...), mapping to existing IDE loops (Intercom, PFD confirmations), **not** direct arbitrary `ide_execute_command` from the Internet. |
| 99 | 3. **Transport:** preferably **WebSocket or SSE + REST** on `localhost` with **TLS** and reverse proxy only when explicitly configured; default - **loopback** + pairing token. |
| 100 | 4. **Recovery parity** - in spirit [0043](0043-mcp-transport-recovery-human-agent-parity.md): when the remote client breaks, it shows “session unavailable”, does not simulate success. |
| 101 | |
| 102 | **Connection with [0108](0108-web-ai-portal-host-object-tools-bridge.md):** Web AI Portal - **foreign origin** in WebView2; ROS-PWA - **our own** origin gateway, separate allowlist. Do not mix portal and remote control. |
| 103 | |
| 104 | ### 2.4. Difference from multi-window [0017](0017-multi-window-workspace-and-agent-surfaces.md) |
| 105 | |
| 106 | | | ADR 0017 | ADR 0117 | |
| 107 | |---|----------|----------| |
| 108 | | Devices | one PC, N monitors | N devices, 1 “heavy” station | |
| 109 | | UI | Avalonia `TopLevel` | **PWA** (canon) | |
| 110 | | Synchronization | shared process, shared `DataContext` | network/loopback gateway | |
| 111 | | Criterion | cockpit layout | surveillance + solutions outside the cockpit | |
| 112 | |
| 113 | Both can work **simultaneously** (second monitor + phone as a remote control). |
| 114 | |
| 115 | ### 2.5. Friction criterion (negative scenario) |
| 116 | |
| 117 | We consider ROS successful for scenario **S**: |
| 118 | |
| 119 | 1. The operator has moved away from the workstation; The remote control (or PWA) is open on the phone. |
| 120 | 2. The agent in the IDE has requested clarification or confirmation. |
| 121 | 3. The operator **sees** the request within a reasonable TTL and can **answer** or **reject** without installing full CIDE on the phone. |
| 122 | 4. Agent **continues** on desktop; in the audit log there is a combination of `operator_device_id` + action. |
| 123 | |
| 124 | If only “open laptop with IDE” is available for S, ROS **hasn’t** removed the friction for this class of tasks. |
| 125 | |
| 126 | --- |
| 127 | |
| 128 | ## 3. Phases (road map) |
| 129 | |
| 130 | | Phase | Contents | Readiness criterion | |
| 131 | |------|-----------|---------------------| |
| 132 | | **0** | This ADR; list of DTOs and allowlists in a drawing | Borders agreed with 0017/0108/0031 | |
| 133 | | **1** | Read-only: gateway + **PWA** (manifest, service worker shell); snapshot + SSE | PWA on loopback: status and “waiting for operator” | |
| 134 | | **2** | Write + pairing; installable PWA on phone on LAN | Scenario S passes | |
| 135 | | **3** | Web Push / adapters; optional TLS + VPN to gateway | Operator outside LAN with secure channel | |
| 136 | **MVP-zero (outside CIDE code):** async via Issues / org Discussions / Telegram-relay MCP - **doesn't** replace ROS, acceptable as a temporary workaround. |
| 137 | |
| 138 | --- |
| 139 | |
| 140 | ## 4. Security and trust |
| 141 | |
| 142 | - **Secrets** are not transferred to the remote client; `ai-keys.toml` and MCP tokens remain on the IDE station. |
| 143 | - **Pairing:** short-lived code; list of recalled devices; by default gateway listens to **127.0.0.1**. |
| 144 | - **Allowlist of commands** on gateway - separate from [0108](0108-web-ai-portal-host-object-tools-bridge.md) and from the full MCP. |
| 145 | - **Audit:** each remote action is logged with `principal=human`, `channel=remote_operator`, `device_id`. |
| 146 | - **Do not** expose the gateway to the Internet without an explicit org policy (VPN, mTLS). |
| 147 | |
| 148 | --- |
| 149 | |
| 150 | ## 5. Alternatives (rejected or postponed) |
| 151 | |
| 152 | | Alternative | Why not the target path | |
| 153 | |--------------|-----------------------| |
| 154 | | **Full web IDE** | Roslyn/debugging parity; huge friction; duplicates cockpit | |
| 155 | | **RDP/VNC on desktop only** | Works, but not mobile-friendly; does not reduce friction scenario S | |
| 156 | | **Public MCP endpoint** without layers | Risk of workspace leakage; see backlog remote MCP in agent-notes | |
| 157 | | **Single chatbot without IDE connection** | There is no session truth; hallucination “I did” | |
| 158 | | **Native companion (v1)** | Two support circuits; PWA closes scenario S; native - if proven necessary | |
| 159 | |
| 160 | --- |
| 161 | |
| 162 | ## 6. Consequences |
| 163 | |
| 164 | - A new **feature slice** will appear (for example `Features/OperatorGateway/`): host service, DTO, allowlist, **PWA statics** (`manifest.webmanifest`, service worker, UI). |
| 165 | - Requires **contract tests** snapshot for wire-DTO ([0008](0008-mcp-contracts-and-testable-infrastructure.md), [0052](0052-agent-contract-cli-and-snapshot-tests.md)). |
| 166 | - **Intercom / 0031 / 0042:** approve remote must converge with the same state machines as PFD/MFD on the desktop - not a second parallel “source of truth”. |
| 167 | - Operator documentation: separate page in handbook/kb-public **after** phase 1 (not in this ADR). |
| 168 | |
| 169 | --- |
| 170 | |
| 171 | ## 7. Open questions |
| 172 | |
| 173 | 1. **Built-in gateway** vs separate process `cascade-operator-gateway` (easier to update, harder to deploy)? |
| 174 | 2. **One workspace - several remote clients** (family of devices) or one active remote control? |
| 175 | 3. **Communication with ACP [0016](0016-agent-client-protocol-external-agent.md):** Does the remote surface control only the built-in IDE agent or also the external ACP? |
| 176 | 4. **Offline:** command queue on the client vs hard failure? |
| 177 | 5. Intersection with **[0034](0034-pilot-incapacitation-emergency-mode-and-presence-sensing.md)** (emergency / presence) - is it a general notification channel or a separate one? |
| 178 | 6. **Web Push:** VAPID and delivery via gateway vs only external adapter (Telegram) in phase 2? |
| 179 | |
| 180 | --- |
| 181 | |
| 182 | ## 8. Implementation status |
| 183 | |
| 184 | | Region | State | |
| 185 | |---------|-----------| |
| 186 | | ADR/borders | this document | |
| 187 | | Operator Gateway in code | no | |
| 188 | | PWA (manifest + SW + UI) | no | |
| 189 | | Pairing | no | |
| 190 | | DataBus/Intercom integration | no | |
| 191 | |
| 192 | When the code appears, update §8 and the ADR status to **Accepted · Implemented** (by [status-lifecycle.md](status-lifecycle.md)). |