"use client"; import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useSetAtom } from "jotai"; import { ArrowRightIcon, ClockIcon, CornerDownLeftIcon, MapPinIcon, Pen, UsersIcon, } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import type { ExtraField } from "@/atoms/chat/hitl-edit-panel.atom"; 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 { useHitlPhase } from "@/hooks/use-hitl-phase"; interface GoogleCalendarAccount { id: number; name: string; auth_expired?: boolean; } interface CalendarEvent { event_id: string; summary: string; start: string; end: string; description?: string; location?: string; attendees?: Array<{ email: string }>; calendar_id: string; document_id: number; 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 SuccessResult { status: "success"; event_id: string; html_link?: string; message?: string; } interface ErrorResult { status: "error"; message: string; } interface NotFoundResult { status: "not_found"; message: string; } interface AuthErrorResult { status: "auth_error"; message: string; connector_type?: string; } interface InsufficientPermissionsResult { status: "insufficient_permissions"; connector_id: number; message: string; } type UpdateCalendarEventResult = | 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" && result !== null && "status" in result && (result as ErrorResult).status === "error" ); } function isNotFoundResult(result: unknown): result is NotFoundResult { return ( typeof result === "object" && result !== null && "status" in result && (result as NotFoundResult).status === "not_found" ); } function isAuthErrorResult(result: unknown): result is AuthErrorResult { return ( typeof result === "object" && result !== null && "status" in result && (result as AuthErrorResult).status === "auth_error" ); } function isInsufficientPermissionsResult(result: unknown): result is InsufficientPermissionsResult { return ( typeof result === "object" && result !== null && "status" in result && (result as InsufficientPermissionsResult).status === "insufficient_permissions" ); } function formatDateTime(iso: string): string { try { return new Date(iso).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short", }); } catch { return iso; } } function ApprovalCard({ args, interruptData, onDecision, }: { args: { event_ref: string; new_summary?: string; new_description?: string; new_start_datetime?: string; new_end_datetime?: string; new_location?: string; new_attendees?: string[]; }; interruptData: InterruptResult; onDecision: (decision: { type: "approve" | "reject" | "edit"; message?: string; edited_action?: { name: string; args: Record }; }) => void; }) { const { phase, setProcessing, setRejected } = useHitlPhase(interruptData); const actionArgs = interruptData.action_requests[0]?.args ?? {}; const context = interruptData.context; const account = context?.account; const event = context?.event; const [isPanelOpen, setIsPanelOpen] = useState(false); const [wasEdited, setWasEdited] = useState(false); const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom); const [pendingEdits, setPendingEdits] = useState<{ summary: string; description: string; start_datetime: string; end_datetime: string; location: string; attendees: string; } | null>(null); const reviewConfig = interruptData.review_configs[0]; const allowedDecisions = reviewConfig?.allowed_decisions ?? ["approve", "reject"]; const canEdit = allowedDecisions.includes("edit"); const currentAttendees = event?.attendees?.map((a) => a.email) ?? []; const proposedAttendees = Array.isArray(actionArgs.new_attendees) ? (actionArgs.new_attendees as string[]) : null; const effectiveNewSummary = actionArgs.new_summary ?? args.new_summary; const effectiveNewStartDatetime = actionArgs.new_start_datetime ?? args.new_start_datetime; const effectiveNewEndDatetime = actionArgs.new_end_datetime ?? args.new_end_datetime; const effectiveNewLocation = actionArgs.new_location !== undefined ? actionArgs.new_location : args.new_location; const effectiveNewAttendees = proposedAttendees ?? (Array.isArray(args.new_attendees) ? args.new_attendees : null); const effectiveNewDescription = actionArgs.new_description !== undefined ? actionArgs.new_description : args.new_description; const changes: Array<{ label: string; oldVal: string; newVal: string }> = []; if (effectiveNewSummary && String(effectiveNewSummary) !== (event?.summary ?? "")) { changes.push({ label: "Summary", oldVal: event?.summary ?? "", newVal: String(effectiveNewSummary), }); } if (effectiveNewStartDatetime && String(effectiveNewStartDatetime) !== (event?.start ?? "")) { changes.push({ label: "Start", oldVal: event?.start ? formatDateTime(event.start) : "", newVal: formatDateTime(String(effectiveNewStartDatetime)), }); } if (effectiveNewEndDatetime && String(effectiveNewEndDatetime) !== (event?.end ?? "")) { changes.push({ label: "End", oldVal: event?.end ? formatDateTime(event.end) : "", newVal: formatDateTime(String(effectiveNewEndDatetime)), }); } if ( effectiveNewLocation !== undefined && String(effectiveNewLocation ?? "") !== (event?.location ?? "") ) { changes.push({ label: "Location", oldVal: event?.location ?? "", newVal: String(effectiveNewLocation ?? ""), }); } if (effectiveNewAttendees) { const oldStr = currentAttendees.join(", "); const newStr = effectiveNewAttendees.join(", "); if (oldStr !== newStr) { changes.push({ label: "Attendees", oldVal: oldStr, newVal: newStr }); } } const hasDescriptionChange = effectiveNewDescription !== undefined && String(effectiveNewDescription ?? "") !== (event?.description ?? ""); const buildFinalArgs = useCallback(() => { if (pendingEdits) { const attendeesArr = pendingEdits.attendees ? pendingEdits.attendees .split(",") .map((e) => e.trim()) .filter(Boolean) : null; return { event_id: event?.event_id, document_id: event?.document_id, connector_id: account?.id, new_summary: pendingEdits.summary || null, new_description: pendingEdits.description || null, new_start_datetime: pendingEdits.start_datetime || null, new_end_datetime: pendingEdits.end_datetime || null, new_location: pendingEdits.location || null, new_attendees: attendeesArr, }; } return { event_id: event?.event_id, document_id: event?.document_id, connector_id: account?.id, new_summary: actionArgs.new_summary ?? null, new_description: actionArgs.new_description ?? null, new_start_datetime: actionArgs.new_start_datetime ?? null, new_end_datetime: actionArgs.new_end_datetime ?? null, new_location: actionArgs.new_location ?? null, new_attendees: proposedAttendees ?? null, }; }, [event, account, actionArgs, proposedAttendees, pendingEdits]); const handleApprove = useCallback(() => { if (phase !== "pending" || isPanelOpen) return; if (!allowedDecisions.includes("approve")) return; const isEdited = pendingEdits !== null; setWasEdited(isEdited); setProcessing(); onDecision({ type: isEdited ? "edit" : "approve", edited_action: { name: interruptData.action_requests[0].name, args: buildFinalArgs(), }, }); }, [ phase, isPanelOpen, allowedDecisions, setProcessing, onDecision, interruptData, buildFinalArgs, pendingEdits, ]); useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey && !e.ctrlKey && !e.metaKey) { handleApprove(); } }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [handleApprove]); return (
{/* Header */}

{phase === "rejected" ? "Calendar Event Update Rejected" : phase === "processing" || phase === "complete" ? "Calendar Event Update Approved" : "Update Calendar Event"}

{phase === "processing" ? ( ) : phase === "complete" ? (

{wasEdited ? "Event updated with your changes" : "Event updated"}

) : phase === "rejected" ? (

Event update was cancelled

) : (

Requires your approval to proceed

)}
{phase === "pending" && canEdit && ( )}
{/* Content section */}
{context?.error ? (

{context.error}

) : ( <> {phase === "pending" && account && (

Google Calendar Account

{account.name}
)} {event && (

Current Event

{event.summary}
{(event.start || event.end) && (
{event.start ? formatDateTime(event.start) : ""} {event.start && event.end ? " — " : ""} {event.end ? formatDateTime(event.end) : ""}
)} {event.location && (
{event.location}
)} {currentAttendees.length > 0 && (
{currentAttendees.join(", ")}
)}
)} {/* Proposed Changes - visible in all phases */} {(changes.length > 0 || hasDescriptionChange) && (

Proposed Changes

{changes.map((change) => (
{change.label}
{change.oldVal || "(empty)"} {change.newVal || "(empty)"}
))} {hasDescriptionChange && (
Description
)}
)} {event && changes.length === 0 && !hasDescriptionChange && (

No changes proposed

)} )}
{/* Action buttons - pending only */} {phase === "pending" && ( <>
{allowedDecisions.includes("approve") && ( )} {allowedDecisions.includes("reject") && ( )}
)}
); } function ErrorCard({ result }: { result: ErrorResult }) { return (

Failed to update calendar event

{result.message}

); } function AuthErrorCard({ result }: { result: AuthErrorResult }) { return (

Google Calendar authentication expired

{result.message}

); } function InsufficientPermissionsCard({ result }: { result: InsufficientPermissionsResult }) { return (

Additional Google Calendar permissions required

{result.message}

); } function NotFoundCard({ result }: { result: NotFoundResult }) { return (

Event not found

{result.message}

); } function SuccessCard({ result }: { result: SuccessResult }) { return (

{result.message || "Calendar event updated successfully"}

{result.html_link && ( )}
); } export const UpdateCalendarEventToolUI = ({ args, result, }: ToolCallMessagePartProps< { event_ref: string; new_summary?: string; new_description?: string; new_start_datetime?: string; new_end_datetime?: string; new_location?: string; new_attendees?: string[]; }, UpdateCalendarEventResult >) => { if (!result) return null; if (isInterruptResult(result)) { return ( { window.dispatchEvent( new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) ); }} /> ); } if ( typeof result === "object" && result !== null && "status" in result && (result as { status: string }).status === "rejected" ) { return null; } if (isNotFoundResult(result)) return ; if (isAuthErrorResult(result)) return ; if (isInsufficientPermissionsResult(result)) return ; if (isErrorResult(result)) return ; return ; };