Add Paperclip agent activity mirror to case detail page
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m16s
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>
This commit is contained in:
87
web-ui/src/lib/api/agents.ts
Normal file
87
web-ui/src/lib/api/agents.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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) });
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user