fix(status-bar): emit explicit Running event on turn start#121
Merged
yishuiliunian merged 1 commit intomainfrom Apr 19, 2026
Merged
fix(status-bar): emit explicit Running event on turn start#121yishuiliunian merged 1 commit intomainfrom
yishuiliunian merged 1 commit intomainfrom
Conversation
The status bar stayed on "Idle" until the LLM's first streaming chunk arrived, because `transition(Running)` was a no-op and the session's `observable.status` only flipped on Stream/ToolCall events. This made the UI look unresponsive for 1-5s after pressing Enter while waiting on TTFB. Make the agent the authoritative source: emit a dedicated `Running` event when the runner enters active processing. Hub broadcasts it to all observers (TUI, ACP, MetaHub shadows); session layer updates `observable.status` and starts the turn timer. The TUI reads the projection as before — no optimistic state on the client. Also starts the turn-elapsed timer as soon as the turn begins (previously only on the first Stream/ToolCall). Changes: - protocol: add `AgentEventPayload::Running` variant - runtime: `transition(Running)` now emits the event - session: handle `Running` — begin_turn + set status - acp: `Running` has no ACP counterpart (activity already signaled via Stream) Tests: runtime asserts Running precedes first Stream; session covers status transition, turn timer, and sub-agent creation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The TUI status bar stays on "Idle" after the user presses Enter until the LLM's first streamed chunk arrives (1-5s TTFB). Root cause:
runner.transition(AgentStatus::Running)was a no-op — session-layerobservable.statusonly flipped whenStream/ToolCall/etc. events arrived, so the UI had no signal between "input accepted" and "first token back."Fix by making the agent the authoritative source of status transitions:
AgentEventPayload::Running— emitted the moment the runner enters active processing, before any LLM call.begin_turn()and settingobservable.status = Running.Also fixes a secondary issue: the turn-elapsed timer now starts when the turn begins, not on the first
Stream/ToolCallevent.Architecture rationale
Status is data-plane state; the agent owns it. Any observer (TUI, IDE via ACP, external Hub clients) gets the same authoritative view via event broadcast. No lossy shortcut (TUI-side optimistic updates) that would desync from the server model.
Test plan
Runningis emitted before the firstStreambazel test //...— all 52 test targets passbazel build //... --config=clippy— zero warnings