From 6d176b3802f776e4f0223eb031c0c60a795d0bb2 Mon Sep 17 00:00:00 2001 From: Rolando Santamaria Maso Date: Wed, 29 Jul 2026 19:09:09 +0200 Subject: [PATCH 1/2] feat(tui): refresh context gauge on per-iteration usage events odek serve v1.23.0 streams a "usage" event after every LLM turn (cumulative contextTokens/outputTokens for the run). Handle it to keep the header context gauge live during multi-turn runs instead of stale until "done". Zero-usage events (providers without usage reporting) are ignored so the last known fill is preserved. Renders are coalesced via the existing streaming flush path. Verified end-to-end against a real odek v1.23.0 serve: 3 usage events streamed before the first response token, final value matching done.contextTokens. --- internal/tui/events.go | 10 ++++++++++ internal/tui/stats_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/internal/tui/events.go b/internal/tui/events.go index b8f0211..0e50b4e 100644 --- a/internal/tui/events.go +++ b/internal/tui/events.go @@ -131,6 +131,16 @@ func (m *Model) handleEvent(ev client.Event) (tea.Model, tea.Cmd) { m.winCtxTok = ev.ContextTokens m.lastLatency = ev.Latency + case "usage": + // Per-iteration window fill from odek serve: keeps the header gauge + // live during multi-turn runs instead of waiting for "done". A zero + // value means the provider reported no usage — keep the last known + // fill rather than zeroing the gauge. + if ev.ContextTokens > 0 { + m.winCtxTok = ev.ContextTokens + } + stream = true + case "error": if i := m.cur(); i >= 0 && m.msgs[i].content == "" { m.msgs[i].content = "**Error:** " + ev.Message diff --git a/internal/tui/stats_test.go b/internal/tui/stats_test.go index fe2a696..8f72cbb 100644 --- a/internal/tui/stats_test.go +++ b/internal/tui/stats_test.go @@ -112,6 +112,41 @@ func TestGaugeFollowsTurnFill(t *testing.T) { } } +// TestUsageEventRefreshesGaugeMidTurn is a regression test: odek serve emits +// a per-iteration "usage" event during a run; the header gauge must refresh +// on it instead of staying stale until "done" arrives at the end of the +// whole agent loop. +func TestUsageEventRefreshesGaugeMidTurn(t *testing.T) { + m := newTestModel() + m.model = "big" + m.models = []client.ModelInfo{{ID: "big", MaxContext: 1000}} + m.resolveMaxContext() + m.msgs = append(m.msgs, message{role: roleAsst, streaming: true}) + m.curIdx = len(m.msgs) - 1 + m.busy = true + m.status = "responding" + + m.handleEvent(client.Event{Type: "usage", ContextTokens: 400, OutputTokens: 50}) + if m.winCtxTok != 400 { + t.Fatalf("winCtxTok = %d, want 400", m.winCtxTok) + } + if out := plain(m.header()); !strings.Contains(out, "40%") { + t.Errorf("gauge should refresh mid-run (40%%), got:\n%s", out) + } + + // A zero-usage event (provider without usage reporting) must not zero a + // previously known fill. + m.handleEvent(client.Event{Type: "usage"}) + if m.winCtxTok != 400 { + t.Fatalf("winCtxTok = %d, want 400 (zero usage ignored)", m.winCtxTok) + } + + // The mid-run state must not be disturbed: still busy, still responding. + if !m.busy || m.status != "responding" { + t.Errorf("usage event must not end the turn: busy=%v status=%q", m.busy, m.status) + } +} + func TestContextGauge(t *testing.T) { m := newTestModel() m.model = "big" From 49ecd46baadbb268eb9ff5a09e16c8b8f21ab12b Mon Sep 17 00:00:00 2001 From: Rolando Santamaria Maso Date: Wed, 29 Jul 2026 19:09:09 +0200 Subject: [PATCH 2/2] docs(agents): forbid modifying odek from a bodek session bodek is a pure front-end; odek-side fixes must be handed to the user as a task for an odek session, not edited directly. --- AGENTS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index a113ba9..e14eae4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -111,6 +111,10 @@ feat(tui): compact tool steps with Ctrl+E details toggle ## Workflow rules for agents +- **Never modify the odek project from a bodek session.** bodek is a pure + front-end; if a fix requires changes in odek (protocol, `odek serve`, + engine behaviour), do not edit that repo — instead summarize the required + change and suggest it to the user as a task for an odek session. - Don't run `git commit`/`git push` unless the user explicitly asks. - Don't add dependencies without checking `go.mod` first and flagging it to the user.