mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-28 17:01:53 +03:00
fix: normalize Discord autoThread reply target (#8302) (thanks @gavinbmoore)
This commit is contained in:
+3
-1
@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Discord: route autoThread replies to existing threads instead of the root channel. (#8302) Thanks @gavinbmoore, @thewilloftheshadow.
|
||||||
- Agents/Image tool: cap image-analysis completion `maxTokens` by model capability (`min(4096, model.maxTokens)`) to avoid over-limit provider failures while still preventing truncation. (#11770) Thanks @detecti1.
|
- Agents/Image tool: cap image-analysis completion `maxTokens` by model capability (`min(4096, model.maxTokens)`) to avoid over-limit provider failures while still preventing truncation. (#11770) Thanks @detecti1.
|
||||||
- TUI/Streaming: preserve richer streamed assistant text when final payload drops pre-tool-call text blocks, while keeping non-empty final payload authoritative for plain-text updates. (#15452) Thanks @TsekaLuk.
|
- TUI/Streaming: preserve richer streamed assistant text when final payload drops pre-tool-call text blocks, while keeping non-empty final payload authoritative for plain-text updates. (#15452) Thanks @TsekaLuk.
|
||||||
- Inbound/Web UI: preserve literal `\n` sequences when normalizing inbound text so Windows paths like `C:\\Work\\nxxx\\README.md` are not corrupted. (#11547) Thanks @mcaxtr.
|
- Inbound/Web UI: preserve literal `\n` sequences when normalizing inbound text so Windows paths like `C:\\Work\\nxxx\\README.md` are not corrupted. (#11547) Thanks @mcaxtr.
|
||||||
@@ -380,8 +381,9 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Security: require validated shared-secret auth before skipping device identity on gateway connect.
|
- Security: require validated shared-secret auth before skipping device identity on gateway connect.
|
||||||
- Security: guard skill installer downloads with SSRF checks (block private/localhost URLs).
|
- Security: guard skill installer downloads with SSRF checks (block private/localhost URLs).
|
||||||
- Security: harden Windows exec allowlist; block cmd.exe bypass via single &. Thanks @simecek.
|
- Security: harden Windows exec allowlist; block cmd.exe bypass via single &. Thanks @simecek.
|
||||||
- fix(voice-call): harden inbound allowlist; reject anonymous callers; require Telnyx publicKey for allowlist; token-gate Twilio media streams; cap webhook body size (thanks @simecek)
|
- Discord: route autoThread replies to existing threads instead of the root channel. (#8302) Thanks @gavinbmoore, @thewilloftheshadow.
|
||||||
- Media understanding: apply SSRF guardrails to provider fetches; allow private baseUrl overrides explicitly.
|
- Media understanding: apply SSRF guardrails to provider fetches; allow private baseUrl overrides explicitly.
|
||||||
|
- fix(voice-call): harden inbound allowlist; reject anonymous callers; require Telnyx publicKey for allowlist; token-gate Twilio media streams; cap webhook body size (thanks @simecek)
|
||||||
- fix(webchat): respect user scroll position during streaming and refresh (#7226) (thanks @marcomarandiz)
|
- fix(webchat): respect user scroll position during streaming and refresh (#7226) (thanks @marcomarandiz)
|
||||||
- Telegram: recover from grammY long-poll timed out errors. (#7466) Thanks @macmimi23.
|
- Telegram: recover from grammY long-poll timed out errors. (#7466) Thanks @macmimi23.
|
||||||
- Agents: repair malformed tool calls and session transcripts. (#7473) Thanks @justinhuangcode.
|
- Agents: repair malformed tool calls and session transcripts. (#7473) Thanks @justinhuangcode.
|
||||||
|
|||||||
@@ -210,6 +210,31 @@ describe("resolveDiscordAutoThreadReplyPlan", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("routes replies to an existing thread channel", async () => {
|
||||||
|
const client = { rest: { post: async () => ({ id: "thread" }) } } as unknown as Client;
|
||||||
|
const plan = await resolveDiscordAutoThreadReplyPlan({
|
||||||
|
client,
|
||||||
|
message: {
|
||||||
|
id: "m1",
|
||||||
|
channelId: "parent",
|
||||||
|
} as unknown as import("./listeners.js").DiscordMessageEvent["message"],
|
||||||
|
isGuildMessage: true,
|
||||||
|
channelConfig: {
|
||||||
|
autoThread: true,
|
||||||
|
} as unknown as import("./allow-list.js").DiscordChannelConfigResolved,
|
||||||
|
threadChannel: { id: "thread" },
|
||||||
|
baseText: "hello",
|
||||||
|
combinedBody: "hello",
|
||||||
|
replyToMode: "all",
|
||||||
|
agentId: "agent",
|
||||||
|
channel: "discord",
|
||||||
|
});
|
||||||
|
expect(plan.deliverTarget).toBe("channel:thread");
|
||||||
|
expect(plan.replyTarget).toBe("channel:thread");
|
||||||
|
expect(plan.replyReference.use()).toBe("m1");
|
||||||
|
expect(plan.autoThreadContext).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("does nothing when autoThread is disabled", async () => {
|
it("does nothing when autoThread is disabled", async () => {
|
||||||
const client = { rest: { post: async () => ({ id: "thread" }) } } as unknown as Client;
|
const client = { rest: { post: async () => ({ id: "thread" }) } } as unknown as Client;
|
||||||
const plan = await resolveDiscordAutoThreadReplyPlan({
|
const plan = await resolveDiscordAutoThreadReplyPlan({
|
||||||
|
|||||||
@@ -294,7 +294,9 @@ export async function resolveDiscordAutoThreadReplyPlan(params: {
|
|||||||
agentId: string;
|
agentId: string;
|
||||||
channel: string;
|
channel: string;
|
||||||
}): Promise<DiscordAutoThreadReplyPlan> {
|
}): Promise<DiscordAutoThreadReplyPlan> {
|
||||||
const originalReplyTarget = `channel:${params.message.channelId}`;
|
// Prefer the resolved thread channel ID when available so replies stay in-thread.
|
||||||
|
const targetChannelId = params.threadChannel?.id ?? params.message.channelId;
|
||||||
|
const originalReplyTarget = `channel:${targetChannelId}`;
|
||||||
const createdThreadId = await maybeCreateDiscordAutoThread({
|
const createdThreadId = await maybeCreateDiscordAutoThread({
|
||||||
client: params.client,
|
client: params.client,
|
||||||
message: params.message,
|
message: params.message,
|
||||||
@@ -391,17 +393,11 @@ export function resolveDiscordReplyDeliveryPlan(params: {
|
|||||||
let deliverTarget = originalReplyTarget;
|
let deliverTarget = originalReplyTarget;
|
||||||
let replyTarget = originalReplyTarget;
|
let replyTarget = originalReplyTarget;
|
||||||
|
|
||||||
// When a new thread was created, route to the new thread
|
// When a new thread was created, route to the new thread.
|
||||||
if (params.createdThreadId) {
|
if (params.createdThreadId) {
|
||||||
deliverTarget = `channel:${params.createdThreadId}`;
|
deliverTarget = `channel:${params.createdThreadId}`;
|
||||||
replyTarget = deliverTarget;
|
replyTarget = deliverTarget;
|
||||||
}
|
}
|
||||||
// When in an existing thread (not newly created), ensure we route to the thread
|
|
||||||
// This fixes #8278: autoThread replies sometimes going to root channel
|
|
||||||
else if (params.threadChannel?.id) {
|
|
||||||
deliverTarget = `channel:${params.threadChannel.id}`;
|
|
||||||
replyTarget = deliverTarget;
|
|
||||||
}
|
|
||||||
const allowReference = deliverTarget === originalReplyTarget;
|
const allowReference = deliverTarget === originalReplyTarget;
|
||||||
const replyReference = createReplyReferencePlanner({
|
const replyReference = createReplyReferencePlanner({
|
||||||
replyToMode: allowReference ? params.replyToMode : "off",
|
replyToMode: allowReference ? params.replyToMode : "off",
|
||||||
|
|||||||
Reference in New Issue
Block a user