Menu
popagent
publicLatest change 4565eb356975518dfc0202eab29b56dff51e8f71 - add deterministic agent health validation by AkurAI Build
import type { AgentSettings, AgentSkill } from "./api-types";
export type AgentHealthFinding = {
severity: "error" | "warning";
code: string;
agentId: string;
skillId?: string;
message: string;
};
export type AgentHealthReport = {
status: "ok" | "warning" | "error";
checkedAt: string;
summary: { agents: number; skills: number; errors: number; warnings: number };
findings: AgentHealthFinding[];
};
export function validateAgentHealth(
agents: AgentSettings[],
skills: AgentSkill[],
checkedAt = new Date().toISOString(),
): AgentHealthReport {
const findings: AgentHealthFinding[] = [];
const agentIds = new Set(agents.map(({ id }) => id));
for (const agent of agents) {
if (new Set(agent.tools).size !== agent.tools.length) findings.push({
severity: "error", code: "duplicate-agent-tool", agentId: agent.id,
message: "Agent has duplicate tool assignments.",
});
}
for (const skill of skills) {
if (!agentIds.has(skill.agentId)) findings.push({
severity: "error", code: "orphaned-skill", agentId: skill.agentId, skillId: skill.id,
message: "Skill references an unknown agent.",
});
if (skill.name.endsWith("-practice")) findings.push({
severity: "warning", code: "generic-practice-skill", agentId: skill.agentId, skillId: skill.id,
message: "Generic practice skill duplicates the shared agent execution contract.",
});
}
for (const agent of agents) {
const agentSkills = skills.filter((skill) => skill.agentId === agent.id && skill.enabled);
const seen = new Set<string>();
for (const skill of agentSkills) {
if (seen.has(skill.name)) findings.push({
severity: "error", code: "duplicate-skill-name", agentId: agent.id, skillId: skill.id,
message: `Enabled skill name is duplicated: ${skill.name}.`,
});
seen.add(skill.name);
}
if (agentSkills.length > 12) findings.push({
severity: "warning", code: "excessive-enabled-skills", agentId: agent.id,
message: `Agent has ${agentSkills.length} enabled skills; narrow its selection surface.`,
});
}
findings.sort((left, right) =>
(left.severity === right.severity ? 0 : left.severity === "error" ? -1 : 1)
|| left.code.localeCompare(right.code)
|| left.agentId.localeCompare(right.agentId)
|| (left.skillId ?? "").localeCompare(right.skillId ?? ""));
const errors = findings.filter(({ severity }) => severity === "error").length;
const warnings = findings.length - errors;
return {
status: errors ? "error" : warnings ? "warning" : "ok",
checkedAt,
summary: { agents: agents.length, skills: skills.length, errors, warnings },
findings,
};
}