mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-28 16:02:47 +00:00
feat: tool approval and a question tool for ai agent steps (#11826)
* feat(ai-agent): built-in approval tool that parks the agent An AI agent step can now be given a built-in "Request approval" tool. The model calls `request_approval` with a message; the agent stores its conversation in the new `ai_agent_checkpoint` table, releases its worker by parking its own queue row like a workflow-as-code parent, and resumes with the answer (approved, approver, comment) or a timeout once someone answers from the run page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ai-agent): tie approval answers to their request and hold the turn - A turn that asks for approval runs none of its other calls; they are refused so the model repeats them after the answer. - The run page sends the request's call id. An answer naming another request is ignored and the agent parks again for the time left; `suspend` is zeroed when the answer is read, so answers sent while the agent runs are refused. - The checkpoint is read whatever the current roster holds, the answer's outcome is written by position, and a cancel that beats the park completes the job as cancelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ai-agent): say why calls beside an invalid approval request were refused Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ai-agent): one approval form per request, none on a finished run Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ai-agent): answer approval requests from a flow's chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ai-agent): show chat approvals for agents that do not stream Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ai-agent): approval as a per-tool setting, built-in tool asks a question Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ai-agent): say when a nested agent leaves out tools that require approval Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ai-agent): show chat approvals and questions on the tool's own card Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ai-agent): question choices, and approval buttons shared with approval steps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ai-agent): show a waiting agent as suspended, and refused calls as not run Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ai-agent): each added chat card shows its own call, answered marker keyed by job Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ai-agent): approve and reject a held call through resume_suspended Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ai-agent): require approval in a tool settings tab, plain descriptions on built-in tool panes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ai-agent): sentence case for the web search tool label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ai-agent): built-in tool drawers explain themselves in the header subtitle Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ai-agent): clearer question tool subtitle Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ai-agent): badge the tool settings tab like run settings Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(ai-agent): regenerate system prompts and MCP session files Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a2b91fd17e
commit
a1bc9ec0c2
@@ -9,6 +9,12 @@
|
||||
// frontend/src/lib/components/flows/agentToolUtils.ts applies the same rules.
|
||||
const TOOL_NAME_REGEX = /^[a-zA-Z0-9_]+$/;
|
||||
const WEBSEARCH_ENABLED_NAME = "__wm_web_search";
|
||||
// The worker's `*_ENABLED_NAME`s: built-in tools, each named by its reserved name.
|
||||
const BUILTIN_ENABLED_NAMES = new Set([
|
||||
WEBSEARCH_ENABLED_NAME,
|
||||
"__wm_question",
|
||||
]);
|
||||
const BUILTIN_TOOL_TYPES = new Set(["websearch", "question"]);
|
||||
|
||||
// `forbiddenIds` in frontend/src/lib/components/flows/idUtils.ts.
|
||||
const RESERVED_IDS = new Set([
|
||||
@@ -134,10 +140,10 @@ function checkAgentTools(
|
||||
if (!Array.isArray(tools)) return;
|
||||
const seen = new Map<string, number>();
|
||||
tools.forEach((tool: any, i: number) => {
|
||||
// An mcp entry exposes its server's own tool names and a websearch entry is
|
||||
// named by `WEBSEARCH_ENABLED_NAME`; neither summary reaches the model.
|
||||
// An mcp entry exposes its server's own tool names and a built-in entry is
|
||||
// named by its reserved name; neither summary reaches the model.
|
||||
const kind = tool?.value?.tool_type;
|
||||
if (kind === "mcp" || kind === "websearch") return;
|
||||
if (kind === "mcp" || BUILTIN_TOOL_TYPES.has(kind)) return;
|
||||
const at = `${agent.pointer}/value/tools/${i}`;
|
||||
const name = tool?.summary;
|
||||
if (typeof name !== "string" || name.length === 0) {
|
||||
@@ -152,9 +158,9 @@ function checkAgentTools(
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (name === WEBSEARCH_ENABLED_NAME) {
|
||||
if (BUILTIN_ENABLED_NAMES.has(name)) {
|
||||
report.errors.push(
|
||||
`${at} tool name '${name}' is reserved for enabling web search`,
|
||||
`${at} tool name '${name}' is reserved for a built-in tool`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Generated
+8
-5
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user