Skip to content

Commit 85f56e7

Browse files
committed
added block kit support
1 parent 22347fe commit 85f56e7

File tree

12 files changed

+110
-17
lines changed

12 files changed

+110
-17
lines changed

.github/workflows/test-build.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@ jobs:
3838
key: ${{ github.repository }}-node-modules
3939
path: ./node_modules
4040

41-
- name: Mount Next.js cache (Sticky Disk)
42-
uses: useblacksmith/stickydisk@v1
43-
with:
44-
key: ${{ github.repository }}-next-cache
45-
path: ./apps/sim/.next/cache
46-
4741
- name: Install dependencies
4842
run: bun install --frozen-lockfile
4943

@@ -116,7 +110,7 @@ jobs:
116110
RESEND_API_KEY: 'dummy_key_for_ci_only'
117111
AWS_REGION: 'us-west-2'
118112
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
119-
run: bun run build
113+
run: bunx turbo run build --filter=sim
120114

121115
- name: Upload coverage to Codecov
122116
uses: codecov/codecov-action@v5

apps/docs/content/docs/en/tools/slack.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ Send messages to Slack channels or direct messages. Supports Slack mrkdwn format
8080
| `dmUserId` | string | No | Slack user ID for direct messages \(e.g., U1234567890\) |
8181
| `text` | string | Yes | Message text to send \(supports Slack mrkdwn formatting\) |
8282
| `threadTs` | string | No | Thread timestamp to reply to \(creates thread reply\) |
83+
| `blocks` | json | No | Block Kit layout blocks as a JSON array. When provided, text becomes the fallback notification text. |
8384
| `files` | file[] | No | Files to attach to the message |
8485

8586
#### Output
@@ -160,6 +161,7 @@ Send an ephemeral message visible only to a specific user in a channel. Optional
160161
| `user` | string | Yes | User ID who will see the ephemeral message \(e.g., U1234567890\). Must be a member of the channel. |
161162
| `text` | string | Yes | Message text to send \(supports Slack mrkdwn formatting\) |
162163
| `threadTs` | string | No | Thread timestamp to reply in. When provided, the ephemeral message appears as a thread reply. |
164+
| `blocks` | json | No | Block Kit layout blocks as a JSON array. When provided, text becomes the fallback notification text. |
163165

164166
#### Output
165167

@@ -704,6 +706,7 @@ Update a message previously sent by the bot in Slack
704706
| `channel` | string | Yes | Channel ID where the message was posted \(e.g., C1234567890\) |
705707
| `timestamp` | string | Yes | Timestamp of the message to update \(e.g., 1405894322.002768\) |
706708
| `text` | string | Yes | New message text \(supports Slack mrkdwn formatting\) |
709+
| `blocks` | json | No | Block Kit layout blocks as a JSON array. When provided, text becomes the fallback notification text. |
707710

708711
#### Output
709712

apps/sim/app/api/tools/slack/send-ephemeral/route.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const SlackSendEphemeralSchema = z.object({
1414
user: z.string().min(1, 'User ID is required'),
1515
text: z.string().min(1, 'Message text is required'),
1616
thread_ts: z.string().optional().nullable(),
17+
blocks: z.array(z.record(z.unknown())).optional().nullable(),
1718
})
1819

1920
export async function POST(request: NextRequest) {
@@ -58,6 +59,8 @@ export async function POST(request: NextRequest) {
5859
user: validatedData.user,
5960
text: validatedData.text,
6061
...(validatedData.thread_ts && { thread_ts: validatedData.thread_ts }),
62+
...(validatedData.blocks &&
63+
validatedData.blocks.length > 0 && { blocks: validatedData.blocks }),
6164
}),
6265
})
6366

apps/sim/app/api/tools/slack/send-message/route.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const SlackSendMessageSchema = z
1717
userId: z.string().optional().nullable(),
1818
text: z.string().min(1, 'Message text is required'),
1919
thread_ts: z.string().optional().nullable(),
20+
blocks: z.array(z.record(z.unknown())).optional().nullable(),
2021
files: RawFileInputArraySchema.optional().nullable(),
2122
})
2223
.refine((data) => data.channel || data.userId, {
@@ -63,6 +64,7 @@ export async function POST(request: NextRequest) {
6364
userId: validatedData.userId ?? undefined,
6465
text: validatedData.text,
6566
threadTs: validatedData.thread_ts ?? undefined,
67+
blocks: validatedData.blocks ?? undefined,
6668
files: validatedData.files ?? undefined,
6769
},
6870
requestId,

apps/sim/app/api/tools/slack/update-message/route.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const SlackUpdateMessageSchema = z.object({
1313
channel: z.string().min(1, 'Channel is required'),
1414
timestamp: z.string().min(1, 'Message timestamp is required'),
1515
text: z.string().min(1, 'Message text is required'),
16+
blocks: z.array(z.record(z.unknown())).optional().nullable(),
1617
})
1718

1819
export async function POST(request: NextRequest) {
@@ -57,6 +58,8 @@ export async function POST(request: NextRequest) {
5758
channel: validatedData.channel,
5859
ts: validatedData.timestamp,
5960
text: validatedData.text,
61+
...(validatedData.blocks &&
62+
validatedData.blocks.length > 0 && { blocks: validatedData.blocks }),
6063
}),
6164
})
6265

