fix: restore backgrounded in-flight runs on TUI switch-back via gateway snapshot#88785
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 31, 2026, 9:01 PM ET / 01:01 UTC. Summary PR surface: Source +138, Tests +320. Total +458 across 5 files. Reproducibility: yes. source-reproducible: current main scopes global run events per agent, clears TUI run ownership on switch, and rebuilds only persisted history, while the supplied recording shows the patched switch-back recovery. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the additive gateway snapshot and TUI adoption after the dependency check failure is resolved or deliberately waived by maintainers. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible: current main scopes global run events per agent, clears TUI run ownership on switch, and rebuilds only persisted history, while the supplied recording shows the patched switch-back recovery. Is this the best way to solve the issue? Yes. Gateway-owned active-run state is the best layer because a switched-away client cannot retain events it no longer receives, and the additive history field is narrower than changing delivery routing or persisting partial output. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 432312a17c9e. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +138, Tests +320. Total +458 across 5 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
openclaw-switch-back-fix.movThis is the proof of fix video. You can observed that the TUI is patched with the fix (with the debugging information that does not exist in the PR). The agent's session is still in the streaming state after switching back from another aganet and and another session. the content is properly displayed at the end of the execution |
4677259 to
e4d74bd
Compare
|
CI note: the red lanes here are pre-existing breakage on This PR's diff is 5 files, all under
The branch is rebased onto the latest
Lanes that touch this PR's code are green: Real behavior proof, I'm opening a separate change to fix the trunk breakage so these lanes go green. |
e4d74bd to
3c5b7fc
Compare
|
✅ Green and ready to merge. Rebased onto the latest
|
|
Nice direction — pulling the snapshot from gateway state feels like the right layer for switch-back recovery. One edge case I'd double-check: |
3c5b7fc to
8d1ce3f
Compare
|
Good catch — addressed in Two regression tests cover it in
|
62abaa1 to
7d5da81
Compare
…napshot
Switching agents/sessions while a run is streaming left the TUI blank on return:
the run keeps executing, but its result never appears. Root cause is delivery
scoping, not client rendering — global-session runs are delivered per agent
(server-chat.ts resolveSessionDeliveryKey -> `agent:<id>:global`), so once the
client switches to another agent it stops receiving the backgrounded run's
events entirely. A purely client-side store therefore has nothing to retain, and
chat.history rebuilds only from PERSISTED messages (an in-flight run's text is
not yet persisted), so switch-back shows neither the live run nor a result.
Fix (gateway-sourced snapshot; additive, no protocol break):
- chat-abort.ts: resolveInFlightRunSnapshot() returns the active run for a
session+agent from the authoritative gateway state (chatAbortControllers +
chatRunBuffers). Only projectSessionActive, non-aborted runs are returned, so a
completed run (already in history) is not duplicated; the shared "global"
session is scoped by agentId. The active run is matched
by the requested OR the canonical store key (an abort entry can hold either),
mirroring sessions.list's active-run projection, so a run started under a
different-but-equivalent session key is still restored. The run is returned even with no buffered text:
some runtimes (Codex) withhold incremental assistant text and only emit the
reply at completion, so there is nothing to show mid-run, but the client should
still adopt the run and show `streaming` (not idle) and finalize cleanly.
- server-methods/chat.ts: chat.history includes an additive `inFlightRun`
({ runId, text }) for the requested session+agent.
- tui-session-actions.ts: loadHistory renders any buffered partial and re-adopts
activeChatRunId + `streaming` status, so resumed delivery continues the run and
its completion is owned by the client rather than an unowned error path.
Works across runtimes (Codex/embedded) and for /agent and /session switches
because the run state comes from the gateway on return, not from client event
capture. Supersedes the earlier client-side live-run-store attempt, which
real-gateway testing proved inert (the backgrounded run's events are never
delivered to the switched-away client).
- node scripts/run-vitest.mjs run src/gateway/chat-abort.test.ts src/tui/tui-session-actions.test.ts -> 57 passed
- node scripts/run-vitest.mjs run src/tui/ -> all passed
- gateway chat.history coverage (server-methods + server.chat suites) -> 263 passed
- tsgo -p tsconfig.core.json clean; oxfmt/oxlint clean
- Verified on a real gateway: /agent switch-back now shows `streaming` and the
result lands cleanly (Codex gpt-5.x).
7d5da81 to
2b8bf5f
Compare
|
Land-ready after maintainer fixups. What changed:
Local proof on head
CI observed on the current pushed head:
Known gap: broad non-required CI shards were still queued/pending when checked; no required checks are configured for this branch and no current-head failures were observed in the relevant proof above. |
|
Landed PR #88785 and added two maintainer follow-up fixes on
Proof run after the follow-up commits:
Remote broad proof gap: |
Summary
src/gateway/server-chat.tsresolveSessionDeliveryKey→agent:<id>:global), so once the client switches to another agent it stops receiving the backgrounded run's events entirely.chat.historyrebuilds only from persisted messages, and an in-flight run's text isn't persisted yet — so switch-back shows neither the live run nor a result.chat.historyreturns an additiveinFlightRun({ runId, text }) for any active run on the requested session+agent, read from the authoritative gateway state (chatAbortControllers+chatRunBuffers). The TUI adopts it on switch-back: renders any buffered partial, showsstreaming, and re-adoptsactiveChatRunIdso the run's completion is owned by the client. Works across runtimes (Codex/embedded) and for/agent+/sessionswitches, because the run state comes from the gateway on return rather than client event capture.sessions.list's active-run projection (isTrackedActiveSessionRunForKey): match on the requested OR canonical session key, scope the sharedglobalsession by agent, and include onlyprojectSessionActive !== false, non-aborted runs (so a finalized run already in history is not duplicated and a stale run can't get stuck instreaming).chat.historycallers all checked; final verdict: no remaining substantiated bug).Verification
Real behavior proof
Behavior addressed: switching agents (
/agent) while a task was running left the terminal TUI blank/idle on switch-back — the backgrounded run's result never appeared. Originally observed ongpt-5.4-mini(Codex runtime).Real environment tested: a real Dockerized OpenClaw gateway (the
openclaw:williamliuimage rebuilt from this branch) with theopenai-codexprovider (gpt-5.x), driving the terminal TUI against the live gateway over its real transport.Exact steps or command run after this patch:
Evidence after fix: proof-of-fix screen recording captured on the live Dockerized gateway running a patched build — https://github.com/user-attachments/assets/8922a5d7-3967-4abb-a81c-4fab5f753100 . The recording shows the patched TUI (it carries diagnostic markers not present in this PR), the agent's session remaining in the
streamingstate after switching to a different agent and a different session and back, and the content rendering correctly at the end of the run. (Observed directly in the live TUI terminal/console output.)Observed result after fix: the previously-lost backgrounded run is restored on switch-back (status
streaming, result rendered on completion). Before the fix, the returned session was blank/idle and the result never surfaced. A gateway trace during diagnosis confirmed the run was active for the session but its in-flight text was empty mid-run (Codex emits the answer only at completion), which the fix now adopts asstreamingrather than dropping.What was not tested: a captured screenshot/recording is not attached inline (verified interactively on the live gateway); the WebChat surface was not separately exercised; the embedded (non-Codex) runtime relies on the same
inFlightRunpath, covered by unit tests rather than a separate live capture.