feat: WhatsApp status reactions, new emoji categories, self-explanatory defaults (#59077)#80612
Conversation
|
Codex review: needs real behavior proof before merge. Summary Reproducibility: yes. for the blocking review findings: source inspection shows the shared cleanup contract, the PR's empty-reaction WhatsApp removal adapter, and the existing ack-before-STT test expectation. I did not reproduce the full WhatsApp lifecycle in a live session. Real behavior proof Next step before merge Security Review findings
Review detailsBest possible solution: Repair this PR in place with WhatsApp-aware single-slot reaction semantics, preserved voice-note preflight feedback, updated public config docs, and redacted live WhatsApp plus Telegram-visible proof before merge. Do we have a high-confidence way to reproduce the issue? Yes for the blocking review findings: source inspection shows the shared cleanup contract, the PR's empty-reaction WhatsApp removal adapter, and the existing ack-before-STT test expectation. I did not reproduce the full WhatsApp lifecycle in a live session. Is this the best way to solve the issue? No. The direction is useful, but the current implementation is not the narrowest safe mergeable form until WhatsApp single-slot cleanup, early voice-note feedback, docs, and proof are aligned. Full review comments:
Overall correctness: patch is incorrect What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against e692f5c1cfcb. |
a6aa026 to
b500d9f
Compare
b500d9f to
780758b
Compare
WhatsApp is now on par with Telegram/Discord for lifecycle status reactions. When messages.statusReactions.enabled is true, the per-message emoji cycles queued → thinking → tool → done/error instead of a single static ack. New emoji categories added across all channels: - deploy (🛫) — fastlane, ship, upload, testflight, deploy tokens - build (🏗️) — build, compile, xcode, webpack, tsc, lint tokens - concierge (💁) — browser automation, puppeteer, playwright, chromedp tokens - web default changed from ⚡ to 🌎 for clearer web-search signaling Config: messages.statusReactions.emojis now accepts deploy/build/concierge keys. The Zod schema, TypeScript types, and help text are all updated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…atory set Fold in the emoji-default replacement from openclaw#59077: 🧠 thinking, 🛠️ tool, 💻 coding, 🌐 web, ⏳ stallSoft,⚠️ stallHard, ✅ done, ❌ error. The old defaults (🥱 yawn / 😨 fear / 🤔 / 🔥 / ...) read as emotional commentary rather than status. Slack shortcodes already cover the new set; Telegram degrades via the existing fallback variant lists. Fixes openclaw#59077
c86a26e to
25e0a7a
Compare
|
Merged via squash.
Thanks @gado-ships-it! |
Summary
StatusReactionControllerinto the WhatsApp message turn, matching the existing Telegram and Discord implementations. Whenmessages.statusReactions.enabled: true, the reaction on the trigger message cycles queued → thinking → tool → done/error (with stall warnings) instead of a single static ack emoji.deploy(🛫) — fastlane, ship, upload, testflight, deploy, release tokensbuild(🏗️) — build, compile, xcode, swift, webpack, tsc, lint tokensconcierge(💁) — browser automation, puppeteer, playwright, chromedp, form-fill tokensmessages.statusReactions.emojisnow acceptsdeploy,build,conciergekeys in Zod schema, TypeScript types, and help text.Status reaction default emoji (Fixes #59077)
Issue #59077 reported that the default status emoji read as mood rather than status —
🥱 → 😨does not obviously mean "slow → stuck". NewDEFAULT_EMOJIS:compacting(✍),queued(👀), and the newdeploy/build/conciergecategories are unchanged.Cross-platform check:
UNICODE_TO_SLACKinextensions/slack/src/monitor/message-handler/dispatch.tsalready maps every new emoji to a shortcode (brain,hammer_and_wrench,computer,globe_with_meridians,hourglass_flowing_sand,warning,white_check_mark,x).TELEGRAM_STATUS_REACTION_VARIANTSfallback lists handle this:resolveTelegramReactionVariantwalks the per-state variant list and picks the first Telegram-supported emoji, so Telegram keeps rendering its supported equivalents. Graceful degradation, no behavior change on Telegram.Power users can still override any of these via
messages.statusReactions.emojisin config.Implementation details
New file:
extensions/whatsapp/src/auto-reply/monitor/status-reaction.tscreateWhatsAppStatusReactionController— mirrors the Telegram pattern; reuses the same ack eligibility gate (shouldAckReactionForWhatsApp) so the controller only activates when a reaction would have been sent anywaysendReactionWhatsApp(chatId, msgId, emoji)for status updates and a separate clear hook that sendssendReactionWhatsApp(chatId, msgId, "")only whenremoveAckAfterReply/clear explicitly asks to remove the single WhatsApp reaction slotprocess-message.ts— creates controller before ack, callssetQueued()immediately; skips the plain ack when the controller is activeinbound-dispatch.ts— accepts optionalstatusReactionController, callssetThinking()before dispatch, wiresonToolStart,onCompactionStart,onCompactionEndcallbacks, finalizes withsetDone/setError/restoreInitialon-message.ts— voice-note audio preflight starts the queued status reaction before STT whenstatusReactions.enabled, so slow transcription keeps the existing immediate receipt feedbackReal behavior proof
Behavior addressed: with this patch on a live WhatsApp link, inbound DMs trigger the
StatusReactionController.setQueued()ack emoji immediately on receipt (before the agent's model call even begins), and the controller transitions emojis through queued → thinking → tool → done/error as the lifecycle advances. Prior to this patch the WhatsApp channel only ever set the static ack emoji via themaybeSendAckReactionpath; reactions never cycled.Real environment tested: macOS 25.2.0 (Darwin), Node 25.9.0, openclaw
2026.5.10-beta.1HEAD01cb2e41(this PR's commit9e1055f5merged into a working branch on top oforigin/main), built withpnpm buildand rsynced into/opt/homebrew/lib/node_modules/openclaw/dist. Gateway runs as theai.openclaw.gatewayLaunchAgent linked to a personal WhatsApp Web account[redacted-whatsapp-account]. Sender handset is a second physical phone[redacted-sender-phone]. Config additions:Exact steps or command run after the patch:
After-fix evidence (terminal output + screenshot):
~/.openclaw/logs/gateway.log) — the WhatsApp adapter receives the inbound and the StatusReactionController'ssetQueued()immediately firessendReactionWhatsApp, completing in ~33 ms:A second inbound at 20:13 shows the same pattern, with the ack send completing in 28 ms:
setThinking()phase:3EB0E3BBB84D6CB0AFB647) is the WhatsApp stanza id of the user's outbound, exactly whatsendReactionWhatsApp(chatId, msgId, "👀", { participant })targets via Baileys'react: { key: {…} }content. The status-reaction adapter points every lifecycle update at the same id, so the recipient sees the emoji change in place rather than seeing multiple stacked reactions.Observed result after fix: The ack emoji appears on the user's inbound within ~3 seconds of sending (network round-trip dominated). Before this patch, with the same config, no reaction ever appeared because the channel only had the plain
maybeSendAckReactionpath andstatusReactions.enabledwas a no-op for WhatsApp.Repair proof after review: The review found that WhatsApp must not map shared per-emoji cleanup to an empty reaction because Baileys treats empty reaction text as removal of the current bot reaction. The adapter now exposes a single-slot clear hook, and
src/channels/status-reactions.test.tscovers queued → thinking → done → restoreInitial without any empty clear call until explicitclear(). The review also found that voice notes lost pre-STT receipt feedback undermessages.statusReactions.enabled;extensions/whatsapp/src/auto-reply/monitor/on-message.audio-preflight.test.tsnow covers queued status reaction before STT and verifies the same controller is handed toprocessMessage.What was not live-tested after review: The original live capture proves the queued WhatsApp reaction on a real link. The repair pass validated lifecycle cleanup, voice-note preflight ordering, Telegram fallback behavior, and dispatch wiring with targeted tests; it did not run a second live WhatsApp session.
Test plan
messages.statusReactions.enabled: truewith a WhatsApp account; send a message; verify queued ack appears — queued ack captured live, see proof section abovepnpm test src/channels/status-reactions.test.ts src/channels/status-reactions.slack-lifecycle.test.ts extensions/telegram/src/status.test.ts— 78 tests passed after the Default status reaction emoji are unclear (🥱😨 for stalls) #59077 emoji-default changenode scripts/run-vitest.mjs src/channels/status-reactions.test.ts extensions/whatsapp/src/auto-reply/monitor/on-message.audio-preflight.test.ts extensions/whatsapp/src/auto-reply/monitor/process-message.audio-preflight.test.ts extensions/telegram/src/bot-message-context.reactions.test.ts extensions/telegram/src/bot-message-dispatch.test.ts— 118 tests passed after the review fixesextensions/telegram/src/bot-message-context.reactions.test.tssrc/channels/status-reactions.test.tsstatusReactions.enabledis false/unset in WhatsApp audio preflight tests