Wires the ported Tasks screen + core task subsystem through App.tsx: sticky Clear Completed, cancel/refresh error toasts, core-side per-task progress correlation, Run-as-task on the Tools screen (gated on server support), per-server Task TTL, and live task-status toasts.
This commit is contained in:
parent
8923a9bbf9
commit
ed6eda7492
16 changed files with 1192 additions and 40 deletions
|
|
@ -12,6 +12,7 @@ const defaultSettings: InspectorServerSettings = {
|
|||
metadata: [],
|
||||
connectionTimeout: 30000,
|
||||
requestTimeout: 60000,
|
||||
taskTtl: 60000,
|
||||
};
|
||||
|
||||
function InteractiveRender(args: ServerSettingsFormProps) {
|
||||
|
|
@ -166,6 +167,7 @@ export const AllConfigured: Story = {
|
|||
],
|
||||
connectionTimeout: 15000,
|
||||
requestTimeout: 45000,
|
||||
taskTtl: 45000,
|
||||
oauthClientId: "my-client-id",
|
||||
oauthClientSecret: "super-secret-value",
|
||||
oauthScopes: "read write",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const emptySettings: InspectorServerSettings = {
|
|||
metadata: [],
|
||||
connectionTimeout: 30000,
|
||||
requestTimeout: 60000,
|
||||
taskTtl: 60000,
|
||||
};
|
||||
|
||||
const populatedSettings: InspectorServerSettings = {
|
||||
|
|
@ -19,6 +20,7 @@ const populatedSettings: InspectorServerSettings = {
|
|||
metadata: [{ key: "userId", value: "u-1" }],
|
||||
connectionTimeout: 30000,
|
||||
requestTimeout: 60000,
|
||||
taskTtl: 60000,
|
||||
oauthClientId: "cid",
|
||||
oauthClientSecret: "secret",
|
||||
oauthScopes: "read",
|
||||
|
|
@ -197,6 +199,36 @@ describe("ServerSettingsForm", () => {
|
|||
expect(typeof call[1]).toBe("number");
|
||||
});
|
||||
|
||||
it("invokes onTimeoutChange with the taskTtl field when typing in Task TTL", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onTimeoutChange = vi.fn();
|
||||
renderWithMantine(
|
||||
<ServerSettingsForm
|
||||
{...baseHandlers}
|
||||
onTimeoutChange={onTimeoutChange}
|
||||
settings={emptySettings}
|
||||
expandedSections={["timeouts"]}
|
||||
/>,
|
||||
);
|
||||
const ttlInput = screen.getByLabelText(/Task TTL/);
|
||||
await user.type(ttlInput, "9");
|
||||
expect(onTimeoutChange).toHaveBeenCalled();
|
||||
const call = onTimeoutChange.mock.calls[0];
|
||||
expect(call[0]).toBe("taskTtl");
|
||||
expect(typeof call[1]).toBe("number");
|
||||
});
|
||||
|
||||
it("renders the Task TTL value from settings", () => {
|
||||
renderWithMantine(
|
||||
<ServerSettingsForm
|
||||
{...baseHandlers}
|
||||
settings={{ ...emptySettings, taskTtl: 45000 }}
|
||||
expandedSections={["timeouts"]}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByLabelText(/Task TTL/)).toHaveValue("45000 ms");
|
||||
});
|
||||
|
||||
it("invokes onOAuthChange when typing in client id", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onOAuthChange = vi.fn();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export interface ServerSettingsFormProps {
|
|||
onRemoveMetadata: (index: number) => void;
|
||||
onMetadataChange: (index: number, key: string, value: string) => void;
|
||||
onTimeoutChange: (
|
||||
field: "connectionTimeout" | "requestTimeout",
|
||||
field: "connectionTimeout" | "requestTimeout" | "taskTtl",
|
||||
value: number,
|
||||
) => void;
|
||||
onOAuthChange: (oauth: OAuthSettings) => void;
|
||||
|
|
@ -105,7 +105,7 @@ export function ServerSettingsForm({
|
|||
onOAuthChange,
|
||||
}: ServerSettingsFormProps) {
|
||||
const handleTimeoutChange =
|
||||
(field: "connectionTimeout" | "requestTimeout") =>
|
||||
(field: "connectionTimeout" | "requestTimeout" | "taskTtl") =>
|
||||
(value: number | string) => {
|
||||
const numValue =
|
||||
typeof value === "string" ? parseInt(value, 10) || 0 : value;
|
||||
|
|
@ -193,6 +193,13 @@ export function ServerSettingsForm({
|
|||
value={settings.requestTimeout}
|
||||
onChange={handleTimeoutChange("requestTimeout")}
|
||||
/>
|
||||
<NumberInput
|
||||
label="Task TTL"
|
||||
suffix=" ms"
|
||||
min={1}
|
||||
value={settings.taskTtl}
|
||||
onChange={handleTimeoutChange("taskTtl")}
|
||||
/>
|
||||
</Group>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const initialSettings: InspectorServerSettings = {
|
|||
metadata: [{ key: "userId", value: "user-789" }],
|
||||
connectionTimeout: 30000,
|
||||
requestTimeout: 60000,
|
||||
taskTtl: 60000,
|
||||
oauthClientId: "my-client-id",
|
||||
oauthClientSecret: "super-secret-value",
|
||||
oauthScopes: "read write",
|
||||
|
|
@ -67,6 +68,7 @@ export const EmptySettings: Story = {
|
|||
metadata: [],
|
||||
connectionTimeout: 30000,
|
||||
requestTimeout: 60000,
|
||||
taskTtl: 60000,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const initialSettings: InspectorServerSettings = {
|
|||
metadata: [{ key: "userId", value: "u-1" }],
|
||||
connectionTimeout: 30000,
|
||||
requestTimeout: 60000,
|
||||
taskTtl: 60000,
|
||||
oauthClientId: "cid",
|
||||
oauthClientSecret: "secret",
|
||||
oauthScopes: "read",
|
||||
|
|
@ -19,6 +20,7 @@ const emptySettings: InspectorServerSettings = {
|
|||
metadata: [],
|
||||
connectionTimeout: 30000,
|
||||
requestTimeout: 60000,
|
||||
taskTtl: 60000,
|
||||
};
|
||||
|
||||
describe("ServerSettingsModal", () => {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export function ServerSettingsModal({
|
|||
}
|
||||
|
||||
function handleTimeoutChange(
|
||||
field: "connectionTimeout" | "requestTimeout",
|
||||
field: "connectionTimeout" | "requestTimeout" | "taskTtl",
|
||||
value: number,
|
||||
) {
|
||||
onSettingsChange({ ...settings, [field]: value });
|
||||
|
|
|
|||
|
|
@ -10,8 +10,11 @@ const meta: Meta<typeof ToolDetailPanel> = {
|
|||
onFormChange: fn(),
|
||||
onExecute: fn(),
|
||||
onCancel: fn(),
|
||||
onRunAsTaskChange: fn(),
|
||||
formValues: {},
|
||||
isExecuting: false,
|
||||
serverSupportsTaskToolCalls: false,
|
||||
runAsTask: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -142,3 +145,45 @@ export const WithProgress: Story = {
|
|||
formValues: { batchSize: 100 },
|
||||
},
|
||||
};
|
||||
|
||||
const optionalTaskTool: Tool = {
|
||||
name: "summarize_corpus",
|
||||
description: "Summarizes a large corpus — can run as a background task",
|
||||
execution: { taskSupport: "optional" },
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
corpus: { type: "string", description: "Text to summarize" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const requiredTaskTool: Tool = {
|
||||
name: "train_model",
|
||||
description: "Long-running training job — always runs as a task",
|
||||
execution: { taskSupport: "required" },
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
dataset: { type: "string", description: "Dataset id" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Server supports task tool calls + tool is `optional`: the toggle renders
|
||||
// enabled and off by default.
|
||||
export const RunAsTaskOptional: Story = {
|
||||
args: {
|
||||
tool: optionalTaskTool,
|
||||
serverSupportsTaskToolCalls: true,
|
||||
runAsTask: false,
|
||||
},
|
||||
};
|
||||
|
||||
// Tool is `required`: the toggle is forced on and disabled.
|
||||
export const RunAsTaskRequired: Story = {
|
||||
args: {
|
||||
tool: requiredTaskTool,
|
||||
serverSupportsTaskToolCalls: true,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@ const annotatedTool: Tool = {
|
|||
const baseProps = {
|
||||
formValues: {},
|
||||
isExecuting: false,
|
||||
serverSupportsTaskToolCalls: false,
|
||||
runAsTask: false,
|
||||
onRunAsTaskChange: vi.fn(),
|
||||
onFormChange: vi.fn(),
|
||||
onExecute: vi.fn(),
|
||||
onCancel: vi.fn(),
|
||||
|
|
@ -204,4 +207,144 @@ describe("ToolDetailPanel", () => {
|
|||
await user.type(input, "hi");
|
||||
expect(onFormChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("Run as task toggle", () => {
|
||||
const toolWithSupport = (
|
||||
taskSupport: "forbidden" | "optional" | "required",
|
||||
): Tool => ({
|
||||
name: "run_job",
|
||||
execution: { taskSupport },
|
||||
inputSchema: { type: "object", properties: {} },
|
||||
});
|
||||
|
||||
it("hides the toggle when the server doesn't support task tool calls", () => {
|
||||
renderWithMantine(
|
||||
<ToolDetailPanel
|
||||
{...baseProps}
|
||||
tool={toolWithSupport("optional")}
|
||||
serverSupportsTaskToolCalls={false}
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByLabelText("Run as task")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("hides the toggle for a task-forbidden tool even when the server supports tasks", () => {
|
||||
renderWithMantine(
|
||||
<ToolDetailPanel
|
||||
{...baseProps}
|
||||
tool={toolWithSupport("forbidden")}
|
||||
serverSupportsTaskToolCalls={true}
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByLabelText("Run as task")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows an unchecked, enabled toggle for an optional tool (off by default)", () => {
|
||||
renderWithMantine(
|
||||
<ToolDetailPanel
|
||||
{...baseProps}
|
||||
tool={toolWithSupport("optional")}
|
||||
serverSupportsTaskToolCalls={true}
|
||||
runAsTask={false}
|
||||
/>,
|
||||
);
|
||||
const toggle = screen.getByLabelText("Run as task");
|
||||
expect(toggle).not.toBeChecked();
|
||||
expect(toggle).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it("reflects the runAsTask prop for an optional tool", () => {
|
||||
renderWithMantine(
|
||||
<ToolDetailPanel
|
||||
{...baseProps}
|
||||
tool={toolWithSupport("optional")}
|
||||
serverSupportsTaskToolCalls={true}
|
||||
runAsTask={true}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByLabelText("Run as task")).toBeChecked();
|
||||
});
|
||||
|
||||
it("forces the toggle on and disabled for a required tool", () => {
|
||||
renderWithMantine(
|
||||
<ToolDetailPanel
|
||||
{...baseProps}
|
||||
tool={toolWithSupport("required")}
|
||||
serverSupportsTaskToolCalls={true}
|
||||
runAsTask={false}
|
||||
/>,
|
||||
);
|
||||
const toggle = screen.getByLabelText("Run as task");
|
||||
expect(toggle).toBeChecked();
|
||||
expect(toggle).toBeDisabled();
|
||||
});
|
||||
|
||||
it("invokes onRunAsTaskChange when an optional toggle is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onRunAsTaskChange = vi.fn();
|
||||
renderWithMantine(
|
||||
<ToolDetailPanel
|
||||
{...baseProps}
|
||||
tool={toolWithSupport("optional")}
|
||||
serverSupportsTaskToolCalls={true}
|
||||
runAsTask={false}
|
||||
onRunAsTaskChange={onRunAsTaskChange}
|
||||
/>,
|
||||
);
|
||||
await user.click(screen.getByLabelText("Run as task"));
|
||||
expect(onRunAsTaskChange).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it("passes the effective run-as-task decision to onExecute", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onExecute = vi.fn();
|
||||
// optional + runAsTask=true → effective true
|
||||
renderWithMantine(
|
||||
<ToolDetailPanel
|
||||
{...baseProps}
|
||||
tool={toolWithSupport("optional")}
|
||||
serverSupportsTaskToolCalls={true}
|
||||
runAsTask={true}
|
||||
onExecute={onExecute}
|
||||
/>,
|
||||
);
|
||||
await user.click(screen.getByRole("button", { name: "Execute Tool" }));
|
||||
expect(onExecute).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it("passes runAsTask=true to onExecute for a required tool regardless of the prop", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onExecute = vi.fn();
|
||||
renderWithMantine(
|
||||
<ToolDetailPanel
|
||||
{...baseProps}
|
||||
tool={toolWithSupport("required")}
|
||||
serverSupportsTaskToolCalls={true}
|
||||
runAsTask={false}
|
||||
onExecute={onExecute}
|
||||
/>,
|
||||
);
|
||||
await user.click(screen.getByRole("button", { name: "Execute Tool" }));
|
||||
expect(onExecute).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it("gates onExecute to false when the server lacks task-tool-call support, even with a stale runAsTask", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onExecute = vi.fn();
|
||||
// The toggle is hidden (server doesn't support it), but a leftover
|
||||
// runAsTask=true must not leak into the effective decision.
|
||||
renderWithMantine(
|
||||
<ToolDetailPanel
|
||||
{...baseProps}
|
||||
tool={toolWithSupport("optional")}
|
||||
serverSupportsTaskToolCalls={false}
|
||||
runAsTask={true}
|
||||
onExecute={onExecute}
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByLabelText("Run as task")).not.toBeInTheDocument();
|
||||
await user.click(screen.getByRole("button", { name: "Execute Tool" }));
|
||||
expect(onExecute).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
Image,
|
||||
ScrollArea,
|
||||
Stack,
|
||||
Switch,
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
import type {
|
||||
|
|
@ -27,8 +28,14 @@ export interface ToolDetailPanelProps {
|
|||
formValues: Record<string, unknown>;
|
||||
isExecuting: boolean;
|
||||
progress?: ToolProgress;
|
||||
/** Whether the connected server advertises task-augmented tool calls. */
|
||||
serverSupportsTaskToolCalls: boolean;
|
||||
/** User's "Run as task" preference (only meaningful for `optional` tools). */
|
||||
runAsTask: boolean;
|
||||
onRunAsTaskChange: (value: boolean) => void;
|
||||
onFormChange: (values: Record<string, unknown>) => void;
|
||||
onExecute: () => void;
|
||||
/** Receives the effective run-as-task decision for this execution. */
|
||||
onExecute: (runAsTask: boolean) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +102,24 @@ const CancelButton = Button.withProps({
|
|||
color: "red",
|
||||
});
|
||||
|
||||
// Left-aligned row hosting the "Run as task" toggle, above the execute footer.
|
||||
const TaskToggleRow = Group.withProps({
|
||||
justify: "flex-start",
|
||||
flex: "0 0 auto",
|
||||
});
|
||||
|
||||
const RunAsTaskSwitch = Switch.withProps({
|
||||
size: "sm",
|
||||
label: "Run as task",
|
||||
});
|
||||
|
||||
// A tool's per-tool task support, defaulting to "forbidden" (the SDK default
|
||||
// when `execution` is absent) so tools that say nothing can't be run as tasks.
|
||||
type TaskSupport = "forbidden" | "optional" | "required";
|
||||
function getTaskSupport(tool: Tool): TaskSupport {
|
||||
return tool.execution?.taskSupport ?? "forbidden";
|
||||
}
|
||||
|
||||
function hasAnyAnnotation(annotations?: ToolAnnotations): boolean {
|
||||
return !!(
|
||||
annotations &&
|
||||
|
|
@ -110,6 +135,9 @@ export function ToolDetailPanel({
|
|||
formValues,
|
||||
isExecuting,
|
||||
progress,
|
||||
serverSupportsTaskToolCalls,
|
||||
runAsTask,
|
||||
onRunAsTaskChange,
|
||||
onFormChange,
|
||||
onExecute,
|
||||
onCancel,
|
||||
|
|
@ -117,6 +145,21 @@ export function ToolDetailPanel({
|
|||
const { name, title, description, icons, annotations, inputSchema } = tool;
|
||||
const iconSrc = icons?.[0]?.src;
|
||||
|
||||
// Show the toggle only when the server supports task tool calls and the tool
|
||||
// doesn't forbid them. `required` tools are forced on (checked + disabled);
|
||||
// `optional` tools follow the user's `runAsTask` choice.
|
||||
const taskSupport = getTaskSupport(tool);
|
||||
const showRunAsTask =
|
||||
serverSupportsTaskToolCalls && taskSupport !== "forbidden";
|
||||
// Gate the effective decision on `showRunAsTask` (which includes
|
||||
// `serverSupportsTaskToolCalls`): a stale `runAsTask`/`required` value must
|
||||
// not route through callToolStream when the toggle is hidden because the
|
||||
// server doesn't advertise task tool calls. Per spec, a tool's taskSupport is
|
||||
// only considered when the server advertises `tasks.requests.tools.call`.
|
||||
const effectiveRunAsTask =
|
||||
showRunAsTask &&
|
||||
(taskSupport === "required" || (taskSupport === "optional" && runAsTask));
|
||||
|
||||
return (
|
||||
<PanelStack>
|
||||
<PinnedHeader>
|
||||
|
|
@ -159,11 +202,21 @@ export function ToolDetailPanel({
|
|||
</BodyStack>
|
||||
</BodyScroll>
|
||||
|
||||
{showRunAsTask && (
|
||||
<TaskToggleRow>
|
||||
<RunAsTaskSwitch
|
||||
checked={effectiveRunAsTask}
|
||||
disabled={isExecuting || taskSupport === "required"}
|
||||
onChange={(event) => onRunAsTaskChange(event.currentTarget.checked)}
|
||||
/>
|
||||
</TaskToggleRow>
|
||||
)}
|
||||
|
||||
<FooterRow>
|
||||
{isExecuting && <CancelButton onClick={onCancel}>Cancel</CancelButton>}
|
||||
<Button
|
||||
size="md"
|
||||
onClick={onExecute}
|
||||
onClick={() => onExecute(effectiveRunAsTask)}
|
||||
disabled={isExecuting}
|
||||
loading={isExecuting}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const meta: Meta<typeof ToolsScreen> = {
|
|||
parameters: { layout: "fullscreen" },
|
||||
args: {
|
||||
listChanged: false,
|
||||
serverSupportsTaskToolCalls: false,
|
||||
ui: EMPTY_TOOLS_UI,
|
||||
onUiChange: fn(),
|
||||
onRefreshList: fn(),
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const tools: Tool[] = [
|
|||
const baseProps = {
|
||||
tools,
|
||||
listChanged: false,
|
||||
serverSupportsTaskToolCalls: false,
|
||||
ui: EMPTY_TOOLS_UI,
|
||||
onUiChange: vi.fn(),
|
||||
onRefreshList: vi.fn(),
|
||||
|
|
@ -91,7 +92,7 @@ describe("ToolsScreen", () => {
|
|||
await user.clear(field);
|
||||
await user.type(field, "slow");
|
||||
await user.click(screen.getByRole("button", { name: /Execute/ }));
|
||||
expect(onCallTool).toHaveBeenCalledWith("gamma", { mode: "slow" });
|
||||
expect(onCallTool).toHaveBeenCalledWith("gamma", { mode: "slow" }, false);
|
||||
});
|
||||
|
||||
it("renders selection and result from props (persisted across navigation)", () => {
|
||||
|
|
@ -133,7 +134,7 @@ describe("ToolsScreen", () => {
|
|||
renderWithMantine(<ControlledToolsScreen onCallTool={onCallTool} />);
|
||||
await user.click(screen.getByText("alpha"));
|
||||
await user.click(screen.getByRole("button", { name: /Execute/ }));
|
||||
expect(onCallTool).toHaveBeenCalledWith("alpha", {});
|
||||
expect(onCallTool).toHaveBeenCalledWith("alpha", {}, false);
|
||||
});
|
||||
|
||||
it("seeds schema defaults so untouched fields are sent on Execute", async () => {
|
||||
|
|
@ -143,7 +144,7 @@ describe("ToolsScreen", () => {
|
|||
await user.click(screen.getByText("gamma"));
|
||||
// Execute without editing the form: the default must still be sent.
|
||||
await user.click(screen.getByRole("button", { name: /Execute/ }));
|
||||
expect(onCallTool).toHaveBeenCalledWith("gamma", { mode: "fast" });
|
||||
expect(onCallTool).toHaveBeenCalledWith("gamma", { mode: "fast" }, false);
|
||||
});
|
||||
|
||||
it("invokes onClearResult when Clear is clicked on the result panel", async () => {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@ export interface ToolsUiState {
|
|||
selectedToolName?: string;
|
||||
formValues: Record<string, unknown>;
|
||||
search: string;
|
||||
// Screen-level "Run as task" toggle, shared across tools (not per-tool):
|
||||
// selecting a different tool keeps the current value. Persists across tab
|
||||
// navigation like the rest of the UI state, and is only honored for the
|
||||
// selected tool when its `execution.taskSupport` is "optional" (a "required"
|
||||
// tool is always run as a task, "forbidden" never) — see ToolDetailPanel.
|
||||
runAsTask: boolean;
|
||||
}
|
||||
|
||||
export interface ToolsScreenProps {
|
||||
|
|
@ -29,9 +35,15 @@ export interface ToolsScreenProps {
|
|||
callState?: ToolCallState;
|
||||
ui: ToolsUiState;
|
||||
listChanged: boolean;
|
||||
/** Whether the connected server advertises task-augmented tool calls. */
|
||||
serverSupportsTaskToolCalls: boolean;
|
||||
onUiChange: (next: ToolsUiState) => void;
|
||||
onRefreshList: () => void;
|
||||
onCallTool: (name: string, args: Record<string, unknown>) => void;
|
||||
onCallTool: (
|
||||
name: string,
|
||||
args: Record<string, unknown>,
|
||||
runAsTask?: boolean,
|
||||
) => void;
|
||||
onCancelCall?: () => void;
|
||||
onClearResult?: () => void;
|
||||
}
|
||||
|
|
@ -92,6 +104,7 @@ export function ToolsScreen({
|
|||
callState,
|
||||
ui,
|
||||
listChanged,
|
||||
serverSupportsTaskToolCalls,
|
||||
onUiChange,
|
||||
onRefreshList,
|
||||
onCallTool,
|
||||
|
|
@ -139,10 +152,17 @@ export function ToolsScreen({
|
|||
formValues={formValues}
|
||||
isExecuting={isExecuting}
|
||||
progress={callState?.progress}
|
||||
serverSupportsTaskToolCalls={serverSupportsTaskToolCalls}
|
||||
runAsTask={ui.runAsTask}
|
||||
onRunAsTaskChange={(value) =>
|
||||
onUiChange({ ...ui, runAsTask: value })
|
||||
}
|
||||
onFormChange={(values) =>
|
||||
onUiChange({ ...ui, formValues: values })
|
||||
}
|
||||
onExecute={() => onCallTool(selectedTool.name, formValues)}
|
||||
onExecute={(runAsTask) =>
|
||||
onCallTool(selectedTool.name, formValues, runAsTask)
|
||||
}
|
||||
onCancel={() => onCancelCall?.()}
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ export const EMPTY_TOOLS_UI: ToolsUiState = {
|
|||
selectedToolName: undefined,
|
||||
formValues: {},
|
||||
search: "",
|
||||
runAsTask: false,
|
||||
};
|
||||
|
||||
export const EMPTY_PROMPTS_UI: PromptsUiState = {
|
||||
|
|
|
|||
|
|
@ -356,6 +356,7 @@ const meta: Meta<typeof InspectorView> = {
|
|||
onServerEdit: fn(),
|
||||
onServerClone: fn(),
|
||||
onServerRemove: fn(),
|
||||
serverSupportsTaskToolCalls: false,
|
||||
onToolsUiChange: fn(),
|
||||
onCallTool: fn(),
|
||||
onRefreshTools: fn(),
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ function makeProps(
|
|||
onServerEdit: vi.fn(),
|
||||
onServerClone: vi.fn(),
|
||||
onServerRemove: vi.fn(),
|
||||
serverSupportsTaskToolCalls: false,
|
||||
onToolsUiChange: vi.fn(),
|
||||
onCallTool: vi.fn(),
|
||||
onRefreshTools: vi.fn(),
|
||||
|
|
|
|||
|
|
@ -264,8 +264,14 @@ export interface InspectorViewProps {
|
|||
|
||||
// Per-primitive actions (route to `inspectorClient` methods / hook refresh).
|
||||
// Each `on{Screen}UiChange` persists that screen's lifted UI state (#1417).
|
||||
/** Whether the connected server advertises task-augmented tool calls. */
|
||||
serverSupportsTaskToolCalls: boolean;
|
||||
onToolsUiChange: (next: ToolsUiState) => void;
|
||||
onCallTool: (name: string, args: Record<string, unknown>) => void;
|
||||
onCallTool: (
|
||||
name: string,
|
||||
args: Record<string, unknown>,
|
||||
runAsTask?: boolean,
|
||||
) => void;
|
||||
onCancelToolCall?: () => void;
|
||||
onClearToolResult?: () => void;
|
||||
onRefreshTools: () => void;
|
||||
|
|
@ -362,6 +368,7 @@ export function InspectorView({
|
|||
onServerEdit,
|
||||
onServerClone,
|
||||
onServerRemove,
|
||||
serverSupportsTaskToolCalls,
|
||||
onToolsUiChange,
|
||||
onCallTool,
|
||||
onCancelToolCall,
|
||||
|
|
@ -524,6 +531,7 @@ export function InspectorView({
|
|||
callState={toolCallState}
|
||||
ui={toolsUi}
|
||||
listChanged={false}
|
||||
serverSupportsTaskToolCalls={serverSupportsTaskToolCalls}
|
||||
onUiChange={onToolsUiChange}
|
||||
onRefreshList={onRefreshTools}
|
||||
onCallTool={onCallTool}
|
||||
|
|
|
|||
Loading…
Reference in a new issue