feat(case-ui): agents tab v2 + dismiss/reaper for stale interactions (#215)
מוקאפ 18i v2 (מאושר ב-Claude Design) + תיקון-מקור להצטברות pending interactions. UI (agent-activity-feed.tsx): - רוסטר-סוכנים קומפקטי, כל הסוכנים בשורה אחת (grid-cols-9); הוסרה שורת "מה הסוכן עושה עכשיו" — היא שייכה תגובה לפי role-fallback, כך שסוכנים בעלי אותו תפקיד הציגו טקסט זהה. - כל אירוע "ממתין לתשובתך" מקופל כאקורדיון (הראשון פתוח). - כפתור "התעלם" לכל בקשה ממתינה — מבטל שאלה כפולה/מיושנת בלי להעיר את הסוכן. - ציר-הזמן מקובץ לפי משימה (issue), כל קבוצה אקורדיון עם מזהה+סטטוס+מונה. Backend (#215 — מניעת הצטברות pending): - cancel_interaction: ביטול interaction בודד ישירות ל-cancelled, ללא wake_assignee (resolve רגיל היה מעיר את הסוכן). אין triggers על הטבלה. - reap_stale_interactions: reaper שמבטל pending על issues סגורים (done/cancelled) — המקור הדומיננטי לערימה. רץ כל 15 דק' ב-lifespan. - endpoint POST /api/cases/{case}/agents/interaction-dismiss (מאחורי כפתור "התעלם"). הכל דרך agent_platform_port (G12). supersede-at-creation נדחה (תועד ב-#215): ביטול-אוטומטי של שאלה על issue פעיל אחד על-פני אחר אינו דטרמיניסטי; הליבה הבטוחה = reaper-ליתומים + כפתור-ביטול, והשורש האמיתי הוא ריסון לולאות-recovery של Paperclip. Invariants: מקיים G12 (מגע-Paperclip רק דרך הפורט), G2 (אין מסלול מקביל — מרחיב את endpoints הסוכנים הקיימים), §6 (אין בליעת-שגיאות שקטה — הביטול מחזיר {ok,error}, ה-reaper מתעד warning). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -899,6 +899,69 @@ async def reject_interaction(
|
||||
return resp.json()
|
||||
|
||||
|
||||
async def cancel_interaction(issue_id: str, interaction_id: str) -> dict:
|
||||
"""Dismiss a pending interaction WITHOUT waking the issue assignee.
|
||||
|
||||
For stale/duplicate questions (TaskMaster #215). We flip the row to
|
||||
``cancelled`` directly rather than calling Paperclip's resolve endpoints:
|
||||
those carry the interaction's ``wake_assignee`` continuation policy and would
|
||||
re-wake the agent. A direct status flip has no side effects — the table has
|
||||
no triggers (verified) and nothing reaps ``cancelled`` rows.
|
||||
"""
|
||||
conn = await asyncpg.connect(PAPERCLIP_DB_URL)
|
||||
try:
|
||||
row = await conn.fetchrow(
|
||||
"""UPDATE issue_thread_interactions
|
||||
SET status='cancelled', resolved_at=now(),
|
||||
resolved_by_user_id=$3, updated_at=now()
|
||||
WHERE id=$1::uuid AND issue_id=$2::uuid AND status='pending'
|
||||
RETURNING id, status""",
|
||||
interaction_id, issue_id, CHAIM_USER_ID,
|
||||
)
|
||||
finally:
|
||||
await conn.close()
|
||||
if not row:
|
||||
return {
|
||||
"ok": False,
|
||||
"cancelled": False,
|
||||
"error": "interaction not found or already resolved",
|
||||
}
|
||||
return {"ok": True, "cancelled": True, "id": str(row["id"]), "status": row["status"]}
|
||||
|
||||
|
||||
async def reap_stale_interactions() -> dict:
|
||||
"""Cancel pending interactions stranded on closed issues (done/cancelled).
|
||||
|
||||
Safety-net reaper (TaskMaster #215): a question on a closed issue can never
|
||||
be answered and only inflates the chair's "awaiting you" count — this was the
|
||||
dominant source of the pending pile-up. Scoped to the two legal-ai companies.
|
||||
Direct status flip — no wakeups, no side effects.
|
||||
"""
|
||||
company_ids = list(COMPANIES.values())
|
||||
conn = await asyncpg.connect(PAPERCLIP_DB_URL)
|
||||
try:
|
||||
rows = await conn.fetch(
|
||||
"""UPDATE issue_thread_interactions it
|
||||
SET status='cancelled', resolved_at=now(),
|
||||
resolved_by_user_id=$2, updated_at=now()
|
||||
FROM issues i
|
||||
WHERE i.id = it.issue_id
|
||||
AND it.status='pending'
|
||||
AND it.company_id = ANY($1::uuid[])
|
||||
AND i.status IN ('done', 'cancelled')
|
||||
RETURNING it.id""",
|
||||
company_ids, CHAIM_USER_ID,
|
||||
)
|
||||
finally:
|
||||
await conn.close()
|
||||
if rows:
|
||||
logger.info(
|
||||
"reap_stale_interactions: cancelled %d stranded pending interactions",
|
||||
len(rows),
|
||||
)
|
||||
return {"ok": True, "cancelled": len(rows)}
|
||||
|
||||
|
||||
# Singleton project for the precedent-library extraction queue. One issue per
|
||||
# uploaded precedent — assigned to the CEO who runs the local-MCP extractor.
|
||||
_LIBRARY_PROJECT_NAME = "ספריית פסיקה — תור חילוץ"
|
||||
|
||||
Reference in New Issue
Block a user