openclaw - ✅(Solved) Fix bug(agents): commentary partials can still leak as visible output before final_answer arrives [3 pull requests, 2 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#61475Fetched 2026-04-08 02:58:16
View on GitHub
Comments
2
Participants
1
Timeline
7
Reactions
0
Participants
Timeline (top)
cross-referenced ×5commented ×2

Root Cause

The suppression check at :332-356 only returns early when:

if (deliveryPhase === "commentary" && !phaseAwareVisibleText) return;

So commentary partials with any visible text survive the gate. next = phaseAwareVisibleText || ... then forwards it downstream. Users see commentary first, then it gets replaced when final_answer arrives later — which is exactly the preview-then-replace behavior the maintainer rejected in the #59643 review.

Fix Action

Fixed

PR fix notes

PR #61481: fix(agents): harden OpenAI phase-aware visible text — suppress commentary partials, prevent empty final_answer fallback leak

Description (problem / solution / changelog)

Summary

  • fix phase-aware visible text extraction so an explicit final_answer block never falls back to commentary or legacy unphased text when it sanitizes to empty
  • suppress all commentary-phase partial streaming output regardless of whether extracted visible text is non-empty
  • keep session-history HTTP/SSE sanitization aligned with the hardened chat history path
  • add regression tests covering both leak paths and the session-history follow-through

Context

This hardens the merged #59643 behavior against two P1 leaks:

  • fixes #61474
  • fixes #61475

Related issues / bug family

  • related to #25592
  • related to #59536
  • related to #59918
  • related to #44213
  • related to #49438
  • related to #53960

Parent / sibling PRs

  • parent: #59643 — core phase-separation fix (merged)
  • sibling: #61463 — phase-aware extraction in sessions-helpers, TUI, and history paths

Remaining follow-ups from the same adversarial review

  • #61476 — replay splitting corrupts phase on mixed messages
  • #61477 — late-map buffering gates on key existence, not phase validity
  • #61478 — function-call replay silently loses malformed arguments

Related open PRs

  • #59920 — prefer terminal reply fields in CLI JSONL parser
  • #61151 — drop partialJson streaming artifacts from session history
  • #61337 — disable OpenAI tool-use pairing repair

Testing

  • npm exec -- node --no-maglev ./node_modules/vitest/vitest.mjs run --config vitest.config.ts src/agents/pi-embedded-utils.test.ts src/agents/pi-embedded-subscribe.handlers.messages.test.ts
  • npm exec -- node --no-maglev ./node_modules/vitest/vitest.mjs run --config vitest.config.ts src/gateway/sessions-history-http.test.ts

Changed files

  • .agents/skills/openclaw-parallels-smoke/SKILL.md (modified, +13/-0)
  • .agents/skills/openclaw-qa-testing/SKILL.md (added, +86/-0)
  • .agents/skills/openclaw-qa-testing/agents/openai.yaml (added, +4/-0)
  • .github/labeler.yml (modified, +4/-0)
  • .github/workflows/ci.yml (modified, +7/-1)
  • .github/workflows/control-ui-locale-refresh.yml (modified, +2/-2)
  • .github/workflows/openclaw-npm-release.yml (modified, +1/-1)
  • CHANGELOG.md (modified, +40/-12)
  • appcast.xml (modified, +248/-116)
  • apps/android/app/build.gradle.kts (modified, +2/-2)
  • apps/ios/Config/Version.xcconfig (modified, +3/-3)
  • apps/macos/Sources/OpenClaw/Resources/Info.plist (modified, +2/-2)
  • apps/macos/Sources/OpenClawProtocol/GatewayModels.swift (modified, +14/-0)
  • apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json (modified, +23/-0)
  • apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift (modified, +14/-0)
  • docs/.generated/config-baseline.sha256 (modified, +4/-4)
  • docs/.generated/plugin-sdk-api-baseline.sha256 (modified, +2/-2)
  • docs/automation/tasks.md (modified, +5/-0)
  • docs/channels/discord.md (modified, +1/-1)
  • docs/channels/matrix.md (modified, +29/-5)
  • docs/cli/memory.md (modified, +43/-15)
  • docs/cli/update.md (modified, +3/-1)
  • docs/concepts/dreaming.md (modified, +121/-194)
  • docs/concepts/memory-qmd.md (modified, +17/-1)
  • docs/concepts/memory-search.md (modified, +9/-8)
  • docs/concepts/memory.md (modified, +12/-8)
  • docs/concepts/model-providers.md (modified, +2/-0)
  • docs/concepts/models.md (modified, +2/-0)
  • docs/docs.json (modified, +8/-1)
  • docs/gateway/configuration-reference.md (modified, +31/-12)
  • docs/help/faq.md (modified, +36/-0)
  • docs/help/testing.md (modified, +22/-0)
  • docs/install/updating.md (modified, +1/-0)
  • docs/plugins/architecture.md (modified, +1/-0)
  • docs/plugins/building-plugins.md (modified, +1/-0)
  • docs/plugins/manifest.md (modified, +76/-30)
  • docs/plugins/sdk-migration.md (modified, +11/-1)
  • docs/plugins/sdk-overview.md (modified, +22/-9)
  • docs/providers/bedrock-mantle.md (modified, +20/-7)
  • docs/providers/bedrock.md (modified, +29/-0)
  • docs/providers/comfy.md (added, +201/-0)
  • docs/providers/fal.md (modified, +2/-1)
  • docs/providers/google.md (modified, +30/-0)
  • docs/providers/index.md (modified, +4/-0)
  • docs/providers/minimax.md (modified, +29/-0)
  • docs/providers/models.md (modified, +4/-0)
  • docs/providers/openai.md (modified, +10/-2)
  • docs/providers/runway.md (added, +63/-0)
  • docs/providers/vydra.md (added, +123/-0)
  • docs/reference/memory-config.md (modified, +117/-98)
  • docs/tools/image-generation.md (modified, +21/-17)
  • docs/tools/index.md (modified, +14/-7)
  • docs/tools/lobster.md (modified, +11/-9)
  • docs/tools/music-generation.md (added, +208/-0)
  • docs/tools/plugin.md (modified, +1/-0)
  • docs/tools/slash-commands.md (modified, +1/-1)
  • docs/tools/video-generation.md (modified, +147/-84)
  • docs/web/control-ui.md (modified, +4/-1)
  • docs/web/dashboard.md (modified, +2/-0)
  • dream-diary-preview-v2.html (added, +399/-0)
  • dream-diary-preview-v3.html (added, +323/-0)
  • extensions/amazon-bedrock-mantle/api.ts (modified, +2/-0)
  • extensions/amazon-bedrock-mantle/bedrock-token-generator.d.ts (added, +6/-0)
  • extensions/amazon-bedrock-mantle/discovery.test.ts (modified, +101/-3)
  • extensions/amazon-bedrock-mantle/discovery.ts (modified, +64/-13)
  • extensions/amazon-bedrock-mantle/package.json (modified, +3/-0)
  • extensions/bluebubbles/src/accounts.ts (modified, +5/-1)
  • extensions/bluebubbles/src/monitor.ts (modified, +1/-1)
  • extensions/browser/src/browser/chrome.default-browser.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/client-fetch.loopback-auth.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/control-service.plugin-disabled.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/profiles-service.test.ts (modified, +5/-8)
  • extensions/browser/src/browser/pw-tools-core.clamps-timeoutms-scrollintoview.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/pw-tools-core.interactions.batch.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/pw-tools-core.interactions.evaluate.abort.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/pw-tools-core.interactions.set-input-files.test.ts (modified, +2/-4)
  • extensions/browser/src/browser/pw-tools-core.last-file-chooser-arm-wins.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/pw-tools-core.screenshots-element-selector.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/routes/agent.existing-session.test.ts (modified, +3/-8)
  • extensions/browser/src/browser/routes/basic.existing-session.test.ts (modified, +3/-8)
  • extensions/browser/src/browser/server-context.existing-session.test.ts (modified, +3/-8)
  • extensions/browser/src/browser/server-context.hot-reload-profiles.test.ts (modified, +6/-12)
  • extensions/browser/src/browser/server-context.remote-profile-tab-ops.fallback.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/server-context.remote-profile-tab-ops.playwright.test.ts (modified, +2/-6)
  • extensions/browser/src/browser/server-lifecycle.test.ts (modified, +3/-8)
  • extensions/browser/src/browser/server.control-server.test-harness.ts (modified, +2/-1)
  • extensions/browser/src/browser/server.evaluate-disabled-does-not-block-storage.test.ts (modified, +3/-8)
  • extensions/browser/src/cli/browser-cli.test-support.ts (modified, +1/-1)
  • extensions/browser/src/cli/command-format.ts (modified, +1/-1)
  • extensions/browser/src/config/config.ts (modified, +1/-1)
  • extensions/browser/src/core-api.ts (modified, +25/-20)
  • extensions/browser/src/doctor-browser.ts (modified, +1/-1)
  • extensions/browser/src/gateway/auth.ts (modified, +1/-1)
  • extensions/browser/src/gateway/startup-auth.ts (modified, +1/-1)
  • extensions/browser/src/infra/errors.ts (modified, +1/-1)
  • extensions/browser/src/infra/fs-safe.ts (modified, +1/-1)
  • extensions/browser/src/infra/net/proxy-env.ts (modified, +1/-1)
  • extensions/browser/src/infra/net/ssrf.ts (modified, +1/-1)
  • extensions/browser/src/infra/path-guards.ts (modified, +1/-1)
  • extensions/browser/src/infra/ports.ts (modified, +1/-1)

PR #61463: fix(agents,gateway): phase-aware assistant text extraction — suppress OpenAI commentary leaks in sessions-helpers, TUI, and REST history

Description (problem / solution / changelog)

What this fixes (plain English)

Several places in the codebase that display assistant text were not aware of the phase system (commentary vs. final answer). This meant internal "thinking out loud" commentary could leak into user-visible surfaces like the TUI and HTTP/SSE session history endpoints. This PR makes those surfaces phase-aware, and hardens heartbeat session resolution against subagent key leaks.

Technical details

Follow-up to #59643 and #59150, which fixed phase separation in the core WS path. Post-merge audit found adjacent surfaces still using phase-blind extraction.

Surfaces fixed:

  • src/tui/tui-formatters.ts — assistant text extraction now uses extractAssistantVisibleText() to prefer final_answer over commentary phase blocks
  • src/infra/heartbeat-runner.ts — heartbeat session resolution now rejects subagent session keys, falling back to the main session key instead of leaking into subagent scopes
  • src/gateway/sessions-history-http.test.ts — regression test: REST history applies chat.history sanitization (strips NO_REPLY messages, preserves phase blocks)
  • src/tui/tui-formatters.test.ts — regression test: mixed commentary + final_answer blocks extract only the visible final answer

Explicitly deferred: extractAssistantTextForSilentCheck and buffered delta/final rendering — lower-confidence, more behaviorally sensitive.

Related

  • Follow-up to #59643 and #59150
  • Follow-up issues: #61474, #61475, #61476, #61477, #61478
  • Companion PRs: #61529, #61528, #61527
  • Related PRs: #61855, #61816

Test plan

  • 27/27 TUI formatter tests pass
  • Regression test for mixed commentary + final_answer in TUI extraction
  • HTTP history regression test (REST path shares chat.history sanitization, strips NO_REPLY messages)
  • SSE seq validation for NO_REPLY message stripping
  • sessions-history-http gateway integration tests have 2 pre-existing infra failures (also fail on upstream/main)

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/infra/heartbeat-runner.subagent-session-guard.test.ts (added, +72/-0)
  • src/infra/heartbeat-runner.ts (modified, +24/-17)
  • src/tui/tui-formatters.test.ts (modified, +20/-0)
  • src/tui/tui-formatters.ts (modified, +25/-0)

PR #61829: fix(agents): harden OpenAI phase-aware visible text — suppress commentary partials, prevent empty final_answer fallback leak

Description (problem / solution / changelog)

Fixes #61474, #61475.

Recreated from clean branch (previous PR #61481 closed as dirty).

Summary

  • Prevent final_answer blocks from falling back to commentary text when sanitized to empty
  • Suppress all commentary-phase partial streaming output
  • Align session-history HTTP/SSE sanitization with hardened chat history paths
  • Add regression tests for leak prevention

Related

  • Parent fix: #59643
  • Sibling PR: #61463
  • Initiative: #25592, #59536

Test plan

  • Commentary partials suppressed in streaming
  • Empty final_answer doesn't fall back to commentary
  • SSE history sanitized consistently
  • Bounded SSE paths sanitized

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) [email protected]

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/agents/pi-embedded-subscribe.handlers.messages.test.ts (modified, +5/-1)
  • src/agents/pi-embedded-subscribe.handlers.messages.ts (modified, +11/-9)
  • src/agents/pi-embedded-utils.test.ts (modified, +17/-0)
  • src/agents/pi-embedded-utils.ts (modified, +29/-10)
  • src/agents/tools/chat-history-text.ts (modified, +21/-7)
  • src/gateway/server-methods/chat.ts (modified, +26/-4)
  • src/gateway/sessions-history-http.test.ts (modified, +237/-0)
  • src/gateway/sessions-history-http.ts (modified, +56/-17)

Code Example

if (deliveryPhase === "commentary" && !phaseAwareVisibleText) return;
RAW_BUFFERClick to expand / collapse

Bug

After #59643 merged, the streaming path in src/agents/pi-embedded-subscribe.handlers.messages.ts can still emit commentary-phase partials as user-visible output.

Root cause

The suppression check at :332-356 only returns early when:

if (deliveryPhase === "commentary" && !phaseAwareVisibleText) return;

So commentary partials with any visible text survive the gate. next = phaseAwareVisibleText || ... then forwards it downstream. Users see commentary first, then it gets replaced when final_answer arrives later — which is exactly the preview-then-replace behavior the maintainer rejected in the #59643 review.

Expected behavior

Commentary-phase partials should be fully suppressed regardless of whether they contain visible text. Only final_answer phase should emit visible partial updates.

Code location

  • src/agents/pi-embedded-subscribe.handlers.messages.ts:332-356, 380-390
  • The replacement heuristic at message_end (:487-559) catches some cases, but users still see commentary first during streaming.

Severity

P1 — the maintainer explicitly flagged this class of behavior in the #59643 review.

Related

  • Parent fix: #59643
  • Maintainer review: #59643 (review)
  • Issue: #59150

extent analysis

TL;DR

The fix likely involves modifying the suppression check to fully suppress commentary-phase partials, regardless of visible text.

Guidance

  • Review the suppression check at src/agents/pi-embedded-subscribe.handlers.messages.ts:332-356 and consider modifying the condition to always return early for commentary phases.
  • Verify that the next variable is not forwarding commentary partials downstream by checking the value of phaseAwareVisibleText.
  • Investigate the replacement heuristic at :487-559 to ensure it is correctly catching and replacing commentary partials.
  • Test the changes with different input scenarios to ensure commentary-phase partials are fully suppressed.

Example

if (deliveryPhase === "commentary") return; // potential modification to suppress commentary phases

Notes

The provided code snippet is a potential modification, but its correctness depends on the surrounding code and requirements. Additional testing and verification are necessary to ensure the fix works as expected.

Recommendation

Apply workaround: Modify the suppression check to fully suppress commentary-phase partials, as this directly addresses the identified issue and expected behavior.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Commentary-phase partials should be fully suppressed regardless of whether they contain visible text. Only final_answer phase should emit visible partial updates.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING