Add "Start Workflow" button to trigger CEO agent from web UI
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 49s

New endpoint POST /api/cases/{case_number}/start-workflow creates a
Paperclip issue, wakes the CEO agent via wakeup API, and transitions
case status to "processing". Button appears on case page only when
status is "new" or "documents_ready".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-14 15:51:23 +00:00
parent 82ba4663ba
commit 6228846223
4 changed files with 200 additions and 3 deletions

View File

@@ -12,6 +12,7 @@ import os
import uuid
import asyncpg
import httpx
logger = logging.getLogger(__name__)
@@ -20,6 +21,9 @@ PAPERCLIP_DB_URL = os.environ.get(
)
PLUGIN_ID = "53461b5a-7f58-411a-9952-72f9c8d4a328" # marcusgroup.legal-ai
CEO_AGENT_ID = "752cebdd-6748-4a04-aacd-c7ab0294ef33"
PAPERCLIP_API_URL = os.environ.get("PAPERCLIP_API_URL", "http://localhost:3100")
PAPERCLIP_BOARD_API_KEY = os.environ.get("PAPERCLIP_BOARD_API_KEY", "")
# Company IDs from Paperclip DB
COMPANIES = {
@@ -251,3 +255,80 @@ async def get_project_url(case_number: str) -> str | None:
return None
finally:
await conn.close()
async def create_workflow_issue(case_number: str, title: str) -> dict:
"""Create a workflow issue in the existing Paperclip project for a case.
Returns dict with issue_id, identifier, project_url.
Raises ValueError if no project found.
"""
conn = await asyncpg.connect(PAPERCLIP_DB_URL)
try:
# Find existing project
row = await conn.fetchrow(
"SELECT id, company_id FROM projects WHERE name LIKE $1",
f"%{case_number}%",
)
if not row:
raise ValueError(f"No Paperclip project found for case {case_number}")
project_id = str(row["id"])
company_id = str(row["company_id"])
# Get company prefix
comp_row = await conn.fetchrow(
"SELECT issue_prefix FROM companies WHERE id = $1::uuid", company_id,
)
prefix = comp_row["issue_prefix"] if comp_row and comp_row["issue_prefix"] else "CMP"
# Create the workflow issue
issue_id, identifier = await _create_issue(
conn, company_id, project_id, case_number,
f"התחל תהליך ניסוח — {title}"[:200], prefix,
)
# Link to legal-ai case via plugin state
await _link_case_to_issue(conn, issue_id, case_number)
project_url = f"https://pc.nautilus.marcusgroup.org/{prefix}/projects/{project_id}/issues"
logger.info("Created workflow issue %s for case %s", identifier, case_number)
return {
"issue_id": issue_id,
"identifier": identifier,
"project_url": project_url,
}
finally:
await conn.close()
async def wake_ceo_agent(issue_id: str, case_number: str) -> dict:
"""Wake the CEO agent via Paperclip's wakeup API.
MUST use API, never direct DB insert (agent won't wake from DB insert).
"""
if not PAPERCLIP_BOARD_API_KEY:
raise RuntimeError("PAPERCLIP_BOARD_API_KEY not set — cannot wake CEO agent")
url = f"{PAPERCLIP_API_URL}/api/agents/{CEO_AGENT_ID}/wakeup"
payload = {
"source": "web-ui",
"triggerDetail": "user",
"reason": f"start_workflow_{case_number}",
"payload": {
"issueId": issue_id,
"mutation": "workflow_start",
},
}
async with httpx.AsyncClient(timeout=15) as client:
resp = await client.post(
url,
json=payload,
headers={"Authorization": f"Bearer {PAPERCLIP_BOARD_API_KEY}"},
)
resp.raise_for_status()
result = resp.json()
logger.info("CEO agent wakeup for case %s: %s", case_number, result)
return result