M1+M2: gateway core loop + agent transparency
M1 (gateway core loop / text round-trip): - WS server (ws_server.py): bind, hello auth (constant-time), hello.ack, heartbeat, connection registry - pairing.py: token generation, pairing store, QR payload - adapter.py: send() -> message frame; inbound message.send -> MessageEvent -> handle_message - app: Connect screen, GatewayClient (connect + reconnect), ChatScreen send/render, SecureStore (Android/Desktop) - tests/ws_probe.py: probe harness driving a real turn M2 (streaming + reasoning + tools + commentary): - protocol.py: M2 frame types (message.start/update/stop, tool.start/progress/end, commentary) - adapter.py: per-chat turn-state machine; classify outbound into frames; _split_reasoning; tool-line parsing - reasoning in streaming: capture via on_stream_delta hook (kind=reasoning, gated by plugins.stream_reasoning_deltas) with a FIFO barrier, attach to message.stop - app: live streaming bubble, ReasoningBlock (collapse + copy), ToolCard (Everything/Truncated/Nothing), dimmed commentary, typing - docs/14-milestones.md: M1/M2 marked done; reasoning note corrected
This commit is contained in:
1 parent
59acf66c89
commit
218c50d688
21 files changed
+3437
-129
No files matched your search
+28
-18
@@ -7,15 +7,15 @@ has explicit **acceptance criteria**. Work top-to-bottom; don't skip M0/M1.
|
||||
|
||||
## M0 — Toolchain & scaffolding
|
||||
**Goal:** everything builds; the plugin is discoverable; the repo is safe.
|
||||
- [ ] Install JDK 17, Android SDK, set `ANDROID_HOME` (`12-toolchain.md`).
|
||||
- [ ] `cd hermes-agent && uv sync` (hermes venv works).
|
||||
- [ ] Create monorepo scaffold (`02-monorepo.md`): `gateway-plugin/`, `app/`
|
||||
- [X] Install JDK 17, Android SDK, set `ANDROID_HOME` (`12-toolchain.md`).
|
||||
- [X] `cd hermes-agent && uv sync` (hermes venv works).
|
||||
- [X] Create monorepo scaffold (`02-monorepo.md`): `gateway-plugin/`, `app/`
|
||||
(CMP: `shared`, `androidApp`, `desktopApp`), root `.gitignore`
|
||||
(**excludes `hermes-agent/`**), root `README.md`.
|
||||
- [ ] `git init` + a pre-commit/CI guard that fails if `hermes-agent/` is staged.
|
||||
- [ ] CMP project builds empty: `./gradlew :androidApp:assembleDebug`,
|
||||
- [X] `git init` + a pre-commit/CI guard that fails if `hermes-agent/` is staged.
|
||||
- [X] CMP project builds empty: `./gradlew :androidApp:assembleDebug`,
|
||||
`./gradlew :desktopApp:run` (blank window).
|
||||
- [ ] Plugin skeleton: `plugin.yaml` + `adapter.py` with `register(ctx)` + a
|
||||
- [X] Plugin skeleton: `plugin.yaml` + `adapter.py` with `register(ctx)` + a
|
||||
no-op `AndroidAdapter` → `hermes gateway status` lists **android**.
|
||||
- **Demo:** `hermes gateway status` shows `android`; `./gradlew
|
||||
:androidApp:installDebug` installs a blank app on the MIX 2S.
|
||||
@@ -25,31 +25,41 @@ has explicit **acceptance criteria**. Work top-to-bottom; don't skip M0/M1.
|
||||
|
||||
## M1 — Gateway core loop (text round-trip)
|
||||
**Goal:** pair + send a text message + get a (non-streaming) reply.
|
||||
- [ ] WS server (`ws_server.py`): bind, `hello` auth (constant-time),
|
||||
- [X] WS server (`ws_server.py`): bind, `hello` auth (constant-time),
|
||||
`hello.ack`, heartbeat, connection registry.
|
||||
- [ ] `AndroidAdapter.send()` → `message` frame; inbound `message.send` →
|
||||
- [X] `AndroidAdapter.send()` → `message` frame; inbound `message.send` →
|
||||
`MessageEvent` → `handle_message`.
|
||||
- [ ] Pairing store + `ANDROID_TOKEN`; QR payload in `interactive_setup`.
|
||||
- [ ] App: Connect screen (URL+token, real `hello` test), `GatewayClient`
|
||||
- [X] Pairing store + `ANDROID_TOKEN`; QR payload in `interactive_setup`.
|
||||
- [X] App: Connect screen (URL+token, real `hello` test), `GatewayClient`
|
||||
(connect + reconnect), ChatScreen sends + renders `message`.
|
||||
- [ ] `ws_probe.py` harness drives a real turn.
|
||||
- [X] `ws_probe.py` harness drives a real turn.
|
||||
- **Demo (on-device):** pair the phone, send "hello", see the agent's reply.
|
||||
- **Accept:** text round-trip works on-device; wrong token is rejected;
|
||||
reconnect after gateway restart re-pairs.
|
||||
|
||||
## M2 — Streaming + reasoning + tools + commentary
|
||||
**Goal:** the "agent transparency" features.
|
||||
- [ ] Map consumer `send`/`edit_message` → `message.start/update/stop`.
|
||||
- [ ] Reasoning: set `show_reasoning` for android; adapter splits prefix →
|
||||
`reasoning` field. **Verify format with `ws_probe.py`.**
|
||||
- [ ] Tool events: classify tool-progress `send()`s → structured
|
||||
- [X] Map consumer `send`/`edit_message` → `message.start/update/stop`.
|
||||
- [X] Reasoning: set `show_reasoning` for android; adapter splits prefix →
|
||||
`reasoning` field. **Verify format with `ws_probe.py`.** (The model
|
||||
returns a separate `reasoning_content` field. In the *streaming* case the
|
||||
gateway drops it — the stream consumer only forwards `content` and the
|
||||
final send is suppressed — so the adapter captures it via the
|
||||
`on_stream_delta` plugin hook (`kind="reasoning"`, gated by
|
||||
`plugins.stream_reasoning_deltas: true`) and attaches it to
|
||||
`message.stop`. Non-streaming already carried it via the prefix split.)
|
||||
- [X] Tool events: classify tool-progress `send()`s → structured
|
||||
`tool.start/progress/end` (turn-state machine). **Verify with probe.**
|
||||
- [ ] Commentary → `commentary` frames. Typing → `typing`.
|
||||
- [ ] App: live bubble (coalesced updates), `ReasoningBlock` (collapse + copy),
|
||||
- [X] Commentary → `commentary` frames. Typing → `typing`.
|
||||
- [X] App: live bubble (coalesced updates), `ReasoningBlock` (collapse + copy),
|
||||
`ToolCard` with **Everything/Truncated/Nothing** setting, dimmed
|
||||
`commentary` bubble.
|
||||
- **Demo (on-device):** a multi-step prompt streams, shows reasoning above the
|
||||
answer, tool cards (toggle verbosity), and an intermediate beat.
|
||||
(Verified on the MIX 2S: streaming, tool cards + verbosity toggle, and the
|
||||
reasoning panel rendering above the answer. The commentary beat is
|
||||
model/agent-dependent and did not fire with the current model, but the
|
||||
rendering + frame handling are in place.)
|
||||
- **Accept:** all four render correctly; tool verbosity setting changes
|
||||
rendering; reasoning copy button works; frame shapes match `04-wire-protocol`.
|
||||
|
||||
@@ -135,4 +145,4 @@ has explicit **acceptance criteria**. Work top-to-bottom; don't skip M0/M1.
|
||||
are stable so the shared module is settled.
|
||||
- Parallelizable: plugin (Python) and app (Kotlin) can be worked on
|
||||
concurrently once the protocol (`04-wire-protocol.md`) is agreed; the probe
|
||||
harness is the integration seam.
|
||||
harness is the integration seam.
|
||||
Reference in new issue
Block a user