hop2 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/INTEGRATION.md CHANGED
@@ -1,92 +1,84 @@
1
- # Hop + Termshare Integration Plan
1
+ # Hop + Hay Architecture
2
2
 
3
- This branch integrates [termshare](https://github.com/jzthree/termshare) to replace ttyd/tmux.
3
+ This document describes the current Hop terminal architecture.
4
4
 
5
- ## Benefits
5
+ The historical `ttyd`/`tmux` backend has been removed from the active runtime.
6
+ Hop now uses Hay, a local terminal-sharing stack built on WebSocket plus
7
+ `node-pty`, with browser, CLI, and MCP clients all operating on the same
8
+ session model.
6
9
 
7
- - **No ttyd/tmux dependencies** - Pure Node.js solution
8
- - **Built-in presence** - See who's connected, typing indicators
9
- - **Control modes** - Collaborative or locked single-controller
10
- - **No injection hacks** - Direct control over web UI
11
- - **Mobile-ready** - Responsive design with iOS-style keyboard built-in
12
- - **CLI client** - Connect from real terminals too
10
+ ## Current Stack
13
11
 
14
- ## Architecture Change
15
-
16
- ### Before (ttyd)
17
12
  ```
18
- Browser → Hop Auth Proxy → ttyd (per session) → tmux → shell
19
- ↓
20
- HTML/WebSocket injection for mobile keyboard
13
+ Browser / Hay CLI / Hop MCP
14
+ |
15
+ v
16
+ Hop daemon
17
+ |
18
+ v
19
+ local hay-host process
20
+ |
21
+ v
22
+ Hay room manager
23
+ |
24
+ v
25
+ node-pty
26
+ |
27
+ v
28
+ shell
21
29
  ```
22
30
 
23
- ### After (termshare)
24
- ```
25
- Browser → Hop Auth Proxy → Termshare WebSocket → node-pty → shell
26
- ↓
27
- Native termshare web UI (mobile keyboard built-in)
28
- ```
29
-
30
- ## Implementation Steps
31
-
32
- ### 1. Add Dependencies
33
- - [x] `node-pty-prebuilt-multiarch` - PTY handling
34
- - [x] `ws` - WebSocket server
35
- - [x] Copy termshare room/pty logic into hop (`termshare.js`)
36
-
37
- ### 2. Remove ttyd/tmux Code
38
- - [x] Remove `spawn('ttyd', [...])` in `getOrCreateSession()`
39
- - [ ] Remove `HOP_TMUX_CONF`, `HOP_TMUX_WRAPPER` generation (still present but unused)
40
- - [ ] Remove `listTmuxSessions()`, `waitForTmuxSessionReady()` (still present but unused)
41
- - [ ] Remove ttyd dependency check (still present but unused)
42
- - [x] Remove keyboard HTML injection code (now using termshare's built-in)
43
- - [x] Remove `getIOSKeyboardInjection()` WebSocket patching (still present but unused)
44
-
45
- ### 3. Add Termshare Integration
46
- - [x] Port `Room` and `RoomManager` classes from termshare
47
- - [x] Port `createPty` function
48
- - [x] Create termshare WebSocket handler at `/ws` endpoint
49
- - [x] Map hop sessions to termshare rooms
50
-
51
- ### 4. Update Session Management
52
- ```javascript
53
- // Before: activeSessions[name] = { ttyd: Process, port: number }
54
- // After: activeSessions[name] = { room: Room }
55
- ```
56
- ✅ Completed
57
-
58
- ### 5. Serve Termshare Web UI
59
- - [x] Build termshare web client (with mobile keyboard)
60
- - [x] Serve static files from hop (`termshare-web/` directory)
61
- - [x] Inject room parameter for session routing
62
-
63
- ### 6. Keep Hop Features
64
- - [x] Authentication (TOTP + cookies)
65
- - [x] Cloudflare tunneling
66
- - [x] Session URL routing (`/s/<name>/`)
67
- - [x] Multi-user support
68
-
69
- ## Key Files Changed
70
-
71
- - `hop` - Main server, updated `getOrCreateSession()` and WebSocket handler
72
- - `termshare.js` - New module with Room, RoomManager, createPty
73
- - `termshare-web/` - Built termshare web client with mobile keyboard
74
-
75
- ## Testing
76
-
77
- 1. Start hop: `node hop`
78
- 2. Open browser to tunnel URL
79
- 3. Verify:
80
- - [ ] Login with TOTP works
81
- - [ ] Terminal session works
82
- - [ ] Multiple clients see same session
83
- - [ ] Presence indicators show
84
- - [ ] Control modes work
85
- - [ ] Mobile UI works without injection
86
-
87
- ## Migration Notes
88
-
89
- - Sessions are not persistent (by design) - shell exits when all clients disconnect
90
- - tmux scrollback replaced by termshare's output buffer (200KB)
91
- - iOS keyboard injection replaced by termshare's built-in mobile keyboard
92
- - The old ttyd/tmux code paths are still present but no longer used; can be removed in a cleanup pass
31
+ Key properties:
32
+
33
+ - No `ttyd` dependency
34
+ - No `tmux` dependency
35
+ - Shared session model across browser, CLI, and MCP
36
+ - Session-aware audit logging and history isolation
37
+ - Native mobile UI and virtual keyboard support
38
+ - Local host process survives hop daemon restarts
39
+
40
+ ## Main Components
41
+
42
+ - `hop`
43
+ - user-facing CLI and daemon
44
+ - auth, Cloudflare tunnel management, session metadata, workspace handling
45
+ - serves the built Hay web client from `hay-web/`
46
+ - `scripts/hay-host.js`
47
+ - persistent local host process for active Hay rooms
48
+ - isolates PTY/runtime ownership from the daemon lifecycle
49
+ - `hay/apps/server`
50
+ - room manager, PTY lifecycle, websocket protocol handling
51
+ - `hay/apps/web`
52
+ - browser terminal client
53
+ - `hay/apps/cli`
54
+ - local terminal client used by Hop for direct attach flows
55
+ - `mcp/hop-mcp.js`
56
+ - MCP server exposing Hop terminal/session primitives
57
+
58
+ ## Session Model
59
+
60
+ - Terminal sessions are identified by stable Hop session names.
61
+ - MCP attaches to daemon-scoped terminal handles, but can recover across daemon
62
+ restart by reattaching through the stable session identity.
63
+ - Active PTYs live in Hay rooms managed by the host process.
64
+ - Browser users, local CLI attaches, and agents all talk to the same underlying
65
+ room runtime.
66
+
67
+ ## Build Outputs
68
+
69
+ - `hay/apps/web/dist/` is the source web build output.
70
+ - `hay-web/` is the synced bundle served by `hop`.
71
+ - `npm run build` from the repo root rebuilds Hay and syncs `hay-web/`.
72
+
73
+ ## Operational Notes
74
+
75
+ - Hop always uses the external Hay host runtime.
76
+ - `HOP_TERMINAL_BACKEND` is accepted only for compatibility and ignored.
77
+ - Session history and audit logging are per-session and workspace-aware.
78
+ - Alternate-screen/TUI applications are captured with TUI-aware audit behavior.
79
+
80
+ ## Legacy Notes
81
+
82
+ The earlier `ttyd`/`tmux` approach is now only historical context.
83
+ If you see older references in commit history or archived notes, treat them as
84
+ obsolete unless the current code still uses them explicitly.
@@ -12,19 +12,26 @@ Ensure concurrent agent sessions stay isolated, time-bounded, and recoverable.
12
12
  ## Execution Loop
13
13
 
14
14
  1. **Create session** — one `hop_create_terminal` per agent.
15
- 2. **Validate prompt** — call `hop_wait_terminal(until_prompt=true)` to confirm the shell is ready before sending any command.
16
- 3. **Send command** — write via `hop_write_terminal`; never blind-write into mid-execution output.
17
- 4. **Await result** — use `until_regex` or `until_prompt` with `max_wait_ms` to capture output and detect completion.
18
- 5. **Assert state** — verify exit code / expected output before proceeding.
19
- 6. **Repeat** steps 2–5 for each command in the plan.
15
+ 2. **Validate prompt** — use `hop_wait_terminal(until_prompt=true, start_from="latest")` before the first command.
16
+ 3. **Preferred turn helper** — use `hopx_agent_turn` or `hopx_send_and_wait` for normal multi-turn work.
17
+ 4. **Manual fallback** — drop to `hop_write_terminal` + `hop_wait_terminal` only when you need custom wait conditions or lower-level control.
18
+ 5. **Assert state** — verify expected output before continuing.
19
+ 6. **Repeat** the loop; never write into a terminal that is still busy.
20
+
21
+ Recommended defaults:
22
+
23
+ - `capture="readable_raw"`
24
+ - `start_from="latest"` for prompt waits, `start_from="cursor"` for deltas
25
+ - explicit `max_wait_ms`
26
+ - explicit `idle_ms` when using raw wait conditions
20
27
 
21
28
  ## Recovery
22
29
 
23
30
  | Step | Action |
24
31
  |------|--------|
25
- | 1 | Send `ctrl_c` via `hop_send_key` |
26
- | 2 | If unresponsive after 5 s → `hop_close_terminal(killSession=true)` |
27
- | 3 | On orchestrator crash → kill **all** child terminals (kill switch) |
28
- | 4 | Verify zero orphaned processes before declaring shutdown complete |
32
+ | 1 | Send `ctrl_c` via `hop_send_key` or `control="interrupt"` via `hopx_agent_turn` |
33
+ | 2 | If still running, continue waiting or poll an async wait job instead of blind-resending |
34
+ | 3 | If unresponsive after deadline, `hop_close_terminal(killSession=true)` |
35
+ | 4 | On orchestrator crash, close all child terminals and verify there are no orphaned sessions |
29
36
 
30
37
  **Rule:** Never skip escalation. Always tear down — never leave orphaned sessions.
package/README-MCP.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # Hop MCP Server
2
2
 
3
- The Hop MCP server exposes hop’s terminal/session APIs over the Model Context Protocol (MCP), so agents can create terminals, stream output, and send input through Hop.
3
+ The Hop MCP server exposes Hop terminal and session APIs over MCP so agents can
4
+ create terminals, stream output, send input, and recover cleanly across daemon
5
+ restarts.
4
6
 
5
7
  ## Quick Start
6
8
 
7
- 1) Ensure Hop daemon is running locally (creates `~/.hop2/.tunnel-state`).
9
+ 1. Ensure the Hop daemon is running locally.
8
10
 
9
- 2) Auto-configure supported MCP clients:
11
+ 2. Auto-configure supported MCP clients:
10
12
 
