Skip to content

Commit 9d001ea

Browse files
committed
Jobs
2 parents 17e1bb5 + 3ce9475 commit 9d001ea

File tree

56 files changed

+2940
-592
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2940
-592
lines changed

.github/workflows/test-build.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ jobs:
9090
9191
echo "✅ All feature flags are properly configured"
9292
93+
- name: Check subblock ID stability
94+
run: |
95+
if [ "${{ github.event_name }}" = "pull_request" ]; then
96+
BASE_REF="origin/${{ github.base_ref }}"
97+
git fetch --depth=1 origin "${{ github.base_ref }}" 2>/dev/null || true
98+
else
99+
BASE_REF="HEAD~1"
100+
fi
101+
bun run apps/sim/scripts/check-subblock-id-stability.ts "$BASE_REF"
102+
93103
- name: Lint code
94104
run: bun run lint:check
95105

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ Read records from a ServiceNow table
6969
| `number` | string | No | Record number \(e.g., INC0010001\) |
7070
| `query` | string | No | Encoded query string \(e.g., "active=true^priority=1"\) |
7171
| `limit` | number | No | Maximum number of records to return \(e.g., 10, 50, 100\) |
72+
| `offset` | number | No | Number of records to skip for pagination \(e.g., 0, 10, 20\) |
7273
| `fields` | string | No | Comma-separated list of fields to return \(e.g., sys_id,number,short_description,state\) |
74+
| `displayValue` | string | No | Return display values for reference fields: "true" \(display only\), "false" \(sys_id only\), or "all" \(both\) |
7375

7476
#### Output
7577

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

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Slack
3-
description: Send, update, delete messages, send ephemeral messages, add reactions in Slack or trigger workflows from Slack events
3+
description: Send, update, delete messages, add or remove reactions, manage canvases, get channel info and user presence in Slack
44
---
55

