Task #29: surfaces all 14 agents (7 roles × 2 companies) in /settings as master+mirror pairs with drift detection. Replaces ad-hoc psql + script inspection with a single dashboard. Backend: GET /api/admin/paperclip-agents — fetches via Paperclip API (not direct DB), groups by name, computes drift across model/effort/ timeoutSec/maxTurnsPerRun/skills/runtime_config.heartbeat/budget/status. Frontend: new AgentsTab card-per-pair with side-by-side compare, drift highlighting, expandable details (skills list + instructions path). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
/**
|
|
* Paperclip agents — read-only admin view (Task #29).
|
|
*
|
|
* Backend: `GET /api/admin/paperclip-agents` returns master+mirror pairs
|
|
* (CMP / CMPA) for all 7 agent roles, with drift detection between the pair.
|
|
*/
|
|
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { apiRequest } from "./client";
|
|
|
|
export type PaperclipAgent = {
|
|
id: string;
|
|
company_id: string;
|
|
company_name: string;
|
|
name: string;
|
|
role: string | null;
|
|
status: string | null;
|
|
pause_reason: string | null;
|
|
adapter_type: string | null;
|
|
model: string | null;
|
|
effort: string | null;
|
|
timeoutSec: number | null;
|
|
maxTurnsPerRun: number | null;
|
|
desiredSkills: string[];
|
|
instructionsBundleMode: string | null;
|
|
instructionsRootPath: string | null;
|
|
instructionsEntryFile: string | null;
|
|
instructionsFilePath: string | null;
|
|
graceSec: number | null;
|
|
cooldownSec: number | null;
|
|
wakeOnDemand: boolean | null;
|
|
maxConcurrentRuns: number | null;
|
|
intervalSec: number | null;
|
|
enabled: boolean | null;
|
|
budget_monthly_cents: number | null;
|
|
spent_monthly_cents: number | null;
|
|
last_heartbeat_at: string | null;
|
|
updated_at: string | null;
|
|
};
|
|
|
|
export type DriftEntry = {
|
|
field: string;
|
|
master: unknown;
|
|
mirror: unknown;
|
|
};
|
|
|
|
export type AgentPair = {
|
|
name: string;
|
|
role: string | null;
|
|
master: PaperclipAgent | null;
|
|
mirror: PaperclipAgent | null;
|
|
drift: DriftEntry[];
|
|
};
|
|
|
|
export type PaperclipAgentsResponse = {
|
|
pairs: AgentPair[];
|
|
companies: Array<{
|
|
id: string;
|
|
label: string;
|
|
slot: "master" | "mirror";
|
|
}>;
|
|
};
|
|
|
|
export function usePaperclipAgents() {
|
|
return useQuery({
|
|
queryKey: ["settings", "paperclip-agents"] as const,
|
|
queryFn: ({ signal }) =>
|
|
apiRequest<PaperclipAgentsResponse>("/api/admin/paperclip-agents", { signal }),
|
|
staleTime: 30_000,
|
|
});
|
|
}
|