Skip to content

fix #96840: [Bug]: Targetless message.send fails with 'Action send requires a target' in WebChat despite docs stating source-reply sink should handle it#97167

Merged
steipete merged 2 commits into
openclaw:mainfrom
zhangguiping-xydt:feat/issue-96840
Jul 1, 2026
Merged

Conversation

@zhangguiping-xydt

@zhangguiping-xydt zhangguiping-xydt commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Closes #96840

Related: #96850 (closed as superseded)

What Problem This Solves

Fixes an issue where users in an active WebChat conversation could not use a targetless message.send: OpenClaw returned Action send requires a target instead of showing the tool message in the current chat. The fix also preserves the ordinary automatic final answer after the tool message.

Why This Change Was Made

WebChat direct runs intentionally use automatic final replies, while targetless message-tool sends need the existing current-run internal source sink. This change keeps those contracts separate: the tool send selects the internal sink locally, confirmed source replies are projected into the transcript/UI, and source-mirror payloads bypass outbound-message dedupe. Explicit external routes and message_tool_only runs keep their existing behavior.

No config, schema, dependency, migration, reusable WebChat outbound channel, or protocol change is included.

User Impact

Agents can send visible progress or intermediate results with targetless message.send in the current WebChat conversation and still provide a normal visible final answer. External routed sends are unaffected.

Evidence

The permanent integration executes the real path:

createMessageToolrunMessageAction current-run sink → tool-result extraction → embedded payload construction → final reply dedupe.

It proves the result contains both the source mirror and the automatic final, even when matching message-tool sent-text evidence is present.

Focused proof:

  • node scripts/run-vitest.mjs run --config test/vitest/vitest.agents-tools.config.ts --configLoader runner src/agents/tools/message-tool.internal-source-reply.integration.test.ts --reporter=dot --maxWorkers=1 — 1/1 passed.
  • node scripts/run-vitest.mjs run --config test/vitest/vitest.auto-reply-reply.config.ts --configLoader runner src/auto-reply/reply/agent-runner-payloads.test.ts --reporter=dot --maxWorkers=1 — 65/65 passed.
  • node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner ui/src/ui/e2e/chat-flow.e2e.test.ts -t "keeps a targetless message-tool source reply beside the automatic final reply" --reporter=verbose --maxWorkers=1 — Chromium 1 passed, 16 skipped.
  • Sibling message-tool, embedded-payload, source-delivery, outbound-validation, and Gateway mirror-projection suites passed.
  • Remote check:changed passed on Blacksmith Testbox provider blacksmith-testbox, id tbx_01kwder0yyzbvqg0k1em246p6t, Actions run 28483225303.
  • Fresh final autoreview traced the full sink/extraction/payload/dedupe/Gateway/UI path and returned zero findings (0.96 confidence).
  • Deterministic local Chromium screenshots/video were captured as uncommitted proof artifacts and were not pushed to the repository.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Jun 27, 2026
@clawsweeper

clawsweeper Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 30, 2026, 8:06 PM ET / 00:06 UTC.

Summary
The PR defaults targetless internal WebChat message-tool sends to the current-run source-reply sink, preserves source-reply payloads beside automatic final replies, and adds focused agent, reply-payload, and Control UI coverage.

PR surface: Source +12, Tests +164. Total +176 across 6 files.

Reproducibility: yes. Source inspection shows current main leaves implicit internal WebChat message tools without message_tool_only, the sink requires that mode, and target validation throws once the sink is skipped.

Review metrics: 1 noteworthy metric.

  • Delivery Behavior Surfaces: 1 tool-local routing default plus 2 reply-payload preservation paths changed. The change is small but crosses message-tool routing, source-reply transcript mirroring, and automatic final-reply delivery semantics.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #96840
Summary: This PR is the active candidate fix for the canonical WebChat targetless message.send bug; older same-root-cause PRs are closed unmerged, and the earlier merged sink PR established the infrastructure but did not solve this specific construction/payload gap.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Maintainer confirms the WebChat message-delivery semantics and waits for required checks before merge.

Mantis proof suggestion
A short visible Control UI/WebChat proof would make the delivery change easier for maintainers to validate, even though copied chat.history output is sufficient for the proof gate. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis visual task: verify that targetless tools.message from an active WebChat run appears in the same Control UI chat while the automatic final reply also remains visible.

