fix(reply): honour explicit [[reply_to_*]] tags when replyToMode is off (#16174)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 778fc2559ade85a37209866c2edbe33bfc5bbb86
Co-authored-by: aldoeliacim <17973757+aldoeliacim@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
This commit is contained in:
Aldo
2026-02-14 06:29:42 -06:00
committed by GitHub
parent 0af76f5f0e
commit 7b39543e8d
6 changed files with 24 additions and 2 deletions

View File

@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
- BlueBubbles: gracefully degrade when Private API is disabled by filtering private-only actions, skipping private-only reactions/reply effects, and avoiding private reply markers so non-private flows remain usable. (#16002) Thanks @L-U-C-K-Y. - BlueBubbles: gracefully degrade when Private API is disabled by filtering private-only actions, skipping private-only reactions/reply effects, and avoiding private reply markers so non-private flows remain usable. (#16002) Thanks @L-U-C-K-Y.
- Outbound: add a write-ahead delivery queue with crash-recovery retries to prevent lost outbound messages after gateway restarts. (#15636) Thanks @nabbilkhan, @thewilloftheshadow. - Outbound: add a write-ahead delivery queue with crash-recovery retries to prevent lost outbound messages after gateway restarts. (#15636) Thanks @nabbilkhan, @thewilloftheshadow.
- Auto-reply/Threading: auto-inject implicit reply threading so `replyToMode` works without requiring model-emitted `[[reply_to_current]]`, while preserving `replyToMode: "off"` behavior for implicit Slack replies and keeping block-streaming chunk coalescing stable under `replyToMode: "first"`. (#14976) Thanks @Diaspar4u. - Auto-reply/Threading: auto-inject implicit reply threading so `replyToMode` works without requiring model-emitted `[[reply_to_current]]`, while preserving `replyToMode: "off"` behavior for implicit Slack replies and keeping block-streaming chunk coalescing stable under `replyToMode: "first"`. (#14976) Thanks @Diaspar4u.
- Auto-reply/Threading: honor explicit `[[reply_to_*]]` tags even when `replyToMode` is `off`. (#16174) Thanks @aldoeliacim.
- Outbound/Threading: pass `replyTo` and `threadId` from `message send` tool actions through the core outbound send path to channel adapters, preserving thread/reply routing. (#14948) Thanks @mcaxtr. - Outbound/Threading: pass `replyTo` and `threadId` from `message send` tool actions through the core outbound send path to channel adapters, preserving thread/reply routing. (#14948) Thanks @mcaxtr.
- Auto-reply/Media: allow image-only inbound messages (no caption) to reach the agent instead of short-circuiting as empty text, and preserve thread context in queued/followup prompt bodies for media-only runs. (#11916) Thanks @arosstale. - Auto-reply/Media: allow image-only inbound messages (no caption) to reach the agent instead of short-circuiting as empty text, and preserve thread context in queued/followup prompt bodies for media-only runs. (#11916) Thanks @arosstale.
- Discord: route autoThread replies to existing threads instead of the root channel. (#8302) Thanks @gavinbmoore, @thewilloftheshadow. - Discord: route autoThread replies to existing threads instead of the root channel. (#8302) Thanks @gavinbmoore, @thewilloftheshadow.

View File

@@ -273,6 +273,8 @@ See [Slash commands](/tools/slash-commands) for command catalog and behavior.
- `first` - `first`
- `all` - `all`
Note: `off` disables implicit reply threading. Explicit `[[reply_to_*]]` tags are still honored.
Message IDs are surfaced in context/history so agents can target specific messages. Message IDs are surfaced in context/history so agents can target specific messages.
</Accordion> </Accordion>

View File

@@ -233,6 +233,8 @@ Manual reply tags are supported:
- `[[reply_to_current]]` - `[[reply_to_current]]`
- `[[reply_to:<id>]]` - `[[reply_to:<id>]]`
Note: `replyToMode="off"` disables implicit reply threading. Explicit `[[reply_to_*]]` tags are still honored.
## Media, chunking, and delivery ## Media, chunking, and delivery
<AccordionGroup> <AccordionGroup>

View File

@@ -416,6 +416,8 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
- `first` - `first`
- `all` - `all`
Note: `off` disables implicit reply threading. Explicit `[[reply_to_*]]` tags are still honored.
</Accordion> </Accordion>
<Accordion title="Forum topics and thread behavior"> <Accordion title="Forum topics and thread behavior">

View File

@@ -72,4 +72,17 @@ describe("applyReplyThreading auto-threading", () => {
expect(result[0].replyToId).toBe("42"); expect(result[0].replyToId).toBe("42");
expect(result[0].replyToTag).toBe(true); expect(result[0].replyToTag).toBe(true);
}); });
it("keeps explicit tags for Telegram when off mode is enabled", () => {
const result = applyReplyThreading({
payloads: [{ text: "[[reply_to_current]]A" }],
replyToMode: "off",
replyToChannel: "telegram",
currentMessageId: "42",
});
expect(result).toHaveLength(1);
expect(result[0].replyToId).toBe("42");
expect(result[0].replyToTag).toBe(true);
});
}); });

View File

@@ -54,9 +54,11 @@ export function createReplyToModeFilterForChannel(
channel?: OriginatingChannelType, channel?: OriginatingChannelType,
) { ) {
const provider = normalizeChannelId(channel); const provider = normalizeChannelId(channel);
// Always honour explicit [[reply_to_*]] tags even when replyToMode is "off".
// Per-channel opt-out is possible but the safe default is to allow them.
const allowTagsWhenOff = provider const allowTagsWhenOff = provider
? Boolean(getChannelDock(provider)?.threading?.allowTagsWhenOff) ? (getChannelDock(provider)?.threading?.allowTagsWhenOff ?? true)
: false; : true;
return createReplyToModeFilter(mode, { return createReplyToModeFilter(mode, {
allowTagsWhenOff, allowTagsWhenOff,
}); });