11
13
  ```bash
12
14
  hop-mcp-setup
@@ -27,7 +29,7 @@ Supported auto-detected clients:
27
29
  - VS Code / GitHub Copilot (workspace via `.vscode/mcp.json`)
28
30
  - Antigravity
29
31
 
30
- 3) Or configure your MCP client manually:
32
+ 3. Or configure your MCP client manually:
31
33
 
32
34
  ```json
33
35
  {
@@ -39,21 +41,21 @@ Supported auto-detected clients:
39
41
  }
40
42
  ```
41
43
 
42
- 4) Restart your MCP client.
44
+ 4. Restart your MCP client.
43
45
 
44
46
  The server auto-connects to the local Hop daemon via `~/.hop2/.tunnel-state`.
45
47
 
46
- ## One Subagent Terminal (Claude/Codex)
48
+ ## Recommended Flow: One Agent Terminal
47
49
 
48
- Recommended flow for driving one agent CLI safely:
50
+ Recommended flow for driving one Claude/Codex/Gemini terminal safely:
49
51
 
50
- 1) Launch a dedicated terminal:
52
+ 1. Launch a dedicated terminal:
51
53
 
52
54
  ```
53
55
  hop_create_terminal(name="subagent", cwd="/path/to/repo")
54
56
  ```
55
57
 
56
- 2) Start the agent CLI:
58
+ 2. Start the agent CLI:
57
59
 
58
60
  ```
