Web companion: in-browser debug surface (v0.15.0) #16

Closed
opened 2026-05-27 18:42:24 -07:00 by vh · 1 comment
Owner

Goal

Browser-based debug companion to the Ratatoskr TUI, reusing the existing wire-layer modules (sse_client, sessions, tier3, local_agents). Same five surfaces (transcript, thinking, tools, debug, persona) over the same Worldtree Conversation API SSE wire, viewable from any device on the operator's LAN.

Non-goals

  • Not a chat product
  • Not internet-exposed (no TLS, no auth — internal LAN debug surface)
  • Not a TUI replacement; the TUI stays canonical
  • Not a long-running service (dies on Ctrl-C)

Trust model

Internal-LAN debug surface. Binds 0.0.0.0. No auth, no CORS guard. Trust: anyone routable to the host's port. What stays disciplined regardless of network trust:

  • Transcript HTML-escapes assistant content (model output is untrusted text)
  • Upstream API key stays server-side; never reaches browser DOM / responses

Architecture (high-level)

  • New module src/ratatoskr/web/ with entrypoint.py (lazy-import gate), server.py (Starlette app), static/index.html (UI)
  • Optional-deps group [web] (starlette + uvicorn)
  • New console script ratatoskr-web
  • Streaming via browser-native EventSource GET; prompt-submit is a separate POST (EventSource is GET-only — load-bearing finding from Heid panel review)
  • Small in-memory turn registry to map cancel/disconnect → upstream

Endpoint surface

