diff --git a/api/app/clients/tools/manifest.json b/api/app/clients/tools/manifest.json
index df01b6bbb2..a45fca7b43 100644
--- a/api/app/clients/tools/manifest.json
+++ b/api/app/clients/tools/manifest.json
@@ -94,7 +94,6 @@
"name": "Ask User",
"pluginKey": "ask_user_question",
"description": "Let the agent pause mid-run to ask you a clarifying question and wait for your answer.",
- "icon": "assets/ask-user-question.svg",
"agentsOnly": true,
"authConfig": []
},
diff --git a/client/public/assets/ask-user-question.svg b/client/public/assets/ask-user-question.svg
deleted file mode 100644
index 3ccf453f93..0000000000
--- a/client/public/assets/ask-user-question.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/client/src/components/SidePanel/Agents/Tools/items/__tests__/catalog.spec.ts b/client/src/components/SidePanel/Agents/Tools/items/__tests__/catalog.spec.ts
index 91db59db56..ae70ccdec2 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/__tests__/catalog.spec.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/__tests__/catalog.spec.ts
@@ -57,6 +57,30 @@ describe('buildCatalog', () => {
expect(systemDefined?.kind === 'builtin' && systemDefined.userProvidedAuth).toBe(false);
});
+ test('surfaces ask_user_question as a BUILTIN (not a plugin) when the server lists it', () => {
+ const askPlugin = makePlugin({ pluginKey: 'ask_user_question', name: 'Ask User' });
+ const items = buildCatalog({ ...toolInputs, regularTools: [askPlugin] });
+ const builtin = items.find((i) => i.kind === 'builtin' && i.id === 'ask_user_question');
+ expect(builtin).toBeDefined();
+ expect(builtin?.iconKey).toBe('ask_user_question');
+ // never double-listed in the plugin section
+ expect(items.find((i) => i.kind === 'tool' && i.id === 'ask_user_question')).toBeUndefined();
+ });
+
+ test('omits the ask_user_question builtin when the server filtered it or tools are off', () => {
+ const askPlugin = makePlugin({ pluginKey: 'ask_user_question', name: 'Ask User' });
+ // admin filtered (not in regularTools)
+ expect(
+ buildCatalog(toolInputs).find((i) => i.kind === 'builtin' && i.id === 'ask_user_question'),
+ ).toBeUndefined();
+ // tools capability off
+ expect(
+ buildCatalog({ ...emptyInputs, regularTools: [askPlugin] }).find(
+ (i) => i.kind === 'builtin' && i.id === 'ask_user_question',
+ ),
+ ).toBeUndefined();
+ });
+
test('hides MCP items when the user lacks MCP permission', () => {
const map = new Map();
map.set('srv', { serverName: 'srv', isConfigured: true, tools: [] });
diff --git a/client/src/components/SidePanel/Agents/Tools/items/__tests__/mutations.spec.ts b/client/src/components/SidePanel/Agents/Tools/items/__tests__/mutations.spec.ts
index e485de9d16..b264a4ccb6 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/__tests__/mutations.spec.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/__tests__/mutations.spec.ts
@@ -152,3 +152,24 @@ describe('skillsEnabledTransition', () => {
expect(skillsEnabledTransition([], undefined)).toBeUndefined();
});
});
+
+describe('computeToggleAction — ask_user_question builtin', () => {
+ const item = {
+ kind: 'builtin',
+ id: 'ask_user_question',
+ iconKey: 'ask_user_question',
+ name: 'com_ui_ask_user',
+ description: 'com_agents_ask_user_info',
+ } as const;
+
+ test('toggles agent.tools, never a capability field', () => {
+ expect(computeToggleAction(item as never, { selected: false })).toEqual({
+ type: 'tool-add',
+ id: 'ask_user_question',
+ });
+ expect(computeToggleAction(item as never, { selected: true })).toEqual({
+ type: 'tool-remove',
+ id: 'ask_user_question',
+ });
+ });
+});
diff --git a/client/src/components/SidePanel/Agents/Tools/items/__tests__/selectors.spec.ts b/client/src/components/SidePanel/Agents/Tools/items/__tests__/selectors.spec.ts
index e315507eef..12b904e9af 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/__tests__/selectors.spec.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/__tests__/selectors.spec.ts
@@ -224,3 +224,28 @@ describe('matchesMcpServer', () => {
expect(matchesMcpServer('search_mcp_github_extra', 'github_extra')).toBe(true);
});
});
+
+describe('selection — ask_user_question builtin rides agent.tools', () => {
+ const catalogWithAsk: AgentItem[] = [
+ ...sampleCatalog,
+ {
+ kind: 'builtin',
+ id: 'ask_user_question',
+ name: 'com_ui_ask_user',
+ description: 'com_agents_ask_user_info',
+ iconKey: 'ask_user_question',
+ },
+ ];
+
+ test('selected iff agent.tools contains the tool (no capability field involved)', () => {
+ const selected = deriveSelectedItems(
+ { ...emptyFormState, tools: ['ask_user_question'] },
+ catalogWithAsk,
+ [],
+ );
+ expect(selected.map((i) => i.id)).toContain('ask_user_question');
+
+ const none = deriveSelectedItems(emptyFormState, catalogWithAsk, []);
+ expect(none.map((i) => i.id)).not.toContain('ask_user_question');
+ });
+});
diff --git a/client/src/components/SidePanel/Agents/Tools/items/catalog.ts b/client/src/components/SidePanel/Agents/Tools/items/catalog.ts
index e471828146..1a3f874b31 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/catalog.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/catalog.ts
@@ -118,6 +118,27 @@ export function buildCatalog(inputs: BuildCatalogInputs): AgentItem[] {
});
}
+ /**
+ * Native tool presented with the builtins (it ships with the app and pauses
+ * the run like a first-class feature), while remaining an `agent.tools`
+ * entry mechanically. Availability = the plugin listing itself: it appears
+ * only when the `tools` capability is on AND the server lists the plugin
+ * (i.e. the admin didn't filter it) — the same gates as the plugin section
+ * it graduated from.
+ */
+ if (
+ enabled.has(AgentCapabilities.tools) &&
+ inputs.regularTools.some((plugin) => plugin.pluginKey === 'ask_user_question')
+ ) {
+ items.push({
+ kind: 'builtin',
+ id: 'ask_user_question',
+ iconKey: 'ask_user_question',
+ name: 'com_ui_ask_user',
+ description: 'com_agents_ask_user_info',
+ });
+ }
+
if (inputs.showMemory) {
items.push({
kind: 'builtin',
@@ -150,6 +171,9 @@ export function buildCatalog(inputs: BuildCatalogInputs): AgentItem[] {
if (enabled.has(AgentCapabilities.tools)) {
for (const plugin of inputs.regularTools) {
+ if (plugin.pluginKey === 'ask_user_question') {
+ continue; // surfaced as a builtin above — don't double-list as a plugin
+ }
items.push({
kind: 'tool',
id: plugin.pluginKey,
diff --git a/client/src/components/SidePanel/Agents/Tools/items/icons.ts b/client/src/components/SidePanel/Agents/Tools/items/icons.ts
index 632346700e..0937552f30 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/icons.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/icons.ts
@@ -1,5 +1,6 @@
import {
Code,
+ MessageCircleQuestion,
Globe,
Brain,
Sparkles,
@@ -45,6 +46,10 @@ const BUILTIN_ICONS: Record = {
Icon: Brain,
colorClass: 'bg-indigo-500/15 text-indigo-600 dark:text-indigo-300',
},
+ ask_user_question: {
+ Icon: MessageCircleQuestion,
+ colorClass: 'bg-teal-500/15 text-teal-600 dark:text-teal-300',
+ },
};
const KIND_FALLBACK_ICONS: Record = {
diff --git a/client/src/components/SidePanel/Agents/Tools/items/mutations.ts b/client/src/components/SidePanel/Agents/Tools/items/mutations.ts
index d4fed0a0c1..93272c9a45 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/mutations.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/mutations.ts
@@ -13,8 +13,13 @@ export type TogglePatch =
| { type: 'action-remove'; actionId: string };
function builtinTogglePatch(id: string, selected: boolean): TogglePatch {
- // Every BuiltinId string equals its AgentCapabilities enum value, so the id
- // is already the form field name.
+ if (id === 'ask_user_question') {
+ // Native tool presented as a builtin — it has no capability field; the
+ // toggle edits agent.tools exactly like a plugin.
+ return selected ? { type: 'tool-remove', id } : { type: 'tool-add', id };
+ }
+ // Every other BuiltinId string equals its AgentCapabilities enum value, so
+ // the id is already the form field name.
const field = id as AgentCapabilities;
if (id === 'artifacts') {
return { type: 'builtin', field, value: selected ? '' : ArtifactModes.DEFAULT };
diff --git a/client/src/components/SidePanel/Agents/Tools/items/selectors.ts b/client/src/components/SidePanel/Agents/Tools/items/selectors.ts
index 93b9670242..b626f4d7e1 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/selectors.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/selectors.ts
@@ -42,6 +42,9 @@ function isBuiltinSelected(item: AgentItem, form: FormSelection): boolean {
return Boolean(form.artifacts);
case 'context':
return form.context_files.length > 0;
+ case 'ask_user_question':
+ // Native tool presented as a builtin — selection lives in agent.tools.
+ return form.tools.includes('ask_user_question');
default:
return false;
}
diff --git a/client/src/components/SidePanel/Agents/Tools/items/types.ts b/client/src/components/SidePanel/Agents/Tools/items/types.ts
index 26ac623226..d3c563bb0d 100644
--- a/client/src/components/SidePanel/Agents/Tools/items/types.ts
+++ b/client/src/components/SidePanel/Agents/Tools/items/types.ts
@@ -14,7 +14,14 @@ export type BuiltinId =
| `${AgentCapabilities.file_search}`
| `${AgentCapabilities.artifacts}`
| `${AgentCapabilities.memory}`
- | `${AgentCapabilities.context}`;
+ | `${AgentCapabilities.context}`
+ /**
+ * Native tool, not a capability: PRESENTED with the builtins (it ships with
+ * the app and pauses the run, like a first-class feature), but selection and
+ * toggling ride `agent.tools` exactly like a plugin — see the special cases
+ * in `selectors.ts` / `mutations.ts`.
+ */
+ | 'ask_user_question';
export type AgentItemStatus = 'needs_setup';
diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json
index facc3a492c..386e52c128 100644
--- a/client/src/locales/en/translation.json
+++ b/client/src/locales/en/translation.json
@@ -9,6 +9,7 @@
"com_a11y_summarize_completed": "Context summarized.",
"com_a11y_summarize_failed": "Summarization failed, continuing with available context.",
"com_a11y_summarize_started": "Summarizing context.",
+ "com_agents_ask_user_info": "Lets the agent pause mid-run to ask you a clarifying question and wait for your answer.",
"com_agents_agent_card_label": "{{name}} agent. {{description}}",
"com_agents_all": "All Agents",
"com_agents_all_category": "All",
@@ -856,6 +857,7 @@
"com_ui_artifacts_options": "Artifacts Options",
"com_ui_artifacts_subtext": "Lets the agent render React, HTML, SVG, Markdown, and Mermaid as interactive artifacts in a side panel instead of plain code blocks.",
"com_ui_ascending": "Asc",
+ "com_ui_ask_user": "Ask User",
"com_ui_asked_a_question": "Asked a question",
"com_ui_assistant": "Assistant",
"com_ui_assistant_delete_error": "There was an error deleting the assistant",