import { useEffect, useState, useMemo } from "react"; import { ArrowClockwise, ArrowSquareOut, CheckCircle, ClockCounterClockwise, Download, FolderOpen, ListChecks, MagnifyingGlass, Pencil, Play, ShieldCheck, Trash, XCircle, Warning, Robot, CheckSquare, Gear, Key, Lightning, Target, Briefcase, Buildings, ArrowRight, } from "@phosphor-icons/react"; import type { ActivityRecord, AgentRecord, ApprovalRecord, CommentRecord, DocumentRecord, GoalRecord, ProfileSnapshot, ProjectRecord, RunRecord, SecretRecord, TaskRecord, TaskStatus, WorkspaceRecord, } from "@shared/types"; import { formatMoney, formatTime } from "../lib/formatters"; import { unwrap } from "../lib/desktop"; import { ActionButton, ConfirmDialog, EmptyState, Field, InlineNotice, Input, ListFrame, ListRow, Select, StatusPill, TextArea } from "./ui"; function describeActionError(error: unknown) { return error instanceof Error ? error.message : "The action could not be completed."; } function parseApprovalPayloadJson(approval: ApprovalRecord): Record | null { if (!approval.payloadJson) { return null; } try { const parsed = JSON.parse(approval.payloadJson); if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { return parsed as Record; } } catch { return null; } return null; } const ENTITY_ICONS: Record = { agent: Robot, task: CheckSquare, run: Play, approval: ShieldCheck, connector: Gear, workspace: FolderOpen, secret: Key, goal: Target, project: Briefcase, company: Buildings, }; const ENTITY_TYPE_OPTIONS = [ "all", "agent", "task", "run", "approval", "connector", "workspace", "secret", "goal", "project", "company", ] as const; function WorkspaceStatePill({ state }: { state: string }) { const colorMap: Record = { ready: "text-[color:var(--success)] bg-[color:var(--success-soft)]", busy: "text-[color:var(--warn)] bg-[color:var(--warn-soft)]", blocked: "text-[color:var(--danger)] bg-[color:var(--danger-soft)]", missing: "text-[color:var(--muted-strong)] bg-[color:var(--panel-soft)]", error: "text-[color:var(--danger)] bg-[color:var(--danger-soft)]", }; return ( {state} ); } function PriorityDot({ priority }: { priority: string }) { const color = priority === "critical" ? "bg-[color:var(--danger)]" : priority === "high" ? "bg-[color:var(--warn)]" : priority === "low" ? "bg-[color:var(--muted)]" : "bg-[color:var(--muted-strong)]"; return ; } export function TaskQueue({ tasks, runs, selectedTaskId, onSelect, }: { tasks: TaskRecord[]; runs: RunRecord[]; selectedTaskId: string | null; onSelect: (taskId: string) => void; }) { if (tasks.length === 0) { return ; } return ( {tasks.map((task) => { const activeRun = runs.find((run) => run.id === task.activeRunId) ?? null; return ( ); })} ); } export function TaskInspector({ task, tasks, runs, agents, goals, projects, workspaces, companyId, onRun, onCancelRun, onCreateApproval, onOpenDiscussion, comments, onRefresh, onDeleted, }: { task: TaskRecord; tasks: TaskRecord[]; runs: RunRecord[]; agents: ProfileSnapshot["agents"]; goals: GoalRecord[]; projects: ProjectRecord[]; workspaces: WorkspaceRecord[]; companyId: string; onRun: (taskId: string) => Promise; onCancelRun: (runId: string) => Promise; onCreateApproval: () => Promise; onOpenDiscussion: (taskId: string) => void; comments: CommentRecord[]; onRefresh: () => Promise; onDeleted: () => Promise; }) { const [editing, setEditing] = useState(false); const [editTitle, setEditTitle] = useState(task.title); const [editDescription, setEditDescription] = useState(task.description); const [editPriority, setEditPriority] = useState(task.priority); const [editStatus, setEditStatus] = useState(task.status); const [editAssignee, setEditAssignee] = useState(task.assigneeAgentId ?? ""); const [editWorkspace, setEditWorkspace] = useState(task.workspaceId ?? ""); const [editProject, setEditProject] = useState(task.projectId ?? ""); const [editGoal, setEditGoal] = useState(task.goalId ?? ""); const [confirmDelete, setConfirmDelete] = useState(false); const [actionError, setActionError] = useState(null); const [actionNotice, setActionNotice] = useState(null); const [deleteError, setDeleteError] = useState(null); const [savingChanges, setSavingChanges] = useState(false); const [updatingStatus, setUpdatingStatus] = useState(null); const [creatingApproval, setCreatingApproval] = useState(false); const assignee = agents.find((agent) => agent.id === task.assigneeAgentId) ?? null; const project = projects.find((entry) => entry.id === task.projectId) ?? null; const goal = goals.find((entry) => entry.id === task.goalId) ?? null; const workspace = workspaces.find((entry) => entry.id === task.workspaceId) ?? null; const activeRun = runs.find((run) => run.id === task.activeRunId) ?? null; const reviewer = assignee?.reportsTo ? (agents.find((a) => a.id === assignee.reportsTo) ?? null) : null; const needsGovernance = task.status === "blocked" || (task.status === "in_review" && !reviewer); const budgetWarning = assignee && assignee.budgetMonthlyUsd > 0 ? assignee.spentMonthlyUsd / assignee.budgetMonthlyUsd : 0; function startEdit() { setActionError(null); setActionNotice(null); setEditTitle(task.title); setEditDescription(task.description); setEditPriority(task.priority); setEditStatus(task.status); setEditAssignee(task.assigneeAgentId ?? ""); setEditWorkspace(task.workspaceId ?? ""); setEditProject(task.projectId ?? ""); setEditGoal(task.goalId ?? ""); setEditing(true); } async function saveChanges() { setActionError(null); setActionNotice(null); setSavingChanges(true); try { unwrap(await window.agentCompany.saveTask({ id: task.id, companyId: task.companyId, title: editTitle, description: editDescription, priority: editPriority as TaskRecord["priority"], status: editStatus, assigneeAgentId: editAssignee || null, workspaceId: editWorkspace || null, projectId: editProject || null, goalId: editGoal || null, })); await onRefresh(); setEditing(false); setActionNotice("Task changes saved."); } catch (error) { setActionError(describeActionError(error)); } finally { setSavingChanges(false); } } async function quickStatus(status: TaskStatus) { setActionError(null); setActionNotice(null); setUpdatingStatus(status); try { unwrap(await window.agentCompany.saveTask({ id: task.id, companyId: task.companyId, title: task.title, description: task.description, priority: task.priority, status, assigneeAgentId: task.assigneeAgentId, workspaceId: task.workspaceId, projectId: task.projectId, goalId: task.goalId, })); await onRefresh(); setActionNotice(`Task moved to ${status.replaceAll("_", " ")}.`); } catch (error) { setActionError(describeActionError(error)); } finally { setUpdatingStatus(null); } } async function handleCreateApproval() { setActionError(null); setActionNotice(null); setCreatingApproval(true); try { await onCreateApproval(); setActionNotice(task.status === "blocked" ? "Approval created and routed to leadership." : "Approval created."); } catch (error) { setActionError(describeActionError(error)); } finally { setCreatingApproval(false); } } async function handleDelete() { setDeleteError(null); try { unwrap(await window.agentCompany.deleteTask({ id: task.id, companyId })); setConfirmDelete(false); await onDeleted(); } catch (error) { setDeleteError(describeActionError(error)); } } return (
void handleDelete()} onCancel={() => { setDeleteError(null); setConfirmDelete(false); }} /> {/* Budget warning */} {budgetWarning > 0.8 && assignee ? (
1 ? "border-[color:var(--danger)] bg-[color:var(--danger-soft)] text-[color:var(--danger)]" : "border-[color:var(--warn)] bg-[color:var(--warn-soft)] text-[color:var(--warn)]" }`}> {assignee.name} has used {Math.round(budgetWarning * 100)}% of their monthly budget ({formatMoney(assignee.spentMonthlyUsd)} / {formatMoney(assignee.budgetMonthlyUsd)})
) : null} {actionError ? : null} {!actionError && actionNotice ? : null} {/* Edit mode */} {editing ? (