diff --git a/surfsense_web/components/tool-ui/gmail/create-draft.tsx b/surfsense_web/components/tool-ui/gmail/create-draft.tsx index 97e93fecb..73e5bcd61 100644 --- a/surfsense_web/components/tool-ui/gmail/create-draft.tsx +++ b/surfsense_web/components/tool-ui/gmail/create-draft.tsx @@ -16,6 +16,8 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { HitlDecision, InterruptResult } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface GmailAccount { @@ -25,22 +27,9 @@ interface GmailAccount { auth_expired?: boolean; } -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">; - }>; - context?: { - accounts?: GmailAccount[]; - error?: string; - }; +interface GmailCreateDraftContext { + accounts?: GmailAccount[]; + error?: string; } interface SuccessResult { @@ -68,21 +57,12 @@ interface InsufficientPermissionsResult { } type CreateGmailDraftResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | InsufficientPermissionsResult | AuthErrorResult; -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" && @@ -116,12 +96,8 @@ function ApprovalCard({ onDecision, }: { args: { to: string; subject: string; body: string; cc?: string; bcc?: 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); @@ -473,18 +449,16 @@ export const CreateGmailDraftToolUI = ({ { to: string; subject: string; body: string; cc?: string; bcc?: string }, CreateGmailDraftResult >) => { + 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/gmail/send-email.tsx b/surfsense_web/components/tool-ui/gmail/send-email.tsx index e0e1ffa6c..c95e67d6e 100644 --- a/surfsense_web/components/tool-ui/gmail/send-email.tsx +++ b/surfsense_web/components/tool-ui/gmail/send-email.tsx @@ -16,6 +16,8 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { HitlDecision, InterruptResult } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface GmailAccount { @@ -25,22 +27,9 @@ interface GmailAccount { auth_expired?: boolean; } -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">; - }>; - context?: { - accounts?: GmailAccount[]; - error?: string; - }; +interface GmailSendEmailContext { + accounts?: GmailAccount[]; + error?: string; } interface SuccessResult { @@ -67,21 +56,12 @@ interface InsufficientPermissionsResult { } type SendGmailEmailResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | InsufficientPermissionsResult | AuthErrorResult; -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" && @@ -115,12 +95,8 @@ function ApprovalCard({ onDecision, }: { args: { to: string; subject: string; body: string; cc?: string; bcc?: 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); @@ -471,18 +447,16 @@ export const SendGmailEmailToolUI = ({ { to: string; subject: string; body: string; cc?: string; bcc?: string }, SendGmailEmailResult >) => { + 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/gmail/trash-email.tsx b/surfsense_web/components/tool-ui/gmail/trash-email.tsx index f1640f3fc..b3090a364 100644 --- a/surfsense_web/components/tool-ui/gmail/trash-email.tsx +++ b/surfsense_web/components/tool-ui/gmail/trash-email.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 { HitlDecision, InterruptResult } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface GmailAccount { @@ -25,23 +27,10 @@ interface GmailMessage { 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">; - }>; - context?: { - account?: GmailAccount; - email?: GmailMessage; - error?: string; - }; +interface GmailTrashEmailContext { + account?: GmailAccount; + email?: GmailMessage; + error?: string; } interface SuccessResult { @@ -74,22 +63,13 @@ interface InsufficientPermissionsResult { } type TrashGmailEmailResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | NotFoundResult | InsufficientPermissionsResult | AuthErrorResult; -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" && @@ -134,12 +114,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); @@ -385,18 +361,15 @@ export const TrashGmailEmailToolUI = ({ { email_subject_or_id: string; delete_from_kb?: boolean }, TrashGmailEmailResult >) => { + 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/gmail/update-draft.tsx b/surfsense_web/components/tool-ui/gmail/update-draft.tsx index b3b0d2d21..431744463 100644 --- a/surfsense_web/components/tool-ui/gmail/update-draft.tsx +++ b/surfsense_web/components/tool-ui/gmail/update-draft.tsx @@ -9,6 +9,8 @@ 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 { HitlDecision, InterruptResult } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface GmailAccount { @@ -28,25 +30,12 @@ interface GmailMessage { document_id: number; } -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">; - }>; - context?: { - account?: GmailAccount; - email?: GmailMessage; - draft_id?: string; - existing_body?: string; - error?: string; - }; +interface GmailUpdateDraftContext { + account?: GmailAccount; + email?: GmailMessage; + draft_id?: string; + existing_body?: string; + error?: string; } interface SuccessResult { @@ -78,22 +67,13 @@ interface InsufficientPermissionsResult { } type UpdateGmailDraftResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | NotFoundResult | InsufficientPermissionsResult | AuthErrorResult; -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" && @@ -143,12 +123,8 @@ function ApprovalCard({ cc?: string; bcc?: 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); @@ -522,20 +498,16 @@ export const UpdateGmailDraftToolUI = ({ }, UpdateGmailDraftResult >) => { + 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/google-calendar/create-event.tsx b/surfsense_web/components/tool-ui/google-calendar/create-event.tsx index 37e5028e7..e45c51775 100644 --- a/surfsense_web/components/tool-ui/google-calendar/create-event.tsx +++ b/surfsense_web/components/tool-ui/google-calendar/create-event.tsx @@ -16,6 +16,8 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { isInterruptResult, useHitlDecision } from "@/lib/hitl"; +import type { HitlDecision, InterruptResult } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface GoogleCalendarAccount { @@ -30,24 +32,11 @@ interface CalendarEntry { primary?: boolean; } -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">; - }>; - context?: { - accounts?: GoogleCalendarAccount[]; - calendars?: CalendarEntry[]; - timezone?: string; - error?: string; - }; +interface CalendarCreateEventContext { + accounts?: GoogleCalendarAccount[]; + calendars?: CalendarEntry[]; + timezone?: string; + error?: string; } interface SuccessResult { @@ -75,21 +64,12 @@ interface InsufficientPermissionsResult { } type CreateCalendarEventResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | InsufficientPermissionsResult | AuthErrorResult; -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" && @@ -141,12 +121,8 @@ function ApprovalCard({ location?: string; attendees?: 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); @@ -620,18 +596,16 @@ export const CreateCalendarEventToolUI = ({ }, CreateCalendarEventResult >) => { + 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/google-calendar/delete-event.tsx b/surfsense_web/components/tool-ui/google-calendar/delete-event.tsx index 039c0e3b0..16e0a4112 100644 --- a/surfsense_web/components/tool-ui/google-calendar/delete-event.tsx +++ b/surfsense_web/components/tool-ui/google-calendar/delete-event.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 { HitlDecision, InterruptResult } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface GoogleCalendarAccount { @@ -27,23 +29,10 @@ interface CalendarEvent { indexed_at?: string; } -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">; - }>; - context?: { - account?: GoogleCalendarAccount; - event?: CalendarEvent; - error?: string; - }; +interface CalendarDeleteEventContext { + account?: GoogleCalendarAccount; + event?: CalendarEvent; + error?: string; } interface SuccessResult { @@ -83,7 +72,7 @@ interface InsufficientPermissionsResult { } type DeleteCalendarEventResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | NotFoundResult @@ -91,15 +80,6 @@ type DeleteCalendarEventResult = | InsufficientPermissionsResult | AuthErrorResult; -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" && @@ -162,12 +142,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); @@ -437,18 +413,15 @@ export const DeleteCalendarEventToolUI = ({ { event_title_or_id: string; delete_from_kb?: boolean }, DeleteCalendarEventResult >) => { + 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/google-calendar/update-event.tsx b/surfsense_web/components/tool-ui/google-calendar/update-event.tsx index a2304b179..541c59378 100644 --- a/surfsense_web/components/tool-ui/google-calendar/update-event.tsx +++ b/surfsense_web/components/tool-ui/google-calendar/update-event.tsx @@ -16,6 +16,8 @@ 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 { HitlDecision, InterruptResult } from "@/lib/hitl"; import { useHitlPhase } from "@/hooks/use-hitl-phase"; interface GoogleCalendarAccount { @@ -37,23 +39,10 @@ interface CalendarEvent { indexed_at?: 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">; - }>; - context?: { - account?: GoogleCalendarAccount; - event?: CalendarEvent; - error?: string; - }; +interface CalendarUpdateEventContext { + account?: GoogleCalendarAccount; + event?: CalendarEvent; + error?: string; } interface SuccessResult { @@ -86,22 +75,13 @@ interface InsufficientPermissionsResult { } type UpdateCalendarEventResult = - | InterruptResult + | InterruptResult | SuccessResult | ErrorResult | NotFoundResult | InsufficientPermissionsResult | AuthErrorResult; -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" && @@ -163,12 +143,8 @@ function ApprovalCard({ new_location?: string; new_attendees?: 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 actionArgs = interruptData.action_requests[0]?.args ?? {}; @@ -686,18 +662,16 @@ export const UpdateCalendarEventToolUI = ({ }, UpdateCalendarEventResult >) => { + 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])} /> ); }