fix(agents): deliver DM media completions directly when requester handoff fails#95065
fix(agents): deliver DM media completions directly when requester handoff fails#95065movefaster wants to merge 1 commit into
Conversation
…doff fails Background image_generate completions were silently dropped when the requester session's captured origin had no deliverable channel: the MCP-bridged claude-cli runtime collapses it to the internal "webchat" channel, so deliveryTarget.deliver was false and every direct-delivery fallback no-op'd. Backfill the completion origin from the requester session's persisted delivery context (deliveryContextFromSession), and deliver generated media directly for DM targets when the requester-agent handoff fails, not only on session-write-lock errors. Adds a regression test that supplies no deliverable origin in any param and asserts delivery routes to the backfilled target (negative-control verified: fails without the backfill). Full delivery suite: 102 passing. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close as superseded: the useful stored-origin backfill is already covered by an open proof-positive PR, while this draft's unique generic DM handoff-error fallback is unsafe and lacks real behavior proof. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Canonical path: Review or land #89949 for the session-store origin backfill, and require any future DM handoff-error media fallback to preserve terminal partial-send outcomes and prove no duplicate delivery. So I’m closing this here and keeping the remaining discussion on #89949. Review detailsBest possible solution: Review or land #89949 for the session-store origin backfill, and require any future DM handoff-error media fallback to preserve terminal partial-send outcomes and prove no duplicate delivery. Do we have a high-confidence way to reproduce the issue? Partially. Source inspection and the added tests show the missing-target path when completion origin lacks a deliverable route, but this PR does not provide a real MCP-bridged or live DM media reproduction after the patch. Is this the best way to solve the issue? No. The stored-origin backfill is the right layer but is already covered by #89949, and this PR's generic DM handoff-error fallback is broader than the current no-unknown-side-effect guard. Security review: Security review cleared: The diff changes agent delivery logic and tests only; no dependency, workflow, secret, package, or supply-chain surface is changed. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against aff6e221a7b5. |
|
Closing as a duplicate of #89949, which implements the same fix — backfilling the completion delivery origin from the requester session entry via |
Problem
Background task completions (e.g.
image_generate) were silently dropped for direct-message requesters under the MCP-bridgedclaude-cliruntime. The task completed and its side effects ran, but the user-requested media/text never arrived — the user got nothing until a later message re-triggered a normal turn.Root cause
In
sendSubagentAnnounceDirectly, the requester's captured completion origin can carry no deliverable channel: the MCP-bridged CLI runtime builds tools with an emptyagentChannel, so the origin collapses to the internalwebchatchannel.stripNonDeliverableChannelForCompletionOriginthen strips it, leavingdeliveryTarget.deliver = false.With no deliverable target, the existing direct-delivery fallbacks (
deliverGeneratedMediaCompletionDirect/deliverTextCompletionDirect) all no-op, and the woken requester-agent handoff turn — running undermessage_tool_only— isn't guaranteed to call the message tool. The result is a silent drop with no error.(Native runtimes avoid this because they build tools per-turn with the live inbound origin. The last-mile delivery path itself is shared across all runtimes, so the fix lives at that shared layer.)
Fix
In
sendSubagentAnnounceDirectly:deliveryContextFromSession(requesterEntry)) when the captured origin has no deliverable channel. This restores a real target so the existing fallbacks can fire.Real behavior proof
Behavior or issue addressed: A background
image_generatecompletion to a Telegram DM was silently dropped — the generated image never reached the user until they sent another message. After the fix the completion delivers the image to the DM autonomously.Real environment tested: Live OpenClaw gateway on the patched build,
claude-cliruntime over the MCP bridge, real Telegram DM channel. User/bot IDs and meal content redacted.Exact steps or command run after fix: In a real Telegram DM, sent a request that fires a background
image_generate, then left the session idle (no further messages, no manual ping) and watched the gateway journal for the completion delivery.Evidence after fix: redacted runtime log (2026-06-18) — the background completion wakes the idle session and delivers the image with no ping and no "delivery not confirmed" warning:
Observed result after fix: the image (
sendPhoto messageId=1293) was delivered to the DM autonomously, with no manual ping. Reproduced across multiple background image completions the same day (sendPhoto messageIds 1287 / 1293 / 1298 / 1304) — every one delivered with noreason=no_active_rundrop and nocompletion delivery was not confirmed after successful generationwarning.Before evidence (optional): diagnosed 2026-06-16/06-17 (those daily logs have since rotated). The same background completion was silently dropped:
reason=no_active_run, the fallback requester-agent handoff turn wentprivate-final(never called the message tool undermessage_tool_only), and the runtime logged... completion delivery was not confirmed after successful generation(emitted atsrc/agents/tools/media-generate-background-shared.ts:443). The user received nothing until a manual ping re-triggered a turn.What was not tested: the active-session steered path (a completion landing in a genuinely active run) is intentionally unchanged — it stays delegated to that turn to avoid double-messaging a live conversation, so it was not separately exercised here.
Testing (supplemental)
subagent-announce-deliverysuite: 102 passing.Notes
This intentionally does not change the active-session steered path (covered by
does not also direct-run a queued active completion): a completion landing in a genuinely active run is still delegated to that turn, to avoid double-messaging a live conversation.🤖 Generated with Claude Code