Show per-case agent status instead of global — fix Hebrew translation of "running"
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m41s

Agent status widget now checks heartbeat_runs + wakeup_requests to determine
if an agent is running on *this* case. Agents running on other cases show as idle.
Added "running" to STATUS_DOT/STATUS_LABEL maps so it displays in Hebrew.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 14:47:42 +00:00
parent 3288624349
commit d7a79cf5ec
3 changed files with 42 additions and 2 deletions

View File

@@ -8,12 +8,14 @@ import { Bot } from "lucide-react";
const STATUS_DOT: Record<string, string> = { const STATUS_DOT: Record<string, string> = {
active: "bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.6)]", active: "bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.6)]",
running: "bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.6)]",
idle: "bg-gray-300", idle: "bg-gray-300",
error: "bg-red-500", error: "bg-red-500",
}; };
const STATUS_LABEL: Record<string, string> = { const STATUS_LABEL: Record<string, string> = {
active: "פעיל", active: "פעיל",
running: "פעיל",
idle: "ממתין", idle: "ממתין",
error: "שגיאה", error: "שגיאה",
}; };
@@ -51,7 +53,7 @@ export function AgentStatusWidget({
if (!data?.issues?.length) return null; if (!data?.issues?.length) return null;
const agents = data.agents ?? []; const agents = data.agents ?? [];
const activeCount = agents.filter((a) => a.status === "active").length; const activeCount = agents.filter((a) => a.status === "active" || a.status === "running").length;
return ( return (
<div className="space-y-2"> <div className="space-y-2">

View File

@@ -38,6 +38,7 @@ from web.gitea_client import create_repo, setup_remote_and_push
from web.paperclip_client import ( from web.paperclip_client import (
create_project as pc_create_project, create_project as pc_create_project,
create_workflow_issue as pc_create_workflow_issue, create_workflow_issue as pc_create_workflow_issue,
get_agents_for_case as pc_get_agents_for_case,
get_agents_for_company as pc_get_agents, get_agents_for_company as pc_get_agents,
get_case_issues as pc_get_case_issues, get_case_issues as pc_get_case_issues,
get_issue_comments as pc_get_issue_comments, get_issue_comments as pc_get_issue_comments,
@@ -2232,7 +2233,7 @@ async def api_case_agents(case_number: str):
issue_ids = [i["id"] for i in issues] issue_ids = [i["id"] for i in issues]
company_id = issues[0]["company_id"] company_id = issues[0]["company_id"]
comments, agents = await pc_get_issue_comments(issue_ids), await pc_get_agents(company_id) comments, agents = await pc_get_issue_comments(issue_ids), await pc_get_agents_for_case(company_id, issue_ids)
return {"issues": issues, "comments": comments, "agents": agents} return {"issues": issues, "comments": comments, "agents": agents}

View File

@@ -408,6 +408,43 @@ async def get_agents_for_company(company_id: str) -> list[dict]:
await conn.close() await conn.close()
async def get_agents_for_case(company_id: str, issue_ids: list[str]) -> list[dict]:
"""Get agents with per-case status (running on *this* case vs globally)."""
conn = await asyncpg.connect(PAPERCLIP_DB_URL)
try:
rows = await conn.fetch(
"""SELECT a.id, a.name, a.role, a.title, a.icon,
a.status AS global_status, a.last_heartbeat_at,
EXISTS(
SELECT 1 FROM heartbeat_runs hr
JOIN agent_wakeup_requests wr ON hr.wakeup_request_id = wr.id
WHERE hr.agent_id = a.id
AND hr.status = 'running'
AND wr.payload->>'issueId' = ANY($2::text[])
) AS active_on_case
FROM agents a
WHERE a.company_id = $1::uuid
ORDER BY a.role, a.name""",
company_id, issue_ids,
)
return [
{
"id": str(r["id"]),
"name": r["name"],
"role": r["role"],
"title": r["title"],
"status": "running" if r["active_on_case"] else (
"idle" if r["global_status"] == "running" else r["global_status"]
),
"icon": r["icon"],
"last_heartbeat_at": r["last_heartbeat_at"].isoformat() if r["last_heartbeat_at"] else None,
}
for r in rows
]
finally:
await conn.close()
async def post_comment(issue_id: str, company_id: str, body: str) -> dict: async def post_comment(issue_id: str, company_id: str, body: str) -> dict:
"""Post a comment on a Paperclip issue. """Post a comment on a Paperclip issue.