Risk before merge

  • [P1] Merging intentionally changes a WebChat message-delivery path: targetless internal WebChat message-tool sends can now project to the current chat while automatic final replies remain visible, so maintainers should explicitly own that behavior.

Maintainer options:

  1. Use This As The Landing Candidate (recommended)
    Maintainers can confirm the intended WebChat delivery semantics, wait for required checks, and land this PR as the canonical fix for the linked issue.
  2. Request A Short Visual Proof
    If maintainers want UI-level evidence beyond copied chat.history output, ask for a short Control UI/WebChat capture before merge.
  3. Pause For A Different Boundary
    If maintainers prefer solving this in gateway tool resolution or the internal sink instead, pause this PR and ask for that narrower replacement shape.

Next step before merge

  • No automated repair is needed; the remaining action is maintainer review of the intentional WebChat message-delivery behavior and normal merge gates.

Security
Cleared: The diff changes message-tool routing and tests only; it does not touch dependencies, workflows, credentials, permissions, lockfiles, package metadata, or artifact execution paths.

Review details

Best possible solution:

Land one narrow fix after maintainer message-delivery review confirms the source-reply plus automatic-final behavior, then close the linked issue and closed duplicate candidates as resolved by the selected branch.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main leaves implicit internal WebChat message tools without message_tool_only, the sink requires that mode, and target validation throws once the sink is skipped.

Is this the best way to solve the issue?

Yes. After the follow-up commit, the fix is at the message-tool construction and payload projection boundary rather than making all WebChat runs message-tool-only; that preserves ordinary automatic finals while repairing targetless tool sends.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5e572dcf781a.

Label changes

Label justifications:

  • P2: This is a normal-priority documented WebChat message-delivery bug fix with limited but user-visible blast radius.
  • merge-risk: 🚨 message-delivery: The PR changes how targetless internal WebChat message-tool sends are delivered and mirrored beside automatic final replies.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes copied live output from a connected Control UI WebSocket chat.history path showing the targetless message-tool send in visible history after the fix, plus focused regression commands.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes copied live output from a connected Control UI WebSocket chat.history path showing the targetless message-tool send in visible history after the fix, plus focused regression commands.
Evidence reviewed

PR surface:

Source +12, Tests +164. Total +176 across 6 files.

View PR surface stats
Area Files Added Removed Net
Source 3 23 11 +12
Tests 3 164 0 +164
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 6 187 11 +176

What I checked:

  • Current main still forwards no implicit WebChat sink mode: On current main, createMessageTool still passes only options?.sourceReplyDeliveryMode into the outbound runner, so an implicit internal WebChat message tool does not opt into the sink without this PR. (src/agents/tools/message-tool.ts:1390, 5e572dcf781a)
  • Internal sink contract requires message_tool_only: The current internal source-reply sink accepts targetless sends only when action is send, sourceReplyDeliveryMode is message_tool_only, a current source context exists, a session key exists, and no explicit route is supplied. (src/infra/outbound/internal-source-reply.ts:86, 5e572dcf781a)
  • Fallback target validation matches the reported failure: If the sink is skipped, message-action normalization still throws Action send requires a target for target-requiring sends without inferred or explicit target. (src/infra/outbound/message-action-normalization.ts:72, 5e572dcf781a)
  • Documented WebChat behavior expects the sink: The WebChat docs state that targetless message.send from an active WebChat run is projected into the same chat and mirrored to the transcript without making WebChat a reusable outbound channel. Public docs: docs/web/webchat.md. (docs/web/webchat.md:55, 5e572dcf781a)
  • PR head separates tool-local sink routing from final-answer guidance: At PR head, createMessageTool derives a sourceReplySinkDeliveryMode for internal WebChat sends and passes that local mode to runMessageActionForTool while leaving the description on the run-level sourceReplyDeliveryMode. (src/agents/tools/message-tool.ts:1150, f3a0dabf3efb)
  • PR head preserves source mirrors alongside automatic final replies: The top commit changes embedded payload construction so internal source-reply mirrors are emitted even when the run-level mode is automatic, while only message_tool_only suppresses the separate automatic final answer. (src/agents/embedded-agent-runner/run/payloads.ts:279, f3a0dabf3efb)

