Skip to content

fix(ui): clear webchat pending state only for completed active run#73368

Merged
vincentkoc merged 1 commit intomainfrom
clownfish/ghcrawl-156728-autonomous-smoke
Apr 28, 2026
Merged

fix(ui): clear webchat pending state only for completed active run#73368
vincentkoc merged 1 commit intomainfrom
clownfish/ghcrawl-156728-autonomous-smoke

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

Summary

Credit

This replacement carries forward the stuck-typing diagnosis from haoyu-haoyu in source PR #57887. That PR could not be safely landed because its broad cross-run final clearing was uneditable and Codex review flagged P1 premature-unlock regressions.

Validation

  • pnpm -s vitest run ui/src/ui/controllers/chat.test.ts
  • pnpm check:changed

Changelog

  • Fixes a user-visible WebChat/Control UI stuck typing indicator after delegated/subagent completion without unlocking unrelated active runs prematurely.

ProjectClownfish replacement details:

@vincentkoc vincentkoc added clownfish:human-review clawsweeper Tracked by ClawSweeper automation labels Apr 28, 2026
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui size: S maintainer Maintainer-authored PR labels Apr 28, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR makes WebChat pending-run reconciliation ownership-aware by treating payloads without a runId as foreign events when an active run is in progress. The core fix is in two places: chat.ts drops the payload.runId && guard so that undefined-runId finals route into the foreign-run branch (preserving chatRunId/chatStream), and app-gateway.ts mirrors this in isEventForDifferentActiveRun so the deferred session.message reload is not prematurely resolved by unowned events.

Confidence Score: 4/5

Safe to merge; the fix is narrowly scoped and well-tested, with only P2 concerns remaining.

The logic is coherent and the new tests cover the key scenarios. The only open questions are P2: the undocumented assumption that owned-run terminal events always carry a runId (if violated, aborted/error would stick the indicator), and a benign semantic shift in isEventForDifferentActiveRun for undefined payloads.

ui/src/ui/controllers/chat.ts — the aborted/error case in the new foreign-run branch warrants a clarifying comment about the runId-always-present assumption.

Comments Outside Diff (1)

  1. ui/src/ui/controllers/chat.ts, line 712-722 (link)

    P2 Unowned-branch silently drops aborted/error for undefined-runId payloads

    With runId now optional, any aborted or error event that arrives without a runId while an active run is in progress falls into the foreign-run branch and returns null (line 721) without clearing chatRunId, chatStream, or chatStreamStartedAt. If the server ever sends an aborted or error terminal event for the current active run without a runId (e.g. a timeout path or a gateway-level abort that doesn't carry run metadata), the typing indicator will be permanently stuck — the inverse of the bug this PR is fixing. The PR implicitly relies on the guarantee that all terminal events for the active run always include runId; that assumption is worth a code comment here.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: ui/src/ui/controllers/chat.ts
    Line: 712-722
    
    Comment:
    **Unowned-branch silently drops `aborted`/`error` for `undefined`-runId payloads**
    
    With `runId` now optional, any `aborted` or `error` event that arrives without a `runId` while an active run is in progress falls into the foreign-run branch and returns `null` (line 721) without clearing `chatRunId`, `chatStream`, or `chatStreamStartedAt`. If the server ever sends an `aborted` or `error` terminal event for the current active run without a `runId` (e.g. a timeout path or a gateway-level abort that doesn't carry run metadata), the typing indicator will be permanently stuck — the inverse of the bug this PR is fixing. The PR implicitly relies on the guarantee that all terminal events for the active run always include `runId`; that assumption is worth a code comment here.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: ui/src/ui/controllers/chat.ts
Line: 712-722

Comment:
**Unowned-branch silently drops `aborted`/`error` for `undefined`-runId payloads**

With `runId` now optional, any `aborted` or `error` event that arrives without a `runId` while an active run is in progress falls into the foreign-run branch and returns `null` (line 721) without clearing `chatRunId`, `chatStream`, or `chatStreamStartedAt`. If the server ever sends an `aborted` or `error` terminal event for the current active run without a `runId` (e.g. a timeout path or a gateway-level abort that doesn't carry run metadata), the typing indicator will be permanently stuck — the inverse of the bug this PR is fixing. The PR implicitly relies on the guarantee that all terminal events for the active run always include `runId`; that assumption is worth a code comment here.

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

---

This is a comment left during a code review.
Path: ui/src/ui/app-gateway.ts
Line: 588

Comment:
**Behavior change for `undefined` `payload` is benign but unintentional**

Removing the `payload?.runId &&` guard means that when `payload` itself is `undefined` and `activeRunId` is set, `payload?.runId` evaluates to `undefined`, so `undefined !== activeRunId` is `true` and the function now returns `true` instead of the prior `false`. The practical impact is nil — an `undefined` payload produces `null` from `handleChatEvent` and fails the `isTerminalChatState` check, so neither the deferred-reload nor the history-reload branch can fire. But the stated intent of the PR is only to treat *"payloads present but without a runId"* as foreign; adding a guard like `payload != null && payload.runId !== activeRunId` would make that intent explicit.

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

Reviews (1): Last reviewed commit: "fix(ui): clear webchat pending state onl..." | Re-trigger Greptile

Comment thread ui/src/ui/app-gateway.ts Outdated
@vincentkoc vincentkoc force-pushed the clownfish/ghcrawl-156728-autonomous-smoke branch 2 times, most recently from fdd46f5 to b89d7ed Compare April 28, 2026 08:36
@vincentkoc vincentkoc force-pushed the clownfish/ghcrawl-156728-autonomous-smoke branch from b89d7ed to 1c4af67 Compare April 28, 2026 08:41
@vincentkoc vincentkoc merged commit 02908db into main Apr 28, 2026
65 of 67 checks passed
@vincentkoc vincentkoc deleted the clownfish/ghcrawl-156728-autonomous-smoke branch April 28, 2026 08:47
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui clawsweeper Tracked by ClawSweeper automation maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WebChat can leave parent agent stuck in typing state after subagent completion

1 participant