From 7938640b384766a36871edbf5289848de5db84ef Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 19:46:39 +0000 Subject: [PATCH] fix(slack): accept omitted public search arguments Co-Authored-By: David Cramer --- .../src/chat/slack/tools/public-search.ts | 26 ++++++++++++---- .../integration/slack-public-search.test.ts | 30 +++++++++++++++++++ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/packages/junior/src/chat/slack/tools/public-search.ts b/packages/junior/src/chat/slack/tools/public-search.ts index 47f81e1552..b4e46344fe 100644 --- a/packages/junior/src/chat/slack/tools/public-search.ts +++ b/packages/junior/src/chat/slack/tools/public-search.ts @@ -338,7 +338,7 @@ function explicitSearchError(error: SlackActionError): string | undefined { } function normalizeContentTypes( - contentTypes: Array<(typeof CONTENT_TYPES)[number]> | undefined, + contentTypes: Array<(typeof CONTENT_TYPES)[number]> | null | undefined, ): Array<(typeof CONTENT_TYPES)[number]> { const selected = contentTypes?.length ? contentTypes @@ -368,27 +368,41 @@ export function createSlackPublicSearchTool(actionToken?: SlackActionToken) { .array(z.enum(CONTENT_TYPES)) .min(1) .max(4) - .describe("Content types to include. Defaults to messages.") + .nullable() + .describe("Content types to include. Omit or use null for messages.") .optional(), - after: optionalUnixTimestampParam("Unix timestamp lower bound."), - before: optionalUnixTimestampParam("Unix timestamp upper bound."), + after: optionalUnixTimestampParam( + "Unix timestamp lower bound.", + ).nullable(), + before: optionalUnixTimestampParam( + "Unix timestamp upper bound.", + ).nullable(), cursor: z .string() .min(1) - .describe("Cursor for the next result page.") + .nullable() + .describe( + "Cursor for the next result page. Omit or use null for the first page.", + ) .optional(), limit: z.coerce .number() .int() .min(1) .max(20) + .nullable() .describe("Maximum results to return; Slack allows at most 20.") .optional(), sort: z .enum(["score", "timestamp"]) + .nullable() .describe("Rank by relevance or timestamp.") .optional(), - sort_dir: z.enum(["asc", "desc"]).describe("Sort direction.").optional(), + sort_dir: z + .enum(["asc", "desc"]) + .nullable() + .describe("Sort direction.") + .optional(), }), outputSchema: publicSearchOutputSchema, execute: async ({ diff --git a/packages/junior/tests/integration/slack-public-search.test.ts b/packages/junior/tests/integration/slack-public-search.test.ts index 17c6129280..85f0d22d02 100644 --- a/packages/junior/tests/integration/slack-public-search.test.ts +++ b/packages/junior/tests/integration/slack-public-search.test.ts @@ -292,6 +292,36 @@ describe("Slack public search", () => { expect(params).not.toHaveProperty("before"); }); + it("treats null optional arguments as omitted", async () => { + queueSlackApiResponse("assistant.search.context", { + body: { ok: true, results: { messages: [] } }, + }); + const tool = createSlackPublicSearchTool(actionToken); + const input = tool.prepareArguments({ + query: "company announcement", + content_types: null, + after: null, + before: null, + cursor: null, + limit: null, + sort: null, + sort_dir: null, + }); + + await executeTool(tool, input); + + expect( + getCapturedSlackApiCalls("assistant.search.context")[0]?.params, + ).toEqual({ + action_token: "action-123", + query: "company announcement", + channel_types: ["public_channel"], + content_types: ["messages"], + include_bots: "true", + limit: "10", + }); + }); + it("reports a missing public-search scope explicitly", async () => { queueSlackApiError("assistant.search.context", { error: "missing_scope",