Likely related people:

  • pashpashpash: Authored and merged the WebChat/TUI internal UI message-tool sink and the WebChat docs contract that this PR builds on. (role: feature-history owner; confidence: high; commits: 78eb92e62277; files: src/infra/outbound/message-action-runner.ts, src/infra/outbound/internal-source-reply.ts, docs/web/webchat.md)
  • steipete: Recent history changed WebChat delivery prompt/source behavior around the automatic versus message-tool-only boundary, and the current PR now includes a follow-up commit from this account. (role: recent related behavior owner; confidence: high; commits: 0cfccdb0c7ee, f3a0dabf3efb; files: src/gateway/tool-resolution.ts, src/gateway/tool-resolution.test.ts, src/auto-reply/reply/source-reply-delivery-mode.ts)
  • vincentkoc: Recent history touched the internal source-reply and message-action runner paths that gate targetless current-source sends. (role: recent adjacent owner; confidence: medium; commits: 0ea08076c3b5; files: src/infra/outbound/internal-source-reply.ts, src/infra/outbound/message-action-runner.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 27, 2026
@steipete steipete self-assigned this Jun 30, 2026
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui size: S and removed size: XS labels Jul 1, 2026
@steipete

steipete commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Maintainer proof complete for final exact head 0eb5874676ea641c14ad9062b6ebf96b30f82a33, synced after #96544 landed.

The final patch routes targetless WebChat message.send through the current-run internal sink without changing the run-level automatic final-reply contract. It carries confirmed source mirrors through embedded payload construction and final outbound dedupe, so the tool reply and ordinary final both remain visible. Explicit external routes and existing message_tool_only suppression remain unchanged.

Proof:

  • Real tool-to-source-sink integration: node scripts/run-vitest.mjs src/agents/tools/message-tool.internal-source-reply.integration.test.ts --reporter=dot --maxWorkers=1 — 1/1 passed after the final local rebase.
  • Embedded payload overlap: node scripts/run-vitest.mjs src/agents/embedded-agent-runner/run/payloads.test.ts --reporter=dot --maxWorkers=1 — 40/40 passed after the final local rebase.
  • Final reply owner suite: node scripts/run-vitest.mjs run --config test/vitest/vitest.auto-reply-reply.config.ts --configLoader runner src/auto-reply/reply/agent-runner-payloads.test.ts --reporter=dot --maxWorkers=1 — 65/65 passed.
  • Control UI Chromium E2E: node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner ui/src/ui/e2e/chat-flow.e2e.test.ts -t "keeps a targetless message-tool source reply beside the automatic final reply" --reporter=verbose --maxWorkers=1 — 1 passed, 16 skipped. Screenshots/video were kept uncommitted.
  • Sibling message-tool, source-delivery, outbound-validation, and Gateway mirror-projection suites passed.
  • Remote changed gate: provider blacksmith-testbox, id tbx_01kwder0yyzbvqg0k1em246p6t, Actions 28483225303 — passed.
  • Final exact-head CI: Actions 28484794766 — 44 successful jobs, nine expected skips, zero failures/pending.
  • All six reviewed local/remote blob IDs match exactly; git diff --check origin/main...HEAD passes and only the six reviewed files differ.
  • Fresh final autoreview: zero accepted/actionable findings; 0.96 confidence.
  • scripts/pr review-validate-artifacts 97167 passed; wrapper sync used the exact-lease force-with-lease transport and verified the remote tree.

No unresolved review threads or known proof gaps remain. Ready to land.

@steipete
steipete force-pushed the feat/issue-96840 branch from f3a0dab to 0eb5874 Compare July 1, 2026 00:24
@steipete
steipete merged commit 29371cf into openclaw:main Jul 1, 2026
94 checks passed
@steipete

steipete commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 1, 2026
… send requires a target' in WebChat despite docs stating source-reply sink should handle it (openclaw#97167)

* fix(message): route WebChat sends to source sink

* fix(webchat): preserve message tool source replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
… send requires a target' in WebChat despite docs stating source-reply sink should handle it (openclaw#97167)

* fix(message): route WebChat sends to source sink

* fix(webchat): preserve message tool source replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
… send requires a target' in WebChat despite docs stating source-reply sink should handle it (openclaw#97167)

* fix(message): route WebChat sends to source sink

* fix(webchat): preserve message tool source replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
… send requires a target' in WebChat despite docs stating source-reply sink should handle it (openclaw#97167)

* fix(message): route WebChat sends to source sink

* fix(webchat): preserve message tool source replies

---------

Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: web-ui App: web-ui merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Targetless message.send fails with 'Action send requires a target' in WebChat despite docs stating source-reply sink should handle it

2 participants