diff --git a/surfsense_web/components/tool-ui/confluence/create-confluence-page.tsx b/surfsense_web/components/tool-ui/confluence/create-confluence-page.tsx index d22f317b8..b60817536 100644 --- a/surfsense_web/components/tool-ui/confluence/create-confluence-page.tsx +++ b/surfsense_web/components/tool-ui/confluence/create-confluence-page.tsx @@ -15,6 +15,8 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { InterruptResult, HitlDecision } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface ConfluenceAccount { @@ -30,24 +32,10 @@ interface ConfluenceSpace { name: string; } -interface InterruptResult { - __interrupt__: true; - __decided__?: "approve" | "reject" | "edit"; - __completed__?: boolean; - action_requests: Array<{ - name: string; - args: Record; - }>; - review_configs: Array<{ - action_name: string; - allowed_decisions: Array<"approve" | "edit" | "reject">; - }>; - interrupt_type?: string; - context?: { - accounts?: ConfluenceAccount[]; - spaces?: ConfluenceSpace[]; - error?: string; - }; +interface CreateConfluencePageInterruptContext { + accounts?: ConfluenceAccount[]; + spaces?: ConfluenceSpace[]; + error?: string; } interface SuccessResult { @@ -76,21 +64,12 @@ interface InsufficientPermissionsResult { } type CreateConfluencePageResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | AuthErrorResult | InsufficientPermissionsResult; -function isInterruptResult(result: unknown): result is InterruptResult { - return ( - typeof result === "object" && - result !== null && - "__interrupt__" in result && - (result as InterruptResult).__interrupt__ === true - ); -} - function isErrorResult(result: unknown): result is ErrorResult { return ( typeof result === "object" && @@ -124,12 +103,8 @@ function ApprovalCard({ onDecision, }: { args: { title: string; content?: string; space_id?: string }; - interruptData: InterruptResult; - onDecision: (decision: { - type: "approve" | "reject" | "edit"; - message?: string; - edited_action?: { name: string; args: Record }; - }) => void; + interruptData: InterruptResult; + onDecision: (decision: HitlDecision) => void; }) { const { phase, setProcessing, setRejected } = useHitlPhase(interruptData); const [isPanelOpen, setIsPanelOpen] = useState(false); @@ -464,18 +439,16 @@ export const CreateConfluencePageToolUI = ({ { title: string; content?: string; space_id?: string }, CreateConfluencePageResult >) => { + const { dispatch } = useHitlDecision(); + if (!result) return null; if (isInterruptResult(result)) { return ( { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} + interruptData={result as InterruptResult} + onDecision={(decision) => dispatch([decision])} /> ); } diff --git a/surfsense_web/components/tool-ui/confluence/delete-confluence-page.tsx b/surfsense_web/components/tool-ui/confluence/delete-confluence-page.tsx index 258f259f0..ca45e26ee 100644 --- a/surfsense_web/components/tool-ui/confluence/delete-confluence-page.tsx +++ b/surfsense_web/components/tool-ui/confluence/delete-confluence-page.tsx @@ -6,38 +6,26 @@ import { useCallback, useEffect, useState } from "react"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { InterruptResult, HitlDecision } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; -interface InterruptResult { - __interrupt__: true; - __decided__?: "approve" | "reject"; - __completed__?: boolean; - action_requests: Array<{ +interface DeleteConfluencePageInterruptContext { + account?: { + id: number; name: string; - args: Record; - }>; - review_configs: Array<{ - action_name: string; - allowed_decisions: Array<"approve" | "reject">; - }>; - interrupt_type?: string; - context?: { - account?: { - id: number; - name: string; - base_url: string; - auth_expired?: boolean; - }; - page?: { - page_id: string; - page_title: string; - space_id: string; - connector_id?: number; - document_id?: number; - indexed_at?: string; - }; - error?: string; + base_url: string; + auth_expired?: boolean; }; + page?: { + page_id: string; + page_title: string; + space_id: string; + connector_id?: number; + document_id?: number; + indexed_at?: string; + }; + error?: string; } interface SuccessResult { @@ -77,7 +65,7 @@ interface InsufficientPermissionsResult { } type DeleteConfluencePageResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | NotFoundResult @@ -85,15 +73,6 @@ type DeleteConfluencePageResult = | AuthErrorResult | InsufficientPermissionsResult; -function isInterruptResult(result: unknown): result is InterruptResult { - return ( - typeof result === "object" && - result !== null && - "__interrupt__" in result && - (result as InterruptResult).__interrupt__ === true - ); -} - function isErrorResult(result: unknown): result is ErrorResult { return ( typeof result === "object" && @@ -145,12 +124,8 @@ function ApprovalCard({ interruptData, onDecision, }: { - interruptData: InterruptResult; - onDecision: (decision: { - type: "approve" | "reject"; - message?: string; - edited_action?: { name: string; args: Record }; - }) => void; + interruptData: InterruptResult; + onDecision: (decision: HitlDecision) => void; }) { const { phase, setProcessing, setRejected } = useHitlPhase(interruptData); const [deleteFromKb, setDeleteFromKb] = useState(false); @@ -402,18 +377,15 @@ export const DeleteConfluencePageToolUI = ({ { page_title_or_id: string; delete_from_kb?: boolean }, DeleteConfluencePageResult >) => { + const { dispatch } = useHitlDecision(); + if (!result) return null; if (isInterruptResult(result)) { return ( { - const event = new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }); - window.dispatchEvent(event); - }} + interruptData={result as InterruptResult} + onDecision={(decision) => dispatch([decision])} /> ); } diff --git a/surfsense_web/components/tool-ui/confluence/update-confluence-page.tsx b/surfsense_web/components/tool-ui/confluence/update-confluence-page.tsx index 8bc0772a4..7f75f9ac8 100644 --- a/surfsense_web/components/tool-ui/confluence/update-confluence-page.tsx +++ b/surfsense_web/components/tool-ui/confluence/update-confluence-page.tsx @@ -8,39 +8,27 @@ import { openHitlEditPanelAtom } from "@/atoms/chat/hitl-edit-panel.atom"; import { PlateEditor } from "@/components/editor/plate-editor"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; import { Button } from "@/components/ui/button"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { InterruptResult, HitlDecision } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; -interface InterruptResult { - __interrupt__: true; - __decided__?: "approve" | "reject" | "edit"; - __completed__?: boolean; - action_requests: Array<{ +interface UpdateConfluencePageInterruptContext { + account?: { + id: number; name: string; - args: Record; - }>; - review_configs: Array<{ - action_name: string; - allowed_decisions: Array<"approve" | "edit" | "reject">; - }>; - interrupt_type?: string; - context?: { - account?: { - id: number; - name: string; - base_url: string; - auth_expired?: boolean; - }; - page?: { - page_id: string; - page_title: string; - space_id: string; - body: string; - version: number; - document_id: number; - indexed_at?: string; - }; - error?: string; + base_url: string; + auth_expired?: boolean; }; + page?: { + page_id: string; + page_title: string; + space_id: string; + body: string; + version: number; + document_id: number; + indexed_at?: string; + }; + error?: string; } interface SuccessResult { @@ -74,22 +62,13 @@ interface InsufficientPermissionsResult { } type UpdateConfluencePageResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | NotFoundResult | AuthErrorResult | InsufficientPermissionsResult; -function isInterruptResult(result: unknown): result is InterruptResult { - return ( - typeof result === "object" && - result !== null && - "__interrupt__" in result && - (result as InterruptResult).__interrupt__ === true - ); -} - function isErrorResult(result: unknown): result is ErrorResult { return ( typeof result === "object" && @@ -136,12 +115,8 @@ function ApprovalCard({ new_title?: string; new_content?: string; }; - interruptData: InterruptResult; - onDecision: (decision: { - type: "approve" | "reject" | "edit"; - message?: string; - edited_action?: { name: string; args: Record }; - }) => void; + interruptData: InterruptResult; + onDecision: (decision: HitlDecision) => void; }) { const { phase, setProcessing, setRejected } = useHitlPhase(interruptData); @@ -502,18 +477,16 @@ export const UpdateConfluencePageToolUI = ({ }, UpdateConfluencePageResult >) => { + const { dispatch } = useHitlDecision(); + if (!result) return null; if (isInterruptResult(result)) { return ( { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} + interruptData={result as InterruptResult} + onDecision={(decision) => dispatch([decision])} /> ); } diff --git a/surfsense_web/components/tool-ui/jira/create-jira-issue.tsx b/surfsense_web/components/tool-ui/jira/create-jira-issue.tsx index cabb0d840..4561cd9dd 100644 --- a/surfsense_web/components/tool-ui/jira/create-jira-issue.tsx +++ b/surfsense_web/components/tool-ui/jira/create-jira-issue.tsx @@ -15,6 +15,8 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { InterruptResult, HitlDecision } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface JiraAccount { @@ -40,26 +42,12 @@ interface JiraPriority { name: string; } -interface InterruptResult { - __interrupt__: true; - __decided__?: "approve" | "reject" | "edit"; - __completed__?: boolean; - action_requests: Array<{ - name: string; - args: Record; - }>; - review_configs: Array<{ - action_name: string; - allowed_decisions: Array<"approve" | "edit" | "reject">; - }>; - interrupt_type?: string; - context?: { - accounts?: JiraAccount[]; - projects?: JiraProject[]; - issue_types?: JiraIssueType[]; - priorities?: JiraPriority[]; - error?: string; - }; +interface CreateJiraIssueInterruptContext { + accounts?: JiraAccount[]; + projects?: JiraProject[]; + issue_types?: JiraIssueType[]; + priorities?: JiraPriority[]; + error?: string; } interface SuccessResult { @@ -88,21 +76,12 @@ interface InsufficientPermissionsResult { } type CreateJiraIssueResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | AuthErrorResult | InsufficientPermissionsResult; -function isInterruptResult(result: unknown): result is InterruptResult { - return ( - typeof result === "object" && - result !== null && - "__interrupt__" in result && - (result as InterruptResult).__interrupt__ === true - ); -} - function isErrorResult(result: unknown): result is ErrorResult { return ( typeof result === "object" && @@ -142,12 +121,8 @@ function ApprovalCard({ description?: string; priority?: string; }; - interruptData: InterruptResult; - onDecision: (decision: { - type: "approve" | "reject" | "edit"; - message?: string; - edited_action?: { name: string; args: Record }; - }) => void; + interruptData: InterruptResult; + onDecision: (decision: HitlDecision) => void; }) { const { phase, setProcessing, setRejected } = useHitlPhase(interruptData); const [isPanelOpen, setIsPanelOpen] = useState(false); @@ -549,18 +524,16 @@ export const CreateJiraIssueToolUI = ({ }, CreateJiraIssueResult >) => { + const { dispatch } = useHitlDecision(); + if (!result) return null; if (isInterruptResult(result)) { return ( { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} + interruptData={result as InterruptResult} + onDecision={(decision) => dispatch([decision])} /> ); } diff --git a/surfsense_web/components/tool-ui/jira/delete-jira-issue.tsx b/surfsense_web/components/tool-ui/jira/delete-jira-issue.tsx index 2cad2be46..cb8da37ea 100644 --- a/surfsense_web/components/tool-ui/jira/delete-jira-issue.tsx +++ b/surfsense_web/components/tool-ui/jira/delete-jira-issue.tsx @@ -6,6 +6,8 @@ import { useCallback, useEffect, useState } from "react"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { InterruptResult, HitlDecision } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface JiraAccount { @@ -23,24 +25,10 @@ interface JiraIssue { document_id?: number; } -interface InterruptResult { - __interrupt__: true; - __decided__?: "approve" | "reject"; - __completed__?: boolean; - action_requests: Array<{ - name: string; - args: Record; - }>; - review_configs: Array<{ - action_name: string; - allowed_decisions: Array<"approve" | "reject">; - }>; - interrupt_type?: string; - context?: { - account?: JiraAccount; - issue?: JiraIssue; - error?: string; - }; +interface DeleteJiraIssueInterruptContext { + account?: JiraAccount; + issue?: JiraIssue; + error?: string; } interface SuccessResult { @@ -79,7 +67,7 @@ interface InsufficientPermissionsResult { } type DeleteJiraIssueResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | NotFoundResult @@ -87,15 +75,6 @@ type DeleteJiraIssueResult = | AuthErrorResult | InsufficientPermissionsResult; -function isInterruptResult(result: unknown): result is InterruptResult { - return ( - typeof result === "object" && - result !== null && - "__interrupt__" in result && - (result as InterruptResult).__interrupt__ === true - ); -} - function isErrorResult(result: unknown): result is ErrorResult { return ( typeof result === "object" && @@ -147,12 +126,8 @@ function ApprovalCard({ interruptData, onDecision, }: { - interruptData: InterruptResult; - onDecision: (decision: { - type: "approve" | "reject"; - message?: string; - edited_action?: { name: string; args: Record }; - }) => void; + interruptData: InterruptResult; + onDecision: (decision: HitlDecision) => void; }) { const { phase, setProcessing, setRejected } = useHitlPhase(interruptData); const [deleteFromKb, setDeleteFromKb] = useState(false); @@ -399,18 +374,15 @@ export const DeleteJiraIssueToolUI = ({ { issue_title_or_key: string; delete_from_kb?: boolean }, DeleteJiraIssueResult >) => { + const { dispatch } = useHitlDecision(); + if (!result) return null; if (isInterruptResult(result)) { return ( { - const event = new CustomEvent("hitl-decision", { - detail: { decisions: [decision] }, - }); - window.dispatchEvent(event); - }} + interruptData={result as InterruptResult} + onDecision={(decision) => dispatch([decision])} /> ); } diff --git a/surfsense_web/components/tool-ui/jira/update-jira-issue.tsx b/surfsense_web/components/tool-ui/jira/update-jira-issue.tsx index 4132fad06..24643b542 100644 --- a/surfsense_web/components/tool-ui/jira/update-jira-issue.tsx +++ b/surfsense_web/components/tool-ui/jira/update-jira-issue.tsx @@ -16,6 +16,8 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { InterruptResult, HitlDecision } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface JiraIssue { @@ -43,25 +45,11 @@ interface JiraPriority { name: string; } -interface InterruptResult { - __interrupt__: true; - __decided__?: "approve" | "reject" | "edit"; - __completed__?: boolean; - action_requests: Array<{ - name: string; - args: Record; - }>; - review_configs: Array<{ - action_name: string; - allowed_decisions: Array<"approve" | "edit" | "reject">; - }>; - interrupt_type?: string; - context?: { - account?: JiraAccount; - issue?: JiraIssue; - priorities?: JiraPriority[]; - error?: string; - }; +interface UpdateJiraIssueInterruptContext { + account?: JiraAccount; + issue?: JiraIssue; + priorities?: JiraPriority[]; + error?: string; } interface SuccessResult { @@ -95,22 +83,13 @@ interface InsufficientPermissionsResult { } type UpdateJiraIssueResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | NotFoundResult | AuthErrorResult | InsufficientPermissionsResult; -function isInterruptResult(result: unknown): result is InterruptResult { - return ( - typeof result === "object" && - result !== null && - "__interrupt__" in result && - (result as InterruptResult).__interrupt__ === true - ); -} - function isErrorResult(result: unknown): result is ErrorResult { return ( typeof result === "object" && @@ -158,12 +137,8 @@ function ApprovalCard({ new_description?: string; new_priority?: string; }; - interruptData: InterruptResult; - onDecision: (decision: { - type: "approve" | "reject" | "edit"; - message?: string; - edited_action?: { name: string; args: Record }; - }) => void; + interruptData: InterruptResult; + onDecision: (decision: HitlDecision) => void; }) { const { phase, setProcessing, setRejected } = useHitlPhase(interruptData); @@ -563,18 +538,16 @@ export const UpdateJiraIssueToolUI = ({ }, UpdateJiraIssueResult >) => { + const { dispatch } = useHitlDecision(); + if (!result) return null; if (isInterruptResult(result)) { return ( { - window.dispatchEvent( - new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) - ); - }} + interruptData={result as InterruptResult} + onDecision={(decision) => dispatch([decision])} /> ); }