Skip to content

Commit e1fd27f

Browse files
committed
feat(messages): add global visible replies mode
1 parent 1c45592 commit e1fd27f

14 files changed

Lines changed: 81 additions & 12 deletions

CHANGELOG.md

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

77
### Changes
88

9+
- Messages: add global `messages.visibleReplies` so operators can require visible output to go through `message(action=send)` for any source chat, while `messages.groupChat.visibleReplies` stays available as the group/channel override. Thanks @scoootscooob.
910
- Gateway/dev: run `pnpm gateway:watch` through a named tmux session by default, with `gateway:watch:raw` and `OPENCLAW_GATEWAY_WATCH_TMUX=0` for foreground mode, so repeated starts respawn an inspectable watcher without trapping the invoking agent shell. Thanks @vincentkoc.
1011
- Plugin SDK: mark remaining legacy alias exports and diffs tool/config aliases with deprecation metadata, and add a guard so future legacy alias comments require `@deprecated` tags. Thanks @vincentkoc.
1112
- CLI/QR/dependencies: internalize small terminal progress and QR wrapper helpers while keeping the real QR encoder dependency direct, reducing the default runtime dependency graph without changing QR output behavior. Thanks @vincentkoc.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
592d25e08647ced4fae0c4fdbff95e50d1749c42d39070f6b6bc6a3e0475d4f0 config-baseline.json
2-
9cd2c40b4a45976b74458f9ada8ecc31c532ee81f10145a9828bbff31777c03e config-baseline.core.json
1+
664d715fc9aba21236c9ef31e30a81f7ff96ede9a3b77273af569288ece0e7f7 config-baseline.json
2+
0cc8ae3ae49d324face60240b4d3ed545c9ccec9b333bf1a1d98887151d37b77 config-baseline.core.json
33
9f5fad66a49fa618d64a963470aa69fed9fe4b4639cc4321f9ec04bfb2f8aa50 config-baseline.channel.json
44
0dd6583fafae6c9134e46c4cf9bddee9822d6436436dcb1a6dcba6d012962e51 config-baseline.plugin.json

docs/channels/groups.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ otherwise -> reply
4343
For group/channel rooms, OpenClaw defaults to `messages.groupChat.visibleReplies: "message_tool"`.
4444
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)`.
4545

46+
For direct chats and any other source turn, use `messages.visibleReplies: "message_tool"` to apply the same tool-only visible-reply behavior globally. `messages.groupChat.visibleReplies` remains the more specific override for group/channel rooms.
47+
4648
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.
4749

4850
Typing indicators are still sent while the agent works in tool-only mode. The default group typing mode is upgraded from "message" to "instant" for these turns because there may never be normal assistant message text before the agent decides whether to call the message tool. Explicit typing-mode config still wins.
@@ -59,6 +61,16 @@ To restore legacy automatic final replies for group/channel rooms:
5961
}
6062
```
6163

64+
To require visible output to go through the message tool for every source chat:
65+
66+
```json5
67+
{
68+
messages: {
69+
visibleReplies: "message_tool",
70+
},
71+
}
72+
```
73+
6274
Native slash commands (Discord, Telegram, and other surfaces with native command support) bypass `visibleReplies: "message_tool"` and always reply visibly so the channel-native command UI gets the response it expects. This applies to validated native command turns only; text-typed `/...` commands and ordinary chat turns still follow the configured group default.
6375

6476
## Context visibility and allowlists

docs/gateway/config-channels.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ See the full channel index: [Channels](/channels).
770770

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

773-
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.
773+
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"`.
774774

775775
**Mention types:**
776776