66
import { BlockInfoCard } from "@/components/ui/block-info-card"
@@ -39,7 +39,7 @@ If you encounter issues with the Slack integration, contact us at [help@sim.ai](
3939

4040
## Usage Instructions
4141

42-
Integrate Slack into the workflow. Can send, update, and delete messages, send ephemeral messages visible only to a specific user, create canvases, read messages, and add reactions. Requires Bot Token instead of OAuth in advanced mode. Can be used in trigger mode to trigger a workflow when a message is sent to a channel.
42+
Integrate Slack into the workflow. Can send, update, and delete messages, send ephemeral messages visible only to a specific user, create canvases, read messages, and add or remove reactions. Requires Bot Token instead of OAuth in advanced mode. Can be used in trigger mode to trigger a workflow when a message is sent to a channel.
4343

4444

4545

@@ -799,4 +799,128 @@ Add an emoji reaction to a Slack message
799799
|`timestamp` | string | Message timestamp |
800800
|`reaction` | string | Emoji reaction name |
801801

802+
### `slack_remove_reaction`
803+
804+
Remove an emoji reaction from a Slack message
805+
806+
#### Input
807+
808+
| Parameter | Type | Required | Description |
809+
| --------- | ---- | -------- | ----------- |
810+
| `authMethod` | string | No | Authentication method: oauth or bot_token |
811+
| `botToken` | string | No | Bot token for Custom Bot |
812+
| `channel` | string | Yes | Channel ID where the message was posted \(e.g., C1234567890\) |
813+
| `timestamp` | string | Yes | Timestamp of the message to remove reaction from \(e.g., 1405894322.002768\) |
814+
| `name` | string | Yes | Name of the emoji reaction to remove \(without colons, e.g., thumbsup, heart, eyes\) |
815+
816+
#### Output
817+
818+
| Parameter | Type | Description |
819+
| --------- | ---- | ----------- |
820+
| `content` | string | Success message |
821+
| `metadata` | object | Reaction metadata |
822+
|`channel` | string | Channel ID |
823+
|`timestamp` | string | Message timestamp |
824+
|`reaction` | string | Emoji reaction name |
825+
826+
### `slack_get_channel_info`
827+
828+
Get detailed information about a Slack channel by its ID
829+
830+
#### Input
831+
832+
| Parameter | Type | Required | Description |
833+
| --------- | ---- | -------- | ----------- |
834+
| `authMethod` | string | No | Authentication method: oauth or bot_token |
835+
| `botToken` | string | No | Bot token for Custom Bot |
836+
| `channel` | string | Yes | Channel ID to get information about \(e.g., C1234567890\) |
837+
| `includeNumMembers` | boolean | No | Whether to include the member count in the response |
838+
839+
#### Output
840+
841+
| Parameter | Type | Description |
842+
| --------- | ---- | ----------- |
843+
| `channelInfo` | object | Detailed channel information |
844+
|`id` | string | Channel ID \(e.g., C1234567890\) |
845+
|`name` | string | Channel name without # prefix |
846+
|`is_channel` | boolean | Whether this is a channel |
847+
|`is_private` | boolean | Whether channel is private |
848+
|`is_archived` | boolean | Whether channel is archived |
849+
|`is_general` | boolean | Whether this is the general channel |
850+
|`is_member` | boolean | Whether the bot/user is a member |
851+
|`is_shared` | boolean | Whether channel is shared across workspaces |
852+
|`is_ext_shared` | boolean | Whether channel is externally shared |
853+
|`is_org_shared` | boolean | Whether channel is org-wide shared |
854+
|`num_members` | number | Number of members in the channel |
855+
|`topic` | string | Channel topic |
856+
|`purpose` | string | Channel purpose/description |
857+
|`created` | number | Unix timestamp when channel was created |
858+
|`creator` | string | User ID of channel creator |
859+
|`updated` | number | Unix timestamp of last update |
860+
861+
### `slack_get_user_presence`
862+
863+
Check whether a Slack user is currently active or away
864+
865+
#### Input
866+
867+
| Parameter | Type | Required | Description |
868+
| --------- | ---- | -------- | ----------- |
869+
| `authMethod` | string | No | Authentication method: oauth or bot_token |
870+
| `botToken` | string | No | Bot token for Custom Bot |
871+
| `userId` | string | Yes | User ID to check presence for \(e.g., U1234567890\) |
872+
873+
#### Output
874+
875+
| Parameter | Type | Description |
876+
| --------- | ---- | ----------- |
877+
| `presence` | string | User presence status: "active" or "away" |
878+
| `online` | boolean | Whether user has an active client connection \(only available when checking own presence\) |
879+
| `autoAway` | boolean | Whether user was automatically set to away due to inactivity \(only available when checking own presence\) |
880+
| `manualAway` | boolean | Whether user manually set themselves as away \(only available when checking own presence\) |
881+
| `connectionCount` | number | Total number of active connections for the user \(only available when checking own presence\) |
882+
| `lastActivity` | number | Unix timestamp of last detected activity \(only available when checking own presence\) |
883+
884+
### `slack_edit_canvas`
885+
886+
Edit an existing Slack canvas by inserting, replacing, or deleting content
887+
888+
#### Input
889+
890+
| Parameter | Type | Required | Description |
891+
| --------- | ---- | -------- | ----------- |
892+
| `authMethod` | string | No | Authentication method: oauth or bot_token |
893+
| `botToken` | string | No | Bot token for Custom Bot |
894+
| `canvasId` | string | Yes | Canvas ID to edit \(e.g., F1234ABCD\) |
895+
| `operation` | string | Yes | Edit operation: insert_at_start, insert_at_end, insert_after, insert_before, replace, delete, or rename |
896+
| `content` | string | No | Markdown content for the operation \(required for insert/replace operations\) |
897+
| `sectionId` | string | No | Section ID to target \(required for insert_after, insert_before, replace, and delete\) |
898+
| `title` | string | No | New title for the canvas \(only used with rename operation\) |
899+
900+
#### Output
901+
902+
| Parameter | Type | Description |
903+
| --------- | ---- | ----------- |
904+
| `content` | string | Success message |
905+
906+
### `slack_create_channel_canvas`
907+
908+
Create a canvas pinned to a Slack channel as its resource hub
909+
910+
#### Input
911+
912+
| Parameter | Type | Required | Description |
913+
| --------- | ---- | -------- | ----------- |
914+
| `authMethod` | string | No | Authentication method: oauth or bot_token |
915+
| `botToken` | string | No | Bot token for Custom Bot |
916+
| `channel` | string | Yes | Channel ID to create the canvas in \(e.g., C1234567890\) |
917+
| `title` | string | No | Title for the channel canvas |
918+
| `content` | string | No | Canvas content in markdown format |
919+
920+
#### Output
921+
922+
| Parameter | Type | Description |
923+
| --------- | ---- | ----------- |
924+
| `canvas_id` | string | ID of the created channel canvas |
925+
802926

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { type NextRequest, NextResponse } from 'next/server'
2+
import { z } from 'zod'
3+
import { checkInternalAuth } from '@/lib/auth/hybrid'
4+
5+
export const dynamic = 'force-dynamic'
6+
7+
const SlackRemoveReactionSchema = z.object({
8+
accessToken: z.string().min(1, 'Access token is required'),
9+
channel: z.string().min(1, 'Channel is required'),
10+
timestamp: z.string().min(1, 'Message timestamp is required'),
11+
name: z.string().min(1, 'Emoji name is required'),
12+
})
13+
14+
export async function POST(request: NextRequest) {
15+
try {
16+
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
17+
18+
if (!authResult.success) {
19+
return NextResponse.json(
20+
{
21+
success: false,
22+
error: authResult.error || 'Authentication required',
23+
},
24+
{ status: 401 }
25+
)
26+
}
27+
28+
const body = await request.json()
29+
const validatedData = SlackRemoveReactionSchema.parse(body)
30+
31+
const slackResponse = await fetch('https://slack.com/api/reactions.remove', {
32+
method: 'POST',
33+
headers: {
34+
'Content-Type': 'application/json',
35+
Authorization: `Bearer ${validatedData.accessToken}`,
36+
},
37+
body: JSON.stringify({
38+
channel: validatedData.channel,
39+
timestamp: validatedData.timestamp,
40+
name: validatedData.name,
41+
}),
42+
})
43+
44+
const data = await slackResponse.json()
45+
46+
if (!data.ok) {
47+
return NextResponse.json(
48+
{
49+
success: false,
50+
error: data.error || 'Failed to remove reaction',
51+
},
52+
{ status: slackResponse.status }
53+
)
54+
}
55+
56+
return NextResponse.json({
57+
success: true,
58+
output: {
59+
content: `Successfully removed :${validatedData.name}: reaction`,
60+
metadata: {
61+
channel: validatedData.channel,
62+
timestamp: validatedData.timestamp,
63+
reaction: validatedData.name,
64+
},
65+
},
66+
})
67+
} catch (error) {
68+
if (error instanceof z.ZodError) {
69+
return NextResponse.json(
70+
{
71+
success: false,
72+
error: 'Invalid request data',
73+
details: error.errors,
74+
},
75+
{ status: 400 }
76+
)
77+
}
78+
79+
return NextResponse.json(
80+
{
81+
success: false,
82+
error: error instanceof Error ? error.message : 'Unknown error occurred',
83+
},
84+
{ status: 500 }
85+
)
86+
}
87+
}

apps/sim/app/api/tools/stt/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ export async function POST(request: NextRequest) {
150150
method: 'GET',
151151
})
152152
if (!response.ok) {
153+
await response.text().catch(() => {})
153154
throw new Error(`Failed to download audio from URL: ${response.statusText}`)
154155
}
155156