GET   /                                     → index.html
GET   /static/*                             → static assets
GET   /version                              → {"ratatoskr": "<version>"}
GET   /api/agents                           → upstream /agents + local tier3 merge
POST  /api/sessions                         → upstream POST /sessions
GET   /api/agents/{id}/persona_state        → upstream persona_state
POST  /api/turns/{session_id}               → submit prompt; returns {"turn_id": <id>}
GET   /api/turns/{session_id}/stream
        ?turn_id=<id>                       → SSE stream (EventSource)
POST  /api/turns/{session_id}/cancel
        ?turn_id=<id>                       → upstream cancel

Ship criteria (v0.15.0)

  • ratatoskr-web console script launches Starlette on 0.0.0.0:8765 (configurable via --host / --port / --open); auto-prints URL to stderr
  • Browser fetches /, picks agent, mints session, submits prompt, renders five panes live as upstream events arrive
  • Cancel via UI works (POST /api/turns/.../cancel)
  • Browser disconnect mid-stream triggers upstream cancel
  • Ctrl-C on server cleanly cancels all in-flight turns within 5s cleanup budget
  • Transcript HTML-escapes assistant content
  • Upstream API key never reaches browser DOM
  • Per-pane copy button works; ratatoskr version visible in footer
  • All existing tests pass
  • New tests: test_web_server.py, test_web_presentation_contract.py, test_web_packaging.py
  • README mentions the web companion + LAN trust assumption

Out of scope (deferred to v0.16.x+)

  • Cross-reload session resume via Last-Event-ID
  • Tier 3 lifecycle UI (define/patch/delete in browser)
  • Markdown rendering with vendored safe-subset renderer (v0.15.0 ships HTML-escaped plain-text only)
  • TLS + real auth (only if a non-LAN use case ever surfaces)

Provenance

  • Heid panel review pass (Gróa + Hulda) on prior scope v1 → 8 load-bearing corrections merged into scope v2 (POST→GET split for SSE, Starlette over FastAPI, lazy-import discipline, browser-disconnect → upstream cancel, presentation-contract fixture, error event contract, static-asset packaging, escaped plain-text at v0.15.0)
  • Operator direction: internal LAN debug surface; auth + CORS deliberately omitted
## Goal Browser-based debug companion to the Ratatoskr TUI, reusing the existing wire-layer modules (`sse_client`, `sessions`, `tier3`, `local_agents`). Same five surfaces (transcript, thinking, tools, debug, persona) over the same Worldtree Conversation API SSE wire, viewable from any device on the operator's LAN. ## Non-goals - Not a chat product - Not internet-exposed (no TLS, no auth — internal LAN debug surface) - Not a TUI replacement; the TUI stays canonical - Not a long-running service (dies on Ctrl-C) ## Trust model Internal-LAN debug surface. Binds `0.0.0.0`. No auth, no CORS guard. Trust: anyone routable to the host's port. What stays disciplined regardless of network trust: - Transcript HTML-escapes assistant content (model output is untrusted text) - Upstream API key stays server-side; never reaches browser DOM / responses ## Architecture (high-level) - New module `src/ratatoskr/web/` with `entrypoint.py` (lazy-import gate), `server.py` (Starlette app), `static/index.html` (UI) - Optional-deps group `[web]` (`starlette` + `uvicorn`) - New console script `ratatoskr-web` - Streaming via browser-native `EventSource` GET; prompt-submit is a separate POST (`EventSource` is GET-only — load-bearing finding from Heid panel review) - Small in-memory turn registry to map cancel/disconnect → upstream ## Endpoint surface ``` GET / → index.html GET /static/* → static assets GET /version → {"ratatoskr": "<version>"} GET /api/agents → upstream /agents + local tier3 merge POST /api/sessions → upstream POST /sessions GET /api/agents/{id}/persona_state → upstream persona_state POST /api/turns/{session_id} → submit prompt; returns {"turn_id": <id>} GET /api/turns/{session_id}/stream ?turn_id=<id> → SSE stream (EventSource) POST /api/turns/{session_id}/cancel ?turn_id=<id> → upstream cancel ``` ## Ship criteria (v0.15.0) - `ratatoskr-web` console script launches Starlette on `0.0.0.0:8765` (configurable via `--host` / `--port` / `--open`); auto-prints URL to stderr - Browser fetches `/`, picks agent, mints session, submits prompt, renders five panes live as upstream events arrive - Cancel via UI works (POST `/api/turns/.../cancel`) - Browser disconnect mid-stream triggers upstream cancel - Ctrl-C on server cleanly cancels all in-flight turns within 5s cleanup budget - Transcript HTML-escapes assistant content - Upstream API key never reaches browser DOM - Per-pane copy button works; ratatoskr version visible in footer - All existing tests pass - New tests: `test_web_server.py`, `test_web_presentation_contract.py`, `test_web_packaging.py` - README mentions the web companion + LAN trust assumption ## Out of scope (deferred to v0.16.x+) - Cross-reload session resume via `Last-Event-ID` - Tier 3 lifecycle UI (define/patch/delete in browser) - Markdown rendering with vendored safe-subset renderer (v0.15.0 ships HTML-escaped plain-text only) - TLS + real auth (only if a non-LAN use case ever surfaces) ## Provenance - Heid panel review pass (Gróa + Hulda) on prior scope v1 → 8 load-bearing corrections merged into scope v2 (POST→GET split for SSE, Starlette over FastAPI, lazy-import discipline, browser-disconnect → upstream cancel, presentation-contract fixture, error event contract, static-asset packaging, escaped plain-text at v0.15.0) - Operator direction: internal LAN debug surface; auth + CORS deliberately omitted
vh added the enhancementtask labels 2026-05-27 18:42:24 -07:00
vh closed this issue 2026-05-27 21:22:43 -07:00
Author
Owner

Delivered across v0.15.0 → v0.16.1.

Shipped:

  • v0.15.0 — web companion: Starlette server (ratatoskr.web), ratatoskr-web console script, [web] optional-deps, single-page UI. 9 endpoints; five-pane parity (transcript / thinking / tools / debug / persona) over the same SSE wire; browser-native EventSource (GET stream + separate POST submit); in-memory turn registry; browser-disconnect → upstream cancel; lifespan-shutdown drain; HTML-escaped transcript; API key server-side only.
  • v0.15.1 — fix: get_persona_state unwraps FastAPI detail-envelope (surfaced by live smoke vs personal:8081).
  • v0.16.0 — Heid code-review pass 1 (7 findings): load-bearing turn_id fix (cancel paths target the upstream Worldtree turn_id captured from the first SSE event, not the browser-local counter); server-configured RATATOSKR_END_USER_ID; narrowed missing-extras import catch; per-turn lifespan-shutdown logging.
  • v0.16.1 — Heid code-review pass 2 (2 minor): full stream-layer event-vocab coverage (Error/Cancelled/AffectUpdate); disconnect-cancel catch narrowed to the cooperative race + structured-log unexpected failures.

Verification: 378 tests passing. End-to-end smoke vs personal:8081 (v0.29.0) confirmed real session-create + SSE proxy. Heid pass 2: Groa zero findings, Hulda 2 minor (closed in v0.16.1). Review loop converged.

Contract: docs/contracts/issues/16.contract.md (v2.1 + v0.16.0 amendment).

Deferred to follow-ups: cross-reload resume via Last-Event-ID; Tier 3 lifecycle UI in browser; Markdown rendering; full structured access logging; TLS + real auth (LAN-trust model only).

Delivered across v0.15.0 → v0.16.1. Shipped: - v0.15.0 — web companion: Starlette server (ratatoskr.web), ratatoskr-web console script, [web] optional-deps, single-page UI. 9 endpoints; five-pane parity (transcript / thinking / tools / debug / persona) over the same SSE wire; browser-native EventSource (GET stream + separate POST submit); in-memory turn registry; browser-disconnect → upstream cancel; lifespan-shutdown drain; HTML-escaped transcript; API key server-side only. - v0.15.1 — fix: get_persona_state unwraps FastAPI detail-envelope (surfaced by live smoke vs personal:8081). - v0.16.0 — Heid code-review pass 1 (7 findings): load-bearing turn_id fix (cancel paths target the upstream Worldtree turn_id captured from the first SSE event, not the browser-local counter); server-configured RATATOSKR_END_USER_ID; narrowed missing-extras import catch; per-turn lifespan-shutdown logging. - v0.16.1 — Heid code-review pass 2 (2 minor): full stream-layer event-vocab coverage (Error/Cancelled/AffectUpdate); disconnect-cancel catch narrowed to the cooperative race + structured-log unexpected failures. Verification: 378 tests passing. End-to-end smoke vs personal:8081 (v0.29.0) confirmed real session-create + SSE proxy. Heid pass 2: Groa zero findings, Hulda 2 minor (closed in v0.16.1). Review loop converged. Contract: docs/contracts/issues/16.contract.md (v2.1 + v0.16.0 amendment). Deferred to follow-ups: cross-reload resume via Last-Event-ID; Tier 3 lifecycle UI in browser; Markdown rendering; full structured access logging; TLS + real auth (LAN-trust model only).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vh/ratatoskr#16