Skip to content

Commit 00bab6d

Browse files
fix(slack): restore assistant operation compatibility
1 parent 1c197c1 commit 00bab6d

4 files changed

Lines changed: 182 additions & 34 deletions

File tree

apps/docs/content/docs/integrations/slack.mdx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,51 @@ Get a stable permalink URL to a specific Slack message.
816816
| `channel` | string | Channel ID containing the message |
817817
| `permalink` | string | The permalink URL to the message |
818818

819+
### Slack Set Assistant Status
820+
821+
Set or clear the assistant thread status indicator (the loading shimmer) on a Slack AI app thread. Pass an empty status to clear it.
822+
823+
#### Input
824+
825+
| Parameter | Type | Required | Description |
826+
| --------- | ---- | -------- | ----------- |
827+
| `authMethod` | string | No | Authentication method: oauth or bot_token |
828+
| `botToken` | string | No | Bot token for Custom Bot |
829+
| `channel` | string | Yes | Channel ID containing the assistant thread \(e.g., C1234567890 or D1234567890\) |
830+
| `threadTs` | string | Yes | Thread timestamp \(thread_ts\) of the assistant thread \(e.g., 1405894322.002768\) |
831+
| `status` | string | No | Status text to display, e.g. 'Working on it…'. Omit or pass an empty string to clear the status. |
832+
| `loadingMessages` | json | No | Optional list of messages to rotate through as an animated loading indicator \(max 10\). |
833+
834+
#### Output
835+
836+
| Parameter | Type | Description |
837+
| --------- | ---- | ----------- |
838+
| `ok` | boolean | Whether the status was set successfully |
839+
| `channel` | string | Channel ID the status was set on |
840+
| `threadTs` | string | Thread timestamp the status was set on |
841+
842+
### Slack Set Assistant Title
843+
844+
Set the title of a Slack assistant thread (shown in the AI app thread header).
845+
846+
#### Input
847+
848+
| Parameter | Type | Required | Description |
849+
| --------- | ---- | -------- | ----------- |
850+
| `authMethod` | string | No | Authentication method: oauth or bot_token |
851+
| `botToken` | string | No | Bot token for Custom Bot |
852+
| `channel` | string | Yes | Channel ID containing the assistant thread \(e.g., C1234567890 or D1234567890\) |
853+
| `threadTs` | string | Yes | Thread timestamp \(thread_ts\) of the assistant thread \(e.g., 1405894322.002768\) |
854+
| `title` | string | Yes | The title to display for the assistant thread |
855+
856+
#### Output
857+
858+
| Parameter | Type | Description |
859+
| --------- | ---- | ----------- |
860+
| `ok` | boolean | Whether the title was set successfully |
861+
| `channel` | string | Channel ID the title was set on |
862+
| `threadTs` | string | Thread timestamp the title was set on |
863+
819864
### Slack Set Suggested Prompts
820865

821866
Set the clickable suggested prompts shown in a Slack assistant thread (the prompt chips in an AI app).

