Skip to content

fix(tui): recover activity status when no runs are in flight#64847

Closed
Yanhu007 wants to merge 1 commit intoopenclaw:mainfrom
Yanhu007:fix/tui-stuck-streaming-indicator
Closed

fix(tui): recover activity status when no runs are in flight#64847
Yanhu007 wants to merge 1 commit intoopenclaw:mainfrom
Yanhu007:fix/tui-stuck-streaming-indicator

Conversation

@Yanhu007
Copy link
Copy Markdown
Contributor

Summary

The TUI status bar can get stuck on streaming indefinitely after a chat turn completes. This happens when state.activeChatRunId is cleared by a concurrent event before the final event arrives, causing wasActiveRun to be false in finalizeRun — the setActivityStatus("idle") call is skipped and the spinner never stops.

Changes

Add a recovery condition to both finalizeRun and terminateRun: when wasActiveRun is false but there are provably no other runs in flight (sessionRuns.size === 0 and activeChatRunId is null), still transition the activity status.

This is safe because it only fires when the state machine has confirmed no other work is active — it cannot interfere with legitimate in-flight runs.

Testing

  • Normal case: wasActiveRun === true → unchanged behavior
  • Edge case: wasActiveRun === false, no runs in flight → status recovers to idle
  • Concurrent runs: sessionRuns.size > 0 → no premature status change

Fixes #64825

The TUI status bar can get stuck on "streaming" indefinitely after a
chat turn completes. This happens when `state.activeChatRunId` is
cleared by a concurrent event before the "final" event arrives, making
`wasActiveRun` false in `finalizeRun` and skipping the
`setActivityStatus("idle")` call.

Add a recovery condition: when `wasActiveRun` is false but there are
provably no other runs in flight (`sessionRuns.size === 0` and
`activeChatRunId` is null), still transition the activity status. This
is safe because it only fires when the state machine has confirmed no
other work is active.

Apply the same fix to `terminateRun` for consistency.

Fixes openclaw#64825
@openclaw-barnacle openclaw-barnacle Bot added size: XS r: too-many-prs Auto-close: author has more than twenty active PRs. labels Apr 11, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 11, 2026

Greptile Summary

Adds a recovery condition to finalizeRun and terminateRun in src/tui/tui-event-handlers.ts so the TUI activity status still transitions to idle/error when wasActiveRun is false but sessionRuns is empty and activeChatRunId is null — preventing the spinner from getting stuck after a run whose active-run flag was cleared prematurely. The fix is conservative and correct: both operations remove the current run from sessionRuns before the check, so the dual guard cannot fire while legitimate concurrent runs are still in flight.

Confidence Score: 5/5

Safe to merge — the recovery condition is well-guarded and cannot interfere with in-flight concurrent runs.

Only finding is a P2 suggestion to add a regression test for the new recovery path; no logic errors or data-integrity concerns.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/tui/tui-event-handlers.ts
Line: 145-147

Comment:
**Missing regression test for the new recovery path**

The existing test suite has good coverage for the concurrent-run guard (the `run-other` + `run-active` scenario at line 505), but there's no test for the new `wasActiveRun === false` + `sessionRuns.size === 0` + `activeChatRunId === null` recovery case that this PR is fixing. Without it, a future refactor could silently revert the fix and the suite would still pass.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(tui): recover activity status when n..." | Re-trigger Greptile

Comment on lines +145 to 147
if (params.wasActiveRun || (sessionRuns.size === 0 && !state.activeChatRunId)) {
setActivityStatus(params.status);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing regression test for the new recovery path

The existing test suite has good coverage for the concurrent-run guard (the run-other + run-active scenario at line 505), but there's no test for the new wasActiveRun === false + sessionRuns.size === 0 + activeChatRunId === null recovery case that this PR is fixing. Without it, a future refactor could silently revert the fix and the suite would still pass.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/tui/tui-event-handlers.ts
Line: 145-147

Comment:
**Missing regression test for the new recovery path**

The existing test suite has good coverage for the concurrent-run guard (the `run-other` + `run-active` scenario at line 505), but there's no test for the new `wasActiveRun === false` + `sessionRuns.size === 0` + `activeChatRunId === null` recovery case that this PR is fixing. Without it, a future refactor could silently revert the fix and the suite would still pass.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f47aacb35

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

clearActiveRunIfMatch(params.runId);
flushPendingHistoryRefreshIfIdle();
if (params.wasActiveRun) {
if (params.wasActiveRun || (sessionRuns.size === 0 && !state.activeChatRunId)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't let background BTW errors override activity status

The new fallback in terminateRun updates activity even when the run was never active, which regresses local /btw behavior: those runs are intentionally excluded from activeChatRunId (src/tui/tui-event-handlers.ts:240) and the send path intentionally avoids status changes for BTW (src/tui/tui-command-handlers.ts:525-554). With this change, if a local BTW run later emits error/aborted and no other run is in flight, the status bar is set to error/aborted, making a side-result failure appear as the main chat state.

Useful? React with 👍 / 👎.

@openclaw-barnacle
Copy link
Copy Markdown

Closing this PR because the author has more than 10 active PRs in this repo. Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

r: too-many-prs Auto-close: author has more than twenty active PRs. size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI stuck on 'streaming' indicator after run completes — finalizeRun() doesn't transition UI when wasActiveRun is false

1 participant