fix: avoid image placeholder for empty text tool results#97423
Conversation
Co-authored-by: Copilot <[email protected]>
|
Codex review: needs maintainer review before merge. Reviewed June 28, 2026, 4:28 AM ET / 08:28 UTC. Summary PR surface: Source +27, Tests +325. Total +352 across 6 files. Reproducibility: yes. Current main source-reproduces the scoped bug: empty non-image tool results are converted to Review metrics: none identified. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Merge this scoped empty non-image replay fix after maintainer review, and keep or narrow the canonical issue for broader session-poisoning symptoms that remain. Do we have a high-confidence way to reproduce the issue? Yes. Current main source-reproduces the scoped bug: empty non-image tool results are converted to Is this the best way to solve the issue? Yes for the scoped bug. Distinguishing empty non-image output from image-bearing output at each OpenAI-compatible replay boundary is the narrow maintainable fix, but it is not complete closure proof for #96857. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 9c95abd49d45. Label changesLabel justifications:
Evidence reviewedPR surface: Source +27, Tests +325. Total +352 across 6 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
|
…w#97423) Co-authored-by: scribe-dandelion-cult <[email protected]> Co-authored-by: Copilot <[email protected]>
…w#97423) Co-authored-by: scribe-dandelion-cult <[email protected]> Co-authored-by: Copilot <[email protected]>
…w#97423) Co-authored-by: scribe-dandelion-cult <[email protected]> Co-authored-by: Copilot <[email protected]>
…w#97423) Co-authored-by: scribe-dandelion-cult <[email protected]> Co-authored-by: Copilot <[email protected]>
What Problem This Solves
Fixes a replay/projection bug where an empty non-image tool result can be replayed to OpenAI-compatible providers as the literal image placeholder:
That placeholder should only represent actual image-bearing tool output. Empty non-image tool output should not make the model believe it received an image.
This matches symptoms reported in #96857, where normal text-producing tool results can degrade into
(see attached image)placeholders in agent context. Related/duplicate history includes #97321, #96861, and #75674.A downstream live reproduction using
github-copilot/gpt-5.5found a strong breadcrumb aroundupdate_plan: it returnscontent: []with plan data indetails, and it is replay-safe. Once that empty non-image result enters replay history, later tool output can appear poisoned in the session. This PR fixes the deterministic bad replay branch; it does not claimupdate_planis the only trigger for every session-local poisoning case.Evidence
The bug is a deterministic conversion branch:
(see attached image);(no output);The regression tests model
update_plan-style empty non-image output and assert the corrected replay directly across OpenAI-compatible paths:src/llm/providers/openai-responses-shared.test.tssrc/agents/openai-transport-stream.test.tssrc/llm/providers/openai-completions.test.tsValidation command run on the fork branch:
Result:
3 files / 340 tests passed.Behavioral proof: N/A — this is a deterministic replay/projection logic error captured by unit tests. No live model call is required to prove the branch condition.
Summary
(no output).(see attached image)reserved for actual image-bearing tool results.Non-goals
update_plansemantics or require it to emit user-visible text.Live operational proof (separate from continuation)
This proof is not part of the continuation PR and does not change the
2723dbeecontinuation proof basis. It is scoped only to this placeholder/replay fix.Live docs proof corpus:
karmaterminal/karmaterminal-openclaw-docs@852173d710cee49c16cc52479d9e0b16cdba077bPR-1120/PROOFS/empty-tool-placeholder-live/INDEX.jsonPR-1120/PROOFS/empty-tool-placeholder-live/manifest.jsonPR-1120/PROOFS/empty-tool-placeholder-live/cael-live-mitigation.mdOperational sequence:
echo,read,write,gh api,web_fetch) rendered only as(see attached image), so he refused blind mutation.frond-build/20260628/2723dbee-empty-tool-placeholderat64324505fcb8be367abe91234bbb811b005466b4, whose parent is exact runtime2723dbee783c113cae70e4fb63a4cff9f55402e3plus only this placeholder fix.echorendered as text:figs love you and this is shit and youre so brave.R-CW-DELEGATE-CHILD-LIVE -> commit d2b0158471c85dddf0eeb1be302e008e85017fbc.Interpretation: this is live mitigation evidence for the same placeholder poisoning class. The deterministic proof remains the unit coverage in this PR; the live proof shows the frond-build containing this fix restored Cael’s ability to read text tool output and complete a docs commit.