Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions apps/docs/content/docs/integrations/slack.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,51 @@ Get a stable permalink URL to a specific Slack message.
| `channel` | string | Channel ID containing the message |
| `permalink` | string | The permalink URL to the message |

### Slack Set Assistant Status

Set or clear the assistant thread status indicator (the loading shimmer) on a Slack AI app thread. Pass an empty status to clear it.

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `authMethod` | string | No | Authentication method: oauth or bot_token |
| `botToken` | string | No | Bot token for Custom Bot |
| `channel` | string | Yes | Channel ID containing the assistant thread \(e.g., C1234567890 or D1234567890\) |
| `threadTs` | string | Yes | Thread timestamp \(thread_ts\) of the assistant thread \(e.g., 1405894322.002768\) |
| `status` | string | No | Status text to display, e.g. 'Working on it…'. Omit or pass an empty string to clear the status. |
| `loadingMessages` | json | No | Optional list of messages to rotate through as an animated loading indicator \(max 10\). |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `ok` | boolean | Whether the status was set successfully |
| `channel` | string | Channel ID the status was set on |
| `threadTs` | string | Thread timestamp the status was set on |

### Slack Set Assistant Title

Set the title of a Slack assistant thread (shown in the AI app thread header).

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `authMethod` | string | No | Authentication method: oauth or bot_token |
| `botToken` | string | No | Bot token for Custom Bot |
| `channel` | string | Yes | Channel ID containing the assistant thread \(e.g., C1234567890 or D1234567890\) |
| `threadTs` | string | Yes | Thread timestamp \(thread_ts\) of the assistant thread \(e.g., 1405894322.002768\) |
| `title` | string | Yes | The title to display for the assistant thread |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `ok` | boolean | Whether the title was set successfully |
| `channel` | string | Channel ID the title was set on |
| `threadTs` | string | Thread timestamp the title was set on |

### Slack Set Suggested Prompts