apps/sim/blocks/blocks/slack.test.ts

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* @vitest-environment node
33
*/
44
import { describe, expect, it } from 'vitest'
5+
import { evaluateSubBlockCondition } from '@/lib/workflows/subblocks/visibility'
56
import {
67
getSlackV2ActionSubBlocks,
78
getSlackV2OperationSentences,
@@ -20,6 +21,8 @@ const AGENT_TOOL_IDS = [
2021
'slack_set_agent_session_status_v2',
2122
'slack_rename_agent_session_v2',
2223
]
24+
const LEGACY_OPERATION_IDS = ['set_status', 'set_title']
25+
const LEGACY_TOOL_IDS = ['slack_set_status', 'slack_set_title']
2326

2427
function operationIds(): string[] {
2528
const operation = SlackV2Block.subBlocks.find((subBlock) => subBlock.id === 'operation')
@@ -32,6 +35,12 @@ function mapSlackV2Params(params: Record<string, unknown>): Record<string, unkno
3235
return mapParams(params)
3336
}
3437

38+
function isSlackV2SubBlockVisible(subBlockId: string, values: Record<string, unknown>): boolean {
39+
const subBlock = SlackV2Block.subBlocks.find((candidate) => candidate.id === subBlockId)
40+
if (!subBlock) throw new Error(`Slack v2 subblock not found: ${subBlockId}`)
41+
return evaluateSubBlockCondition(subBlock.condition, values)
42+
}
43+
3544
describe('Slack block release', () => {
3645
it('releases slack_v2 and keeps the legacy block executable but hidden', () => {
3746
expect(SlackBlock.hideFromToolbar).toBe(true)
@@ -41,21 +50,27 @@ describe('Slack block release', () => {
4150
expect(SlackV2Block.sunset).toBeUndefined()
4251
})
4352

44-
it('replaces legacy assistant operations with custom-bot Agent Sessions operations', () => {
45-
expect(operationIds()).toEqual(expect.arrayContaining(AGENT_OPERATION_IDS))
46-
for (const id of ['set_status', 'set_title']) {
47-
expect(operationIds()).not.toContain(id)
48-
}
49-
expect(getSlackV2ToolAccess()).toEqual(expect.arrayContaining(AGENT_TOOL_IDS))
53+
it('adds custom-bot Agent Sessions operations without removing legacy assistant operations', () => {
54+
expect(operationIds()).toEqual(
55+
expect.arrayContaining([...LEGACY_OPERATION_IDS, ...AGENT_OPERATION_IDS])
56+
)
57+
expect(getSlackV2ToolAccess()).toEqual(
58+
expect.arrayContaining([...LEGACY_TOOL_IDS, ...AGENT_TOOL_IDS])
59+
)
5060
expect(getSlackV2ToolAccess()).toContain('slack_set_suggested_prompts')
51-
for (const id of ['slack_set_status', 'slack_set_title']) {
52-
expect(getSlackV2ToolAccess()).not.toContain(id)
53-
}
5461
expect(Object.keys(getSlackV2OperationSentences())).toEqual(
55-
expect.arrayContaining(AGENT_OPERATION_IDS)
62+
expect.arrayContaining([...LEGACY_OPERATION_IDS, ...AGENT_OPERATION_IDS])
5663
)
5764
})
5865

66+
it('keeps legacy assistant operations routed to their original tools', () => {
67+
const selectTool = SlackV2Block.tools.config?.tool
68+
if (!selectTool) throw new Error('Slack v2 tool selector is required')
69+
70+
expect(selectTool({ operation: 'set_status' })).toBe('slack_set_status')
71+
expect(selectTool({ operation: 'set_title' })).toBe('slack_set_title')
72+
})
73+
5974
it('uses a service-account-only picker for Agent Sessions operations', () => {
6075
const credential = getSlackV2ActionSubBlocks().find(
6176
(subBlock) => subBlock.id === 'agentBotCredential'
@@ -90,6 +105,19 @@ describe('Slack block release', () => {
90105
prompts: '[{"title":"Summarize","message":"Summarize this thread"}]',
91106
promptsTitle: 'Try asking',
92107
})
108+
109+
const legacyValues = {
110+
operation: 'set_suggested_prompts',
111+
credential: 'oauth-credential',
112+
channel: 'C123',
113+
getThreadTimestamp: '1700000000.000001',
114+
}
115+
expect(isSlackV2SubBlockVisible('credential', legacyValues)).toBe(true)
116+
expect(isSlackV2SubBlockVisible('channel', legacyValues)).toBe(true)
117+
expect(isSlackV2SubBlockVisible('getThreadTimestamp', legacyValues)).toBe(true)
118+
expect(isSlackV2SubBlockVisible('agentBotCredential', legacyValues)).toBe(false)
119+
expect(isSlackV2SubBlockVisible('agentChannel', legacyValues)).toBe(false)
120+
expect(isSlackV2SubBlockVisible('agentThreadTs', legacyValues)).toBe(false)
93121
})
94122

95123
it('uses service-account tools for new agent operations', () => {
@@ -116,5 +144,18 @@ describe('Slack block release', () => {
116144
threadTs: '1700000000.000001',
117145
status: 'processing',
118146
})
147+
148+
const agentPromptValues = {
149+
operation: 'set_suggested_prompts',
150+
agentBotCredential: 'custom-bot',
151+
agentChannel: 'D123',
152+
agentThreadTs: '1700000000.000001',
153+
}
154+
expect(isSlackV2SubBlockVisible('credential', agentPromptValues)).toBe(false)
155+
expect(isSlackV2SubBlockVisible('channel', agentPromptValues)).toBe(false)
156+
expect(isSlackV2SubBlockVisible('getThreadTimestamp', agentPromptValues)).toBe(false)
157+
expect(isSlackV2SubBlockVisible('agentBotCredential', agentPromptValues)).toBe(true)
158+
expect(isSlackV2SubBlockVisible('agentChannel', agentPromptValues)).toBe(true)
159+
expect(isSlackV2SubBlockVisible('agentThreadTs', agentPromptValues)).toBe(true)
119160
})
120161
})

apps/sim/blocks/blocks/slack.ts

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,49 @@ const SLACK_V2_AGENT_OPERATIONS = [
2121
'rename_agent_session',
2222
] as const
2323

24+
const SLACK_V2_SESSION_OPERATIONS = ['set_agent_session_status', 'rename_agent_session'] as const
25+
26+
function hasConfiguredSlackValue(
27+
values: Record<string, unknown> | undefined,
28+
fieldIds: readonly string[]
29+
): boolean {
30+
return fieldIds.some((fieldId) => {
31+
const value = values?.[fieldId]
32+
return value !== undefined && value !== null && value !== ''
33+
})
34+
}
35+
36+
function isLegacySuggestedPromptsConfiguration(values?: Record<string, unknown>): boolean {
37+
if (values?.operation !== 'set_suggested_prompts') return false
38+
39+
const hasAgentFields = hasConfiguredSlackValue(values, [
40+
'agentBotCredential',
41+
'manualAgentBotCredential',
42+
'agentChannel',
43+
'manualAgentChannel',
44+
'agentThreadTs',
45+
])
46+
if (hasAgentFields) return false
47+
48+
return hasConfiguredSlackValue(values, [
49+
'credential',
50+
'manualCredential',
51+
'channel',
52+
'manualChannel',
53+
'getThreadTimestamp',
54+
])
55+
}
56+
57+
function getSlackV2AgentOperationCondition(values?: Record<string, unknown>) {
58+
return {
59+
field: 'operation' as const,
60+
value: [
61+
...(isLegacySuggestedPromptsConfiguration(values) ? [] : ['set_suggested_prompts']),
62+
...SLACK_V2_SESSION_OPERATIONS,
63+
],
64+
}
65+
}
66+
2467
const CHANNEL_FIELD = ['channel', 'manualChannel'] as const
2568

2669
/**
@@ -2948,22 +2991,31 @@ function adaptSubBlockForV2(sb: SubBlockConfig): SubBlockConfig {
29482991
serviceAccountGroup: 'Custom bots',
29492992
serviceAccountConnect: 'Set up a custom bot',
29502993
},
2951-
condition: { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS], not: true },
2994+
condition: (values?: Record<string, unknown>) =>
2995+
isLegacySuggestedPromptsConfiguration(values)
2996+
? { field: 'operation', value: 'set_suggested_prompts' }
2997+
: { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS], not: true },
29522998
}
29532999
}
29543000
if (sb.id === 'manualCredential') {
29553001
return {
29563002
...rest,
29573003
placeholder: 'Enter credential ID',
2958-
condition: { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS], not: true },
3004+
condition: (values?: Record<string, unknown>) =>
3005+
isLegacySuggestedPromptsConfiguration(values)
3006+
? { field: 'operation', value: 'set_suggested_prompts' }
3007+
: { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS], not: true },
29593008
}
29603009
}
29613010
if (sb.id === 'channel' || sb.id === 'manualChannel') {
29623011
return {
29633012
...sb,
29643013
dependsOn: ['credential'],
29653014
condition: (values?: Record<string, unknown>) => {
2966-
if (SLACK_V2_AGENT_OPERATIONS.includes(values?.operation as never)) {
3015+
if (
3016+
SLACK_V2_AGENT_OPERATIONS.includes(values?.operation as never) &&
3017+
!isLegacySuggestedPromptsConfiguration(values)
3018+
) {
29673019
return { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS], not: true }
29683020
}
29693021
if (typeof condition !== 'function') {
@@ -2973,15 +3025,24 @@ function adaptSubBlockForV2(sb: SubBlockConfig): SubBlockConfig {
29733025
},
29743026
required: {
29753027
field: 'operation',
2976-
value: ['list_canvases', 'list_scheduled_messages', ...SLACK_V2_AGENT_OPERATIONS],
3028+
value: ['list_canvases', 'list_scheduled_messages', ...SLACK_V2_SESSION_OPERATIONS],
29773029
not: true,
29783030
},
29793031
}
29803032
}
29813033
if (sb.id === 'getThreadTimestamp') {
29823034
return {
29833035
...sb,
2984-
condition: { field: 'operation', value: ['get_thread', 'get_thread_replies'] },
3036+
condition: (values?: Record<string, unknown>) => ({
3037+
field: 'operation',
3038+
value: [
3039+
'get_thread',
3040+
'get_thread_replies',
3041+
'set_status',
3042+
'set_title',
3043+
...(isLegacySuggestedPromptsConfiguration(values) ? ['set_suggested_prompts'] : []),
3044+
],
3045+
}),
29853046
required: true,
29863047
}
29873048
}
@@ -3006,7 +3067,7 @@ function getSlackV2AgentSubBlocks(): SubBlockConfig[] {
30063067
serviceAccountGroup: 'Custom bots',
30073068
serviceAccountConnect: 'Set up a custom bot',
30083069
},
3009-
condition: { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS] },
3070+
condition: getSlackV2AgentOperationCondition,
30103071
required: true,
30113072
mode: 'basic',
30123073
},
@@ -3016,7 +3077,7 @@ function getSlackV2AgentSubBlocks(): SubBlockConfig[] {
30163077
type: 'short-input',
30173078
canonicalParamId: 'agentCredentialId',
30183079
placeholder: 'Enter custom bot credential ID',
3019-
condition: { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS] },
3080+
condition: getSlackV2AgentOperationCondition,
30203081
required: true,
30213082
mode: 'advanced',
30223083
},
@@ -3029,7 +3090,7 @@ function getSlackV2AgentSubBlocks(): SubBlockConfig[] {
30293090
selectorKey: 'slack.channels',
30303091
placeholder: 'Select Slack channel',
30313092
dependsOn: ['agentBotCredential'],
3032-
condition: { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS] },
3093+
condition: getSlackV2AgentOperationCondition,
30333094
required: true,
30343095
mode: 'basic',
30353096
},
@@ -3039,7 +3100,7 @@ function getSlackV2AgentSubBlocks(): SubBlockConfig[] {
30393100
type: 'short-input',
30403101
canonicalParamId: 'agentChannelId',
30413102
placeholder: 'Enter Slack channel ID',
3042-
condition: { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS] },
3103+
condition: getSlackV2AgentOperationCondition,
30433104
required: true,
30443105
mode: 'advanced',
30453106
},
@@ -3048,10 +3109,7 @@ function getSlackV2AgentSubBlocks(): SubBlockConfig[] {
30483109
title: 'Thread Timestamp',
30493110
type: 'short-input',
30503111
placeholder: 'Thread timestamp (thread_ts)',
3051-
condition: {
3052-
field: 'operation',
3053-
value: ['set_suggested_prompts', 'set_agent_session_status', 'rename_agent_session'],
3054-
},
3112+
condition: getSlackV2AgentOperationCondition,
30553113
required: {
30563114
field: 'operation',
30573115
value: ['set_agent_session_status', 'rename_agent_session'],
@@ -3153,17 +3211,9 @@ export function getSlackV2OperationSentences() {
31533211
if (!operationSentences) {
31543212
throw new Error('Slack action sentences must be defined before building slack_v2')
31553213
}
3156-
const { set_status: _setStatus, set_title: _setTitle, ...v2Sentences } = operationSentences
31573214
return {
3158-
...v2Sentences,
3159-
set_suggested_prompts: [
3160-
{
3161-
text: 'Set suggested prompts in',
3162-
field: ['agentChannel', 'manualAgentChannel'],
3163-
core: true,
3164-
},
3165-
{ text: ', for thread', field: 'agentThreadTs' },
3166-
],
3215+
...operationSentences,
3216+
set_suggested_prompts: ['Set suggested prompts'],
31673217
set_agent_session_status: [
31683218
{ text: 'Set agent session to', field: 'agentSessionStatus', core: true },
31693219
{ text: 'on thread', field: 'agentThreadTs', core: true },
@@ -3239,6 +3289,8 @@ export const SlackV2Block: BlockConfig<SlackResponse> = {
32393289
{ label: 'Get Thread Replies', id: 'get_thread_replies' },
32403290
{ label: 'Get Channel History', id: 'get_channel_history' },
32413291
{ label: 'Get Message Permalink', id: 'get_permalink' },
3292+
{ label: 'Set Assistant Status', id: 'set_status' },
3293+
{ label: 'Set Assistant Title', id: 'set_title' },
32423294
{ label: 'Set Suggested Prompts', id: 'set_suggested_prompts' },
32433295
{ label: 'Set Agent Session Status', id: 'set_agent_session_status' },
32443296
{ label: 'Rename Agent Session', id: 'rename_agent_session' },
@@ -3290,6 +3342,8 @@ export const SlackV2Block: BlockConfig<SlackResponse> = {
32903342
'slack_get_thread_replies',
32913343
'slack_get_channel_history',
32923344
'slack_get_permalink',
3345+
'slack_set_status',
3346+
'slack_set_title',
32933347
'slack_set_suggested_prompts',
32943348
'slack_set_suggested_prompts_v2',
32953349
'slack_set_agent_session_status_v2',

packages/deployment-config/src/integrations.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21272,6 +21272,14 @@
2127221272
"name": "Get Message Permalink",
2127321273
"description": "Get a stable permalink URL to a specific Slack message."
2127421274
},
21275+
{
21276+
"name": "Set Assistant Status",
21277+
"description": "Set or clear the assistant thread status indicator (the loading shimmer) on a Slack AI app thread. Pass an empty status to clear it."
21278+
},
21279+
{
21280+
"name": "Set Assistant Title",
21281+
"description": "Set the title of a Slack assistant thread (shown in the AI app thread header)."
21282+
},
2127521283
{
2127621284
"name": "Set Suggested Prompts",
2127721285
"description": "Set the clickable suggested prompts shown in a Slack assistant thread (the prompt chips in an AI app)."
@@ -21405,7 +21413,7 @@
2140521413
"description": "Set the purpose (description) for a Slack channel (max 250 characters)."
2140621414
}
2140721415
],
21408-
"operationCount": 42,
21416+
"operationCount": 44,
2140921417
"triggers": [
2141021418
{
2141121419
"id": "slack_oauth",

0 commit comments

Comments
 (0)