feat(cli): refresh the statusline ctx segment per settled provider request (#4545) - #4550
Conversation
…quest (apache#4545) The ctx segment only moved when the end-of-turn token_usage event landed, so a long agentic turn burned context with the indicator frozen at the previous turn's value. The Host already commits a latest-context snapshot at every settled provider request (the desktop inspector's data source), so pull it on the desktop's own signal: trace-relevant events schedule a 400ms-debounced getContextDiagnostics read, and the latest issued read writes contextRemaining straight into the statusline usage. - tui-context-refresh.ts: desktop's trace-relevant event set + a restart-on-event debouncer whose revision guard lets only the latest issued query apply; a failed read leaves the last value standing. - pi-tui-runner.ts: observe() on the shared turn-drain onEvent (covers user and Host-attached turns) and on resumeLatest; cancel on teardown; stale-session guard drops pre-switch results. ctxRefreshTicker input option injects the timer for tests. Zero protocol/runtime changes: no new event type, no persistence or billing semantics touched (apache#972). Turn-end token_usage stays the authoritative persisted record.
51e29ce to
4be9222
Compare
M4n5ter
left a comment
There was a problem hiding this comment.
Reviewed exact head 4be9222680c3a0fee115eb78d5ac567219c14516.
The new refresh lifecycle correctly coalesces settled provider-request signals, retires stale reads by revision, and fences session switches. I found one P2 display-coherence defect, detailed inline. It affects the accuracy of the context-pressure indicator but not execution or durable state, so I am approving with that follow-up comment.
The exact-head test check is green and the current-main merge result is clean.
Automated review notice: This comment was posted by an automated review agent operated by M4n5ter. It is not an independent human review and does not replace one.
| return; | ||
| // Same formula the token_usage path uses (#1067), from the same | ||
| // settled request — the two cannot disagree. | ||
| state.usage.contextRemaining = Math.max(0, contextWindow - inputTokens); |
There was a problem hiding this comment.
P2: Keep the diagnostics window with the remaining-token snapshot. This path reads one settled request's inputTokens and contextWindow, but stores only their difference. The statusline later subtracts that old remainder from the current mutable model-catalog window. I reproduced a settled 100k/200k snapshot followed by a catalog refresh to 500k: the UI renders ctx 400k/500k 80% instead of ctx 100k/200k 50%. Store and render the numerator/denominator from the same diagnostics snapshot, and add a regression where the diagnostics and catalog windows intentionally differ.
Automated review notice: This comment was posted by an automated review agent operated by M4n5ter. It is not an independent human review and does not replace one.
Closes #4545.
Problem
The TUI statusline
ctx used/window pct%segment only moved once per turn, when the end-of-turntoken_usageevent landed. During a long agentic turn — dozens of tool steps over minutes, exactly when the context grows fastest — the indicator sat frozen at the previous turn's value, hiding the signal that says "time to/compactor wrap up".Approach
Reuse the desktop inspector's pull model instead of growing a parallel push event (design + audit:
docs/tui-live-ctx-updates.md, 中文讲解docs/tui-live-ctx-updates.zh-CN.md):provider-request-telemetry.ts), andcontext.diagnostics.queryis a plain read with no busy gate — the desktop inspector refreshes from it mid-turn on trace-relevant events.packages/cli/src/tui-context-refresh.ts: the desktop trace-relevant event set (tool_start,tool_result,token_usage, …) + a restart-on-event debouncer (400 ms, same as desktop) whose revision guard lets only the latest issued query apply; a failed read leaves the last value standing.pi-tui-runner.ts:observe()on the shared turn-drainonEvent(covers user-submitted and Host-attached turns alike) and on theresumeLatestloop;cancel()on teardown; a stale-session guard drops results that resolve after a switch. The query calls the driver directly —runControl's serial lock exists for mutations, and/context's mid-turn refusal is a TUI-local choice, not a protocol constraint.contextRemaining = contextWindow − inputTokensformula as thetoken_usagepath (fix(runtime): emit contextRemaining in token_usage events so the TUI ctx segment renders #1067), derived from the same settled request — the two cannot disagree. Turn-endtoken_usagestays the authoritative persisted record; this only enriches the live turn.Zero protocol / runtime / persistence changes: no new event type, no billing semantics touched (#972).
Non-goals
/contextmid-turn over the same query path — a free follow-up, kept out to stay small.Test plan
tui-context-refresh.test.ts(9): event-set parity with desktop, debounce coalescing, out-of-order resolution guard, failed read keeps last value, cancel retires scheduled + in-flight queries, real-timer sanity.tool_start(before anytoken_usage); a failing snapshot read leaves the segment untouched and silent.maka-agentpackage suite: 732 passed, 0 failed (3 pre-existing skips); whole-repo typecheck green; biome lint/format clean; ASF header check green.