Manage display of "Run as Task" checkbox on Tools Tab

* In ToolsTab.tsx
  - define ExtendedTool interface for augmenting with execution, meta, icons
  - in hasMeta function,
    - cast tool as ExtendedTool
  - add getTaskSupport function
    - returns forbidden if input is not a tool
    - returns the setting of execution.taskSupport if set and valid
    - returns optional otherwise
    - in useEffect map callback
      - set runAsTask to true if getTaskSupport returns "required", else false
    - in IconDisplay instances cast tool as ExtendedTool instead of as WithIcons
    - hide "Run as Task" checkbox if getTaskSupport returns "forbidden" for the selected tool
    - disable the "Run as Task" checkbox if getTaskSupport returns "required"
* In ToolsTab.test.tsx
  - added unit test
    - "should show/hide/disable run-as-task checkbox based on taskSupport"
      - tests checkbox checked and disabled states for "forbidden", "required", and "optional" states
This commit is contained in:
cliffhall 2026-02-06 16:30:23 -05:00
commit 96b1b586f9

View file

@ -50,11 +50,40 @@ import {
hasValidMetaName,
hasValidMetaPrefix,
isReservedMetaKey,
} from "@/utils/metaUtils";
} from "../utils/metaUtils";
/**
* Extended Tool type that includes optional fields used by the inspector.
*/
interface ExtendedTool extends Tool, WithIcons {
_meta?: Record<string, unknown>;
execution?: {
taskSupport?: "forbidden" | "required" | "optional";
};
}
// Type guard to safely detect the optional _meta field without using `any`
const hasMeta = (tool: Tool): tool is Tool & { _meta: unknown } =>
typeof (tool as { _meta?: unknown })._meta !== "undefined";
const hasMeta = (
tool: Tool,
): tool is ExtendedTool & { _meta: Record<string, unknown> } =>
typeof (tool as ExtendedTool)._meta !== "undefined";
// Type guard to detect execution.taskSupport
const getTaskSupport = (
tool: Tool | null,
): "forbidden" | "required" | "optional" => {
if (!tool) return "forbidden";
const extendedTool = tool as ExtendedTool;
const taskSupport = extendedTool.execution?.taskSupport;
if (
taskSupport === "forbidden" ||
taskSupport === "required" ||
taskSupport === "optional"
) {
return taskSupport;
}
return "optional";
};
const ToolsTab = ({
tools,
@ -131,7 +160,8 @@ const ToolsTab = ({
];
});
setParams(Object.fromEntries(params));
setRunAsTask(false);
const taskSupport = getTaskSupport(selectedTool);
setRunAsTask(taskSupport === "required");
// Reset validation errors when switching tools
setHasValidationErrors(false);
@ -170,7 +200,7 @@ const ToolsTab = ({
renderItem={(tool) => (
<div className="flex items-start w-full gap-2">
<div className="flex-shrink-0 mt-1">
<IconDisplay icons={(tool as WithIcons).icons} size="sm" />
<IconDisplay icons={(tool as ExtendedTool).icons} size="sm" />
</div>
<div className="flex flex-col flex-1 min-w-0">
<span className="truncate">{tool.name}</span>
@ -191,7 +221,7 @@ const ToolsTab = ({
<div className="flex items-center gap-2">
{selectedTool && (
<IconDisplay
icons={(selectedTool as WithIcons).icons}
icons={(selectedTool as ExtendedTool).icons}
size="md"
/>
)}
@ -659,21 +689,24 @@ const ToolsTab = ({
</div>
</div>
)}
<div className="flex items-center space-x-2">
<Checkbox
id="run-as-task"
checked={runAsTask}
onCheckedChange={(checked: boolean) =>
setRunAsTask(checked)
}
/>
<Label
htmlFor="run-as-task"
className="text-sm font-medium text-gray-700 dark:text-gray-300 cursor-pointer"
>
Run as task
</Label>
</div>
{getTaskSupport(selectedTool) !== "forbidden" && (
<div className="flex items-center space-x-2">
<Checkbox
id="run-as-task"
checked={runAsTask}
onCheckedChange={(checked: boolean) =>
setRunAsTask(checked)
}
disabled={getTaskSupport(selectedTool) === "required"}
/>
<Label
htmlFor="run-as-task"
className="text-sm font-medium text-gray-700 dark:text-gray-300 cursor-pointer"
>
Run as task
</Label>
</div>
)}
<Button
onClick={async () => {
// Validate JSON inputs before calling tool