Fix Discord autoThread thread-only replies (#807)

Co-authored-by: Shadow <shadow@clawd.bot>
This commit is contained in:
David Guttman
2026-01-12 13:11:48 -10:00
committed by GitHub
parent cf92099d40
commit 2e654e8d63
2 changed files with 20 additions and 11 deletions

View File

@@ -56,6 +56,7 @@
- Auto-reply: elevated/reasoning toggles now enqueue system events so the model sees the mode change immediately. - Auto-reply: elevated/reasoning toggles now enqueue system events so the model sees the mode change immediately.
- Tools: keep `image` available in sandbox and fail over when image models return empty output (fixes “(no text returned)”). - Tools: keep `image` available in sandbox and fail over when image models return empty output (fixes “(no text returned)”).
- Discord: add per-channel `autoThread` to auto-create threads for top-level messages. (#800) — thanks @davidguttman. - Discord: add per-channel `autoThread` to auto-create threads for top-level messages. (#800) — thanks @davidguttman.
- Discord: fix autoThread routing so replies stay in the created thread and avoid reply references. (#807) — thanks @davidguttman.
- Onboarding: TUI defaults to `deliver: false` to avoid cross-provider auto-delivery leaks; onboarding spawns the TUI with explicit `deliver: false`. (#791 — thanks @roshanasingh4) - Onboarding: TUI defaults to `deliver: false` to avoid cross-provider auto-delivery leaks; onboarding spawns the TUI with explicit `deliver: false`. (#791 — thanks @roshanasingh4)
## 2026.1.11 ## 2026.1.11

View File

@@ -1179,23 +1179,26 @@ export function createDiscordMessageHandler(params: {
OriginatingChannel: "discord" as const, OriginatingChannel: "discord" as const,
OriginatingTo: discordTo, OriginatingTo: discordTo,
}; };
const replyTarget = ctxPayload.To ?? undefined; let replyTarget = ctxPayload.To ?? undefined;
if (!replyTarget) { if (!replyTarget) {
runtime.error?.(danger("discord: missing reply target")); runtime.error?.(danger("discord: missing reply target"));
return; return;
} }
const originalReplyTarget = replyTarget;
let deliverTarget = replyTarget; let deliverTarget = replyTarget;
if (isGuildMessage && channelConfig?.autoThread && !threadChannel) { if (isGuildMessage && channelConfig?.autoThread && !threadChannel) {
try { try {
const base = truncateUtf16Safe( const rawName = baseText || combinedBody || "Thread";
(baseText || combinedBody || "Thread").replace(/\s+/g, " ").trim(), const cleanedName = rawName
80, .replace(/<@!?\d+>/g, "") // user mentions
); .replace(/<@&\d+>/g, "") // role mentions
const authorLabel = author.username ?? author.id; .replace(/<#\d+>/g, "") // channel mentions
const threadName = .replace(/\s+/g, " ")
truncateUtf16Safe(`${authorLabel}: ${base}`.trim(), 100) || .trim();
`Thread ${message.id}`; const base = truncateUtf16Safe(cleanedName || "Thread", 80);
const threadName = truncateUtf16Safe(base, 100) || `Thread ${message.id}`;
const created = (await client.rest.post( const created = (await client.rest.post(
`${Routes.channelMessage(message.channelId, message.id)}/threads`, `${Routes.channelMessage(message.channelId, message.id)}/threads`,
@@ -1210,6 +1213,8 @@ export function createDiscordMessageHandler(params: {
const createdId = created?.id ? String(created.id) : ""; const createdId = created?.id ? String(created.id) : "";
if (createdId) { if (createdId) {
deliverTarget = `channel:${createdId}`; deliverTarget = `channel:${createdId}`;
// When autoThread is enabled, *always* reply in the created thread.
replyTarget = deliverTarget;
} }
} catch (err) { } catch (err) {
logVerbose( logVerbose(
@@ -1251,12 +1256,15 @@ export function createDiscordMessageHandler(params: {
deliver: async (payload) => { deliver: async (payload) => {
await deliverDiscordReply({ await deliverDiscordReply({
replies: [payload], replies: [payload],
target: deliverTarget, target: replyTarget,
token, token,
accountId, accountId,
rest: client.rest, rest: client.rest,
runtime, runtime,
replyToMode: deliverTarget !== replyTarget ? "off" : replyToMode, // The original message is in the parent channel; never try to reply-reference it
// when posting inside the newly-created thread.
replyToMode:
deliverTarget !== originalReplyTarget ? "off" : replyToMode,
textLimit, textLimit,
maxLinesPerMessage: discordConfig?.maxLinesPerMessage, maxLinesPerMessage: discordConfig?.maxLinesPerMessage,
}); });