getLastChar was checking the last character of the full event.prompt,
which includes Conversation/Sender metadata blocks appended by OpenClaw
after the actual message. This meant end symbols like 🔚 at the end of
the message body were invisible — the last char was always backtick or
whitespace from the metadata JSON block.
Fix: strip trailing '(untrusted metadata)' blocks before extracting
the last character. This only affects non-humanList senders (humanList
senders bypass end symbol check via human_list_sender reason).
1. DM bypass: when neither senderId nor channelId can be extracted from
the prompt (DM sessions lack untrusted conversation info), skip the
no-reply gate and allow the message through with end-marker injection.
2. Tool visibility: change whispergateway_tools registration from
optional=true to optional=false so all agents can see the tool
without needing explicit tools.allow entries.
- Add default values for enableDiscordControlTool, enableWhispergatePolicyTool,
discordControlApiBaseUrl, enableDebugLogs, debugLogChannelIds
- Merge defaults in both baseConfig and getLivePluginConfig
- Fixes issue where whispergateway_tools tool was not exposed due to missing
config fields in openclaw.json
Root cause: PluginHookAgentContext in before_model_resolve only has
agentId, sessionKey, sessionId, workspaceDir, messageProvider.
senderId, channelId, input are NOT available in this hook phase.
The plugin was reading ctx.senderId (undefined) -> inHumanList=false
for ALL Discord sessions -> shouldUseNoReply=true -> all silenced.
Fix: use event.prompt which contains the full user message including
the 'Conversation info (untrusted metadata)' JSON block, and extract
sender_id from there. Same fix applied to before_prompt_build.