fix: prevent A2A target from reverse-calling sessions_send to requester (#39476)#91161
fix: prevent A2A target from reverse-calling sessions_send to requester (#39476)#91161sumitaich1998 wants to merge 1 commit into
Conversation
…er (openclaw#39476) Co-authored-by: Cursor <[email protected]>
|
Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 3:55 PM ET / 19:55 UTC. Summary PR surface: Source +42, Tests +91. Total +133 across 7 files. Reproducibility: yes. at source level: current Review metrics: 1 noteworthy metric.
Stored data model 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. Rank-up moves:
Proof guidance:
Proof path suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the provenance-scoped OpenClaw tool-loop guard after redacted real A2A proof shows one requester-visible delivery, no reverse duplicate, and normal non-A2A tool availability unchanged. Do we have a high-confidence way to reproduce the issue? Yes at source level: current Is this the best way to solve the issue? Yes, pending proof: using input provenance to hide only AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 076da567f434. Label changesLabel justifications:
Evidence reviewedPR surface: Source +42, Tests +91. Total +133 across 7 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
|
|
@sumitaich1998 Could you add the requested redacted live A2A behavior proof, or indicate whether help is needed to produce it? We have reproduced user-visible duplicate Telegram delivery in a real |
|
ClawSweeper status: review started. I am starting a fresh review of this pull request: fix: prevent A2A target from reverse-calling sessions_send to requester (#39476) This is item 1/1 in the current shard. Shard 0/20. This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking. Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted. |
|
I ported this fix and its tests onto current The current runtime now groups conversation tools next to |
thankyou brother @dankarization |
Summary
A2A
sessions_sendcould post duplicate messages in the requester's channel. When Agent A callssessions_send(agentId: "agentB", ...), Agent B's reply already flows back to A through thesessions_sendtool result (and the announce flow). But because the A2A-routed turn still exposed thesessions_sendtool to Agent B, B could reverse-callsessions_sendback to A during the same turn, so A received the same content twice.This change removes
sessions_sendfrom any turn that is itself processing an inter-agentsessions_sendmessage, so the target can no longer reverse-call the requester. Result delivery is unchanged (it still happens via the tool return value / announce flow).Fixes #39476
Root cause
The initial send (
src/agents/tools/sessions-send-tool.ts) and the A2A flow (runSessionsSendA2AFlowinsrc/agents/tools/sessions-send-tool.a2a.ts) route the requester's message into the target session as an inter-session turn (inputProvenance.kind = "inter_session",sourceTool = "sessions_send") and expose the requester's session viabuildAgentToAgentMessageContext. The target's per-turn tool list (built increateOpenClawTools) still includedsessions_send, so the target model could call it back at the requester and duplicate the reply that the tool already returns.The fix (Option A: code-level tool guard)
Drop
sessions_sendfrom the target's tool set for the A2A-routed turn:src/sessions/input-provenance.ts: addisAgentToAgentSendInputProvenance(), true whenkind === "inter_session"andsourceTool === "sessions_send".src/agents/embedded-agent-runner/run/attempt.tool-run-context.ts: deriveinterAgentSendTurnfrom the run'sinputProvenanceand forward it into tool construction.src/agents/agent-tools.tsandsrc/agents/openclaw-tools.ts: threadinterAgentSendTurnthrough and skip creatingsessions_sendwhen it is set.This covers all A2A target turns (initial send, ping-pong, announce) because they all carry the same provenance, and it is per-turn: a normal (non-A2A) turn restores
sessions_send. Other session tools (sessions_list,sessions_history) stay available during the routed turn.subagent_announceand other inter-session handoffs use a differentsourceTooland are unaffected.Note: this guards the OpenClaw agent loop's
sessions_sendtool. Native-harness (e.g. Codex MCP-bridged) tool exposure is a separate surface and is not changed here.Verification
Focused colocated unit tests, run single-worker to keep local CPU low:
OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs src/agents/openclaw-tools.inter-agent-send.test.ts-> 2 passed (provessessions_sendis present on a normal turn and absent on an A2A turn, whilesessions_list/sessions_historyremain).node scripts/run-vitest.mjs src/sessions/input-provenance.test.ts-> passed (newisAgentToAgentSendInputProvenancecases).node scripts/run-vitest.mjs src/agents/embedded-agent-runner/run/attempt.test.ts -t "buildEmbeddedAttemptToolRunContext"-> passed (provenance ->interAgentSendTurnderivation).Intentionally NOT run in this environment to limit local CPU/heat:
tsgo/typecheck,pnpm build, and the full test suite. OpenClaw was NOT run locally (no gateway/CLI); a live multi-agent A2A round-trip was not exercised.