import { createNotesApiClient } from "@/lib/api-helpers"; import { RETRY_WHEN_ONLINE_MESSAGE, withMutationRetry } from "@/lib/mutation-retry"; import type { AgentTimelineItem, ApprovalQueueItem, NoteAgentActionDoc, NoteAgentActionListResponse } from "@/lib/types"; function toSeverity(actionType: NoteAgentActionDoc["actionType"]): ApprovalQueueItem["severity"] { if (actionType === "update" || actionType === "extract_tasks") { return "medium"; } if (actionType === "attach_citation") { return "low"; } return "high"; } function toTimelineItem(action: NoteAgentActionDoc): AgentTimelineItem { return { id: action.id, actor: action.actorId, action: `${action.actorType} ${action.actionType.replaceAll("_", " ")}`, timestamp: action.updatedAt, status: action.state, summary: action.afterSummary ?? action.reason ?? action.toolName ?? action.actionType, }; } function toApprovalQueueItem(action: NoteAgentActionDoc): ApprovalQueueItem { return { id: action.id, title: action.afterSummary ?? action.reason ?? `${action.actionType} proposal`, owner: action.actorId, severity: toSeverity(action.actionType), status: action.state, noteId: action.noteId, workspaceId: action.workspaceId, before: action.beforeSummary, after: action.afterSummary, }; } async function updateAgentActionState( id: string, workspaceId: string, state: "approved" | "rejected", reviewNote?: string, ): Promise { const api = createNotesApiClient(); const path = `/note-agent-actions/${encodeURIComponent(id)}?workspaceId=${encodeURIComponent(workspaceId)}`; return withMutationRetry({ run: () => api.fetch(path, { method: "PATCH", body: JSON.stringify({ state, reviewedAt: new Date().toISOString(), ...(reviewNote ? { reviewNote } : {}), }), }), offlineMessage: RETRY_WHEN_ONLINE_MESSAGE, }); } export async function listApprovalQueue(): Promise { const api = createNotesApiClient(); const response = await api.fetch("/note-agent-actions/pending"); return response.items .sort((a: NoteAgentActionDoc, b: NoteAgentActionDoc) => b.updatedAt.localeCompare(a.updatedAt)) .map(toApprovalQueueItem); } export async function listAgentTimeline(noteId?: string): Promise { const api = createNotesApiClient(); const response = await api.fetch("/note-agent-actions/pending"); return response.items .filter((action: NoteAgentActionDoc) => (noteId ? action.noteId === noteId : true)) .sort((a: NoteAgentActionDoc, b: NoteAgentActionDoc) => b.updatedAt.localeCompare(a.updatedAt)) .map(toTimelineItem); } export async function approveReviewItem(item: ApprovalQueueItem, reviewNote?: string): Promise { const updated = await updateAgentActionState(item.id, item.workspaceId, "approved", reviewNote); return toApprovalQueueItem(updated); } export async function rejectReviewItem(item: ApprovalQueueItem, reviewNote?: string): Promise { const updated = await updateAgentActionState(item.id, item.workspaceId, "rejected", reviewNote); return toApprovalQueueItem(updated); } export async function batchReviewItems( items: ApprovalQueueItem[], state: "approved" | "rejected", reviewNote?: string, ): Promise<{ updated: number; total: number }> { const api = createNotesApiClient(); const result = await withMutationRetry({ run: () => api.fetch<{ updated: number; total: number }>("/note-agent-actions/batch-review", { method: "POST", body: JSON.stringify({ ids: items.map((item) => ({ id: item.id, workspaceId: item.workspaceId })), state, ...(reviewNote ? { reviewNote } : {}), }), }), offlineMessage: RETRY_WHEN_ONLINE_MESSAGE, }); return result; }