From 7d25a90d50b7f57d48e601cc0afd750bce142d8d Mon Sep 17 00:00:00 2001 From: hugocasa Date: Thu, 8 Oct 2026 19:45:16 +0200 Subject: [PATCH] fix: keep a named agent on its thread after an empty question and flag a missing Slack signing secret Co-Authored-By: Claude Opus 5.5 --- backend/windmill-api/openapi.yaml | 4 ++++ backend/windmill-api/src/slack_agents.rs | 12 +++++++++--- .../components/flows/content/AgentUsePanel.svelte | 9 +++++++++ .../(logged)/agents/get/[...path]/+page.svelte | 1 + 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 08da137ed6..f8fee1821f 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -13468,12 +13468,16 @@ paths: slack_team_name: type: string description: the connected Slack workspace; absent when Slack is not connected + signing_secret_set: + type: boolean + description: agents answer in Slack only when the instance sets SLACK_SIGNING_SECRET channels: type: array items: $ref: "#/components/schemas/SlackChannelAgent" required: - channels + - signing_secret_set /w/{workspace}/flow_conversations/update/{conversation_id}: post: diff --git a/backend/windmill-api/src/slack_agents.rs b/backend/windmill-api/src/slack_agents.rs index 0366303eea..7a975206ab 100644 --- a/backend/windmill-api/src/slack_agents.rs +++ b/backend/windmill-api/src/slack_agents.rs @@ -82,6 +82,8 @@ async fn list_channel_agents( struct AgentSlack { /// The connected Slack workspace's name; `None` when Slack is not connected. slack_team_name: Option, + /// Agents answer only when the instance verifies Slack's request signatures. + signing_secret_set: bool, channels: Vec, } @@ -111,7 +113,11 @@ async fn list_agent_channels( .fetch_all(&mut *tx) .await?; tx.commit().await?; - Ok(Json(AgentSlack { slack_team_name, channels })) + #[cfg(feature = "oauth2")] + let signing_secret_set = crate::SLACK_SIGNING_SECRET.is_some(); + #[cfg(not(feature = "oauth2"))] + let signing_secret_set = false; + Ok(Json(AgentSlack { slack_team_name, signing_secret_set, channels })) } #[derive(Deserialize)] @@ -653,14 +659,14 @@ async fn answer( } }; + record_thread_agent(db, &msg.team_id, &channel, &thread_root, w_id, &agent_path).await?; + if question.is_empty() { return reply .send(format!("What would you like to ask `{agent_path}`?"), None) .await; } - record_thread_agent(db, &msg.team_id, &channel, &thread_root, w_id, &agent_path).await?; - // Keyed with the workspace's secret: any member could rebuild the plain thread and user ids // and load this person's conversation, tool results included, through the run API. let workspace_key = get_workspace_key(w_id, db).await?; diff --git a/frontend/src/lib/components/flows/content/AgentUsePanel.svelte b/frontend/src/lib/components/flows/content/AgentUsePanel.svelte index 4dfad01c5a..2d8a682e58 100644 --- a/frontend/src/lib/components/flows/content/AgentUsePanel.svelte +++ b/frontend/src/lib/components/flows/content/AgentUsePanel.svelte @@ -19,6 +19,7 @@ workspace, isAdmin, slackTeamName, + signingSecretSet, channels, onChanged }: { @@ -27,6 +28,8 @@ isAdmin: boolean /** Unset when Slack is not connected to the workspace. */ slackTeamName: string | undefined + /** Agents answer in Slack only when the instance verifies Slack's request signatures. */ + signingSecretSet: boolean channels: SlackChannelAgent[] onChanged: () => void } = $props() @@ -148,6 +151,12 @@ A workspace admin can connect it. {/if} {:else} + {#if !signingSecretSet} + + The instance needs SLACK_SIGNING_SECRET set to the Slack app's signing secret, so + Windmill can tell who sent a message. + + {/if}
Ask it anywhere diff --git a/frontend/src/routes/(root)/(logged)/agents/get/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/agents/get/[...path]/+page.svelte index b78fc8ce62..b46c0ad7b1 100644 --- a/frontend/src/routes/(root)/(logged)/agents/get/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/agents/get/[...path]/+page.svelte @@ -214,6 +214,7 @@ workspace={ws} isAdmin={!!($userStore?.is_admin || $userStore?.is_super_admin)} slackTeamName={slack.current?.slack_team_name} + signingSecretSet={slack.current?.signing_secret_set ?? true} channels={slack.current?.channels ?? []} onChanged={() => slack.refetch()} />