apps/sim/app/api/tools/slack/utils.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export async function postSlackMessage(
1111
accessToken: string,
1212
channel: string,
1313
text: string,
14-
threadTs?: string | null
14+
threadTs?: string | null,
15+
blocks?: unknown[] | null
1516
): Promise<{ ok: boolean; ts?: string; channel?: string; message?: any; error?: string }> {
1617
const response = await fetch('https://slack.com/api/chat.postMessage', {
1718
method: 'POST',
@@ -23,6 +24,7 @@ export async function postSlackMessage(
2324
channel,
2425
text,
2526
...(threadTs && { thread_ts: threadTs }),
27+
...(blocks && blocks.length > 0 && { blocks }),
2628
}),
2729
})
2830

@@ -220,6 +222,7 @@ export interface SlackMessageParams {
220222
userId?: string
221223
text: string
222224
threadTs?: string | null
225+
blocks?: unknown[] | null
223226
files?: any[] | null
224227
}
225228

@@ -242,7 +245,7 @@ export async function sendSlackMessage(
242245
}
243246
error?: string
244247
}> {
245-
const { accessToken, text, threadTs, files } = params
248+
const { accessToken, text, threadTs, blocks, files } = params
246249
let { channel } = params
247250

248251
if (!channel && params.userId) {
@@ -258,7 +261,7 @@ export async function sendSlackMessage(
258261
if (!files || files.length === 0) {
259262
logger.info(`[${requestId}] No files, using chat.postMessage`)
260263

261-
const data = await postSlackMessage(accessToken, channel, text, threadTs)
264+
const data = await postSlackMessage(accessToken, channel, text, threadTs, blocks)
262265

263266
if (!data.ok) {
264267
logger.error(`[${requestId}] Slack API error:`, data.error)
@@ -282,7 +285,7 @@ export async function sendSlackMessage(
282285
if (fileIds.length === 0) {
283286
logger.warn(`[${requestId}] No valid files to upload, sending text-only message`)
284287

285-
const data = await postSlackMessage(accessToken, channel, text, threadTs)
288+
const data = await postSlackMessage(accessToken, channel, text, threadTs, blocks)
286289

287290
if (!data.ok) {
288291
return { success: false, error: data.error || 'Failed to send message' }

apps/sim/blocks/blocks/slack.ts

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,20 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
199199
},
200200
required: true,
201201
},
202+
{
203+
id: 'messageFormat',
204+
title: 'Message Format',
205+
type: 'dropdown',
206+
options: [
207+
{ label: 'Plain Text', id: 'text' },
208+
{ label: 'Block Kit', id: 'blocks' },
209+
],
210+
value: () => 'text',
211+
condition: {
212+
field: 'operation',
213+
value: ['send', 'ephemeral', 'update'],
214+
},
215+
},
202216
{
203217
id: 'text',
204218
title: 'Message',
@@ -207,8 +221,29 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
207221
condition: {
208222
field: 'operation',
209223
value: ['send', 'ephemeral'],
224+
and: { field: 'messageFormat', value: 'blocks', not: true },
225+
},
226+
required: {
227+
field: 'operation',
228+
value: ['send', 'ephemeral'],
229+
and: { field: 'messageFormat', value: 'blocks', not: true },
230+
},
231+
},
232+
{
233+
id: 'blocks',
234+
title: 'Block Kit Blocks',
235+
type: 'code',
236+
placeholder: 'JSON array of Block Kit blocks',
237+
condition: {
238+
field: 'operation',
239+
value: ['send', 'ephemeral', 'update'],
240+
and: { field: 'messageFormat', value: 'blocks' },
241+
},
242+
required: {
243+
field: 'operation',
244+
value: ['send', 'ephemeral', 'update'],
245+
and: { field: 'messageFormat', value: 'blocks' },
210246
},
211-
required: true,
212247
},
213248
{
214249
id: 'threadTs',
@@ -480,8 +515,13 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
480515
condition: {
481516
field: 'operation',
482517
value: 'update',
518+
and: { field: 'messageFormat', value: 'blocks', not: true },
519+
},
520+
required: {
521+
field: 'operation',
522+
value: 'update',
523+
and: { field: 'messageFormat', value: 'blocks', not: true },
483524
},
484-
required: true,
485525
},
486526
// Delete Message specific fields
487527
{
@@ -581,12 +621,14 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
581621
destinationType,
582622
channel,
583623
dmUserId,
624+
messageFormat,
584625
text,
585626
title,
586627
content,
587628
limit,
588629
oldest,
589630
files,
631+
blocks,
590632
threadTs,
591633
ephemeralUser,
592634
updateTimestamp,
@@ -630,10 +672,13 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
630672

631673
switch (operation) {
632674
case 'send': {
633-
baseParams.text = text
675+
baseParams.text = messageFormat === 'blocks' && !text ? ' ' : text
634676
if (threadTs) {
635677
baseParams.threadTs = threadTs
636678
}
679+
if (blocks) {
680+
baseParams.blocks = blocks
681+
}
637682
// files is the canonical param from attachmentFiles (basic) or files (advanced)
638683
const normalizedFiles = normalizeFileInput(files)
639684
if (normalizedFiles) {
@@ -643,11 +688,14 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
643688
}
644689

645690
case 'ephemeral': {
646-
baseParams.text = text
691+
baseParams.text = messageFormat === 'blocks' && !text ? ' ' : text
647692
baseParams.user = ephemeralUser ? String(ephemeralUser).trim() : ''
648693
if (threadTs) {
649694
baseParams.threadTs = threadTs
650695
}
696+
if (blocks) {
697+
baseParams.blocks = blocks
698+
}
651699
break
652700
}
653701

@@ -717,7 +765,10 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
717765

718766
case 'update':
719767
baseParams.timestamp = updateTimestamp
720-
baseParams.text = updateText
768+
baseParams.text = messageFormat === 'blocks' && !updateText ? ' ' : updateText
769+
if (blocks) {
770+
baseParams.blocks = blocks
771+
}
721772
break
722773

723774
case 'delete':
@@ -736,6 +787,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
736787
},
737788
inputs: {
738789
operation: { type: 'string', description: 'Operation to perform' },
790+
messageFormat: { type: 'string', description: 'Message format: text or blocks' },
739791
authMethod: { type: 'string', description: 'Authentication method' },
740792
destinationType: { type: 'string', description: 'Destination type (channel or dm)' },
741793
credential: { type: 'string', description: 'Slack access token' },
@@ -770,6 +822,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
770822
userLimit: { type: 'string', description: 'Maximum number of users to return' },
771823
// Ephemeral message inputs
772824
ephemeralUser: { type: 'string', description: 'User ID who will see the ephemeral message' },
825+
blocks: { type: 'json', description: 'Block Kit layout blocks as a JSON array' },
773826
// Get User inputs
774827
userId: { type: 'string', description: 'User ID to look up' },
775828
// Get Message inputs

apps/sim/tools/slack/ephemeral_message.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ export const slackEphemeralMessageTool: ToolConfig<
6464
description:
6565
'Thread timestamp to reply in. When provided, the ephemeral message appears as a thread reply.',
6666
},
67+
blocks: {
68+
type: 'json',
69+
required: false,
70+
visibility: 'user-or-llm',
71+
description:
72+
'Block Kit layout blocks as a JSON array. When provided, text becomes the fallback notification text.',
73+
},
6774
},
6875

6976
request: {
@@ -78,6 +85,8 @@ export const slackEphemeralMessageTool: ToolConfig<
7885
user: params.user?.trim(),
7986
text: params.text,
8087
thread_ts: params.threadTs || undefined,
88+
blocks:
89+
typeof params.blocks === 'string' ? JSON.parse(params.blocks) : params.blocks || undefined,
8190
}),
8291
},
8392

apps/sim/tools/slack/message.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ export const slackMessageTool: ToolConfig<SlackMessageParams, SlackMessageRespon
6363
visibility: 'user-or-llm',
6464
description: 'Thread timestamp to reply to (creates thread reply)',
6565
},
66+
blocks: {
67+
type: 'json',
68+
required: false,
69+
visibility: 'user-or-llm',
70+
description:
71+
'Block Kit layout blocks as a JSON array. When provided, text becomes the fallback notification text.',
72+
},
6673
files: {
6774
type: 'file[]',
6875
required: false,
@@ -85,6 +92,10 @@ export const slackMessageTool: ToolConfig<SlackMessageParams, SlackMessageRespon
8592
userId: isDM ? params.dmUserId : params.userId,
8693
text: params.text,
8794
thread_ts: params.threadTs || undefined,
95+
blocks:
96+
typeof params.blocks === 'string'
97+
? JSON.parse(params.blocks)
98+
: params.blocks || undefined,
8899
files: params.files || null,
89100
}
90101
},

apps/sim/tools/slack/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ export interface SlackMessageParams extends SlackBaseParams {
517517
userId?: string
518518
text: string
519519
threadTs?: string
520+
blocks?: string
520521
files?: UserFile[]
521522
}
522523

@@ -546,6 +547,7 @@ export interface SlackUpdateMessageParams extends SlackBaseParams {
546547
channel: string
547548
timestamp: string
548549
text: string
550+
blocks?: string
549551
}
550552

551553
export interface SlackDeleteMessageParams extends SlackBaseParams {
@@ -589,6 +591,7 @@ export interface SlackEphemeralMessageParams extends SlackBaseParams {
589591
user: string
590592
text: string
591593
threadTs?: string
594+
blocks?: string
592595
}
593596

594597
export interface SlackGetThreadParams extends SlackBaseParams {

0 commit comments

Comments
 (0)