Set the clickable suggested prompts shown in a Slack assistant thread (the prompt chips in an AI app).
Expand Down
98 changes: 85 additions & 13 deletions apps/sim/blocks/blocks/slack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* @vitest-environment node
*/
import { describe, expect, it } from 'vitest'
import { evaluateSubBlockCondition } from '@/lib/workflows/subblocks/visibility'
import {
getSlackV2ActionSubBlocks,
getSlackV2OperationSentences,
Expand All @@ -11,7 +12,7 @@ import {
} from '@/blocks/blocks/slack'

const AGENT_OPERATION_IDS = [
'set_suggested_prompts',
'set_agent_suggested_prompts',
'set_agent_session_status',
'rename_agent_session',
]
Expand All @@ -20,6 +21,8 @@ const AGENT_TOOL_IDS = [
'slack_set_agent_session_status_v2',
'slack_rename_agent_session_v2',
]
const ASSISTANT_OPERATION_IDS = ['set_status', 'set_title', 'set_suggested_prompts']
const ASSISTANT_TOOL_IDS = ['slack_set_status', 'slack_set_title']

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

function isSlackV2SubBlockVisible(subBlockId: string, values: Record<string, unknown>): boolean {
const subBlock = SlackV2Block.subBlocks.find((candidate) => candidate.id === subBlockId)
if (!subBlock) throw new Error(`Slack v2 subblock not found: ${subBlockId}`)
return evaluateSubBlockCondition(subBlock.condition, values)
}

describe('Slack block release', () => {
it('releases slack_v2 and keeps the legacy block executable but hidden', () => {
expect(SlackBlock.hideFromToolbar).toBe(true)
Expand All @@ -41,21 +50,27 @@ describe('Slack block release', () => {
expect(SlackV2Block.sunset).toBeUndefined()
})

it('replaces legacy assistant operations with custom-bot Agent Sessions operations', () => {
expect(operationIds()).toEqual(expect.arrayContaining(AGENT_OPERATION_IDS))
for (const id of ['set_status', 'set_title']) {
expect(operationIds()).not.toContain(id)
}
expect(getSlackV2ToolAccess()).toEqual(expect.arrayContaining(AGENT_TOOL_IDS))
it('adds custom-bot Agent Sessions operations without removing assistant operations', () => {
expect(operationIds()).toEqual(
expect.arrayContaining([...ASSISTANT_OPERATION_IDS, ...AGENT_OPERATION_IDS])
)
expect(getSlackV2ToolAccess()).toEqual(
expect.arrayContaining([...ASSISTANT_TOOL_IDS, ...AGENT_TOOL_IDS])
)
expect(getSlackV2ToolAccess()).toContain('slack_set_suggested_prompts')
for (const id of ['slack_set_status', 'slack_set_title']) {
expect(getSlackV2ToolAccess()).not.toContain(id)
}
expect(Object.keys(getSlackV2OperationSentences())).toEqual(
expect.arrayContaining(AGENT_OPERATION_IDS)
expect.arrayContaining([...ASSISTANT_OPERATION_IDS, ...AGENT_OPERATION_IDS])
)
})

it('keeps assistant operations routed to their original tools', () => {
const selectTool = SlackV2Block.tools.config?.tool
if (!selectTool) throw new Error('Slack v2 tool selector is required')

expect(selectTool({ operation: 'set_status' })).toBe('slack_set_status')
expect(selectTool({ operation: 'set_title' })).toBe('slack_set_title')
})

it('uses a service-account-only picker for Agent Sessions operations', () => {
const credential = getSlackV2ActionSubBlocks().find(
(subBlock) => subBlock.id === 'agentBotCredential'
Expand All @@ -67,7 +82,7 @@ describe('Slack block release', () => {
})
})

it('keeps persisted OAuth suggested prompts on the compatibility tool', () => {
it('keeps assistant suggested prompts on the original fields and tool', () => {
const selectTool = SlackV2Block.tools.config?.tool
if (!selectTool) throw new Error('Slack v2 tool selector is required')

Expand All @@ -90,14 +105,28 @@ describe('Slack block release', () => {
prompts: '[{"title":"Summarize","message":"Summarize this thread"}]',
promptsTitle: 'Try asking',
})

const assistantValues = {
operation: 'set_suggested_prompts',
credential: 'oauth-credential',
channel: 'C123',
getThreadTimestamp: '1700000000.000001',
}
expect(isSlackV2SubBlockVisible('credential', assistantValues)).toBe(true)
expect(isSlackV2SubBlockVisible('channel', assistantValues)).toBe(true)
expect(isSlackV2SubBlockVisible('getThreadTimestamp', assistantValues)).toBe(true)
expect(isSlackV2SubBlockVisible('suggestedPrompts', assistantValues)).toBe(true)
expect(isSlackV2SubBlockVisible('agentBotCredential', assistantValues)).toBe(false)
expect(isSlackV2SubBlockVisible('agentChannel', assistantValues)).toBe(false)
expect(isSlackV2SubBlockVisible('agentThreadTs', assistantValues)).toBe(false)
})

it('uses service-account tools for new agent operations', () => {
const selectTool = SlackV2Block.tools.config?.tool
if (!selectTool) throw new Error('Slack v2 tool selector is required')

expect(
selectTool({ operation: 'set_suggested_prompts', agentCredentialId: 'custom-bot' })
selectTool({ operation: 'set_agent_suggested_prompts', agentCredentialId: 'custom-bot' })
).toBe('slack_set_suggested_prompts_v2')
expect(selectTool({ operation: 'set_agent_session_status' })).toBe(
'slack_set_agent_session_status_v2'
Expand All @@ -116,5 +145,48 @@ describe('Slack block release', () => {
threadTs: '1700000000.000001',
status: 'processing',
})

const agentPromptValues = {
operation: 'set_agent_suggested_prompts',
agentBotCredential: 'custom-bot',
agentChannel: 'D123',
agentThreadTs: '1700000000.000001',
}
expect(isSlackV2SubBlockVisible('credential', agentPromptValues)).toBe(false)
expect(isSlackV2SubBlockVisible('channel', agentPromptValues)).toBe(false)
expect(isSlackV2SubBlockVisible('getThreadTimestamp', agentPromptValues)).toBe(false)
expect(isSlackV2SubBlockVisible('suggestedPrompts', agentPromptValues)).toBe(true)
expect(isSlackV2SubBlockVisible('agentBotCredential', agentPromptValues)).toBe(true)
expect(isSlackV2SubBlockVisible('agentChannel', agentPromptValues)).toBe(true)
expect(isSlackV2SubBlockVisible('agentThreadTs', agentPromptValues)).toBe(true)

expect(
mapSlackV2Params({
operation: 'set_agent_suggested_prompts',
agentCredentialId: 'custom-bot',
agentChannelId: 'D123',
agentThreadTs: '1700000000.000001',
suggestedPrompts: '[{"title":"Summarize","message":"Summarize this thread"}]',
promptsTitle: 'Try asking',
})
).toMatchObject({
credential: 'custom-bot',
channel: 'D123',
threadTs: '1700000000.000001',
prompts: '[{"title":"Summarize","message":"Summarize this thread"}]',
promptsTitle: 'Try asking',
})

const repurposedValues = {
operation: 'set_agent_suggested_prompts',
credential: 'stale-credential',
channel: 'C123',
suggestedPrompts: '[{"title":"Summarize","message":"Summarize this thread"}]',
}
expect(isSlackV2SubBlockVisible('credential', repurposedValues)).toBe(false)
expect(isSlackV2SubBlockVisible('channel', repurposedValues)).toBe(false)
expect(isSlackV2SubBlockVisible('agentBotCredential', repurposedValues)).toBe(true)
expect(isSlackV2SubBlockVisible('agentChannel', repurposedValues)).toBe(true)
expect(selectTool(repurposedValues)).toBe('slack_set_suggested_prompts_v2')
})
})
52 changes: 35 additions & 17 deletions apps/sim/blocks/blocks/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ import { getTrigger } from '@/triggers'
const DESTINATION_SWITCH_OPERATIONS = ['send', 'read', 'schedule_message'] as const

const SLACK_V2_AGENT_OPERATIONS = [
'set_suggested_prompts',
'set_agent_suggested_prompts',
'set_agent_session_status',
'rename_agent_session',
] as const

const SLACK_V2_SESSION_OPERATIONS = ['set_agent_session_status', 'rename_agent_session'] as const

const CHANNEL_FIELD = ['channel', 'manualChannel'] as const

/**
Expand Down Expand Up @@ -2981,10 +2983,28 @@ function adaptSubBlockForV2(sb: SubBlockConfig): SubBlockConfig {
if (sb.id === 'getThreadTimestamp') {
return {
...sb,
condition: { field: 'operation', value: ['get_thread', 'get_thread_replies'] },
condition: {
field: 'operation',
value: [
'get_thread',
'get_thread_replies',
'set_status',
'set_title',
'set_suggested_prompts',
],
},
required: true,
}
}
if (sb.id === 'suggestedPrompts' || sb.id === 'promptsTitle') {
return {
...sb,
condition: {
field: 'operation',
value: ['set_suggested_prompts', 'set_agent_suggested_prompts'],
},
}
}
if (dependsOn && !Array.isArray(dependsOn) && dependsOn.all?.includes('authMethod')) {
return { ...sb, dependsOn: ['credential'] }
}
Expand Down Expand Up @@ -3048,10 +3068,7 @@ function getSlackV2AgentSubBlocks(): SubBlockConfig[] {
title: 'Thread Timestamp',
type: 'short-input',
placeholder: 'Thread timestamp (thread_ts)',
condition: {
field: 'operation',
value: ['set_suggested_prompts', 'set_agent_session_status', 'rename_agent_session'],
},
condition: { field: 'operation', value: [...SLACK_V2_AGENT_OPERATIONS] },
required: {
field: 'operation',
value: ['set_agent_session_status', 'rename_agent_session'],
Expand Down Expand Up @@ -3153,12 +3170,11 @@ export function getSlackV2OperationSentences() {
if (!operationSentences) {
throw new Error('Slack action sentences must be defined before building slack_v2')
}
const { set_status: _setStatus, set_title: _setTitle, ...v2Sentences } = operationSentences
return {
...v2Sentences,
set_suggested_prompts: [
...operationSentences,
set_agent_suggested_prompts: [
{
text: 'Set suggested prompts in',
text: 'Set agent suggested prompts in',
field: ['agentChannel', 'manualAgentChannel'],
core: true,
},
Expand Down Expand Up @@ -3239,7 +3255,10 @@ export const SlackV2Block: BlockConfig<SlackResponse> = {
{ label: 'Get Thread Replies', id: 'get_thread_replies' },
{ label: 'Get Channel History', id: 'get_channel_history' },
{ label: 'Get Message Permalink', id: 'get_permalink' },
{ label: 'Set Suggested Prompts', id: 'set_suggested_prompts' },
{ label: 'Set Assistant Status', id: 'set_status' },
{ label: 'Set Assistant Title', id: 'set_title' },
{ label: 'Set Assistant Suggested Prompts', id: 'set_suggested_prompts' },
{ label: 'Set Agent Suggested Prompts', id: 'set_agent_suggested_prompts' },
{ label: 'Set Agent Session Status', id: 'set_agent_session_status' },
{ label: 'Rename Agent Session', id: 'rename_agent_session' },
{ label: 'List Channels', id: 'list_channels' },
Expand Down Expand Up @@ -3290,6 +3309,8 @@ export const SlackV2Block: BlockConfig<SlackResponse> = {
'slack_get_thread_replies',
'slack_get_channel_history',
'slack_get_permalink',
'slack_set_status',
'slack_set_title',
'slack_set_suggested_prompts',
'slack_set_suggested_prompts_v2',
'slack_set_agent_session_status_v2',
Expand Down Expand Up @@ -3329,9 +3350,9 @@ export const SlackV2Block: BlockConfig<SlackResponse> = {
tool: (params) => {
switch (params.operation) {
case 'set_suggested_prompts':
return params.agentCredentialId
? 'slack_set_suggested_prompts_v2'
: 'slack_set_suggested_prompts'
return 'slack_set_suggested_prompts'
case 'set_agent_suggested_prompts':
return 'slack_set_suggested_prompts_v2'
case 'set_agent_session_status':
return 'slack_set_agent_session_status_v2'
case 'rename_agent_session':
Expand All @@ -3348,9 +3369,6 @@ export const SlackV2Block: BlockConfig<SlackResponse> = {
if (!mapParams) throw new Error('Slack parameter mapper is required')
const baseParams = mapParams(params)
if (!SLACK_V2_AGENT_OPERATIONS.includes(params.operation as never)) return baseParams
if (params.operation === 'set_suggested_prompts' && !params.agentCredentialId) {
return baseParams
}

return {
...baseParams,
Expand Down
16 changes: 14 additions & 2 deletions packages/deployment-config/src/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -21273,9 +21273,21 @@
"description": "Get a stable permalink URL to a specific Slack message."
},
{
"name": "Set Suggested Prompts",
"name": "Set Assistant Status",
"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."
},
{
"name": "Set Assistant Title",
"description": "Set the title of a Slack assistant thread (shown in the AI app thread header)."
},
{
"name": "Set Assistant Suggested Prompts",
"description": "Set the clickable suggested prompts shown in a Slack assistant thread (the prompt chips in an AI app)."
},
{
"name": "Set Agent Suggested Prompts",
"description": "Set suggested prompts in Slack Agent View, optionally scoped to a specific thread."
},
{
"name": "Set Agent Session Status",
"description": "Create or update the state of a Slack agent session associated with a thread."
Expand Down Expand Up @@ -21405,7 +21417,7 @@
"description": "Set the purpose (description) for a Slack channel (max 250 characters)."
}
],
"operationCount": 42,
"operationCount": 45,
"triggers": [
{
"id": "slack_oauth",
Expand Down
Loading