-
Notifications
You must be signed in to change notification settings - Fork 967
feat(slack): split responses into summary + side effects messages #1101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,8 +2,8 @@ import type { GenericMessageEvent } from "@slack/types"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { db } from "@superset/db/client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { integrationConnections } from "@superset/db/schema"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { and, eq } from "drizzle-orm"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { runSlackAgent } from "../utils/run-agent"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { formatActionsAsText } from "../utils/slack-blocks"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { formatErrorForSlack, runSlackAgent } from "../utils/run-agent"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { formatSideEffectsMessage } from "../utils/slack-blocks"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { createSlackClient } from "../utils/slack-client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface ProcessAssistantMessageParams { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -43,15 +43,18 @@ export async function processAssistantMessage({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const threadTs = event.thread_ts ?? event.ts; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Post an initial message that gets updated as the agent works | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let messageTs: string | undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.assistant.threads.setStatus({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel_id: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const initialMsg = await slack.chat.postMessage({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_ts: threadTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: "Thinking...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: "Thinking...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messageTs = initialMsg.ts; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "[slack/process-assistant-message] Failed to set status:", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "[slack/process-assistant-message] Failed to post initial message:", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -63,34 +66,67 @@ export async function processAssistantMessage({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| threadTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| organizationId: connection.organizationId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| slackToken: connection.accessToken, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onProgress: messageTs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? async (status) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.chat.update({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ts: messageTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Non-critical: progress updates are best-effort | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Format actions as text with URLs (enables Slack unfurling) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hasActions = result.actions.length > 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const responseText = hasActions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? formatActionsAsText(result.actions) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : result.text; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Update the message with Claude's final summary | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (messageTs) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.chat.update({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ts: messageTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: result.text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.chat.postMessage({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_ts: threadTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: result.text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.chat.postMessage({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_ts: threadTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: responseText, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Post side effects as a separate message | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (result.actions.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.chat.postMessage({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_ts: threadTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: formatSideEffectsMessage(result.actions), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "[slack/process-assistant-message] Failed to post side effects:", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("[slack/process-assistant-message] Agent error:", err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.chat.postMessage({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_ts: threadTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: `Sorry, something went wrong: ${err instanceof Error ? err.message : "Unknown error"}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.assistant.threads.setStatus({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel_id: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const errorText = await formatErrorForSlack(err); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (messageTs) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.chat.update({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ts: messageTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: errorText, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await slack.chat.postMessage({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: event.channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_ts: threadTs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: errorText, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
114
to
+130
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrap error-handler Slack calls in try-catch to prevent masking original error. If 💡 Suggested fix } catch (err) {
console.error("[slack/process-assistant-message] Agent error:", err);
const errorText = `Sorry, something went wrong: ${err instanceof Error ? err.message : "Unknown error"}`;
- if (messageTs) {
- await slack.chat.update({
- channel: event.channel,
- ts: messageTs,
- text: errorText,
- });
- } else {
- await slack.chat.postMessage({
- channel: event.channel,
- thread_ts: threadTs,
- text: errorText,
- });
+ try {
+ if (messageTs) {
+ await slack.chat.update({
+ channel: event.channel,
+ ts: messageTs,
+ text: errorText,
+ });
+ } else {
+ await slack.chat.postMessage({
+ channel: event.channel,
+ thread_ts: threadTs,
+ text: errorText,
+ });
+ }
+ } catch (postError) {
+ console.error(
+ "[slack/process-assistant-message] Failed to post error message:",
+ postError,
+ );
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,8 @@ import type { AppMentionEvent } from "@slack/types"; | |
| import { db } from "@superset/db/client"; | ||
| import { integrationConnections } from "@superset/db/schema"; | ||
| import { and, eq } from "drizzle-orm"; | ||
| import { runSlackAgent } from "../utils/run-agent"; | ||
| import { formatActionsAsText } from "../utils/slack-blocks"; | ||
| import { formatErrorForSlack, runSlackAgent } from "../utils/run-agent"; | ||
| import { formatSideEffectsMessage } from "../utils/slack-blocks"; | ||
| import { createSlackClient } from "../utils/slack-client"; | ||
|
|
||
| interface ProcessMentionParams { | ||
|
|
@@ -53,34 +53,91 @@ export async function processSlackMention({ | |
|
|
||
| const threadTs = event.thread_ts ?? event.ts; | ||
|
|
||
| // Post an initial message that gets updated as the agent works | ||
| let messageTs: string | undefined; | ||
| try { | ||
| const initialMsg = await slack.chat.postMessage({ | ||
| channel: event.channel, | ||
| thread_ts: threadTs, | ||
| text: "Thinking...", | ||
| }); | ||
| messageTs = initialMsg.ts; | ||
| } catch (err) { | ||
| console.error( | ||
| "[slack/process-mention] Failed to post initial message:", | ||
| err, | ||
| ); | ||
| } | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| try { | ||
| const result = await runSlackAgent({ | ||
| prompt: event.text, | ||
| channelId: event.channel, | ||
| threadTs, | ||
| organizationId: connection.organizationId, | ||
| slackToken: connection.accessToken, | ||
| onProgress: messageTs | ||
| ? async (status) => { | ||
| try { | ||
| await slack.chat.update({ | ||
| channel: event.channel, | ||
| ts: messageTs, | ||
| text: status, | ||
| }); | ||
| } catch { | ||
| // Non-critical: progress updates are best-effort | ||
| } | ||
|
Comment on lines
+79
to
+89
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: fd "process-mention.ts" --type fRepository: superset-sh/superset Length of output: 145 🏁 Script executed: cat -n apps/api/src/app/api/integrations/slack/events/process-mention/process-mention.ts | sed -n '65,85p'Repository: superset-sh/superset Length of output: 695 Log progress update failures instead of silently swallowing them. The silent catch block violates the coding guidelines requiring that errors are always logged with context. Add logging using the 💡 Suggested fix- } catch {
- // Non-critical: progress updates are best-effort
- }
+ } catch (error) {
+ console.warn(
+ "[slack/process-mention] Failed to update progress:",
+ error,
+ );
+ }🤖 Prompt for AI Agents |
||
| } | ||
| : undefined, | ||
| }); | ||
|
|
||
| // Format actions as text with URLs (enables Slack unfurling) | ||
| const hasActions = result.actions.length > 0; | ||
| const responseText = hasActions | ||
| ? formatActionsAsText(result.actions) | ||
| : result.text; | ||
| // Update the message with Claude's final summary | ||
| if (messageTs) { | ||
| await slack.chat.update({ | ||
| channel: event.channel, | ||
| ts: messageTs, | ||
| text: result.text, | ||
| }); | ||
| } else { | ||
| await slack.chat.postMessage({ | ||
| channel: event.channel, | ||
| thread_ts: threadTs, | ||
| text: result.text, | ||
| }); | ||
| } | ||
|
|
||
| await slack.chat.postMessage({ | ||
| channel: event.channel, | ||
| thread_ts: threadTs, | ||
| text: responseText, | ||
| }); | ||
| // Post side effects as a separate message | ||
| if (result.actions.length > 0) { | ||
| try { | ||
| await slack.chat.postMessage({ | ||
| channel: event.channel, | ||
| thread_ts: threadTs, | ||
| text: formatSideEffectsMessage(result.actions), | ||
| }); | ||
| } catch (err) { | ||
| console.error( | ||
| "[slack/process-mention] Failed to post side effects:", | ||
| err, | ||
| ); | ||
| } | ||
| } | ||
| } catch (err) { | ||
| console.error("[slack/process-mention] Agent error:", err); | ||
|
|
||
| await slack.chat.postMessage({ | ||
| channel: event.channel, | ||
| thread_ts: threadTs, | ||
| text: `Sorry, something went wrong: ${err instanceof Error ? err.message : "Unknown error"}`, | ||
| }); | ||
| const errorText = await formatErrorForSlack(err); | ||
| if (messageTs) { | ||
| await slack.chat.update({ | ||
| channel: event.channel, | ||
| ts: messageTs, | ||
| text: errorText, | ||
| }); | ||
| } else { | ||
| await slack.chat.postMessage({ | ||
| channel: event.channel, | ||
| thread_ts: threadTs, | ||
| text: errorText, | ||
| }); | ||
| } | ||
|
Comment on lines
124
to
+140
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrap error-handler Slack calls in try-catch to prevent masking the original error. Same issue as in 💡 Suggested fix } catch (err) {
console.error("[slack/process-mention] Agent error:", err);
const errorText = `Sorry, something went wrong: ${err instanceof Error ? err.message : "Unknown error"}`;
- if (messageTs) {
- await slack.chat.update({
- channel: event.channel,
- ts: messageTs,
- text: errorText,
- });
- } else {
- await slack.chat.postMessage({
- channel: event.channel,
- thread_ts: threadTs,
- text: errorText,
- });
+ try {
+ if (messageTs) {
+ await slack.chat.update({
+ channel: event.channel,
+ ts: messageTs,
+ text: errorText,
+ });
+ } else {
+ await slack.chat.postMessage({
+ channel: event.channel,
+ thread_ts: threadTs,
+ text: errorText,
+ });
+ }
+ } catch (postError) {
+ console.error(
+ "[slack/process-mention] Failed to post error message:",
+ postError,
+ );
}
}🤖 Prompt for AI Agents |
||
| } finally { | ||
| try { | ||
| await slack.reactions.remove({ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,2 @@ | ||
| export type { SlackAgentResult } from "./run-agent"; | ||
| export { runSlackAgent } from "./run-agent"; | ||
| export { formatErrorForSlack, runSlackAgent } from "./run-agent"; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -58,13 +58,42 @@ interface RunSlackAgentParams { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| threadTs: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| organizationId: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| slackToken: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onProgress?: (status: string) => void | Promise<void>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| export interface SlackAgentResult { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| actions: AgentAction[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function formatErrorForSlack(error: unknown): Promise<string> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const message = | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| error instanceof Error ? error.message : "Unknown error occurred"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const anthropic = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await anthropic.messages.create({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: "claude-3-5-haiku-latest", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| max_tokens: 256, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| role: "user", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: `Rewrite this API error as a brief, friendly Slack message (1-2 sentences). No technical jargon, no JSON. If it's a rate limit, tell them to try again shortly.\n\nError: ${message}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const text = response.content.find( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| (b): b is Anthropic.TextBlock => b.type === "text", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return text?.text ?? "Sorry, something went wrong. Please try again."; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Haiku itself failed (possibly also rate limited) — use static fallback | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (error instanceof Anthropic.APIError && error.status === 429) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "I'm a bit overloaded right now — please try again in a moment."; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "Sorry, something went wrong. Please try again."; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
+95
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Haiku fallback failures for observability. 💡 Suggested fix- } catch {
- // Haiku itself failed (possibly also rate limited) — use static fallback
+ } catch (err) {
+ console.warn(
+ "[slack-agent/format-error] Failed to format error message:",
+ err,
+ );
+ // Haiku itself failed (possibly also rate limited) — use static fallback
if (error instanceof Anthropic.APIError && error.status === 429) {
return "I'm a bit overloaded right now — please try again in a moment.";
}
return "Sorry, something went wrong. Please try again.";
}As per coding guidelines, "Never swallow errors silently; at minimum log them with context" and "Use prefixed console logging with pattern 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| function getActionFromToolResult( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolName: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // biome-ignore lint/suspicious/noExplicitAny: MCP result varies by tool | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -100,6 +129,19 @@ function getActionFromToolResult( | |||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (toolName === "delete_task" && data.deleted) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "task_deleted", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| tasks: ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| data.deleted as { id: string; slug: string; title: string }[] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).map((t) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: t.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| slug: t.slug, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: t.title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| })), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (toolName === "create_workspace" && data.workspaceId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "workspace_created", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -149,6 +191,21 @@ function parseTextContent(content: any): Record<string, unknown> | null { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const TOOL_PROGRESS_STATUS: Record<string, string> = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| create_task: "Creating task...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| update_task: "Updating task...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| delete_task: "Deleting task...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| list_tasks: "Searching tasks...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| get_task: "Fetching task details...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| list_task_statuses: "Fetching statuses...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| create_workspace: "Creating workspace...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| list_workspaces: "Fetching workspaces...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| list_devices: "Fetching devices...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| list_projects: "Fetching projects...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| list_members: "Fetching team members...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| slack_get_channel_history: "Reading channel history...", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Desktop-only tools that don't make sense in Slack context | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DENIED_SUPERSET_TOOLS = new Set([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "navigate_to_workspace", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -271,7 +328,7 @@ export async function runSlackAgent( | |||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "web_search_20250305" as const, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: "web_search" as const, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| max_uses: 3, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| max_uses: 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -313,6 +370,11 @@ Current context: | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // pause_turn: server-side tool (web search) paused a long-running turn | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (response.stop_reason === "pause_turn") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await params.onProgress?.("Searching the web..."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Non-critical | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+373
to
+377
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log onProgress failures in the pause_turn path. This silent catch mirrors the issue already flagged in the tool execution loop. Progress failures should be logged for observability. 💡 Suggested fix try {
await params.onProgress?.("Searching the web...");
- } catch {
- // Non-critical
+ } catch (error) {
+ console.warn(
+ "[slack-agent/on-progress] Failed to emit progress:",
+ error,
+ );
}As per coding guidelines, "Never swallow errors silently; at minimum log them with context" and "Use prefixed console logging with pattern [domain/operation] message". 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages.push({ role: "assistant", content: response.content }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = await anthropic.messages.create({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: "claude-sonnet-4-5", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -333,6 +395,18 @@ Current context: | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const toolUse of toolUseBlocks) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { toolName: rawToolName } = parseToolName(toolUse.name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const progressStatus = | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| TOOL_PROGRESS_STATUS[toolUse.name] ?? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| TOOL_PROGRESS_STATUS[rawToolName] ?? | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Working..."; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await params.onProgress?.(progressStatus); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Non-critical: don't fail the agent if progress update fails | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+398
to
+408
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log onProgress failures instead of swallowing them silently. This currently hides callback failures, making Slack update issues hard to diagnose. 💡 Suggested fix- try {
- await params.onProgress?.(progressStatus);
- } catch {
- // Non-critical: don't fail the agent if progress update fails
- }
+ try {
+ await params.onProgress?.(progressStatus);
+ } catch (error) {
+ console.warn(
+ "[slack-agent/on-progress] Failed to emit progress:",
+ error,
+ );
+ }As per coding guidelines, "Never swallow errors silently; at minimum log them with context" and "Use prefixed console logging with pattern [domain/operation] message". 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| let resultContent: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (toolUse.name === "slack_get_channel_history") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -407,9 +481,12 @@ Current context: | |||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const textBlock = response.content.find( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Use the last text block — server-side tools like web_search produce | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // multiple text blocks (preamble + synthesis) and we want the final one. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const textBlocks = response.content.filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| (b): b is Anthropic.TextBlock => b.type === "text", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const textBlock = textBlocks.at(-1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: textBlock?.text ?? "Done!", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,2 @@ | ||
| export type { AgentAction } from "./slack-blocks"; | ||
| export { formatActionsAsText } from "./slack-blocks"; | ||
| export { formatActionsAsText, formatSideEffectsMessage } from "./slack-blocks"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: superset-sh/superset
Length of output: 167
🏁 Script executed:
Repository: superset-sh/superset
Length of output: 881
🏁 Script executed:
Repository: superset-sh/superset
Length of output: 234
🏁 Script executed:
Repository: superset-sh/superset
Length of output: 3803
🏁 Script executed:
Repository: superset-sh/superset
Length of output: 2766
🏁 Script executed:
Repository: superset-sh/superset
Length of output: 102
Log progress update failures for observability.
The silent catch block at line 69 violates the coding guideline requiring all errors to be logged with context. While progress updates are best-effort, failures should still be logged at minimum for Slack API observability.
💡 Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents