diff --git a/src/slack/monitor/message-handler/prepare.test.ts b/src/slack/monitor/message-handler/prepare.test.ts index 0db6a635303..3c16609a1a2 100644 --- a/src/slack/monitor/message-handler/prepare.test.ts +++ b/src/slack/monitor/message-handler/prepare.test.ts @@ -516,7 +516,7 @@ describe("slack prepareSlackMessage inbound contract", () => { expect(replies).toHaveBeenCalledTimes(2); }); - it("keeps loading thread history when thread session already exists in store", async () => { + it("skips loading thread history when thread session already exists in store (bloat fix)", async () => { const { storePath } = makeTmpStorePath(); const cfg = { session: { store: storePath }, @@ -533,24 +533,15 @@ describe("slack prepareSlackMessage inbound contract", () => { baseSessionKey: route.sessionKey, threadId: "200.000", }); + // Simulate existing session - thread history should NOT be fetched (bloat fix) fs.writeFileSync( storePath, JSON.stringify({ [threadKeys.sessionKey]: { updatedAt: Date.now() } }, null, 2), ); - const replies = vi - .fn() - .mockResolvedValueOnce({ - messages: [{ text: "starter", user: "U2", ts: "200.000" }], - }) - .mockResolvedValueOnce({ - messages: [ - { text: "starter", user: "U2", ts: "200.000" }, - { text: "assistant follow-up", bot_id: "B1", ts: "200.500" }, - { text: "user follow-up", user: "U1", ts: "200.800" }, - { text: "current message", user: "U1", ts: "201.000" }, - ], - }); + const replies = vi.fn().mockResolvedValueOnce({ + messages: [{ text: "starter", user: "U2", ts: "200.000" }], + }); const slackCtx = createThreadSlackCtx({ cfg, replies }); slackCtx.resolveUserName = async () => ({ name: "Alice" }); slackCtx.resolveChannelName = async () => ({ name: "general", type: "channel" }); @@ -563,10 +554,12 @@ describe("slack prepareSlackMessage inbound contract", () => { expect(prepared).toBeTruthy(); expect(prepared!.ctxPayload.IsFirstThreadTurn).toBeUndefined(); - expect(prepared!.ctxPayload.ThreadHistoryBody).toContain("assistant follow-up"); - expect(prepared!.ctxPayload.ThreadHistoryBody).toContain("user follow-up"); - expect(prepared!.ctxPayload.ThreadHistoryBody).not.toContain("current message"); - expect(replies).toHaveBeenCalledTimes(2); + // Thread history should NOT be fetched for existing sessions (bloat fix) + expect(prepared!.ctxPayload.ThreadHistoryBody).toBeUndefined(); + // Thread starter should also be skipped for existing sessions + expect(prepared!.ctxPayload.ThreadStarterBody).toBeUndefined(); + // Replies API should only be called once (for thread starter lookup, not history) + expect(replies).toHaveBeenCalledTimes(1); }); it("includes thread_ts and parent_user_id metadata in thread replies", async () => { diff --git a/src/slack/monitor/message-handler/prepare.ts b/src/slack/monitor/message-handler/prepare.ts index 247dac92edc..4bf192858e1 100644 --- a/src/slack/monitor/message-handler/prepare.ts +++ b/src/slack/monitor/message-handler/prepare.ts @@ -594,7 +594,8 @@ export async function prepareSlackMessage(params: { storePath, sessionKey, // Thread-specific session key }); - if (threadInitialHistoryLimit > 0) { + // Only fetch thread history for NEW sessions (existing sessions already have this context in their transcript) + if (threadInitialHistoryLimit > 0 && !threadSessionPreviousTimestamp) { const threadHistory = await resolveSlackThreadHistory({ channelId: message.channel, threadTs, @@ -684,7 +685,8 @@ export async function prepareSlackMessage(params: { // Preserve thread context for routed tool notifications. MessageThreadId: threadContext.messageThreadId, ParentSessionKey: threadKeys.parentSessionKey, - ThreadStarterBody: threadStarterBody, + // Only include thread starter body for NEW sessions (existing sessions already have it in their transcript) + ThreadStarterBody: !threadSessionPreviousTimestamp ? threadStarterBody : undefined, ThreadHistoryBody: threadHistoryBody, IsFirstThreadTurn: isThreadReply && threadTs && !threadSessionPreviousTimestamp ? true : undefined,