apps/sim/app/api/tools/textract/parse/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ async function fetchDocumentBytes(url: string): Promise<{ bytes: string; content
135135
method: 'GET',
136136
})
137137
if (!response.ok) {
138+
await response.text().catch(() => {})
138139
throw new Error(`Failed to fetch document: ${response.statusText}`)
139140
}
140141

apps/sim/app/api/tools/tts/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export async function POST(request: NextRequest) {
6565
})
6666

6767
if (!response.ok) {
68+
await response.body?.cancel().catch(() => {})
6869
logger.error(`Failed to generate TTS: ${response.status} ${response.statusText}`)
6970
return NextResponse.json(
7071
{ error: `Failed to generate TTS: ${response.status} ${response.statusText}` },

apps/sim/app/api/tools/vision/analyze/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ export async function POST(request: NextRequest) {
184184
method: 'GET',
185185
})
186186
if (!response.ok) {
187+
await response.text().catch(() => {})
187188
return NextResponse.json(
188189
{ success: false, error: 'Failed to fetch image for Gemini' },
189190
{ status: 400 }

apps/sim/app/api/workflows/[id]/execute/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
964964
logger.error(`[${requestId}] Error streaming block content:`, error)
965965
} finally {
966966
try {
967-
reader.releaseLock()
967+
await reader.cancel().catch(() => {})
968968
} catch {}
969969
}
970970
}

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -501,17 +501,6 @@ export function Chat() {
501501
}
502502
}, [])
503503

504-
useEffect(() => {
505-
if (!isExecuting && isStreaming) {
506-
const lastMessage = workflowMessages[workflowMessages.length - 1]
507-
if (lastMessage?.isStreaming) {
508-
streamReaderRef.current?.cancel()
509-
streamReaderRef.current = null
510-
finalizeMessageStream(lastMessage.id)
511-
}
512-
}
513-
}, [isExecuting, isStreaming, workflowMessages, finalizeMessageStream])
514-
515504
const handleStopStreaming = useCallback(() => {
516505
streamReaderRef.current?.cancel()
517506
streamReaderRef.current = null

0 commit comments

Comments
 (0)