diff --git a/src/agents/tools/slack-actions.test.ts b/src/agents/tools/slack-actions.test.ts index 1e86fe85b..4113328a6 100644 --- a/src/agents/tools/slack-actions.test.ts +++ b/src/agents/tools/slack-actions.test.ts @@ -131,10 +131,7 @@ describe("handleSlackAction", () => { it("requires canvases to be enabled", async () => { const cfg = { channels: { slack: { botToken: "tok" } } } as ClawdbotConfig; await expect( - handleSlackAction( - { action: "createCanvas", title: "Test", content: "hello" }, - cfg, - ), + handleSlackAction({ action: "createCanvas", title: "Test", content: "hello" }, cfg), ).rejects.toThrow(/Slack canvases are disabled/); }); @@ -142,10 +139,7 @@ describe("handleSlackAction", () => { const cfg = { channels: { slack: { botToken: "tok", actions: { canvases: true } } }, } as ClawdbotConfig; - await handleSlackAction( - { action: "createCanvas", title: "Test", content: "hello" }, - cfg, - ); + await handleSlackAction({ action: "createCanvas", title: "Test", content: "hello" }, cfg); expect(createSlackCanvas).toHaveBeenCalledWith( { title: "Test", content: "hello", channelId: undefined }, undefined, @@ -156,10 +150,7 @@ describe("handleSlackAction", () => { const cfg = { channels: { slack: { botToken: "tok", actions: { canvases: true } } }, } as ClawdbotConfig; - await handleSlackAction( - { action: "updateCanvas", canvasId: "C123", content: "update" }, - cfg, - ); + await handleSlackAction({ action: "updateCanvas", canvasId: "C123", content: "update" }, cfg); expect(updateSlackCanvas).toHaveBeenCalledWith( { canvasId: "C123", channelId: undefined, content: "update", updateMode: undefined }, undefined, diff --git a/src/channels/plugins/message-action-names.ts b/src/channels/plugins/message-action-names.ts index c884f6da3..1c1e42fa0 100644 --- a/src/channels/plugins/message-action-names.ts +++ b/src/channels/plugins/message-action-names.ts @@ -47,6 +47,8 @@ export const CHANNEL_MESSAGE_ACTION_NAMES = [ "timeout", "kick", "ban", + "canvas-create", + "canvas-update", ] as const; export type ChannelMessageActionName = (typeof CHANNEL_MESSAGE_ACTION_NAMES)[number]; diff --git a/src/slack/canvases.test.ts b/src/slack/canvases.test.ts index 3150a6124..50e657f07 100644 --- a/src/slack/canvases.test.ts +++ b/src/slack/canvases.test.ts @@ -56,11 +56,12 @@ describe("extractCanvasRefsFromEvent", () => { describe("fetchSlackCanvasContent", () => { it("downloads and parses JSON canvas content", async () => { - const fetchMock = vi.fn(async () => - new Response(JSON.stringify({ text: "hello canvas" }), { - status: 200, - headers: { "content-type": "application/json" }, - }), + const fetchMock = vi.fn( + async () => + new Response(JSON.stringify({ text: "hello canvas" }), { + status: 200, + headers: { "content-type": "application/json" }, + }), ); vi.stubGlobal("fetch", fetchMock); @@ -127,11 +128,12 @@ describe("fetchSlackCanvasContent", () => { }); it("truncates large payloads", async () => { - const fetchMock = vi.fn(async () => - new Response(JSON.stringify({ text: "1234567890" }), { - status: 200, - headers: { "content-type": "application/json" }, - }), + const fetchMock = vi.fn( + async () => + new Response(JSON.stringify({ text: "1234567890" }), { + status: 200, + headers: { "content-type": "application/json" }, + }), ); vi.stubGlobal("fetch", fetchMock); diff --git a/src/slack/canvases.ts b/src/slack/canvases.ts index 76be2e79e..1e81f0013 100644 --- a/src/slack/canvases.ts +++ b/src/slack/canvases.ts @@ -173,7 +173,10 @@ function collectJsonText( } } -function extractTextFromJson(payload: unknown, maxChars: number): { text: string; truncated: boolean } { +function extractTextFromJson( + payload: unknown, + maxChars: number, +): { text: string; truncated: boolean } { const results: string[] = []; const seen = new Set(); collectJsonText(payload, { maxChars, results, seen, depth: 0 }); @@ -197,10 +200,22 @@ export async function fetchSlackCanvasContent(params: { return { ok: false, error: "no_file_id" }; } - let fileInfo: { file?: SlackFile & { title?: string; name?: string; url_private?: string; url_private_download?: string } }; + let fileInfo: { + file?: SlackFile & { + title?: string; + name?: string; + url_private?: string; + url_private_download?: string; + }; + }; try { fileInfo = (await client.files.info({ file: fileId })) as { - file?: SlackFile & { title?: string; name?: string; url_private?: string; url_private_download?: string }; + file?: SlackFile & { + title?: string; + name?: string; + url_private?: string; + url_private_download?: string; + }; }; } catch (err) { const label = buildErrorLabel(err); @@ -242,7 +257,11 @@ export async function fetchSlackCanvasContent(params: { let extracted: { text: string; truncated: boolean }; let rawFormat: SlackCanvasContent["rawFormat"] = "unknown"; - if (contentType.includes("application/json") || text.trim().startsWith("{") || text.trim().startsWith("[")) { + if ( + contentType.includes("application/json") || + text.trim().startsWith("{") || + text.trim().startsWith("[") + ) { rawFormat = "json"; try { const payload = JSON.parse(text); diff --git a/src/slack/monitor/message-handler/prepare.inbound-contract.test.ts b/src/slack/monitor/message-handler/prepare.inbound-contract.test.ts index ad643a50b..c0a875275 100644 --- a/src/slack/monitor/message-handler/prepare.inbound-contract.test.ts +++ b/src/slack/monitor/message-handler/prepare.inbound-contract.test.ts @@ -151,11 +151,12 @@ describe("slack prepareSlackMessage inbound contract", () => { }); it("appends canvas content to the inbound body", async () => { - const fetchMock = vi.fn(async () => - new Response(JSON.stringify({ title: "Canvas", text: "hello canvas" }), { - status: 200, - headers: { "content-type": "application/json" }, - }), + const fetchMock = vi.fn( + async () => + new Response(JSON.stringify({ title: "Canvas", text: "hello canvas" }), { + status: 200, + headers: { "content-type": "application/json" }, + }), ); vi.stubGlobal("fetch", fetchMock); diff --git a/src/slack/monitor/message-handler/prepare.ts b/src/slack/monitor/message-handler/prepare.ts index 2027dbd39..51ccd6992 100644 --- a/src/slack/monitor/message-handler/prepare.ts +++ b/src/slack/monitor/message-handler/prepare.ts @@ -371,7 +371,8 @@ export async function prepareSlackMessage(params: { } const canvasContext = canvasBlocks.length > 0 ? canvasBlocks.join("\n\n") : ""; - const baseBody = (message.text ?? "").trim() || media?.placeholder || (canvasContext ? "[Slack canvas]" : ""); + const baseBody = + (message.text ?? "").trim() || media?.placeholder || (canvasContext ? "[Slack canvas]" : ""); if (!baseBody) return null; const rawBody = baseBody; const rawBodyWithCanvas = canvasContext ? `${rawBody}\n\n${canvasContext}` : rawBody;