Skip to content

Commit a6cc290

Browse files
committed
fix(auto-reply): fallback group finals when message tool is missed
1 parent e71d10f commit a6cc290

17 files changed

Lines changed: 93 additions & 48 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
1717

1818
### Fixes
1919

20+
- Group/channel replies: fall back to posting substantive final text for normal requests when a message-tool-preferred turn misses the message tool, while keeping ambient room events strict and quiet.
2021
- Gateway/WebChat: route image attachments through a configured vision-capable `imageModel` plan before inlining images, and carry that image-model fallback chain through runtime retries. (#82524) Thanks @frankekn.
2122
- WebChat: show progress while manual `/compact` is running by streaming a session operation event to subscribed Control UI clients. Fixes #82407. Thanks @Conan-Scott.
2223
- Codex app-server: limit canonical OpenAI Codex app-server attribution rewrites to local transcript and trajectory records, leaving runtime/tool routing on the selected OpenAI model metadata so OpenAI API-key backup profiles keep their billing path.

docs/channels/discord.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,9 @@ Once DMs are working, you can set up your Discord server as a full workspace whe
250250
<Step title="Allow responses without @mention">
251251
By default, your agent only responds in guild channels when @mentioned. For a private server, you probably want it to respond to every message.
252252

253-
In guild channels, normal assistant final replies stay private by default. Visible Discord output must be sent explicitly with the `message` tool, so the agent can lurk by default and only post when it decides a channel reply is useful.
253+
In guild channels, visible Discord output should use the `message` tool by default, so the agent can lurk and only post when it decides a channel reply is useful. For normal requests, OpenClaw falls back to the assistant's final text when the model misses the tool; ambient room events stay quiet unless the tool sends.
254254

255-
This means the selected model must reliably call tools. If Discord shows typing and the logs show token usage but no posted message, check the session log for assistant text with `didSendViaMessagingTool: false`. That means the model produced a private final answer instead of calling `message(action=send)`. Switch to a stronger tool-calling model, or use the config below to restore legacy automatic final replies.
255+
This means the selected model should reliably call tools. If Discord shows typing and the logs show token usage but no posted message, check whether the turn was configured as an ambient room event or use the config below to restore legacy automatic final replies.
256256

257257
<Tabs>
258258
<Tab title="Ask your agent">

docs/channels/groups.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Default behavior:
1616

1717
- Groups are restricted (`groupPolicy: "allowlist"`).
1818
- Replies require a mention unless you explicitly disable mention gating.
19-
- Normal final replies in groups/channels are private by default. Visible room output uses the `message` tool.
19+
- Visible replies in groups/channels prefer the `message` tool, with final-text fallback for normal requests.
2020

2121
Translation: allowlisted senders can trigger OpenClaw by mentioning it.
2222

@@ -43,15 +43,15 @@ always-on group chatter -> user request, or room event when configured
4343

4444
For group/channel rooms, OpenClaw defaults to `messages.groupChat.visibleReplies: "message_tool"`.
4545
`openclaw doctor --fix` writes this default into configured-channel configs that omit it.
46-
That means the agent still processes the turn and can update memory/session state, but its normal final answer is not automatically posted back into the room. To speak visibly, the agent uses `message(action=send)`.
46+
That means the agent still processes the turn and can update memory/session state, and it should speak visibly with `message(action=send)` when it has a room reply. If the model misses that tool and returns substantive final text on a normal group/channel request, OpenClaw now falls back to posting that final text instead of leaving the room silent.
4747

4848
This default depends on a model/runtime that reliably calls tools. If logs show
4949
assistant text but `didSendViaMessagingTool: false`, the model answered
50-
privately instead of calling the message tool. That is not a
51-
Discord/Slack/Telegram send failure. Use a tool-call-reliable model for
52-
group/channel sessions, or set
53-
`messages.groupChat.visibleReplies: "automatic"` to restore legacy visible
54-
final replies for group requests.
50+
through the fallback instead of calling the message tool. That is not a
51+
Discord/Slack/Telegram send failure, but a tool-discipline signal. Use a
52+
tool-call-reliable model for group/channel sessions, or set
53+
`messages.groupChat.visibleReplies: "automatic"` when you want all visible group
54+
replies to use the legacy final-reply path.
5555

5656
If the message tool is unavailable under the active tool policy, OpenClaw falls
5757
back to automatic visible replies instead of silently suppressing the response.
@@ -61,7 +61,7 @@ For direct chats and any other source turn, use `messages.visibleReplies: "messa
6161

6262
This replaces the old pattern of forcing the model to answer `NO_REPLY` for most lurk-mode turns. In tool-only mode, doing nothing visible simply means not calling the message tool.
6363

64-
Typing indicators are still sent for direct group requests. Ambient always-on room events, when enabled, stay quiet unless the agent calls the message tool.
64+
Typing indicators are still sent for direct group requests. Ambient always-on room events, when enabled, stay strict and quiet unless the agent calls the message tool.
6565

6666
To submit always-on ambient group chatter as quiet room context instead of legacy user requests:
6767

docs/channels/slack.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,7 @@ Primary reference: [Configuration reference - Slack](/gateway/config-channels#sl
12971297
- channel allowlist (`channels.slack.channels`) — **keys must be channel IDs** (`C12345678`), not names (`#channel-name`). Name-based keys silently fail under `groupPolicy: "allowlist"` because channel routing is ID-first by default. To find an ID: right-click the channel in Slack → **Copy link** — the `C...` value at the end of the URL is the channel ID.
12981298
- `requireMention`
12991299
- per-channel `users` allowlist
1300-
- `messages.groupChat.visibleReplies`: if it is `"message_tool"` and logs show assistant text with no `message(action=send)` call, the turn was processed but the final answer was kept private. Set it to `"automatic"` if you want normal assistant final replies posted back to Slack channels.
1300+
- `messages.groupChat.visibleReplies`: if it is `"message_tool"` and logs show assistant text with no `message(action=send)` call, the model missed the preferred message-tool path. Current builds fall back to final text for normal requests; ambient room events still stay quiet. Set it to `"automatic"` if you want every normal assistant final reply posted through the legacy path.
13011301

13021302
```json5
13031303
{

docs/channels/troubleshooting.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ Full troubleshooting: [Telegram troubleshooting](/channels/telegram#troubleshoot
8080

8181
### Discord failure signatures
8282

83-
| Symptom | Fastest check | Fix |
84-
| ----------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
85-
| Bot online but no guild replies | `openclaw channels status --probe` | Allow guild/channel and verify message content intent. |
86-
| Group messages ignored | Check logs for mention gating drops | Mention bot or set guild/channel `requireMention: false`. |
87-
| Typing/token usage but no Discord message | Session log shows assistant text with `didSendViaMessagingTool: false` | The model answered privately instead of calling the message tool. Use a tool-call-reliable model, or set `messages.groupChat.visibleReplies: "automatic"` to auto-post. |
88-
| DM replies missing | `openclaw pairing list discord` | Approve DM pairing or adjust DM policy. |
83+
| Symptom | Fastest check | Fix |
84+
| ----------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
85+
| Bot online but no guild replies | `openclaw channels status --probe` | Allow guild/channel and verify message content intent. |
86+
| Group messages ignored | Check logs for mention gating drops | Mention bot or set guild/channel `requireMention: false`. |
87+
| Typing/token usage but no Discord message | Check whether this was an ambient room event or an older build before final-text fallback | Upgrade, verify `messages.groupChat.ambientTurns` is not routing the turn as quiet room context, or set `messages.groupChat.visibleReplies: "automatic"` to use the legacy final-reply path. |
88+
| DM replies missing | `openclaw pairing list discord` | Approve DM pairing or adjust DM policy. |
8989

9090
Full troubleshooting: [Discord troubleshooting](/channels/discord#troubleshooting)
9191

docs/gateway/config-channels.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -786,14 +786,14 @@ See the full channel index: [Channels](/channels).
786786

787787
Group messages default to **require mention** (metadata mention or safe regex patterns). Applies to WhatsApp, Telegram, Discord, Google Chat, and iMessage group chats.
788788

789-
Visible replies are controlled separately. Group/channel rooms default to `messages.groupChat.visibleReplies: "message_tool"`: OpenClaw still processes the turn, but normal final replies stay private and visible room output requires `message(action=send)`. Set `"automatic"` only when you want the legacy behavior where normal replies are posted back to the room. To apply the same tool-only visible-reply behavior to direct chats too, set `messages.visibleReplies: "message_tool"`; the Codex harness also uses that tool-only behavior as its unset direct-chat default.
789+
Visible replies are controlled separately. Group/channel rooms default to `messages.groupChat.visibleReplies: "message_tool"`: OpenClaw still processes the turn and asks the agent to use `message(action=send)` for visible room output. For normal group/channel requests, substantive final text falls back to the source room when the model misses the message tool; ambient room events stay strict and quiet unless the tool sends. Set `"automatic"` when you want all visible group replies to use the legacy final-reply path. To apply the same tool-only visible-reply behavior to direct chats too, set `messages.visibleReplies: "message_tool"`; the Codex harness also uses that tool-only behavior as its unset direct-chat default.
790790

791791
Tool-only visible replies require a model/runtime that reliably calls tools. If
792792
the session log shows assistant text with `didSendViaMessagingTool: false`, the
793-
model produced a private final answer instead of calling the message tool.
794-
Switch to a stronger tool-calling model for that channel, or set
795-
`messages.groupChat.visibleReplies: "automatic"` to restore legacy visible final
796-
replies.
793+
model produced final text fallback instead of calling the message tool. Switch
794+
to a stronger tool-calling model for that channel, or set
795+
`messages.groupChat.visibleReplies: "automatic"` to use legacy visible final
796+
replies for every group/channel request.
797797

798798
If the message tool is unavailable under the active tool policy, OpenClaw falls back to automatic visible replies instead of silently suppressing the response. `openclaw doctor` warns about this mismatch.
799799

docs/gateway/configuration-examples.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
5151
messages: {
5252
visibleReplies: "automatic",
5353
groupChat: {
54-
visibleReplies: "message_tool", // default; use "automatic" for legacy room replies
54+
visibleReplies: "message_tool", // default; falls back to final text for normal requests
5555
},
5656
},
5757
}
@@ -110,7 +110,7 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
110110
ackReactionScope: "group-mentions",
111111
groupChat: {
112112
historyLimit: 50,
113-
visibleReplies: "message_tool", // normal final replies stay private in groups/channels
113+
visibleReplies: "message_tool", // prefer message tool; final text falls back for normal requests
114114
},
115115
queue: {
116116
mode: "followup",

docs/gateway/configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,14 @@ candidate contains redacted secret placeholders such as `***`.
175175
</Accordion>
176176

177177
<Accordion title="Set up group chat mention gating">
178-
Group messages default to **require mention**. Configure trigger patterns per agent, and keep visible room replies on the default message-tool path unless you intentionally want legacy automatic final replies:
178+
Group messages default to **require mention**. Configure trigger patterns per agent, and keep visible room replies on the default message-tool path unless you intentionally want every normal group reply to use the legacy automatic final-reply path:
179179

180180
```json5
181181
{
182182
messages: {
183183
visibleReplies: "automatic", // set "message_tool" to require message-tool sends everywhere
184184
groupChat: {
185-
visibleReplies: "message_tool", // default; use "automatic" for legacy room replies
185+
visibleReplies: "message_tool", // default; falls back to final text for normal requests
186186
},
187187
},
188188
agents: {

src/agents/system-prompt.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -907,12 +907,11 @@ describe("buildAgentSystemPrompt", () => {
907907
},
908908
});
909909

910-
expect(prompt).toContain("private by default for this source channel");
911-
expect(prompt).toContain("use `message(action=send)` for visible channel output");
910+
expect(prompt).toContain("use `message(action=send)` for visible source-channel output");
912911
expect(prompt).toContain("Attach media with message-tool attachment fields");
913912
expect(prompt).not.toContain("Attach media: `MEDIA:<path-or-url>`");
914913
expect(prompt).toContain("The target defaults to the current source channel");
915-
expect(prompt).toContain("final answers are private in this mode");
914+
expect(prompt).toContain("do not repeat that visible content in your final answer");
916915
expect(prompt).not.toContain("## Silent Replies");
917916
expect(prompt).not.toContain(SILENT_REPLY_TOKEN);
918917
expect(prompt).not.toContain(

src/agents/system-prompt.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ function buildMessagingSection(params: {
508508
return [
509509
"## Messaging",
510510
messageToolOnly
511-
? "- Reply in current session → private by default for this source channel; use `message(action=send)` for visible channel output."
511+
? "- Reply in current session → use `message(action=send)` for visible source-channel output; normal final text may stay private or route only as a fallback."
512512
: "- Reply in current session → automatically routes to the source channel (Signal, Telegram, etc.)",
513513
"- Cross-session messaging → use sessions_send(sessionKey, message)",
514514
subagentOrchestrationGuidance,
@@ -526,7 +526,7 @@ function buildMessagingSection(params: {
526526
? `- No current/default source channel: include \`channel\` for proactive sends; valid ids: ${params.messageChannelOptions}.`
527527
: "- Pass `channel` only when sending outside the current/default source channel.",
528528
messageToolOnly
529-
? "- If you use `message` (`action=send`) to deliver visible output, do not repeat that visible content in your final answer; final answers are private in this mode."
529+
? "- If you use `message` (`action=send`) to deliver visible output, do not repeat that visible content in your final answer."
530530
: suppressSilentTokenGuidance
531531
? "- Do not use `message(action=send)` to deliver the current source-channel reply; reply normally so OpenClaw can route it once."
532532
: `- If you use \`message\` (\`action=send\`) to deliver your user-visible reply, respond with ONLY: ${SILENT_REPLY_TOKEN} (avoid duplicate replies).`,

0 commit comments

Comments
 (0)