import { agents } from "@paperclipai/db";
import { getAgentWorkEligibility } from "@paperclipai/shared";
import { eq } from "drizzle-orm";
const DIRECT_NON_INVOKABLE_STATUSES = new Set([
    "paused",
    "terminated",
    "pending_approval",
]);
function blocked(reason, message, details, invalidOrgChain = false) {
    return { invokable: false, reason, message, details, invalidOrgChain };
}
function statusBlockReason(status) {
    if (status === "paused")
        return "paused";
    if (status === "terminated")
        return "terminated";
    if (status === "pending_approval")
        return "pending_approval";
    return null;
}
function toEligibilityAgent(row) {
    return {
        id: row.id,
        companyId: row.companyId,
        name: row.name,
        status: row.status,
        reportsTo: row.reportsTo,
    };
}
function invalidChainReason(health) {
    if (health.reason === "terminated_ancestor")
        return "manager_terminated";
    if (health.reason === "cycle")
        return "reporting_cycle";
    return "manager_missing";
}
export function evaluateAgentInvokability(agent, companyAgents) {
    if (!agent) {
        return blocked("missing", "Agent no longer exists", {}, false);
    }
    const eligibility = getAgentWorkEligibility({
        agent: toEligibilityAgent(agent),
        agents: companyAgents.map(toEligibilityAgent),
    });
    if (eligibility.invokable)
        return { invokable: true };
    const directStatusReason = eligibility.invokabilityReason === "unknown_status"
        ? "unknown_status"
        : statusBlockReason(agent.status);
    if (directStatusReason) {
        return blocked(directStatusReason, "Agent is not invokable in its current state", { agentId: agent.id, agentStatus: agent.status }, false);
    }
    const health = eligibility.orgChainHealth;
    const firstInvalidAncestor = health.firstInvalidAncestor;
    return blocked(invalidChainReason(health), "Agent is not invokable because its reporting chain is invalid", {
        agentId: agent.id,
        managerId: firstInvalidAncestor?.id ?? null,
        managerStatus: firstInvalidAncestor?.status ?? null,
        reportingChainAgentIds: health.fullChain
            .filter((entry) => entry.relation === "ancestor")
            .map((entry) => entry.id),
        orgChainHealth: health,
    }, true);
}
export async function evaluateAgentInvokabilityFromDb(db, agent) {
    if (!agent)
        return evaluateAgentInvokability(agent, []);
    const companyAgents = await db
        .select({
        id: agents.id,
        companyId: agents.companyId,
        name: agents.name,
        reportsTo: agents.reportsTo,
        status: agents.status,
    })
        .from(agents)
        .where(eq(agents.companyId, agent.companyId));
    return evaluateAgentInvokability(agent, companyAgents);
}
export function listInvalidOrgChainDescendantIds(terminatedAgentId, companyAgents) {
    const byManager = new Map();
    for (const row of companyAgents) {
        const siblings = byManager.get(row.reportsTo ?? null) ?? [];
        siblings.push(row);
        byManager.set(row.reportsTo ?? null, siblings);
    }
    const invalidDescendantIds = [];
    const stack = [...(byManager.get(terminatedAgentId) ?? [])];
    const seen = new Set([terminatedAgentId]);
    while (stack.length > 0) {
        const current = stack.pop();
        if (!current || seen.has(current.id))
            continue;
        seen.add(current.id);
        if (current.status !== "terminated") {
            invalidDescendantIds.push(current.id);
        }
        stack.push(...(byManager.get(current.id) ?? []));
    }
    return invalidDescendantIds;
}
export function shouldCancelRunsForNonInvokableAgent(result) {
    return !result.invokable && (result.reason === "terminated" || result.invalidOrgChain);
}
//# sourceMappingURL=agent-invokability.js.map