diff --git a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx index 0b1369340..58eb58f4b 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx @@ -798,7 +798,7 @@ export default function NewChatPage() { }); } else { const tcId = `interrupt-${action.name}`; - addToolCall(contentPartsState, TOOLS_WITH_UI, tcId, action.name, action.args); + addToolCall(contentPartsState, TOOLS_WITH_UI, tcId, action.name, action.args, true); updateToolCall(contentPartsState, tcId, { result: { __interrupt__: true, ...interruptData }, }); @@ -1125,7 +1125,7 @@ export default function NewChatPage() { }); } else { const tcId = `interrupt-${action.name}`; - addToolCall(contentPartsState, TOOLS_WITH_UI, tcId, action.name, action.args); + addToolCall(contentPartsState, TOOLS_WITH_UI, tcId, action.name, action.args, true); updateToolCall(contentPartsState, tcId, { result: { __interrupt__: true, diff --git a/surfsense_web/components/assistant-ui/tool-fallback.tsx b/surfsense_web/components/assistant-ui/tool-fallback.tsx index b658dba6d..d9833b387 100644 --- a/surfsense_web/components/assistant-ui/tool-fallback.tsx +++ b/surfsense_web/components/assistant-ui/tool-fallback.tsx @@ -1,14 +1,16 @@ import type { ToolCallMessagePartComponent } from "@assistant-ui/react"; import { CheckIcon, ChevronDownIcon, ChevronUpIcon, XCircleIcon } from "lucide-react"; import { useMemo, useState } from "react"; +import { GenericHitlApprovalToolUI } from "@/components/tool-ui/generic-hitl-approval"; import { getToolIcon } from "@/contracts/enums/toolIcons"; +import { isInterruptResult } from "@/lib/hitl"; import { cn } from "@/lib/utils"; function formatToolName(name: string): string { return name.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); } -export const ToolFallback: ToolCallMessagePartComponent = ({ +const DefaultToolFallbackInner: ToolCallMessagePartComponent = ({ toolName, argsText, result, @@ -145,3 +147,10 @@ export const ToolFallback: ToolCallMessagePartComponent = ({ ); }; + +export const ToolFallback: ToolCallMessagePartComponent = (props) => { + if (isInterruptResult(props.result)) { + return ; + } + return ; +}; diff --git a/surfsense_web/components/tool-ui/generic-hitl-approval.tsx b/surfsense_web/components/tool-ui/generic-hitl-approval.tsx new file mode 100644 index 000000000..48d0d3764 --- /dev/null +++ b/surfsense_web/components/tool-ui/generic-hitl-approval.tsx @@ -0,0 +1,268 @@ +"use client"; + +import type { ToolCallMessagePartComponent } from "@assistant-ui/react"; +import { CornerDownLeftIcon, ShieldAlertIcon, ShieldCheckIcon } from "lucide-react"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { TextShimmerLoader } from "@/components/prompt-kit/loader"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { useHitlPhase } from "@/hooks/use-hitl-phase"; +import { connectorsApiService } from "@/lib/apis/connectors-api.service"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { HitlDecision, InterruptResult } from "@/lib/hitl"; + +function ParamEditor({ + params, + onChange, + disabled, +}: { + params: Record; + onChange: (updated: Record) => void; + disabled: boolean; +}) { + const entries = Object.entries(params); + if (entries.length === 0) return null; + + return ( +
+ {entries.map(([key, value]) => { + const strValue = value == null ? "" : String(value); + const isLong = strValue.length > 120; + const fieldId = `hitl-param-${key}`; + + return ( +
+ + {isLong ? ( +