Files
windmill/frontend/src/lib/components/flows/content/AgentUsePanel.svelte
T
hugocasaandClaude Opus 5.5 2e4c0789a2 feat: agent to flow, tabbed agent page, one webhook panel with an AI prompt (#11903)
* feat: open a flow linking an agent from its menu

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: toast when the flow handoff cannot be stored

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: turn an agent into a flow from its editor, and into a chat app from its page

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: let sandboxed apps run agents, and keep the flow handoff race-free

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: save the new flow as a draft before letting the agent draft go

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: offer the chat app only to those who may build apps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: give the agent page's chat app section a tab on small screens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: show the flow handoff as an info alert, framed around context and output

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep the flow handoff from operators and say why the chat app is locked

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: collapse the flow handoff hint to its title and button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: show the flow handoff as a corner hint, as the flow editor shows its own

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: name the agent on the app button and say why it is disabled

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: ask whether the agent needs steps before or after it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: name what a flow adds around the agent in its hint

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: say a never-deployed agent turns into a flow

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* revert: leave agent runs and the raw-app guidance to the chat SDK's runnable transport

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: show the agent page's sections as tabs at every width, as a chat-mode flow's page does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: one webhook panel for scripts, flows and agents, with an AI prompt and chat-mode examples

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: describe the preprocessor's event argument instead of the v1 wm_trigger

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: give the flow chat's preprocessor example the v2 event signature

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: group a webhook's request choices and examples under an HTTP tab, beside the AI prompt and chat SDK

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: drop a comment about guidance this branch no longer adds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: say where the AI prompt expects the webhook token

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: tell the chat-flow prompt that memory_id is required

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep the webhook tabs next to their content

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep the webhook's REST tab name

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: build an agent's chat app from a template, not an AI session

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: don't send the agent chat app's message on an IME confirmation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: agent API tab with one row of call examples and a copy-prompt button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: open build app in an AI session when AI sessions are on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: app conversations through the memory_id query parameter, chat-mode flows included

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: regenerate the mcp session instructions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: type backendAsync's memoryId option in the eval diagnostics

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: pin git-sync hub scripts to windmill-cli 1.849.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep build app in the editor when windmill ai is off

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: webhook panel with http and chat sdk tabs over toggle settings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: memoryId option on the sync backend call too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: bump the ui builder artifact for the memoryId option

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep the webhook panel's http and chat sdk tabs close to their content

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: shared-utils 1.0.14

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: cli takes shared-utils 1.0.14 for the memoryId option

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: pin the app conversation scoping and the memoryId signatures

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: assert the ownership refusal, not any refusal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: match the memoryId option in the cli wmill.ts signature tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: show the webhook request formats in a card with their toggle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: put the chat sdk example in the same card as the http request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: add a windmill app tab to chat-mode flow pages

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: title the webhook example cards and label the ai prompt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: offer the chat app from the conversations sidebar instead of a tab

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: raise the chat app's chat-sdk floor to 1.851.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: render the agent's chat app card only once the workspace is known

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: gate the flow's build chat app menu item like the sidebar card

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: build the agent's chat app through the page's navigation, for app-only authors too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep an imported app in the editor inside a session preview

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: build a form app from an agent without managed memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: cover the agent form app's generated source

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:32:20 +01:00

180 lines
6.2 KiB
Svelte

<script lang="ts">
import { Alert, Badge, Button, CopyButton, TabContent, Tabs } from '$lib/components/common'
import Tooltip from '$lib/components/Tooltip.svelte'
import type { SlackAgentChannel } from '$lib/gen'
import WebhooksPanel from '$lib/components/triggers/webhook/WebhooksPanel.svelte'
/**
* The ways to use an agent outside its page: in the Slack channels it is shared in, set in its
* settings and summed up here, and over the API.
*/
let {
agentPath,
isAdmin,
chat,
slackLoading,
slackTeamName,
botName,
missingScopes,
signingSecretSet,
runsAs,
anyone,
canShare,
channels,
section
}: {
agentPath: string
isAdmin: boolean
/** Whether the agent keeps the conversation. Slack is offered only then: a thread is one. */
chat: boolean
/** Whether the workspace's Slack connection is still being read. */
slackLoading: boolean
/** Unset when Slack is not connected to the workspace. */
slackTeamName: string | undefined
/** Unset when Slack can't be reached with the workspace's bot token. */
botName: string | undefined
/** Agent scopes the connection lacks: Slack was connected without AI agents enabled. */
missingScopes: string[]
/** Agents answer in Slack only when the instance verifies Slack's request signatures. */
signingSecretSet: boolean
/** Who the agent runs as; unset unless it runs on behalf of someone. */
runsAs: string | undefined
/** Whether anyone in its channels can ask it, rather than workspace members only. */
anyone: boolean
/** Whether the user can change its Slack settings: anyone who can edit it. */
canShare: boolean
channels: SlackAgentChannel[]
/** The section shown: the page offers them as its tabs. */
section: 'api' | 'slack'
} = $props()
let handle = $derived(`+${agentPath.split('/').pop()}`)
</script>
<div class="flex flex-col h-full">
<Tabs selected={section} hideTabs wrapperClass="flex-none w-full">
{#snippet content()}
<div class="min-h-0 grow overflow-y-auto">
<TabContent value="slack" class="flex flex-col gap-6 p-4">
{#if slackLoading}
<span class="text-xs text-hint">Loading…</span>
{:else if !slackTeamName}
<span class="text-xs text-secondary">
Connect Slack to the workspace, with AI agents enabled, to ask this agent from Slack,
by its name or as a channel's default.
</span>
{#if isAdmin}
<div>
<Button
variant="default"
unifiedSize="md"
href="/workspace_settings?tab=slack&agents=true"
>
Connect Slack
</Button>
</div>
{:else}
<span class="text-2xs text-hint">A workspace admin can connect it.</span>
{/if}
{:else}
{#if !signingSecretSet}
<Alert type="warning" title="Agents can't answer in Slack yet" size="xs">
Windmill needs the Slack app's signing secret to tell who sent a message.
{#if isAdmin}
A workspace's own Slack app takes it in the workspace settings, the instance's app
in the instance settings.
{:else}
A workspace or instance admin can set it.
{/if}
</Alert>
{/if}
{#if !botName}
<Alert type="warning" title="Windmill can't reach Slack" size="xs">
Slack refused the workspace's bot token or couldn't be reached, so agents can't
answer there.
{#if isAdmin}
<a href="/workspace_settings?tab=slack&agents=true">Reconnect Slack</a> to fix it.
{:else}
A workspace admin can reconnect Slack to fix it.
{/if}
</Alert>
{/if}
{#if botName && missingScopes.length > 0}
<Alert type="warning" title="Slack was connected without AI agents" size="xs">
The connection lacks {missingScopes.join(', ')}, so agents can't check who is
asking.
{#if isAdmin}
<a href="/workspace_settings?tab=slack&agents=true">Disconnect Slack</a>, then
connect it again with AI agents enabled.
{:else}
A workspace admin can reconnect Slack with AI agents enabled.
{/if}
</Alert>
{/if}
<div class="flex flex-col gap-2">
<div class="flex items-center justify-between gap-2">
<span class="text-xs font-semibold text-emphasis">Ask it in Slack</span>
{#if canShare}
<Button
variant="default"
unifiedSize="sm"
href="/agents/edit/{agentPath}?settings=true"
>
Edit in settings
</Button>
{/if}
</div>
{#if channels.length === 0}
<span class="text-2xs text-hint">Not shared in any channel of {slackTeamName}.</span
>
{:else}
<div class="flex items-center gap-2">
<code
class="text-2xs font-mono text-emphasis bg-surface-secondary rounded px-2 py-1"
>@{botName ?? '<Slack app name>'} {handle} your question</code
>
<CopyButton value={handle} title="Copy {handle}" />
<Tooltip>
{handle} matches the last part of the agent's path. Use the whole path,
<code>+{agentPath}</code>, when two agents shared in a channel have that name.
Replies in the thread that mention the bot go to the same agent, no name needed.
</Tooltip>
</div>
<div class="flex flex-wrap items-center gap-1">
<span class="text-2xs text-hint">in</span>
{#each channels as channel (channel.channel_id)}
<Badge small
>#{channel.channel_name}{channel.is_default ? ' · default' : ''}</Badge
>
{/each}
</div>
<span class="text-2xs text-hint">
{#if anyone}
Anyone in these channels can ask it, with {runsAs}'s permissions.
{:else if runsAs}
Workspace members can ask it, with {runsAs}'s permissions.
{:else}
Workspace members can ask it, with their own permissions.
{/if}
</span>
{/if}
</div>
{/if}
</TabContent>
<TabContent value="api" class="p-4">
<WebhooksPanel
agent
{chat}
path={agentPath}
token=""
args={{ user_message: 'Hello' }}
scopes={[`jobs:run:agents:${agentPath}`]}
/>
</TabContent>
</div>
{/snippet}
</Tabs>
</div>