fix(auto-reply): deliver /compact in room_event via explicit-command bypass; notifyUser independent of internal callbacks (#87107)#87171
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 29, 2026, 2:38 AM ET / 06:38 UTC. Summary PR surface: Source +31, Tests +125. Total +156 across 6 files. Reproducibility: yes. for the core paths by source inspection: current main still suppresses marked Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused shared auto-reply fix after maintainer review confirms the current-main merge result keeps ambient room-event privacy while restoring visible command and compaction status delivery. Do we have a high-confidence way to reproduce the issue? Yes for the core paths by source inspection: current main still suppresses marked Is this the best way to solve the issue? Yes, the proposed direction is the narrow shared fix: mark command terminal replies, allow only explicit command room-event bypasses, and keep hook-message overlap suppression for compaction notices. A Feishu-only workaround would be less maintainable. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 4829d30cf015. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +31, Tests +125. Total +156 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
|
|
ClawSweeper PR egg ✨ Hatched: 🥚 common Cosmic Branchling Hatch commandComment Hatchability rules:
Rarity: 🥚 common. What is this egg doing here?
|
909f696 to
d92d021
Compare
d92d021 to
0cd11b8
Compare
0cd11b8 to
d029447
Compare
This comment was marked as spam.
This comment was marked as spam.
…icit command turns; deliver /compact + notifyUser independently of internal callbacks Three distinct silent-delivery failures of /compact and compaction notices on 2026.5.22 (openclaw#87107): 1. dispatch-from-config previously rejected the deliverDespiteSourceReplySuppression marker for every room_event, so a marked authorized /compact reply (Feishu group / WebChat room-event) was silently dropped. Narrow the bypass to honor the marker when the current ctx represents an explicit command turn (isExplicitCommandTurn: native or authorized text-slash); ambient marked runtime failure notices and sendPolicy: deny stay suppressed. 2. handleInlineActions terminal replies (/compact, /status, skill tool blocked / error replies) did not carry the marker, so the same suppress branch silently dropped them under messages.visibleReplies: "message_tool" configs. Wrap every { kind: "reply", reply } exit in a local helper that calls markReplyPayloadForSourceSuppressionDelivery. 3. The compaction-event handler in runAgentTurnWithFallback gated notifyUser on the absence of onCompactionStart / onCompactionEnd callbacks, conflating internal Control-UI callbacks with the opt-in user-channel notice. Drop the callback predicates from the gate so notifyUser fires whenever configured; keep hookMessages overlap suppression (same user-channel audience). Refs openclaw#87107
|
Maintainer verification before merge: Behavior addressed: Real environment tested: local source checkout on rebased PR head Exact steps or command run after this patch:
Evidence after fix:
Observed result after fix: marked explicit command replies can bypass source suppression in What was not tested: live Feishu/WebChat transport. The proof is source-level dispatch/command/compaction coverage plus CI on the exact pushed head. |
Summary
/compactand compaction notices on OpenClaw 2026.5.22. (a) Authorized/compactreply silently dropped on Feishu / WebChat room_event channels — reporter's logs showdelivered=truebut nodeliver: sending text chunksand no Feishu API call. (b) Adjacent: command-handler terminal replies (/status, skill-tool blocked / error) silently dropped undermessages.visibleReplies: "message_tool"configs even on DM. (c)agents.defaults.compaction.notifyUser: truenotices silently skipped whenever any internalonCompactionStart/onCompactionEndcallback is registered (e.g. by Control UI).src/auto-reply/reply/dispatch-from-config.tsrejecteddeliverDespiteSourceReplySuppressionfor everyctx.InboundEventKind === "room_event", conflating user-initiated command terminal replies with ambient runtime failure notices. Thedispatch-from-config.test.tssuppresses marked runtime failure notices for room eventstest was protecting ambient privacy but locked the bypass too tight, so the marker had no effect for the exact channels the reporter was using.src/auto-reply/reply/get-reply-inline-actions.tsreturned{ kind: "reply", reply }for six terminal paths (skill-tool not-available, blocked, success, error, inline-action terminal command reply, top-level terminal command reply) without callingmarkReplyPayloadForSourceSuppressionDelivery, even though peer system-meta replies (model-fallback warning inagent-runner.ts, embedded-runner errors inpayloads.ts) already use the same marker. Result: even after (a), the suppress branch would still drop them.src/auto-reply/reply/agent-runner-execution.ts(theelse ifchain introduced by feat: send compaction start and completion notices #67830 to de-duplicate between internal callbacks and the default notice) gated the defaultnotifyUsernotice on the absence ofonCompactionStart/onCompactionEnd. Audited every productiononCompactionEndconsumer:extensions/telegram+extensions/discord+extensions/whatsappuse reaction-emoji controllers (setThinking()),extensions/feishuclears a streaming-status line, andsrc/agents/pi-embedded-subscribe.handlers.compaction.ts(the pi-embedded / Control UI implementation) writes a log + reconciles a counter + emits an internalagent-event. None emit user-visible chat text. The intended de-duplication had no runtime competitor; the gate suppressed the documented🧹 Compaction completenotice (perdocs/gateway/config-agents.md) without anything filling the gap. ClawSweeper review on /compact command reply silently dropped after upgrade to 2026.5.22 (delivered=true but no actual delivery) #87107 also flagged this as source-reproducible against the same doc.shouldDeliverDespiteSourceReplySuppressionprecondition insrc/auto-reply/reply/dispatch-from-config.tsfrom "never in room_event" to "in room_event only if the current ctx is an explicit command turn" (isExplicitSourceReplyCommand(ctx, cfg)— the same helper the source-reply visibility policy uses; matches native commands, authorized text-slash, and the legacyCommandAuthorized + CommandBodyfallback viaisExplicitCommandTurnContext). Ambient marked notices andsendPolicy: denystay suppressed.markReplyPayloadForSourceSuppressionDeliveryat thesrc/auto-reply/reply/get-reply-inline-actions.tschokepoint via a local helper that wraps every terminal{ kind: "reply" }exit. Mirrors the marker pattern already used for the model-fallback and embedded-runner notices.else ifchain insrc/auto-reply/reply/agent-runner-execution.ts(start / completed-end / non-completed-end branches) so the user-channel notice fires on its documented contract while internal callbacks continue to fire on their own (non-chat-text) channels — reactions, status indicators, agent-events.hookMessages.length > 0still suppresses the default notice in the same phase because that IS a real chat-text overlap (plugin-authored user-channel text on the same channel).src/auto-reply/reply/dispatch-from-config.ts— narrow the bypass predicate via the sharedisExplicitSourceReplyCommand(ctx)helper fromsource-reply-delivery-mode.tsso the bypass and the visibility policy stay aligned.src/auto-reply/reply/dispatch-from-config.test.ts— two new dispatch-level tests:delivers marked explicit command terminal replies in room events (#87107)(CommandSource: "text"shape) anddelivers marked /compact reply in room event when CommandSource is undefined (#87107)(legacyCommandAuthorized: true + CommandBody: "/compact"shape thatisExplicitSourceReplyCommandnow resolves via the control-command-body fallback); existing ambient-failure room-event test stays as-is.src/auto-reply/reply/get-reply-inline-actions.ts—markCommandReplyForDeliverylocal helper around six terminal returns.src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts— new test asserting the marker.src/auto-reply/reply/agent-runner-execution.ts— split theelse ifchain.src/auto-reply/reply/agent-runner-execution.test.ts— rewrote the previously-passing test that locked in the buggyelse ifbehavior; new assertions cover both callbacks AND both notices firing.src/auto-reply/reply/source-reply-delivery-mode.ts— policy resolver untouched.src/auto-reply/reply-payload.ts— marker helper unchanged.CommandTurn→ still suppressed in room_event) is preserved by the existingsuppresses marked runtime failure notices for room eventstest, which keeps passing.sendPolicy: denycontinues to suppress every reply regardless of marker.docs/reference/configedits).extensions/api.ts/runtime-api.ts, registry, or loader edits).Reproduction
agents.defaults.compaction.notifyUser: trueand accumulate a long conversation (70k+ tokens) so/compactproduces a non-trivial summary./compactfrom a WebChat / Feishu room-event channel./compactsummary appears in the user channel; no compaction start / end notice appears; gateway log shows command was dispatched but no channel adapter send./compactsummary appears (room_event explicit-command bypass); both🧹 Compacting context…and🧹 Compaction completenotices appear (notifyUser fires independently of internal callbacks).Real behavior proof
Behavior addressed (#87107): room_event source-suppression now honors the existing
deliverDespiteSourceReplySuppressionmarker for explicit command turns (native or authorized text-slash), command-handler terminal replies from inline-actions now carry that marker, and the compactionnotifyUsernotice is no longer gated on internal-callback absence.Real environment tested (Linux, Node 22, real-component tsx harness driving production source modules against origin/main HEAD): tsx-resolved imports of production
resolveSourceReplyVisibilityPolicy,markReplyPayloadForSourceSuppressionDelivery,getReplyPayloadMetadata, andisExplicitSourceReplyCommand(ctx, cfg)(which now routes throughisExplicitCommandTurnContextand covers the legacyCommandAuthorized + CommandBodyshape withoutCommandSource). The harness mirrors the post-fixdispatch-from-configdecision verbatim across six behavior-relevant scenarios.Exact steps or command run after this patch:
node_modules/.bin/tsx /tmp/qmd87107/repro.mts(real-component policy + marker + command-turn sweep)node scripts/run-vitest.mjs src/auto-reply/reply/dispatch-from-config.test.ts src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts src/auto-reply/reply/agent-runner-execution.test.tspnpm exec oxfmt --check --threads=1 src/auto-reply/reply/dispatch-from-config.ts src/auto-reply/reply/dispatch-from-config.test.ts src/auto-reply/reply/get-reply-inline-actions.ts src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts src/auto-reply/reply/agent-runner-execution.ts src/auto-reply/reply/agent-runner-execution.test.tsEvidence after fix (verbatim real-component harness output):
Vitest output for the three touched test files:
Observed result after fix:
/compactinroom_event(Feishu group / WebChat-as-room) moves fromsilently-droppedtodelivered— the exact branch issue /compact command reply silently dropped after upgrade to 2026.5.22 (delivered=true but no actual delivery) #87107 hits for the reported channels.CommandTurn) inroom_eventstayssilently-dropped, preserving the existing privacy contract pinned bysuppresses marked runtime failure notices for room events.messages.visibleReplies: "message_tool"configs, marker recovers non-explicit / unauthorized-command paths (adjacent hygiene)./compacton DM was already delivered (policy short-circuits toautomatic) and stays so.compaction.notifyUser: truewith both internal callbacks registered now produces both callbacks AND both user-channel notices (was: 0 notices).What was not tested:
sendPolicy: denysemantics were not re-tested in this PR (unchanged).Regression tests:
dispatch-from-config.test.ts→delivers marked explicit command terminal replies in room events (#87107)(CommandSource: "text" shape) +delivers marked /compact reply in room event when CommandSource is undefined (#87107)(legacy fallback shape) + the preservedsuppresses marked runtime failure notices for room events(ambient privacy contract).get-reply-inline-actions.skip-when-config-empty.test.ts→ marker presence on terminal command replies.agent-runner-execution.test.ts→ both callbacks + both notices fire whencompaction.notifyUseris enabled.Risk / Mitigation
room_eventcould leak ambient runtime warnings to group chats. Mitigation: the new predicate only matchesisExplicitSourceReplyCommand(native command or authorized text-slash) — the same helper already used by the source-reply visibility policy, so the bypass and the policy stay aligned; ambient notices (noCommandTurn) keep the existing suppression, pinned bysuppresses marked runtime failure notices for room eventswhich still passes.suppressAutomaticSourceDelivery && !sendPolicyDenied;sendPolicy: denystill drops everything regardless of marker.notifyUsernotice could over-notify when a plugin also sends ahookMessagefor the same phase. Mitigation:hookMessages.length > 0still suppresses the default notice in that phase (same user channel, one text per phase preserved).prefers onCompactionEnd callback over default notice when notifyUser is enabled(introduced by feat: send compaction start and completion notices #67830) could mask a legitimate de-duplication constraint. Mitigation: audited every productiononCompactionEndconsumer in the repo —extensions/telegram,extensions/discord,extensions/whatsapp(reaction emoji),extensions/feishu(streaming-status-line clear),src/agents/pi-embedded-subscribe.handlers.compaction.ts(log + counter reconcile + internalagent-event). None emit user-visible chat text, so there is no chat-text source for the default notice to de-duplicate against. The originalelse ifwould have removed the only user-channel chat notice; this restores the documenteddocs/gateway/config-agents.mdcontract.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Fixes #87107