

















@@ -126,15 +126,65 @@ describe("maybeCreateDiscordAutoThread", () => {
126126127127it("creates auto-thread if channelType is GuildText", async () => {
128128postMock.mockResolvedValueOnce({ id: "thread1" });
129+getMock.mockResolvedValueOnce({});
129130const result = await maybeCreateDiscordAutoThread(createBaseParams());
130131expect(result).toBe("thread1");
131132expect(postMock).toHaveBeenCalled();
132133});
134+135+it("reuses an existing message thread before creating a new one", async () => {
136+getMock.mockResolvedValueOnce({ thread: { id: "existing-thread" } });
137+const result = await maybeCreateDiscordAutoThread(createBaseParams());
138+139+expect(result).toBe("existing-thread");
140+expect(postMock).not.toHaveBeenCalled();
141+});
142+143+it("reuses an existing message thread before skipping bot-authored messages", async () => {
144+getMock.mockResolvedValueOnce({ thread: { id: "existing-thread" } });
145+const result = await maybeCreateDiscordAutoThread(
146+createBaseParams({
147+message: {
148+ ...mockMessage,
149+author: { bot: true },
150+} as Parameters<MaybeCreateDiscordAutoThreadFn>[0]["message"],
151+}),
152+);
153+154+expect(result).toBe("existing-thread");
155+expect(postMock).not.toHaveBeenCalled();
156+});
157+158+it("skips creating new auto-threads for bot-authored messages", async () => {
159+getMock.mockResolvedValueOnce({});
160+const result = await maybeCreateDiscordAutoThread(
161+createBaseParams({
162+message: {
163+ ...mockMessage,
164+author: { bot: true },
165+} as Parameters<MaybeCreateDiscordAutoThreadFn>[0]["message"],
166+}),
167+);
168+169+expect(result).toBeUndefined();
170+expect(postMock).not.toHaveBeenCalled();
171+});
172+173+it("still creates an auto-thread when the existing-thread lookup fails", async () => {
174+getMock.mockRejectedValueOnce(new Error("transient fetch failure"));
175+postMock.mockResolvedValueOnce({ id: "thread1" });
176+177+const result = await maybeCreateDiscordAutoThread(createBaseParams());
178+179+expect(result).toBe("thread1");
180+expect(postMock).toHaveBeenCalled();
181+});
133182});
134183135184describe("maybeCreateDiscordAutoThread autoArchiveDuration", () => {
136185it("uses configured autoArchiveDuration", async () => {
137186postMock.mockResolvedValueOnce({ id: "thread1" });
187+getMock.mockResolvedValueOnce({});
138188await maybeCreateDiscordAutoThread(
139189createBaseParams({
140190channelConfig: { allowed: true, autoThread: true, autoArchiveDuration: "10080" },
@@ -145,6 +195,7 @@ describe("maybeCreateDiscordAutoThread autoArchiveDuration", () => {
145195146196it("accepts numeric autoArchiveDuration", async () => {
147197postMock.mockResolvedValueOnce({ id: "thread1" });
198+getMock.mockResolvedValueOnce({});
148199await maybeCreateDiscordAutoThread(
149200createBaseParams({
150201channelConfig: { allowed: true, autoThread: true, autoArchiveDuration: 4320 },
@@ -155,6 +206,7 @@ describe("maybeCreateDiscordAutoThread autoArchiveDuration", () => {
155206156207it("defaults to 60 when autoArchiveDuration not set", async () => {
157208postMock.mockResolvedValueOnce({ id: "thread1" });
209+getMock.mockResolvedValueOnce({});
158210await maybeCreateDiscordAutoThread(createBaseParams());
159211expectRestBodyField(postMock, "auto_archive_duration", 60);
160212});
@@ -163,6 +215,7 @@ describe("maybeCreateDiscordAutoThread autoArchiveDuration", () => {
163215describe("maybeCreateDiscordAutoThread autoThreadName", () => {
164216it("renames created thread when generated mode is enabled", async () => {
165217postMock.mockResolvedValueOnce({ id: "thread1" });
218+getMock.mockResolvedValueOnce({});
166219patchMock.mockResolvedValueOnce({});
167220generateThreadTitleMock.mockResolvedValueOnce("Deploy rollout summary");
168221@@ -193,6 +246,7 @@ describe("maybeCreateDiscordAutoThread autoThreadName", () => {
193246194247it("does not block thread creation while title summary is pending", async () => {
195248postMock.mockResolvedValueOnce({ id: "thread1" });
249+getMock.mockResolvedValueOnce({});
196250patchMock.mockResolvedValueOnce({});
197251let resolveTitle: ((value: string | null) => void) | undefined;
198252generateThreadTitleMock.mockReturnValueOnce(
@@ -219,6 +273,7 @@ describe("maybeCreateDiscordAutoThread autoThreadName", () => {
219273220274it("uses channel-specific thread override for generated title model", async () => {
221275postMock.mockResolvedValueOnce({ id: "thread1" });
276+getMock.mockResolvedValueOnce({});
222277patchMock.mockResolvedValueOnce({});
223278generateThreadTitleMock.mockResolvedValueOnce("Deploy rollout summary");
224279@@ -248,6 +303,7 @@ describe("maybeCreateDiscordAutoThread autoThreadName", () => {
248303249304it("falls back to parent channel override for generated title model", async () => {
250305postMock.mockResolvedValueOnce({ id: "thread1" });
306+getMock.mockResolvedValueOnce({});
251307patchMock.mockResolvedValueOnce({});
252308generateThreadTitleMock.mockResolvedValueOnce("Deploy rollout summary");
253309@@ -277,6 +333,7 @@ describe("maybeCreateDiscordAutoThread autoThreadName", () => {
277333278334it("skips summarization when cfg or agentId is missing", async () => {
279335postMock.mockResolvedValueOnce({ id: "thread1" });
336+getMock.mockResolvedValueOnce({});
280337await maybeCreateDiscordAutoThread(
281338createBaseParams({
282339channelConfig: { allowed: true, autoThread: true, autoThreadName: "generated" },
@@ -289,6 +346,7 @@ describe("maybeCreateDiscordAutoThread autoThreadName", () => {
289346290347it("does not rename when autoThreadName is not set", async () => {
291348postMock.mockResolvedValueOnce({ id: "thread1" });
349+getMock.mockResolvedValueOnce({});
292350await maybeCreateDiscordAutoThread(
293351createBaseParams({
294352channelConfig: { allowed: true, autoThread: true },
@@ -301,6 +359,7 @@ describe("maybeCreateDiscordAutoThread autoThreadName", () => {
301359302360it("does not rename when generated title sanitizes to fallback thread name", async () => {
303361postMock.mockResolvedValueOnce({ id: "thread1" });
362+getMock.mockResolvedValueOnce({});
304363generateThreadTitleMock.mockResolvedValueOnce("<@123456789012345678> <#987654321098765432>");
305364306365const cfg = { agents: { defaults: { model: "anthropic/claude-opus-4-6" } } } as OpenClawConfig;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。