All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m16s
New "Agents" tab in case detail shows all Paperclip agent comments,
issue status, and agent status for each case — eliminating the need
to switch between Legal-AI and Paperclip UIs.
Backend: 4 new DB query functions in paperclip_client.py (issues,
comments, agents, post_comment) + 2 new API endpoints (GET/POST
/api/cases/{case_number}/agents). Comment posting uses Board API
with DB+wakeup fallback to ensure CEO routing.
Frontend: agents.ts hooks (10s polling), AgentActivityFeed component
(markdown timeline + comment input), AgentStatusWidget (sidebar),
4th tab in case detail page.
Also includes new-company-setup-guide.md documenting the process
for setting up the betterment levy (CMPA) company.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
88 lines
2.5 KiB
TypeScript
88 lines
2.5 KiB
TypeScript
/**
|
|
* 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 AgentActivityResponse = {
|
|
issues: PaperclipIssue[];
|
|
comments: PaperclipComment[];
|
|
agents: PaperclipAgent[];
|
|
};
|
|
|
|
// ── 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<AgentActivityResponse>(
|
|
`/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) });
|
|
}
|
|
},
|
|
});
|
|
}
|