bodek: a beautiful Bubble Tea TUI for the odek agent#1
Conversation
bodek is a beautiful terminal front-end for odek. Rather than re-implementing any agent logic, it launches (or attaches to) an `odek serve` instance and renders its streaming WebSocket protocol — reusing odek's tools, danger approval engine, sandbox, skills, memory, and sessions verbatim. - internal/server: spawn/attach to `odek serve`, resolve the CSRF token via GET / Set-Cookie, supervise the subprocess lifecycle - internal/client: odek serve WebSocket protocol (x/net/websocket) — transport plus decoding of every event (token, thinking, tool_call, tool_result, done, error, approval_request, skill/memory/agent events) - internal/tui: Bubble Tea model with live token streaming, per-tool activity, inline danger approvals (a/d/t), glamour-rendered Markdown, gradient wordmark, and session token/latency telemetry - cmd/bodek: CLI entry, flags (--url, --odek-bin, --sandbox), lifecycle Verified end-to-end against a locally built odek serve: connect, token auth, WebSocket dial, prompt, and event decoding all work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012rWqu7pktbd2ejfNw3a7Vf
Make bodek feel premium and fluent, and add file/session attachments by reusing odek serve's resource index — no new agent logic. Fluency & polish: - smooth braille spinner; live elapsed timer and cycling status verbs while the agent reasons - per-tool glyphs in the activity feed (shell, read, search, browser…) - gradient hairline under the header (cached per width) - smart autoscroll: follows the stream while busy, never yanks you when scrolled up reading history; scroll-position indicator in the footer File attachments (@-references): - internal/client: Resources() queries odek's /api/resources endpoint - internal/tui: live @-completion popup (↑/↓ select, ⏎/⇥ insert, esc cancel) for files and sessions; dynamic relayout reserves its space - references resolve server-side via odek's resource registry, going through the same untrusted-content boundary as any external input Tests: resource-event decode, ref parsing (activeRef/refStart), tool glyphs, and a no-TTY smoke test driving a full streaming turn plus the approval and autocomplete panels at full and narrow widths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012rWqu7pktbd2ejfNw3a7Vf
…lease
Surface the rest of odek serve's API in the TUI and harden the project.
TUI / endpoints (all odek serve APIs now used):
- /api/sessions + /api/sessions/{id}: session browser (^R) to resume,
replay transcript, and delete past conversations
- /api/models: model switcher (^O)
- /api/cancel: Esc aborts the running turn
- per-session auth tokens persisted in ~/.bodek/sessions.json so resume/
cancel/delete work across runs (internal/tokens)
- sandbox status shown in the header (🛡 sandboxed / ⚠ host access)
- context-aware progress messages derived from the running tool/command
(🧪 running tests, 📖 reading X, 🚀 pushing) with live elapsed timer
Tests & quality:
- comprehensive unit + integration tests against an in-process odek serve
stand-in; internal-package coverage ~99% (client 100, tui 99, tokens 98,
server 95 — remainder is unreachable OS-error handling)
- golangci-lint v2 config (clean), Makefile test/cover/lint targets
- GitHub Actions: CI (build, vet, race tests + coverage, lint) and a
GoReleaser release workflow on version tags
- README updated with new features, key bindings, badges, and dev docs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012rWqu7pktbd2ejfNw3a7Vf
Verification-protocol finding (Axis 2.8 Adversarial Surface / 2.3 Security): agent-influenced output — streamed tokens, tool args/results, reasoning, engine notices, and resumed transcripts — was written to the terminal without stripping ANSI/control escape sequences, a terminal-injection risk (cursor/screen manipulation, OSC 52 clipboard exfiltration). - add sanitize()/isControl(): strip C0 control bytes + DEL (incl. ESC), keeping newlines/tabs; applied to all untrusted display paths (collapse, streamed token + thinking ingestion, addNote, resumed transcripts) - tests for the sanitizer and end-to-end defanging CI: golangci-lint v2 requires golangci-lint-action v7+ (v6 rejects v2.x) — bump the action so the Lint job runs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012rWqu7pktbd2ejfNw3a7Vf
🔎 Verification CertificateApplied The AI Verification Protocol v5.2.7 to this PR. This was a single-agent pass (no multi-provider B/C/D/E pipeline, no replay-sandbox/fuzz infra), so η is reported qualitatively with coverage as a proxy rather than a full mutation/oracle derivation — unverified gaps are listed explicitly per §9. Classification: Axes (✅ pass ·
|
| Axis | Status | Finding / Resolution |
|---|---|---|
| 2.1 Semantic Correctness | ✅ | Logic exercised by unit + integration tests against an in-process odek serve stand-in. |
| 2.2 Behavioral Contract | ✅ | Event/REST field mapping verified against cmd/odek/serve.go (the spec source), not inferred from bodek. |
| 2.3 Security Surface | 🔴→✅ | Fixed: untrusted agent/tool output rendered to the terminal without stripping ANSI/control escapes. |
| 2.4 Structural Integrity | ✅ | Clean client/server/tokens/tui separation; no cycles. |
| 2.5 Behavioral Exploration | Disconnect/approval/cancel/empty-size paths covered by tests; a live LLM turn was not exercised (no key/egress). | |
| 2.6 Dependency Integrity | ✅ | All deps pinned in go.mod (no */floating ranges). |
| 2.7 Generator Provenance | ✅ | N/A (verifier side); commits attributed. |
| 2.8 Adversarial Surface | 🔴→✅ | Fixed: terminal-injection via ESC/OSC 52 (clipboard exfil) / cursor & screen manipulation in streamed tokens, tool args/results, reasoning, notices, and resumed transcripts. |
| 2.9 Documentation Coverage | ✅ | README updated with every new feature, key bindings, badges, dev/CI docs. |
Remediation applied (auto-repairable, additive — §7.1)
fee8bbe — sanitize()/isControl() strip C0 control bytes + DEL (incl. ESC) while keeping newlines/tabs, applied to all untrusted display paths. Added unit + end-to-end defang tests. This is purely defensive (no behavior change to legitimate content), so it was auto-applied rather than queued for human-only review.
Also fixed: CI Lint job (golangci-lint-action v6 → v7, required for golangci-lint v2).
Quality signals (η proxy)
- Internal-package statement coverage 98.7% (1122/1137) — client 100%, tui 99%, tokens 98%, server 95% (residual is unreachable OS-error handling).
go vet,gofmt,golangci-lint v2all clean; race-enabled tests pass.
Unverified gaps (§9 — honest disclosure)
- No end-to-end LLM turn (no API key / network egress in the build env) — streaming render verified by construction + simulated events, not a real provider round-trip.
- No TTY run of the interactive Bubble Tea screen.
- η signals
m(mutation),f(fuzz), and the independent-oracleowere not computed — out of scope for a single-agent pass.
Rationale: no residual 🔴 after remediation; verdict is HumanReviewRecommended (not AutoApprove) because this is AI-generated code verified by a single agent, and the live behavioral layer (2.5) could not be exercised here — that's the layer the protocol says AI cannot self-evaluate.
🤖 Generated with Claude Code
Generated by Claude Code
Add standard slash-command support and a completion palette that mirrors the @ popup: - type "/" for a command palette (↑/↓ select, ⇥ complete, ⏎ run, esc): /help, /clear, /sessions, /model [name], /thinking [on|off], /cancel, /quit - a fully-typed "/cmd args" + ⏎ runs directly; commands work mid-turn (e.g. /cancel) and never get sent to the agent - /help renders a command + keybinding card Scope @ to attachments only: the completion now suggests files (sessions are reached via /sessions or ^R), with a relabeled popup and a command glyph. README, welcome tips, and key-binding docs updated. Fully covered by tests (commands.go 100%); suite + lint green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012rWqu7pktbd2ejfNw3a7Vf
Summary
bodek is a beautiful Bubble Tea terminal interface for the odek agent.
It is a pure front-end: it launches (or attaches to) an
odek serveinstance and renders the agent's live stream over odek's WebSocket + REST protocol. All agent behaviour — tools, thedangerapproval engine, the Docker sandbox, skills, memory, sessions — is provided by odek itself; bodek never re-implements any of it.What it does
tool_call/tool_resultwith a per-tool glyph, spinner, argument preview, and one-line result.dangerprompts surface as an inline panel; answera/d/t.@file/session attachments — live autocomplete via/api/resources; references resolve server-side through odek's untrusted-content boundary.^R) — resume, replay, and delete past conversations (/api/sessions).^O) —/api/models.Esc) — abort a running turn via/api/cancel.🛡 sandboxed/⚠ host access;--sandboxopts into Docker isolation.🧪 running tests,📖 reading client.go,🚀 pushing, with a live elapsed timer.Per-session auth tokens are persisted in
~/.bodek/sessions.jsonso resume/cancel/delete work across runs (mirroring the Web UI's localStorage).Project layout
cmd/bodekinternal/serverodek serve, resolve the auth tokeninternal/clientinternal/tokensinternal/tuiQuality
odek servestand-in.golangci-lintv2 clean,go vetclean,gofmtclean.build,vet, race tests + coverage, lint) and a GoReleaser release workflow on version tags.Verification
End-to-end validated against a locally built
odek serve: token auth, WebSocket dial, prompt/event streaming, and the resource/session/model/cancel REST endpoints all work. The interactive TUI itself was not run on a TTY in CI and a full LLM turn was not exercised (no API key / network egress in the build env).🤖 Generated with Claude Code
https://claude.ai/code/session_012rWqu7pktbd2ejfNw3a7Vf
Generated by Claude Code