Skip to content

feat: WhatsApp status reactions, new emoji categories, self-explanatory defaults (#59077)#80612

Merged
velvet-shark merged 3 commits into
openclaw:mainfrom
gado-ships-it:feat/whatsapp-status-reactions
May 14, 2026
Merged

feat: WhatsApp status reactions, new emoji categories, self-explanatory defaults (#59077)#80612
velvet-shark merged 3 commits into
openclaw:mainfrom
gado-ships-it:feat/whatsapp-status-reactions

Conversation

@gado-ships-it

@gado-ships-it gado-ships-it commented May 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • WhatsApp lifecycle reactions: Wires StatusReactionController into the WhatsApp message turn, matching the existing Telegram and Discord implementations. When messages.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.
  • New emoji categories across all channels (WhatsApp, Telegram, Discord):
    • deploy (🛫) — fastlane, ship, upload, testflight, deploy, release tokens
    • build (🏗️) — build, compile, xcode, swift, webpack, tsc, lint tokens
    • concierge (💁) — browser automation, puppeteer, playwright, chromedp, form-fill tokens
  • Self-explanatory default emoji (Fixes Default status reaction emoji are unclear (🥱😨 for stalls) #59077): replaces the status reaction defaults that read as emotional commentary (🥱 yawn / 😨 fear / 🤔 / 🔥 / 👨‍💻 / 👍 / 😱) with universally readable status indicators. Folded in from the now-closed fix(channels): sync status reaction DEFAULT_EMOJIS with documented defaults #81762.
  • Config schema updated: messages.statusReactions.emojis now accepts deploy, build, concierge keys 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". New DEFAULT_EMOJIS:

State Old New
thinking 🤔 🧠
tool 🔥 🛠️
coding 👨‍💻 💻
web 🌐
done 👍
error 😱
stallSoft 🥱
stallHard 😨 ⚠️

compacting (✍), queued (👀), and the new deploy/build/concierge categories are unchanged.

Cross-platform check:

  • SlackUNICODE_TO_SLACK in extensions/slack/src/monitor/message-handler/dispatch.ts already maps every new emoji to a shortcode (brain, hammer_and_wrench, computer, globe_with_meridians, hourglass_flowing_sand, warning, white_check_mark, x).
  • Telegram — Telegram only allows a fixed reaction set, and none of the new emoji are in it. The existing TELEGRAM_STATUS_REACTION_VARIANTS fallback lists handle this: resolveTelegramReactionVariant walks 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.
  • Discord / WhatsApp / others — use raw unicode reactions; all new emoji are standard and valid as reactions.

Power users can still override any of these via messages.statusReactions.emojis in config.

Implementation details

New file: extensions/whatsapp/src/auto-reply/monitor/status-reaction.ts

  • createWhatsAppStatusReactionController — mirrors the Telegram pattern; reuses the same ack eligibility gate (shouldAckReactionForWhatsApp) so the controller only activates when a reaction would have been sent anyway
  • Adapter uses sendReactionWhatsApp(chatId, msgId, emoji) for status updates and a separate clear hook that sends sendReactionWhatsApp(chatId, msgId, "") only when removeAckAfterReply/clear explicitly asks to remove the single WhatsApp reaction slot

process-message.ts — creates controller before ack, calls setQueued() immediately; skips the plain ack when the controller is active

inbound-dispatch.ts — accepts optional statusReactionController, calls setThinking() before dispatch, wires onToolStart, onCompactionStart, onCompactionEnd callbacks, finalizes with setDone/setError/restoreInitial

on-message.ts — voice-note audio preflight starts the queued status reaction before STT when statusReactions.enabled, so slow transcription keeps the existing immediate receipt feedback

Real 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 the maybeSendAckReaction path; reactions never cycled.

Real environment tested: macOS 25.2.0 (Darwin), Node 25.9.0, openclaw 2026.5.10-beta.1 HEAD 01cb2e41 (this PR's commit 9e1055f5 merged into a working branch on top of origin/main), built with pnpm build and rsynced into /opt/homebrew/lib/node_modules/openclaw/dist. Gateway runs as the ai.openclaw.gateway LaunchAgent linked to a personal WhatsApp Web account [redacted-whatsapp-account]. Sender handset is a second physical phone [redacted-sender-phone]. Config additions:

"messages": { "statusReactions": { "enabled": true } },
"channels": {
  "whatsapp": {
    "reactionLevel": "extensive",
    "ackReaction": { "emoji": "👀", "direct": true, "group": "mentions" }
  }
}

Exact steps or command run after the patch:

git checkout 01cb2e41                  // this PR (9e1055f5) merged on top of origin/main
pnpm build
rsync -a --delete dist/ /opt/homebrew/lib/node_modules/openclaw/dist/
launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway
// enable status reactions in ~/.openclaw/openclaw.json (block above)
// then sent a real WhatsApp DM "reaction test" from [redacted-sender-phone] to the bot

After-fix evidence (terminal output + screenshot):

  1. Live gateway log (~/.openclaw/logs/gateway.log) — the WhatsApp adapter receives the inbound and the StatusReactionController's setQueued() immediately fires sendReactionWhatsApp, completing in ~33 ms:
2026-05-11T20:10:52.194+02:00 [whatsapp] Sending reaction "👀" -> message 3EB0E3BBB84D6CB0AFB647
2026-05-11T20:10:52.200+02:00 [whatsapp] Inbound message [redacted-sender-phone] -> [redacted-whatsapp-account] (direct, 79 chars)
2026-05-11T20:10:55.227+02:00 [whatsapp] Sent reaction "👀" -> message 3EB0E3BBB84D6CB0AFB647

A second inbound at 20:13 shows the same pattern, with the ack send completing in 28 ms:

2026-05-11T20:13:21.581+02:00 [whatsapp] Sending reaction "👀" -> message 3EB0BF9BC49DD814A3595B
2026-05-11T20:13:21.589+02:00 [whatsapp] Inbound message [redacted-sender-phone] -> [redacted-whatsapp-account] (direct, 1189 chars)
2026-05-11T20:13:21.609+02:00 [whatsapp] Sent reaction "👀" -> message 3EB0BF9BC49DD814A3595B
  1. Recipient-side screenshot — WhatsApp app on the sender's iPhone showing the 👀 reaction overlaid on the user's "reaction test" message at 20:10 (delivered/read double check), with the bot's typing indicator below as the lifecycle continues into the setThinking() phase:

reaction-test inbound with 👀 ack overlay

  1. The reaction message id (3EB0E3BBB84D6CB0AFB647) is the WhatsApp stanza id of the user's outbound, exactly what sendReactionWhatsApp(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 maybeSendAckReaction path and statusReactions.enabled was 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.ts covers queued → thinking → done → restoreInitial without any empty clear call until explicit clear(). The review also found that voice notes lost pre-STT receipt feedback under messages.statusReactions.enabled; extensions/whatsapp/src/auto-reply/monitor/on-message.audio-preflight.test.ts now covers queued status reaction before STT and verifies the same controller is handed to processMessage.

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

  • Set messages.statusReactions.enabled: true with a WhatsApp account; send a message; verify queued ack appears — queued ack captured live, see proof section above
  • pnpm 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 change
  • node 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 fixes
  • Verify Telegram reactions still work through fallback variants in extensions/telegram/src/bot-message-context.reactions.test.ts
  • Verify new token categorization in src/channels/status-reactions.test.ts
  • Verify plain ack still works when statusReactions.enabled is false/unset in WhatsApp audio preflight tests

@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram channel: whatsapp-web Channel integration: whatsapp-web size: M triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 11, 2026
@clawsweeper

clawsweeper Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge.

Summary
The PR adds WhatsApp lifecycle status reactions, deploy/build/concierge status-reaction categories and config keys, clearer default status emojis, Slack/Telegram mapping updates, and a changelog entry.

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
Needs stronger real behavior proof before merge: The PR body includes redacted logs and a screenshot showing only the queued WhatsApp ack on a live link; it does not prove the full lifecycle, cleanup/restore behavior, or Telegram-visible behavior after the current patch. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, ask a maintainer to comment @clawsweeper re-review.

Next step before merge
Human follow-up is needed because this active external PR needs branch repair plus contributor-owned live WhatsApp and Telegram proof before merge.

Security
Cleared: The diff does not add dependencies, workflow changes, secret handling, or code-execution surface, and the current proof text/screenshot uses redacted private identifiers.

Review findings

  • [P2] Preserve WhatsApp's final status reaction — extensions/whatsapp/src/auto-reply/monitor/status-reaction.ts:97-99
  • [P2] Keep voice-note acknowledgements before STT — extensions/whatsapp/src/auto-reply/monitor/on-message.ts:153-166
  • [P3] Update the public status reaction docs — src/config/schema.help.ts:1853
Review details

Best 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:

  • [P2] Preserve WhatsApp's final status reaction — extensions/whatsapp/src/auto-reply/monitor/status-reaction.ts:97-99
    The shared controller calls removeReaction for every tracked prior emoji after setDone/setError or restoreInitial. This adapter implements each removal as sendReactionWhatsApp(..., ""), and Baileys treats empty reaction text as removing the current reaction, so terminal/restored states can clear the only WhatsApp reaction slot instead of leaving the final or ack emoji visible.
    Confidence: 0.92
  • [P2] Keep voice-note acknowledgements before STT — extensions/whatsapp/src/auto-reply/monitor/on-message.ts:153-166
    When status reactions are enabled, this preflight path no longer sends any ack before transcribeFirstAudio. The status controller is created later in processMessage, so slow voice-note STT loses the immediate receipt reaction current code and tests preserve.
    Confidence: 0.87
  • [P3] Update the public status reaction docs — src/config/schema.help.ts:1853
    The PR adds WhatsApp status reactions and new override keys, but the public config guide still says status reactions are Slack/Discord/Telegram-only, and this help text keeps Telegram-only emoji wording. Update the docs so operators can discover WhatsApp support and channel-specific constraints.
    Confidence: 0.78

Overall correctness: patch is incorrect
Overall confidence: 0.9

What I checked:

Likely related people:

  • @steipete: Recent commits own the shared status reaction cleanup contract and WhatsApp ack-reaction removal path that this PR reuses. (role: recent area contributor; confidence: high; commits: 5039a35a332a, caa7f7c4cc17, 9b93b7df62c1; files: src/channels/status-reactions.ts, extensions/whatsapp/src/auto-reply/monitor/ack-reaction.ts, extensions/whatsapp/src/auto-reply/monitor/on-message.ts)
  • @hclsys: Authored recent WhatsApp voice-note preflight routing changes on the same on-message path affected by the ack-before-STT regression. (role: recent audio-preflight contributor; confidence: medium; commits: 55e7f5f27ce2; files: extensions/whatsapp/src/auto-reply/monitor/on-message.ts)
  • @velvet-shark: Co-authored/reviewed recent WhatsApp voice-note preflight work and is assigned on this PR according to the timeline. (role: reviewer and recent adjacent owner; confidence: medium; commits: 55e7f5f27ce2; files: extensions/whatsapp/src/auto-reply/monitor/on-message.ts)
  • @mcaxtr: Reviewed prior WhatsApp voice-note work and is tied to recent WhatsApp reaction guidance and multi-account behavior history. (role: adjacent WhatsApp owner; confidence: medium; commits: ac6db066d341, 458a52610a4d, ea168c22ce8b; files: extensions/whatsapp/src/auto-reply/monitor/on-message.ts, extensions/whatsapp/src/auto-reply/monitor/ack-reaction.ts)

Remaining risk / open question:

  • The PR body claims repair tests and adapter changes that are not present in the current head diff, so the branch and proof are out of sync.
  • No live proof currently shows full WhatsApp lifecycle transitions, cleanup/restore behavior, or Telegram-visible fallback behavior on the PR head.

Codex review notes: model gpt-5.5, reasoning high; reviewed against e692f5c1cfcb.

@clawsweeper clawsweeper Bot added the mantis: telegram-visible-proof Mantis should capture Telegram visible proof. label May 11, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 12, 2026
@gado-ships-it gado-ships-it changed the title feat: wire StatusReactionController into WhatsApp + new emoji categories feat: WhatsApp status reactions, new emoji categories, self-explanatory defaults (#59077) May 14, 2026
@gado-ships-it gado-ships-it force-pushed the feat/whatsapp-status-reactions branch from a6aa026 to b500d9f Compare May 14, 2026 09:04
@openclaw-barnacle openclaw-barnacle Bot added the channel: slack Channel integration: slack label May 14, 2026
@gado-ships-it gado-ships-it force-pushed the feat/whatsapp-status-reactions branch from b500d9f to 780758b Compare May 14, 2026 09:06
@velvet-shark velvet-shark self-assigned this May 14, 2026
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed proof: supplied External PR includes structured after-fix real behavior proof. labels May 14, 2026
Gado and others added 3 commits May 14, 2026 14:14
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
@velvet-shark velvet-shark requested review from a team as code owners May 14, 2026 12:35
@github-actions github-actions Bot added the dependencies-changed PR changes dependency-related files label May 14, 2026
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: voice-call Channel integration: voice-call app: android App: android app: web-ui App: web-ui gateway Gateway runtime labels May 14, 2026
@velvet-shark velvet-shark force-pushed the feat/whatsapp-status-reactions branch from c86a26e to 25e0a7a Compare May 14, 2026 12:36
@github-actions github-actions Bot removed the dependencies-changed PR changes dependency-related files label May 14, 2026
@velvet-shark velvet-shark merged commit 83b8289 into openclaw:main May 14, 2026
15 of 23 checks passed
@velvet-shark

Copy link
Copy Markdown
Member

Merged via squash.

Thanks @gado-ships-it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: whatsapp-web Channel integration: whatsapp-web docs Improvements or additions to documentation gateway Gateway runtime mantis: telegram-visible-proof Mantis should capture Telegram visible proof. size: L triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default status reaction emoji are unclear (🥱😨 for stalls)

2 participants