feat(settings): add Agents tab — read-only Paperclip agent config view
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>
This commit is contained in:
71
web-ui/src/lib/api/paperclip-agents.ts
Normal file
71
web-ui/src/lib/api/paperclip-agents.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user