59
61
  hop_write_terminal(data="claude\n")
@@ -61,22 +63,21 @@ hop_write_terminal(data="claude\n")
61
63
  hop_write_terminal(data="codex\n")
62
64
  ```
63
65
 
64
- 3) Wait for a prompt before sending work:
66
+ 3. Wait for a prompt before sending work:
65
67
 
66
68
  ```
67
69
  hop_wait_terminal(until_prompt=true, start_from="latest")
68
70
  ```
69
71
 
70
- 4) Run a multi-turn loop:
71
- - preferred: `hopx_send_and_wait(data="<task>", press_enter=true, capture="readable_raw")`
72
- - helper alternative: `hopx_agent_turn(data="<task>", mode="auto")`
73
- - manual split (if needed):
74
- - send one instruction with `hop_write_terminal(data="<task>\n")`
75
- - wait for output with `hop_wait_terminal(capture="readable_raw")` (defaults to `until_agent_done`)
76
- - continue with cursor-deltas via `hop_read_terminal(start_from="cursor", cursor=...)`
77
- - send the next instruction only after output is captured
72
+ 4. Run a multi-turn loop:
73
+ - Preferred: `hopx_agent_turn(data="<task>", mode="auto")`
74
+ - Good lower-level helper: `hopx_send_and_wait(data="<task>", press_enter=true, capture="readable_raw")`
75
+ - Manual split only when needed:
76
+ - send with `hop_write_terminal(data="<task>\n")`
77
+ - wait with `hop_wait_terminal(capture="readable_raw")`
78
+ - continue via cursor deltas from `hop_read_terminal(start_from="cursor", cursor=...)`
78
79
 
79
- 5) Stop/cleanup:
80
+ 5. Stop or clean up:
80
81
  - interrupt with `hop_send_key(key="ctrl_c")`
81
82
  - close with `hop_close_terminal(killSession=false)`
82
83
 
@@ -143,6 +144,13 @@ Helper tools (`hopx_`): convenience wrappers built on top of core tools.
143
144
  - `terminal_id` is an ephemeral daemon attachment handle; underlying hop `sessionName` is the stable identity.
144
145
  - On daemon restart, terminal-scoped tools auto-reattach once using stored session identity and retry transparently when possible.
145
146
  - `hop_read_terminal` returns a cursor for incremental polling.
147
+ - Default fallback terminal size is `140x40` when no explicit `cols`/`rows` are provided.
148
+ - `hop_create_terminal` and `hop_attach_terminal` do a short output warmup before returning, reducing first-command races.
149
+ - Hop isolates shell history per session and writes per-session NDJSON audit logs under `~/.hop2/workspaces/<workspace>/logs/<session>/audit.ndjson`.
150
+ - Audit logging automatically switches to diff-suppressed `tui_keyframe` snapshots for alternate-screen/TUI apps while still capturing all input.
151
+
152
+ ### `hop_read_terminal`
153
+
146
154
  - `hop_read_terminal` supports `mode: "ui"` for structured terminal snapshots:
147
155
  - `ui.lines`: visible screen lines
148
156
  - `ui.cursor`: cursor position
@@ -166,6 +174,9 @@ Helper tools (`hopx_`): convenience wrappers built on top of core tools.
166
174
  - parses incrementally across chunk boundaries to avoid split ANSI artifacts
167
175
  - reconstructs common inline edits (backspace/cursor rewrites) so text is cleaner for agent parsing
168
176
  - avoids full ANSI noise while retaining interaction structure
177
+
178
+ ### `hop_wait_terminal`
179
+
169
180
  - `hop_send_key` sends normalized named keys (for example `enter`, `esc`, `up`, `ctrl_c`) to improve interactive TUI control without raw escape-string handling.
170
181
  - `hop_wait_terminal` blocks on output conditions without client polling loops:
171
182
  - `start_from` controls where matching begins:
@@ -180,15 +191,24 @@ Helper tools (`hopx_`): convenience wrappers built on top of core tools.
180
191
  - quiet-period detection via `idle_ms`
181
192
  - bounded event capture in `raw` or `readable_raw`
182
193
  - for `capture="readable_raw"`, supports `control_level`, `noise_filter`, `coalesce_ms`, `coalesce_max_chars`, and optional `includeMetaEvents`
194
+
195
+ ### Async wait helpers
196
+
183
197
  - `hop_wait_start` / `hop_wait_poll` provide explicit async wait orchestration for long-running conditions:
184
198
  - start with `hop_wait_start(...)` and poll with `hop_wait_poll(wait_id=..., wait=true)`
185
199
  - use `consume=true` on poll to remove completed jobs
200
+
201
+ ### `hopx_send_and_wait`
202
+
186
203
  - `hopx_send_and_wait` combines input send + optional enter/key + wait, and defaults to cursor-based delta capture from just before send.
187
204
  - helper defaults are token-thrifty for interactive loops: `capture_max_events=60`, `control_level=none`, `noise_filter=balanced`, `coalesce_ms=350` (unless overridden)
188
205
  - for `capture="readable_raw"`, `text_only` defaults to `true`; this condenses wait payloads by returning joined `wait.text`, setting `wait.events=[]`, `wait.eventCount=0`, and preserving prior count in `wait.originalEventCount` (set `text_only=false` to keep full events)
189
206
  - `hop_read_terminal` supports deterministic delta reads:
190
207
  - `start_from`: `beginning` (default), `cursor`, or `latest`
191
208
  - response includes `cursorStart`, `cursorEnd`, and `next_cursor`
209
+
210
+ ### `hopx_agent_turn`
211
+
192
212
  - `hopx_agent_turn` is a helper wrapper (not a core primitive):
193
213
  - default `mode="auto"` chooses `ui` when terminal is in alternate-screen, otherwise `readable_raw`
194
214
  - in `mode="auto"`, if alternate-screen starts during the same turn, it auto-promotes to `ui` for that response
@@ -202,12 +222,11 @@ Helper tools (`hopx_`): convenience wrappers built on top of core tools.
202
222
  - `mode="ui"` defaults wait capture to `capture_max_events=0` (override when you need readable wait-event diagnostics)
203
223
  - when using default `until_agent_done` semantics in `mode="ui"`, hopx applies a short UI busy-guard pass to avoid returning while status lines still indicate active work (`esc to interrupt`, `working`, `waiting for process`, etc.)
204
224
  - `mode="readable_raw"` / `mode="raw"` returns the same wait payload shape as `hopx_send_and_wait`
225
+
226
+ ### Error model
227
+
205
228
  - Tool calls that fail at the Hop API layer now return MCP errors (`isError: true`) with normalized fields:
206
229
  - `ok`
207
230
  - `status`
208
231
  - `endpoint`
209
232
  - `error`
210
- - Hop now isolates shell history per session and writes per-session NDJSON audit logs under `~/.hop2/workspaces/<workspace>/logs/<session>/audit.ndjson` with truncation metadata for large chunks.
211
- - Audit logging auto-switches to diff-suppressed `tui_keyframe` snapshots when alternate-screen apps (vim/htop/agent TUIs) are detected, while still capturing all input events.
212
- - Default fallback terminal size is `140x40` when no explicit `cols`/`rows` are provided.
213
- - `hop_create_terminal` / `hop_attach_terminal` now do a short output warmup before returning, reducing first-command races in interactive shells.