@@ -781,6 +781,7 @@ Visible replies are controlled separately. Group/channel rooms default to `messa
781781
```json5
782782
{
783783
messages: {
784+
visibleReplies: "automatic", // global default for direct/source chats
784785
groupChat: {
785786
historyLimit: 50,
786787
visibleReplies: "message_tool", // default; use "automatic" for legacy final replies
@@ -794,7 +795,7 @@ Visible replies are controlled separately. Group/channel rooms default to `messa
794795

795796
`messages.groupChat.historyLimit` sets the global default. Channels can override with `channels.<channel>.historyLimit` (or per-account). Set `0` to disable.
796797

797-
`messages.groupChat.visibleReplies` is global for group/channel source turns; channel allowlists and mention gating still decide whether a turn is processed.
798+
`messages.visibleReplies` is the global source-turn default; `messages.groupChat.visibleReplies` overrides it for group/channel source turns. Channel allowlists and mention gating still decide whether a turn is processed.
798799

799800
#### DM history limits
800801

docs/gateway/configuration-examples.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
4242
},
4343
},
4444
messages: {
45+
visibleReplies: "automatic",
4546
groupChat: {
4647
visibleReplies: "message_tool", // default; use "automatic" for legacy room replies
4748
},
@@ -101,6 +102,7 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
101102
// Message formatting
102103
messages: {
103104
messagePrefix: "[openclaw]",
105+
visibleReplies: "automatic",
104106
responsePrefix: ">",
105107
ackReaction: "👀",
106108
ackReactionScope: "group-mentions",

docs/gateway/configuration.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ cannot roll back unrelated user settings.
184184
```json5
185185
{
186186
messages: {
187+
visibleReplies: "automatic", // set "message_tool" to require message-tool sends everywhere
187188
groupChat: {
188189
visibleReplies: "message_tool", // default; use "automatic" for legacy room replies
189190
},
@@ -208,7 +209,7 @@ cannot roll back unrelated user settings.
208209

209210
- **Metadata mentions**: native @-mentions (WhatsApp tap-to-mention, Telegram @bot, etc.)
210211
- **Text patterns**: safe regex patterns in `mentionPatterns`
211-
- **Visible replies**: `message_tool` keeps normal final replies private; the agent must call `message(action=send)` to post visibly in the group/channel.
212+
- **Visible replies**: `messages.visibleReplies` can require message-tool sends globally; `messages.groupChat.visibleReplies` overrides that for groups/channels.
212213
- See [full reference](/gateway/config-channels#group-chat-mention-gating) for visible reply modes, per-channel overrides, and self-chat mode.
213214

214215
</Accordion>

src/auto-reply/reply/source-reply-delivery-mode.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ const automaticGroupReplyConfig = {
1313
},
1414
},
1515
} as const satisfies OpenClawConfig;
16+
const globalToolOnlyReplyConfig = {
17+
messages: {
18+
visibleReplies: "message_tool",
19+
},
20+
} as const satisfies OpenClawConfig;
1621

1722
describe("resolveSourceReplyDeliveryMode", () => {
1823
it("defaults groups and channels to message-tool-only delivery", () => {
@@ -43,6 +48,28 @@ describe("resolveSourceReplyDeliveryMode", () => {
4348
).toBe("automatic");
4449
});
4550

51+
it("allows message-tool-only delivery for any source chat via global config", () => {
52+
for (const ChatType of ["direct", "group", "channel"] as const) {
53+
expect(
54+
resolveSourceReplyDeliveryMode({ cfg: globalToolOnlyReplyConfig, ctx: { ChatType } }),
55+
).toBe("message_tool_only");
56+
}
57+
});
58+
59+
it("lets group/channel config override the global visible reply mode", () => {
60+
expect(
61+
resolveSourceReplyDeliveryMode({
62+
cfg: {
63+
messages: {
64+
visibleReplies: "message_tool",
65+
groupChat: { visibleReplies: "automatic" },
66+
},
67+
},
68+
ctx: { ChatType: "channel" },
69+
}),
70+
).toBe("automatic");
71+
});
72+
4673
it("treats native commands as explicit replies in groups", () => {
4774
expect(
4875
resolveSourceReplyDeliveryMode({

src/auto-reply/reply/source-reply-delivery-mode.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ export function resolveSourceReplyDeliveryMode(params: {
2121
}
2222
const chatType = normalizeChatType(params.ctx.ChatType);
2323
if (chatType === "group" || chatType === "channel") {
24-
return params.cfg.messages?.groupChat?.visibleReplies === "automatic"
25-
? "automatic"
26-
: "message_tool_only";
24+
const configuredMode =
25+
params.cfg.messages?.groupChat?.visibleReplies ?? params.cfg.messages?.visibleReplies;
26+
return configuredMode === "automatic" ? "automatic" : "message_tool_only";
2727
}
28-
return "automatic";
28+
return params.cfg.messages?.visibleReplies === "message_tool" ? "message_tool_only" : "automatic";
2929
}
3030

3131
export type SourceReplyVisibilityPolicy = {

src/config/schema.base.generated.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18873,6 +18873,13 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
1887318873
description:
1887418874
"Prefix text prepended to inbound user messages before they are handed to the agent runtime. Use this sparingly for channel context markers and keep it stable across sessions.",
1887518875
},
18876+
visibleReplies: {
18877+
type: "string",
18878+
enum: ["automatic", "message_tool"],
18879+
title: "Visible Replies",
18880+
description:
18881+
'Controls visible source replies across direct, group, and channel conversations. "message_tool" keeps normal final replies private and requires message(action=send) for visible output; "automatic" posts normal replies as before.',
18882+
},
1887618883
responsePrefix: {
1887718884
type: "string",
1887818885
title: "Outbound Response Prefix",
@@ -18904,7 +18911,7 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
1890418911
enum: ["automatic", "message_tool"],
1890518912
title: "Group Visible Replies",
1890618913
description:
18907-
'Controls visible group/channel replies. "message_tool" keeps normal final replies private and requires message(action=send) for room output; "automatic" posts normal replies as before.',
18914+
'Overrides visible source replies for group/channel conversations. "message_tool" keeps normal final replies private and requires message(action=send) for room output; "automatic" posts normal replies as before.',
1890818915
},
1890918916
},
1891018917
additionalProperties: false,
@@ -28198,6 +28205,11 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
2819828205
help: "Prefix text prepended to inbound user messages before they are handed to the agent runtime. Use this sparingly for channel context markers and keep it stable across sessions.",
2819928206
tags: ["advanced"],
2820028207
},
28208+
"messages.visibleReplies": {
28209+
label: "Visible Replies",
28210+
help: 'Controls visible source replies across direct, group, and channel conversations. "message_tool" keeps normal final replies private and requires message(action=send) for visible output; "automatic" posts normal replies as before.',
28211+
tags: ["advanced"],
28212+
},
2820128213
"messages.responsePrefix": {
2820228214
label: "Outbound Response Prefix",
2820328215
help: "Prefix text prepended to outbound assistant replies before sending to channels. Use for lightweight branding/context tags and avoid long prefixes that reduce content density.",
@@ -28220,7 +28232,7 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
2822028232
},
2822128233
"messages.groupChat.visibleReplies": {
2822228234
label: "Group Visible Replies",
28223-
help: 'Controls visible group/channel replies. "message_tool" keeps normal final replies private and requires message(action=send) for room output; "automatic" posts normal replies as before.',
28235+
help: 'Overrides visible source replies for group/channel conversations. "message_tool" keeps normal final replies private and requires message(action=send) for room output; "automatic" posts normal replies as before.',
2822428236
tags: ["advanced"],
2822528237
},
2822628238
"messages.queue": {

src/config/schema.help.quality.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ const TARGET_KEYS = [
244244
"hooks.internal.load.extraDirs",
245245
"messages",
246246
"messages.messagePrefix",
247+
"messages.visibleReplies",
247248
"messages.responsePrefix",
248249
"messages.groupChat",
249250
"messages.groupChat.mentionPatterns",

0 commit comments

Comments
 (0)