Skip to content

bodek: a beautiful Bubble Tea TUI for the odek agent#1

Merged
jkyberneees merged 5 commits into
mainfrom
claude/trusting-cerf-cswat2
Jun 18, 2026
Merged

bodek: a beautiful Bubble Tea TUI for the odek agent#1
jkyberneees merged 5 commits into
mainfrom
claude/trusting-cerf-cswat2

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

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 serve instance and renders the agent's live stream over odek's WebSocket + REST protocol. All agent behaviour — tools, the danger approval engine, the Docker sandbox, skills, memory, sessions — is provided by odek itself; bodek never re-implements any of it.

┌──────────────┐   WebSocket (RFC 6455, JSON) + REST   ┌──────────────────┐
│    bodek     │ ◄───────────────────────────────────► │   odek serve      │
│ (Bubble Tea) │   tokens · tools · approvals · @refs   │  (ReAct engine)   │
└──────────────┘                                        └──────────────────┘

What it does

  • Streaming chat — token-by-token answers rendered as Markdown (glamour).
  • Live tool activity — every tool_call/tool_result with a per-tool glyph, spinner, argument preview, and one-line result.
  • Security approvals — odek's danger prompts surface as an inline panel; answer a/d/t.
  • @ file/session attachments — live autocomplete via /api/resources; references resolve server-side through odek's untrusted-content boundary.
  • Session browser (^R) — resume, replay, and delete past conversations (/api/sessions).
  • Model switcher (^O) — /api/models.
  • Cancellation (Esc) — abort a running turn via /api/cancel.
  • Sandbox aware — header shows 🛡 sandboxed / ⚠ host access; --sandbox opts into Docker isolation.
  • Context-aware progress🧪 running tests, 📖 reading client.go, 🚀 pushing, with a live elapsed timer.
  • Fluent UX — gradient wordmark/hairline, smooth braille spinner, smart autoscroll, scroll indicator.

Per-session auth tokens are persisted in ~/.bodek/sessions.json so resume/cancel/delete work across runs (mirroring the Web UI's localStorage).

Project layout

Path Responsibility
cmd/bodek CLI entry: flags, lifecycle, wiring
internal/server Launch/attach to odek serve, resolve the auth token
internal/client odek serve WebSocket + REST protocol
internal/tokens Local persistence of per-session auth tokens
internal/tui Bubble Tea model, update loop, panels, view

Quality

  • ~99% internal-package statement coverage (client 100%, tui 99%, tokens 98%, server 95% — the remainder is unreachable OS-error handling), via unit + integration tests against an in-process odek serve stand-in.
  • golangci-lint v2 clean, go vet clean, gofmt clean.
  • CI (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

jkyberneees and others added 4 commits June 18, 2026 08:50
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

Copy link
Copy Markdown
Contributor Author

🔎 Verification Certificate

Applied 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: GeneratedCode (AI-authored) · Verdict: HumanReviewRecommended

Axes (✅ pass · ⚠️ note · 🔴 fail→fixed)

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)

fee8bbesanitize()/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 v2 all 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-oracle o were 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
@jkyberneees jkyberneees merged commit 49d4b09 into main Jun 18, 2026
2 checks passed
@jkyberneees jkyberneees deleted the claude/trusting-cerf-cswat2 branch June 18, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant