/** * Paperclip agent activity hooks — mirror agent work into Legal-AI UI. */ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { apiRequest } from "./client"; // ── Types ──────────────────────────────────────────────────────── export type PaperclipIssue = { id: string; title: string; status: string; identifier: string; priority: string; assignee_name: string | null; started_at: string | null; completed_at: string | null; created_at: string | null; company_id: string; }; export type PaperclipComment = { id: string; issue_id: string; body: string; created_at: string | null; author_agent_id: string | null; author_user_id: string | null; agent_name: string | null; agent_role: string | null; agent_icon: string | null; }; export type PaperclipAgent = { id: string; name: string; role: string; title: string | null; status: string; icon: string | null; last_heartbeat_at: string | null; }; export type InteractionKind = | "ask_user_questions" | "request_confirmation" | "suggest_tasks"; export type InteractionStatus = | "pending" | "answered" | "accepted" | "rejected" | "expired" | "failed"; export type InteractionOption = { id: string; label: string; description?: string | null; }; export type InteractionQuestion = { id: string; prompt: string; selectionMode?: "single" | "multi"; required?: boolean; options: InteractionOption[]; }; export type InteractionTask = { clientKey: string; parentClientKey?: string | null; title: string; description?: string | null; }; /** Free-form payload — shape depends on `kind`. Common fields surfaced for the * UI; everything else is preserved on the wire. */ export type InteractionPayload = { version?: number; submitLabel?: string; acceptLabel?: string; rejectLabel?: string; questions?: InteractionQuestion[]; tasks?: InteractionTask[]; body?: string; [key: string]: unknown; }; export type Interaction = { id: string; issue_id: string; kind: InteractionKind; status: InteractionStatus; title: string | null; summary: string | null; payload: InteractionPayload; result: Record | null; created_at: string | null; resolved_at: string | null; }; export type AgentActivityResponse = { issues: PaperclipIssue[]; comments: PaperclipComment[]; agents: PaperclipAgent[]; interactions: Interaction[]; }; export type InteractionAction = "respond" | "accept" | "reject"; export type InteractionSubmitVars = { issue_id: string; interaction_id: string; action: InteractionAction; payload: Record; }; // ── Query Keys ─────────────────────────────────────────────────── export const agentKeys = { activity: (caseNumber: string) => ["agents", "activity", caseNumber] as const, }; // ── Hooks ──────────────────────────────────────────────────────── export function useAgentActivity(caseNumber: string | undefined) { return useQuery({ queryKey: agentKeys.activity(caseNumber ?? ""), queryFn: ({ signal }) => apiRequest( `/api/cases/${caseNumber}/agents`, { signal }, ), enabled: !!caseNumber, refetchInterval: 10_000, }); } export function useSendComment(caseNumber: string | undefined) { const qc = useQueryClient(); return useMutation({ mutationFn: (vars: { body: string; issue_id?: string }) => apiRequest<{ comment_id: string; issue_id: string; issue_identifier: string }>( `/api/cases/${caseNumber}/agents/comment`, { method: "POST", body: vars }, ), onSuccess: () => { if (caseNumber) { qc.invalidateQueries({ queryKey: agentKeys.activity(caseNumber) }); } }, }); } export function useSubmitInteraction(caseNumber: string | undefined) { const qc = useQueryClient(); return useMutation({ mutationFn: (vars: InteractionSubmitVars) => apiRequest( `/api/cases/${caseNumber}/agents/interaction-response`, { method: "POST", body: vars }, ), onSuccess: () => { if (caseNumber) { qc.invalidateQueries({ queryKey: agentKeys.activity(caseNumber) }); } }, }); }