Skip to content

Commit 628f800

Browse files
fix(slack): use public output selectors
1 parent 4245489 commit 628f800

7 files changed

Lines changed: 41 additions & 16 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1971,7 +1971,7 @@ Trigger from Slack events, interactions, and slash commands
19711971
| `manualChannelFilter` | string | No | Comma-separated channel IDs to restrict to. Set IDs directly here. |
19721972
| `threads` | string | No | Include thread replies, exclude them \(top-level only\), or fire only on thread replies. |
19731973
| `streamResponse` | boolean | No | Create a Slack agent session and stream selected workflow outputs into the conversation that started this run. Custom bots only. |
1974-
| `streamOutputs` | workflow-output-selector | No | Each selected block invocation creates its own Slack response. Agent outputs stream live; other outputs are sent when the block completes. |
1974+
| `streamOutputs` | workflow-output-selector | No | Output selectors use the same blockId.path form as the streaming API. Each selected block invocation creates its own Slack response. Agent outputs stream live; other outputs are sent when the block completes. |
19751975
| `streamTaskTitle` | string | No | Optional status Slack shows while each selected response is being produced. Leave empty to use Running. |
19761976
| `streamTaskDisplayMode` | string | No | Choose how Slack displays thinking and tool progress. |
19771977
| `streamIncludeThinking` | boolean | No | Show agent thinking as Slack task updates while the response is generated. |

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.test.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,24 +184,30 @@ beforeEach(() => {
184184
function outputSelect(
185185
workflowId: string,
186186
selectedOutputs: string[],
187-
onOutputSelect: (outputIds: string[]) => void
187+
onOutputSelect: (outputIds: string[]) => void,
188+
valueMode: 'id' | 'label' | 'public' = 'id'
188189
) {
189190
return (
190191
<OutputSelect
191192
workflowId={workflowId}
192193
selectedOutputs={selectedOutputs}
193194
onOutputSelect={onOutputSelect}
195+
valueMode={valueMode}
194196
/>
195197
)
196198
}
197199

198-
function renderOutputSelect(selectedOutputs: string[], onOutputSelect = vi.fn()) {
200+
function renderOutputSelect(
201+
selectedOutputs: string[],
202+
onOutputSelect = vi.fn(),
203+
valueMode: 'id' | 'label' | 'public' = 'id'
204+
) {
199205
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
200206
container = document.createElement('div')
201207
document.body.appendChild(container)
202208
root = createRoot(container)
203209
act(() => {
204-
root?.render(outputSelect('root', selectedOutputs, onOutputSelect))
210+
root?.render(outputSelect('root', selectedOutputs, onOutputSelect, valueMode))
205211
})
206212
return onOutputSelect
207213
}
@@ -256,6 +262,17 @@ describe('OutputSelect nested workflow menu', () => {
256262
expect(onOutputSelect).toHaveBeenCalledWith(['workflow/agent_answer'])
257263
})
258264

265+
it('emits public dot selectors for trigger authoring', () => {
266+
const onOutputSelect = renderOutputSelect([], vi.fn(), 'public')
267+
268+
clickOption('content')
269+
expect(onOutputSelect).toHaveBeenCalledWith(['summary.content'])
270+
271+
clickOption('Outputs')
272+
clickOption('answer')
273+
expect(onOutputSelect).toHaveBeenCalledWith(['workflow/agent.answer'])
274+
})
275+
259276
it('returns to the root menu when the owning workflow changes', () => {
260277
const onOutputSelect = renderOutputSelect([])
261278
clickOption('Outputs')

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
type WorkflowOutputMenuNode,
1919
type WorkflowOutputOption,
2020
} from '@/lib/workflows/streaming/nested-output-options'
21+
import { formatPublicOutputSelector } from '@/lib/workflows/streaming/output-selector'
2122
import { BlockTile } from '@/blocks/block-tile'
2223
import { DEFAULTS } from '@/executor/constants'
2324
import { useWorkflowStates } from '@/hooks/queries/workflows'
@@ -41,8 +42,8 @@ interface OutputSelectProps {
4142
disabled?: boolean
4243
/** Placeholder text when no outputs are selected */
4344
placeholder?: string
44-
/** Whether to emit output IDs or labels in onOutputSelect callback */
45-
valueMode?: 'id' | 'label'
45+
/** Whether to emit internal IDs, display labels, or public dot selectors */
46+
valueMode?: 'id' | 'label' | 'public'
4647
/** Alignment of the dropdown relative to the trigger */
4748
align?: 'start' | 'end' | 'center'
4849
/** Maximum height of the dropdown content in pixels */
@@ -64,14 +65,20 @@ interface OutputSelectMenuProps {
6465
onOutputSelect: (outputIds: string[]) => void
6566
disabled: boolean
6667
placeholder: string
67-
valueMode: 'id' | 'label'
68+
valueMode: 'id' | 'label' | 'public'
6869
align: 'start' | 'end' | 'center'
6970
maxHeight: number
7071
size: 'sm' | 'md'
7172
className?: string
7273
}
7374

74-
function getOutputValue(output: WorkflowOutputOption, valueMode: 'id' | 'label'): string {
75+
function getOutputValue(
76+
output: WorkflowOutputOption,
77+
valueMode: 'id' | 'label' | 'public'
78+
): string {
79+
if (valueMode === 'public') {
80+
return formatPublicOutputSelector(output.blockId, output.path)
81+
}
7582
return valueMode === 'label' && !output.blockId.includes('/') ? output.label : output.id
7683
}
7784

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-output-selector/workflow-output-selector.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export function WorkflowOutputSelector({
3434
onOutputSelect={setStoredValue}
3535
disabled={disabled || isPreview}
3636
placeholder={placeholder}
37+
valueMode='public'
3738
size='md'
3839
className='w-full'
3940
/>

apps/sim/lib/webhooks/slack-stream-config.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('Slack stream response config', () => {
1313
const providerConfig: Record<string, unknown> = {
1414
eventType: 'app_mention',
1515
streamResponse: true,
16-
streamOutputs: ['block-1_content', 'workflow-block/block-2_result.value'],
16+
streamOutputs: ['block-1.content', 'workflow-block/block-2.result.value'],
1717
streamIncludeThinking: true,
1818
streamIncludeToolCalls: false,
1919
streamTaskTitle: ' Working ',
@@ -44,14 +44,14 @@ describe('Slack stream response config', () => {
4444
normalizeSlackStreamResponseConfig({
4545
eventType: 'message',
4646
streamResponse: true,
47-
streamOutputs: ['block_content'],
47+
streamOutputs: ['block.content'],
4848
})?.taskTitle
4949
).toBe('Running')
5050
expect(
5151
normalizeSlackStreamResponseConfig({
5252
eventType: 'message',
5353
streamResponse: true,
54-
streamOutputs: ['block_content'],
54+
streamOutputs: ['block.content'],
5555
streamTaskTitle: ' ',
5656
})?.taskTitle
5757
).toBe('Running')
@@ -88,14 +88,14 @@ describe('Slack stream response config', () => {
8888
normalizeSlackStreamResponseConfig({
8989
eventType: 'reaction_added',
9090
streamResponse: true,
91-
streamOutputs: ['block_content'],
91+
streamOutputs: ['block.content'],
9292
})
9393
).toThrow('reply-capable')
9494
expect(() =>
9595
normalizeSlackStreamResponseConfig({
9696
eventType: 'message',
9797
streamResponse: true,
98-
streamOutputs: ['content'],
98+
streamOutputs: ['block_content'],
9999
})
100100
).toThrow('Invalid Slack stream output selector')
101101
})

apps/sim/lib/webhooks/slack-stream-config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isRecordLike } from '@sim/utils/object'
2-
import { parseInternalOutputSelector } from '@/lib/workflows/streaming/output-selector'
2+
import { parsePublicOutputSelector } from '@/lib/workflows/streaming/output-selector'
33

44
export const SLACK_STREAM_RESPONSE_EVENTS = [
55
'message',
@@ -24,7 +24,7 @@ export interface SlackStreamResponseConfig {
2424
const SLACK_TASK_TITLE_LIMIT = 256
2525

2626
function parseSlackOutputSelector(selector: string): SlackStreamOutputConfig {
27-
const parsed = parseInternalOutputSelector(selector)
27+
const parsed = parsePublicOutputSelector(selector)
2828
if (!parsed.path) {
2929
throw new Error(`Invalid Slack stream output selector: ${selector}`)
3030
}

apps/sim/triggers/slack/oauth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export const slackOAuthTrigger: TriggerConfig = {
190190
type: 'workflow-output-selector',
191191
placeholder: 'Select workflow outputs',
192192
description:
193-
'Each selected block invocation creates its own Slack response. Agent outputs stream live; other outputs are sent when the block completes.',
193+
'Output selectors use the same blockId.path form as the streaming API. Each selected block invocation creates its own Slack response. Agent outputs stream live; other outputs are sent when the block completes.',
194194
required: {
195195
field: 'streamResponse',
196196
value: true,

0 commit comments

Comments
 (0)