Add Paperclip agent activity mirror to case detail page
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:
2026-04-15 10:44:42 +00:00
parent 2e2d2d42b6
commit 1e4c5c1518
7 changed files with 1051 additions and 1 deletions

View File

@@ -37,7 +37,11 @@ from web.gitea_client import create_repo, setup_remote_and_push
from web.paperclip_client import (
create_project as pc_create_project,
create_workflow_issue as pc_create_workflow_issue,
get_agents_for_company as pc_get_agents,
get_case_issues as pc_get_case_issues,
get_issue_comments as pc_get_issue_comments,
get_project_url,
post_comment as pc_post_comment,
wake_ceo_agent as pc_wake_ceo,
)
@@ -2214,6 +2218,55 @@ async def api_start_workflow(case_number: str):
}
# ── Agent Activity Mirror ─────────────────────────────────────────
@app.get("/api/cases/{case_number}/agents")
async def api_case_agents(case_number: str):
"""Get all Paperclip agent activity for a case: issues, comments, agent status."""
issues = await pc_get_case_issues(case_number)
if not issues:
return {"issues": [], "comments": [], "agents": []}
issue_ids = [i["id"] for i in issues]
company_id = issues[0]["company_id"]
comments, agents = await pc_get_issue_comments(issue_ids), await pc_get_agents(company_id)
return {"issues": issues, "comments": comments, "agents": agents}
class AgentCommentRequest(BaseModel):
body: str
issue_id: str | None = None
@app.post("/api/cases/{case_number}/agents/comment")
async def api_post_agent_comment(case_number: str, req: AgentCommentRequest):
"""Post a comment on a Paperclip issue linked to a case.
If issue_id is omitted, the most recent non-done issue is used.
"""
issues = await pc_get_case_issues(case_number)
if not issues:
raise HTTPException(404, f"לא נמצא פרויקט Paperclip לתיק {case_number}")
if req.issue_id:
target = next((i for i in issues if i["id"] == req.issue_id), None)
if not target:
raise HTTPException(404, f"Issue {req.issue_id} לא שייך לתיק {case_number}")
else:
# Pick the most recent non-done issue, or the last one
active = [i for i in issues if i["status"] != "done"]
target = active[-1] if active else issues[-1]
result = await pc_post_comment(target["id"], target["company_id"], req.body)
# Find the identifier for the response
result["issue_identifier"] = target.get("identifier", "")
return result
# ── Settings: Tag → Company Mappings ──────────────────────────────
@app.get("/api/settings/paperclip-companies")