Skip to content

Commit 209501e

Browse files
authored
feat(slack): assistant thread ops, paginated history/replies, and permalink (#4934)
* feat(slack): add assistant thread ops, paginated history/replies, and permalink - New tools: slack_set_status, slack_set_title, slack_set_suggested_prompts (assistant.threads.*), slack_get_channel_history + slack_get_thread_replies (paginated conversations.history/replies via directExecution), slack_get_permalink - Wire all six as Slack block operations with subBlocks, conditions, params, outputs - Add scopes: assistant:write, im:history, mpim:read, mpim:history (+ consent labels) - Add manifest capabilities: action_read_history, action_assistant - Fix invite_to_conversation no_user error code; widen DM history scope hints - Regenerate tool docs and integrations catalog * fix(slack): correct thread parent on cursor-resume, complete DM scope hints, reject empty suggested prompts - get_thread_replies: identify thread parent by ts === thread_ts instead of assuming index 0, so cursor-resumed pages (which omit the parent) no longer mislabel a reply as the parent - get_channel_history + get_thread_replies: include im:history/mpim:history in the missing_scope hint to match the OAuth grant and DM/MPIM reads - set_suggested_prompts: throw a clear error when no valid prompt is provided instead of silently calling Slack with an empty prompts array * fix(slack): guard non-numeric limit/maxPages; drop ungranted im/mpim history scopes - Add resolvePositiveInt helper and use it for limit/maxPages in get_channel_history and get_thread_replies, so a non-numeric value (e.g. from an LLM) no longer becomes NaN and silently disables pagination (returning an empty success) - Remove im:history/mpim:read/mpim:history from the Slack OAuth grant — the Slack app does not have these scopes; assistant:write is the only added scope - Drop the explanatory inline comment in get_thread_replies * improvement(slack): move history pagination cursor/maxPages to advanced mode Matches the existing paginationCursor convention in the block — keeps the basic UI focused on channel + time filters while pagination internals live under advanced. * fix(slack): narrow set_status missing_scope hint to assistant:write A user hitting missing_scope on set_status already has chat:write (long granted); the scope they actually need is assistant:write, which is also what set_title/set_suggested_prompts point to. Narrow the hint to assistant:write for accurate, consistent guidance.
1 parent efa4f27 commit 209501e

19 files changed

Lines changed: 1883 additions & 6 deletions

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

Lines changed: 364 additions & 0 deletions
Large diffs are not rendered by default.

apps/sim/blocks/blocks/slack.ts

Lines changed: 294 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
3333
{ label: 'Read Messages', id: 'read' },
3434
{ label: 'Get Message', id: 'get_message' },
3535
{ label: 'Get Thread', id: 'get_thread' },
36+
{ label: 'Get Thread Replies', id: 'get_thread_replies' },
37+
{ label: 'Get Channel History', id: 'get_channel_history' },
38+
{ label: 'Get Message Permalink', id: 'get_permalink' },
39+
{ label: 'Set Assistant Status', id: 'set_status' },
40+
{ label: 'Set Assistant Title', id: 'set_title' },
41+
{ label: 'Set Suggested Prompts', id: 'set_suggested_prompts' },
3642
{ label: 'List Channels', id: 'list_channels' },
3743
{ label: 'List Channel Members', id: 'list_members' },
3844
{ label: 'List Users', id: 'list_users' },
@@ -548,7 +554,7 @@ Do not include any explanations, markdown formatting, or other text outside the
548554
placeholder: 'Message timestamp (e.g., 1405894322.002768)',
549555
condition: {
550556
field: 'operation',
551-
value: 'get_message',
557+
value: ['get_message', 'get_permalink'],
552558
},
553559
required: true,
554560
wandConfig: {
@@ -574,7 +580,13 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
574580
placeholder: 'Thread timestamp (thread_ts, e.g., 1405894322.002768)',
575581
condition: {
576582
field: 'operation',
577-
value: 'get_thread',
583+
value: [
584+
'get_thread',
585+
'get_thread_replies',
586+
'set_status',
587+
'set_title',
588+
'set_suggested_prompts',
589+
],
578590
},
579591
required: true,
580592
wandConfig: {
@@ -602,6 +614,152 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
602614
value: 'get_thread',
603615
},
604616
},
617+
// Set Assistant Status specific fields
618+
{
619+
id: 'status',
620+
title: 'Status Text',
621+
type: 'short-input',
622+
placeholder: 'e.g., Working on it… (leave empty to clear)',
623+
condition: {
624+
field: 'operation',
625+
value: 'set_status',
626+
},
627+
required: false,
628+
},
629+
{
630+
id: 'loadingMessages',
631+
title: 'Loading Messages',
632+
type: 'long-input',
633+
placeholder: 'Optional JSON array of phrases to animate (max 10)',
634+
condition: {
635+
field: 'operation',
636+
value: 'set_status',
637+
},
638+
required: false,
639+
},
640+
// Set Assistant Title specific fields
641+
{
642+
id: 'assistantTitle',
643+
title: 'Thread Title',
644+
type: 'short-input',
645+
placeholder: 'Title to display for the assistant thread',
646+
condition: {
647+
field: 'operation',
648+
value: 'set_title',
649+
},
650+
required: true,
651+
},
652+
// Set Suggested Prompts specific fields
653+
{
654+
id: 'suggestedPrompts',
655+
title: 'Suggested Prompts',
656+
type: 'long-input',
657+
placeholder: '[{"title": "Summarize", "message": "Summarize this thread"}]',
658+
condition: {
659+
field: 'operation',
660+
value: 'set_suggested_prompts',
661+
},
662+
required: true,
663+
wandConfig: {
664+
enabled: true,
665+
prompt: `Generate a JSON array of Slack assistant suggested prompts from the user's description.
666+
Each entry must be an object with exactly two string fields:
667+
- "title": the short label shown on the clickable chip
668+
- "message": the full message sent into the thread when the chip is clicked
669+
Return at most 4 prompts.
670+
Example:
671+
[{"title": "Summarize", "message": "Summarize the key points of this thread"}, {"title": "Next steps", "message": "What are the next steps?"}]
672+
673+
Return ONLY the JSON array - no explanations, no quotes around the array, no extra text.`,
674+
placeholder: 'Describe the prompts you want (e.g., "summarize and list action items")...',
675+
generationType: 'json-object',
676+
},
677+
},
678+
{
679+
id: 'promptsTitle',
680+
title: 'Prompts Heading',
681+
type: 'short-input',
682+
placeholder: 'e.g., Suggested Prompts (optional)',
683+
condition: {
684+
field: 'operation',
685+
value: 'set_suggested_prompts',
686+
},
687+
mode: 'advanced',
688+
required: false,
689+
},
690+
// Get Channel History / Get Thread Replies shared pagination fields
691+
{
692+
id: 'historyOldest',
693+
title: 'Oldest Timestamp',
694+
type: 'short-input',
695+
placeholder: 'Unix seconds, e.g., 1700000000 (only messages after)',
696+
condition: {
697+
field: 'operation',
698+
value: ['get_channel_history', 'get_thread_replies'],
699+
},
700+
required: false,
701+
},
702+
{
703+
id: 'historyLatest',
704+
title: 'Latest Timestamp',
705+
type: 'short-input',
706+
placeholder: 'Unix seconds, e.g., 1700000000 (only messages before)',
707+
condition: {
708+
field: 'operation',
709+
value: ['get_channel_history', 'get_thread_replies'],
710+
},
711+
required: false,
712+
},
713+
{
714+
id: 'historyLimit',
715+
title: 'Page Size',
716+
type: 'short-input',
717+
placeholder: '200 (max 999)',
718+
condition: {
719+
field: 'operation',
720+
value: ['get_channel_history', 'get_thread_replies'],
721+
},
722+
required: false,
723+
},
724+
{
725+
id: 'historyMaxPages',
726+
title: 'Max Pages',
727+
type: 'short-input',
728+
placeholder: '10',
729+
mode: 'advanced',
730+
condition: {
731+
field: 'operation',
732+
value: ['get_channel_history', 'get_thread_replies'],
733+
},
734+
required: false,
735+
},
736+
{
737+
id: 'historyCursor',
738+
title: 'Start Cursor',
739+
type: 'short-input',
740+
placeholder: 'Resume from a previous nextCursor',
741+
mode: 'advanced',
742+
condition: {
743+
field: 'operation',
744+
value: ['get_channel_history', 'get_thread_replies'],
745+
},
746+
required: false,
747+
},
748+
{
749+
id: 'historyInclusive',
750+
title: 'Inclusive',
751+
type: 'dropdown',
752+
options: [
753+
{ label: 'No', id: 'false' },
754+
{ label: 'Yes', id: 'true' },
755+
],
756+
value: () => 'false',
757+
condition: {
758+
field: 'operation',
759+
value: ['get_channel_history', 'get_thread_replies'],
760+
},
761+
required: false,
762+
},
605763
{
606764
id: 'oldest',
607765
title: 'Oldest Timestamp',
@@ -1196,6 +1354,12 @@ Do not include any explanations, markdown formatting, or other text outside the
11961354
'slack_message_reader',
11971355
'slack_get_message',
11981356
'slack_get_thread',
1357+
'slack_get_thread_replies',
1358+
'slack_get_channel_history',
1359+
'slack_get_permalink',
1360+
'slack_set_status',
1361+
'slack_set_title',
1362+
'slack_set_suggested_prompts',
11991363
'slack_list_channels',
12001364
'slack_list_members',
12011365
'slack_list_users',
@@ -1235,6 +1399,18 @@ Do not include any explanations, markdown formatting, or other text outside the
12351399
return 'slack_get_message'
12361400
case 'get_thread':
12371401
return 'slack_get_thread'
1402+
case 'get_thread_replies':
1403+
return 'slack_get_thread_replies'
1404+
case 'get_channel_history':
1405+
return 'slack_get_channel_history'
1406+
case 'get_permalink':
1407+
return 'slack_get_permalink'
1408+
case 'set_status':
1409+
return 'slack_set_status'
1410+
case 'set_title':
1411+
return 'slack_set_title'
1412+
case 'set_suggested_prompts':
1413+
return 'slack_set_suggested_prompts'
12381414
case 'list_channels':
12391415
return 'slack_list_channels'
12401416
case 'list_members':
@@ -1318,6 +1494,17 @@ Do not include any explanations, markdown formatting, or other text outside the
13181494
getMessageTimestamp,
13191495
getThreadTimestamp,
13201496
threadLimit,
1497+
status,
1498+
loadingMessages,
1499+
assistantTitle,
1500+
suggestedPrompts,
1501+
promptsTitle,
1502+
historyOldest,
1503+
historyLatest,
1504+
historyLimit,
1505+
historyMaxPages,
1506+
historyCursor,
1507+
historyInclusive,
13211508
includeNumMembers,
13221509
presenceUserId,
13231510
editCanvasId,
@@ -1438,6 +1625,65 @@ Do not include any explanations, markdown formatting, or other text outside the
14381625
break
14391626
}
14401627

1628+
case 'set_status': {
1629+
baseParams.threadTs = getThreadTimestamp
1630+
baseParams.status = status ?? ''
1631+
if (loadingMessages) {
1632+
baseParams.loadingMessages = loadingMessages
1633+
}
1634+
break
1635+
}
1636+
1637+
case 'set_title': {
1638+
baseParams.threadTs = getThreadTimestamp
1639+
baseParams.title = assistantTitle
1640+
break
1641+
}
1642+
1643+
case 'set_suggested_prompts': {
1644+
baseParams.threadTs = getThreadTimestamp
1645+
baseParams.prompts = suggestedPrompts
1646+
if (promptsTitle) {
1647+
baseParams.promptsTitle = promptsTitle
1648+
}
1649+
break
1650+
}
1651+
1652+
case 'get_permalink': {
1653+
baseParams.messageTs = getMessageTimestamp
1654+
break
1655+
}
1656+
1657+
case 'get_channel_history':
1658+
case 'get_thread_replies': {
1659+
if (operation === 'get_thread_replies') {
1660+
baseParams.threadTs = getThreadTimestamp
1661+
}
1662+
if (historyOldest) {
1663+
baseParams.oldest = String(historyOldest).trim()
1664+
}
1665+
if (historyLatest) {
1666+
baseParams.latest = String(historyLatest).trim()
1667+
}
1668+
if (historyLimit) {
1669+
const parsedLimit = Number.parseInt(historyLimit, 10)
1670+
if (!Number.isNaN(parsedLimit) && parsedLimit > 0) {
1671+
baseParams.limit = parsedLimit
1672+
}
1673+
}
1674+
if (historyMaxPages) {
1675+
const parsedMaxPages = Number.parseInt(historyMaxPages, 10)
1676+
if (!Number.isNaN(parsedMaxPages) && parsedMaxPages > 0) {
1677+
baseParams.maxPages = parsedMaxPages
1678+
}
1679+
}
1680+
if (historyCursor) {
1681+
baseParams.cursor = String(historyCursor).trim()
1682+
}
1683+
baseParams.inclusive = historyInclusive === 'true'
1684+
break
1685+
}
1686+
14411687
case 'list_channels': {
14421688
baseParams.includePrivate = includePrivate !== 'false'
14431689
baseParams.excludeArchived = true
@@ -1682,6 +1928,36 @@ Do not include any explanations, markdown formatting, or other text outside the
16821928
type: 'string',
16831929
description: 'Maximum number of messages to return from thread',
16841930
},
1931+
// Set Assistant Status inputs
1932+
status: { type: 'string', description: 'Status text to display (empty clears the status)' },
1933+
loadingMessages: {
1934+
type: 'json',
1935+
description: 'Optional array of phrases to animate as a loading indicator (max 10)',
1936+
},
1937+
// Set Assistant Title inputs
1938+
assistantTitle: { type: 'string', description: 'Title to display for the assistant thread' },
1939+
// Set Suggested Prompts inputs
1940+
suggestedPrompts: {
1941+
type: 'json',
1942+
description: 'Array of { title, message } prompt objects (max 4)',
1943+
},
1944+
promptsTitle: { type: 'string', description: 'Optional heading for the prompt list' },
1945+
// Get Channel History / Get Thread Replies inputs
1946+
historyOldest: {
1947+
type: 'string',
1948+
description: 'Only include messages after this Unix timestamp',
1949+
},
1950+
historyLatest: {
1951+
type: 'string',
1952+
description: 'Only include messages before this Unix timestamp',
1953+
},
1954+
historyLimit: { type: 'string', description: 'Messages to request per page (max 999)' },
1955+
historyMaxPages: { type: 'string', description: 'Maximum number of pages to fetch' },
1956+
historyCursor: { type: 'string', description: 'Pagination cursor to resume from' },
1957+
historyInclusive: {
1958+
type: 'string',
1959+
description: 'Include messages matching oldest/latest (true/false)',
1960+
},
16851961
// Get Channel Info inputs
16861962
includeNumMembers: { type: 'string', description: 'Include member count (true/false)' },
16871963
// Get User Presence inputs
@@ -1808,6 +2084,22 @@ Do not include any explanations, markdown formatting, or other text outside the
18082084
description: 'Whether there are more messages in the thread',
18092085
},
18102086

2087+
// slack_get_channel_history / slack_get_thread_replies pagination outputs
2088+
pages: {
2089+
type: 'number',
2090+
description: 'Number of pages fetched during a paginated history/replies read',
2091+
},
2092+
threadTs: {
2093+
type: 'string',
2094+
description: 'Thread timestamp an assistant status/title/prompts op was set on',
2095+
},
2096+
2097+
// slack_get_permalink outputs (get_permalink operation)
2098+
permalink: {
2099+
type: 'string',
2100+
description: 'Permalink URL to the message',
2101+
},
2102+
18112103
// slack_list_channels outputs (list_channels operation)
18122104
channels: {
18132105
type: 'json',

0 commit comments

Comments
 (0)