mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
perf(slack): bound thread history pagination
This commit is contained in:
@@ -997,6 +997,55 @@ describe("resolveSlackThreadHistory", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns no thread history when pagination exceeds the bounded fetched window", async () => {
|
||||
vi.mocked(logVerbose).mockClear();
|
||||
const replies = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({
|
||||
messages: Array.from({ length: 200 }, (_, i) => ({
|
||||
text: `msg-${i + 1}`,
|
||||
user: "U1",
|
||||
ts: `${i + 1}.000`,
|
||||
})),
|
||||
response_metadata: { next_cursor: "cursor-2" },
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
messages: Array.from({ length: 200 }, (_, i) => ({
|
||||
text: `msg-${i + 201}`,
|
||||
user: "U1",
|
||||
ts: `${i + 201}.000`,
|
||||
})),
|
||||
response_metadata: { next_cursor: "cursor-3" },
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
messages: Array.from({ length: 200 }, (_, i) => ({
|
||||
text: `msg-${i + 401}`,
|
||||
user: "U1",
|
||||
ts: `${i + 401}.000`,
|
||||
})),
|
||||
response_metadata: { next_cursor: "cursor-4" },
|
||||
});
|
||||
const client = {
|
||||
conversations: { replies },
|
||||
} as unknown as Parameters<typeof resolveSlackThreadHistory>[0]["client"];
|
||||
|
||||
const result = await resolveSlackThreadHistory({
|
||||
channelId: "C1",
|
||||
threadTs: "1.000",
|
||||
client,
|
||||
limit: 3,
|
||||
});
|
||||
|
||||
expect(replies).toHaveBeenCalledTimes(3);
|
||||
expect(requireMockCall(replies, 2, "conversations.replies")[0]).toMatchObject({
|
||||
cursor: "cursor-3",
|
||||
limit: 200,
|
||||
});
|
||||
expect(result).toEqual([]);
|
||||
expectVerboseLogContains("slack thread history capped");
|
||||
expectVerboseLogContains("channel=C1");
|
||||
});
|
||||
|
||||
it("includes file-only messages and drops empty-only entries", async () => {
|
||||
const replies = vi.fn().mockResolvedValueOnce({
|
||||
messages: [
|
||||
|
||||
@@ -185,12 +185,14 @@ type SlackRepliesPage = {
|
||||
response_metadata?: { next_cursor?: string };
|
||||
};
|
||||
|
||||
const SLACK_THREAD_HISTORY_MAX_PAGES = 3;
|
||||
|
||||
/**
|
||||
* Fetches the most recent messages in a Slack thread (excluding the current message).
|
||||
* Used to populate thread context when a new thread session starts.
|
||||
*
|
||||
* Uses cursor pagination and keeps only the latest N retained messages so long threads
|
||||
* still produce up-to-date context without unbounded memory growth.
|
||||
* Uses cursor pagination and keeps only the latest N retained messages when the full
|
||||
* thread fits in the bounded fetch window.
|
||||
*/
|
||||
export async function resolveSlackThreadHistory(params: {
|
||||
channelId: string;
|
||||
@@ -208,9 +210,11 @@ export async function resolveSlackThreadHistory(params: {
|
||||
const fetchLimit = 200;
|
||||
const retained: SlackRepliesPageMessage[] = [];
|
||||
let cursor: string | undefined;
|
||||
let pagesFetched = 0;
|
||||
|
||||
try {
|
||||
do {
|
||||
pagesFetched += 1;
|
||||
const response = (await params.client.conversations.replies({
|
||||
channel: params.channelId,
|
||||
ts: params.threadTs,
|
||||
@@ -236,7 +240,16 @@ export async function resolveSlackThreadHistory(params: {
|
||||
|
||||
const next = response.response_metadata?.next_cursor;
|
||||
cursor = typeof next === "string" && next.trim().length > 0 ? next.trim() : undefined;
|
||||
} while (cursor);
|
||||
// Slack replies paginate oldest to newest with no reverse cursor; cap cold
|
||||
// thread seeding so pathological long threads cannot block dispatch.
|
||||
} while (cursor && pagesFetched < SLACK_THREAD_HISTORY_MAX_PAGES);
|
||||
|
||||
if (cursor) {
|
||||
logVerbose(
|
||||
`slack thread history capped channel=${params.channelId} ts=${params.threadTs} pages=${SLACK_THREAD_HISTORY_MAX_PAGES}`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return retained.map((msg) => ({
|
||||
// For file-only messages, create a placeholder showing attached filenames.
|
||||
|
||||
Reference in New Issue
Block a user