Files
playbook/antigravity-awesome-skills/tools/lib/aas-v1/recommend.js
T
2026-07-18 00:02:59 +00:00

346 lines
16 KiB
JavaScript

"use strict";
const versions = require("./versions");
const { compareStrings, normalizeRecommendationInput, sortedUnique } = require("./normalize");
const FIXED_POINT = 1000;
const MAX_RETURNED_CANDIDATES = 25;
const MAX_RETURNED_EXCLUSIONS = 25;
const MAX_EVIDENCE_REFS = 8;
const PUBLIC_METADATA_FIELDS = Object.freeze([
"capabilities",
"risk",
"source",
"setup",
"targets",
"dependencies",
"conflicts",
"validation",
"tests",
"reviews",
]);
function targetCompatibility(skill, targets) {
const states = targets.map((target) => skill.metadata.targets?.[target.host]?.value ?? null);
if (states.includes("blocked")) return "blocked";
if (states.every((state) => state === "supported")) return "supported";
return "unknown";
}
function eligibility(skill, input) {
const reasonCodes = [];
const unknownFields = [];
const risk = skill.metadata.risk?.value;
if (!risk) unknownFields.push("risk");
else if (!input.policy.allowedRisk.includes(risk)) reasonCodes.push("AAS_ELIGIBILITY_RISK_DISALLOWED");
const source = skill.metadata.source?.value;
if (!source) {
unknownFields.push("source");
if (input.policy.requireKnownSource) reasonCodes.push("AAS_ELIGIBILITY_SOURCE_REQUIRED");
}
const setup = skill.metadata.setup?.value;
if (!setup) unknownFields.push("setup");
else if (setup === "manual" && !input.policy.allowManualSetup) reasonCodes.push("AAS_ELIGIBILITY_MANUAL_SETUP_DISALLOWED");
const compatibility = targetCompatibility(skill, input.targets);
if (compatibility === "blocked") reasonCodes.push("AAS_ELIGIBILITY_TARGET_BLOCKED");
if (compatibility === "unknown") unknownFields.push("targetCompatibility");
if (skill.metadata.capabilities?.status === "notApplicable") {
reasonCodes.push("AAS_ELIGIBILITY_CAPABILITY_NOT_SUPPORTED");
} else if (skill.metadata.capabilities?.status !== "known") unknownFields.push("capabilities");
for (const field of ["dependencies", "conflicts", "validation"]) {
if (!["known", "notApplicable"].includes(skill.metadata[field]?.status)) unknownFields.push(field);
}
const hardBlocked = reasonCodes.length > 0;
const evidenceBacked = !hardBlocked
&& skill.metadata.capabilities?.status === "known"
&& skill.metadata.risk?.status === "known"
&& skill.metadata.source?.status === "known"
&& compatibility === "supported"
&& skill.metadata.setup?.status === "known"
&& ["known", "notApplicable"].includes(skill.metadata.dependencies?.status)
&& ["known", "notApplicable"].includes(skill.metadata.conflicts?.status)
&& ["known", "notApplicable"].includes(skill.metadata.validation?.status);
return {
eligibleForRecommendation: evidenceBacked,
hardBlocked,
eligibilityReasonCodes: reasonCodes.sort(),
evidenceLevel: evidenceBacked ? "evidence-backed" : "incomplete",
unknownFields: sortedUnique(unknownFields),
targetCompatibility: compatibility,
};
}
function documentFrequency(skills) {
const frequencies = new Map();
for (const skill of skills) {
for (const token of new Set(skill.recommendationTokens || skill.searchTokens || [])) {
frequencies.set(token, (frequencies.get(token) || 0) + 1);
}
}
return frequencies;
}
function bm25Fixed(skill, queryTokens, corpusSize, frequencies, totalDocumentTokens) {
const tokens = skill.recommendationTokens || skill.searchTokens || [];
const counts = new Map();
for (const token of tokens) counts.set(token, (counts.get(token) || 0) + 1);
let score = 0;
for (const token of queryTokens) {
const tf = counts.get(token) || 0;
if (!tf) continue;
const df = frequencies.get(token) || 0;
const idf = Math.max(1, Math.min(8000, Math.floor(((corpusSize - df + 1) * FIXED_POINT) / (df + 1))));
const lengthNorm = Math.max(1, 250 + Math.floor((750 * tokens.length * corpusSize) / Math.max(1, totalDocumentTokens)));
const tfFactor = Math.round((tf * 2200 * FIXED_POINT) / (tf * FIXED_POINT + 1200 * lengthNorm / FIXED_POINT));
score += Math.round((idf * tfFactor) / FIXED_POINT);
}
return score;
}
function goalCoverage(skill, goals) {
const capabilities = new Set(skill.metadata.capabilities?.value || []);
return goals.filter((goal) => capabilities.has(goal));
}
function scoreCandidates(catalog, input) {
const frequencies = documentFrequency(catalog.skills);
const totalDocumentTokens = catalog.skills.reduce(
(sum, skill) => sum + (skill.recommendationTokens || skill.searchTokens || []).length,
0,
);
const allGoals = [...input.criticalGoals, ...input.nonCriticalGoals];
return catalog.skills.map((skill) => {
const eligible = eligibility(skill, input);
const coveredGoals = goalCoverage(skill, allGoals);
const lexical = bm25Fixed(skill, input.queryTokens, catalog.skills.length, frequencies, totalDocumentTokens);
const criticalMatches = coveredGoals.filter((goal) => input.criticalGoals.includes(goal)).length;
const nonCriticalMatches = coveredGoals.length - criticalMatches;
const metadataKnown = Object.values(skill.metadata).filter((entry) => entry?.status === "known").length;
const metadataTotal = Object.values(skill.metadata).filter((entry) => entry && "status" in entry).length;
const factors = {
lexical,
criticalGoalCoverage: criticalMatches * 5000,
nonCriticalGoalCoverage: nonCriticalMatches * 2500,
metadataCompleteness: Math.round((metadataKnown * FIXED_POINT) / Math.max(1, metadataTotal)),
unknownPenalty: eligible.unknownFields.length * -500,
};
const totalScore = Object.values(factors).reduce((sum, value) => sum + value, 0);
return {
id: skill.id,
factors,
totalScore,
coveredGoals,
eligibility: eligible,
metadata: skill.metadata,
};
}).sort((left, right) => right.totalScore - left.totalScore || compareStrings(left.id, right.id));
}
function knownStringList(candidate, field) {
const judgment = candidate.metadata[field];
return judgment?.status === "known" && Array.isArray(judgment.value) ? judgment.value : [];
}
function candidatesConflict(left, right) {
return knownStringList(left, "conflicts").includes(right.id)
|| knownStringList(right, "conflicts").includes(left.id);
}
function dependencyClosure(candidate, candidatesById, visiting = new Set(), resolved = new Map()) {
if (resolved.has(candidate.id)) return resolved;
if (visiting.has(candidate.id)) return null;
visiting.add(candidate.id);
for (const dependencyId of knownStringList(candidate, "dependencies")) {
const dependency = candidatesById.get(dependencyId);
if (!dependency?.eligibility.eligibleForRecommendation) return null;
if (!dependencyClosure(dependency, candidatesById, visiting, resolved)) return null;
}
visiting.delete(candidate.id);
resolved.set(candidate.id, candidate);
return resolved;
}
function composeStack(candidates, input) {
const uncovered = new Set([...input.criticalGoals, ...input.nonCriticalGoals]);
const selected = [];
const remaining = candidates.filter((candidate) => candidate.eligibility.eligibleForRecommendation);
const candidatesById = new Map(remaining.map((candidate) => [candidate.id, candidate]));
while (selected.length < input.maxSkills && uncovered.size > 0) {
const uncoveredCritical = input.criticalGoals.filter((goal) => uncovered.has(goal));
const ranked = remaining
.filter((candidate) => !selected.some((entry) => entry.id === candidate.id))
.map((candidate) => {
const closure = dependencyClosure(candidate, candidatesById);
if (!closure) return null;
const additions = [...closure.values()]
.filter((entry) => !selected.some((selectedEntry) => selectedEntry.id === entry.id))
.sort((left, right) => compareStrings(left.id, right.id));
if (selected.length + additions.length > input.maxSkills) return null;
const future = [...selected, ...additions];
if (future.some((entry, index) => future.slice(index + 1).some((other) => candidatesConflict(entry, other)))) return null;
const newGoals = sortedUnique(additions.flatMap((entry) => entry.coveredGoals).filter((goal) => uncovered.has(goal)));
const critical = newGoals.filter((goal) => input.criticalGoals.includes(goal)).length;
if (uncoveredCritical.length > 0 && critical === 0) return null;
const nonCritical = newGoals.length - critical;
const alreadyCovered = additions.reduce((sum, entry) => sum + entry.coveredGoals.filter((goal) => !uncovered.has(goal)).length, 0);
const dependencyCost = Math.max(0, additions.length - 1) * 750;
const overlapPenalty = alreadyCovered * 500;
const marginalValue = critical * 10000 + nonCritical * 5000 - dependencyCost - overlapPenalty;
return { candidate, additions, newGoals, critical, nonCritical, dependencyCost, overlapPenalty, marginalValue };
})
.filter((entry) => entry && entry.newGoals.length > 0 && entry.marginalValue >= 1000)
.sort((left, right) => (
right.critical - left.critical
|| right.nonCritical - left.nonCritical
|| left.additions.length - right.additions.length
|| left.overlapPenalty - right.overlapPenalty
|| left.dependencyCost - right.dependencyCost
|| right.candidate.totalScore - left.candidate.totalScore
|| compareStrings(left.candidate.id, right.candidate.id)
));
if (!ranked.length) break;
const winner = ranked[0];
for (const addition of winner.additions) {
selected.push({
...addition,
insertionReason: {
newGoals: addition.id === winner.candidate.id ? winner.newGoals : [],
marginalValue: addition.id === winner.candidate.id ? winner.marginalValue : 0,
dependencyOf: addition.id === winner.candidate.id ? null : winner.candidate.id,
},
});
}
for (const goal of winner.newGoals) uncovered.delete(goal);
}
return { selected, uncovered: [...uncovered].sort(compareStrings) };
}
function collectEvidenceRefs(value, refs = new Set()) {
if (refs.size >= MAX_EVIDENCE_REFS || value === null || value === undefined) return refs;
if (typeof value === "string") {
if (/^sha256-[a-f0-9]{64}$/.test(value)) refs.add(value);
return refs;
}
if (Array.isArray(value)) {
for (const entry of value) collectEvidenceRefs(entry, refs);
return refs;
}
if (typeof value === "object") {
for (const key of Object.keys(value).sort(compareStrings)) collectEvidenceRefs(value[key], refs);
}
return refs;
}
function publicJudgment(judgment) {
const status = typeof judgment?.status === "string" ? judgment.status : "unknown";
const value = judgment && Object.hasOwn(judgment, "value") ? judgment.value : null;
return { status, value };
}
function publicTargets(targets) {
const entries = Object.entries(targets || {}).sort(([left], [right]) => compareStrings(left, right));
const value = Object.fromEntries(entries.map(([host, judgment]) => [host, publicJudgment(judgment)]));
return {
status: entries.length > 0 && entries.every(([, judgment]) => judgment?.status === "known") ? "known" : "unknown",
value,
};
}
function publicCandidate(candidate) {
return {
id: candidate.id,
factors: candidate.factors,
totalScore: candidate.totalScore,
coveredGoals: candidate.coveredGoals,
eligibility: candidate.eligibility,
evidenceRefs: [...collectEvidenceRefs(candidate.metadata)].sort(compareStrings),
metadata: Object.fromEntries(PUBLIC_METADATA_FIELDS.map((field) => [
field,
field === "targets" ? publicTargets(candidate.metadata.targets) : publicJudgment(candidate.metadata[field]),
])),
...(candidate.insertionReason ? { insertionReason: candidate.insertionReason } : {}),
};
}
function exclusionReasonCounts(exclusions) {
const counts = new Map();
for (const exclusion of exclusions) {
for (const code of exclusion.reasonCodes) counts.set(code, (counts.get(code) || 0) + 1);
}
return Object.fromEntries([...counts.entries()].sort(([left], [right]) => compareStrings(left, right)));
}
function recommendStack(catalog, rawInput) {
const input = normalizeRecommendationInput(rawInput);
const candidates = scoreCandidates(catalog, input);
const exclusions = candidates
.filter((candidate) => candidate.eligibility.hardBlocked)
.map((candidate) => ({ id: candidate.id, reasonCodes: candidate.eligibility.eligibilityReasonCodes }));
const recommended = candidates.filter((candidate) => candidate.eligibility.eligibleForRecommendation);
const discoveryCandidates = candidates
.filter((candidate) => !candidate.eligibility.hardBlocked && !candidate.eligibility.eligibleForRecommendation && candidate.totalScore > 0)
.slice(0, 25);
const composition = composeStack(recommended, input);
const coveredGoals = sortedUnique(composition.selected.flatMap((candidate) => candidate.coveredGoals));
const criticalCovered = input.criticalGoals.filter((goal) => coveredGoals.includes(goal)).length;
const nonCriticalCovered = input.nonCriticalGoals.filter((goal) => coveredGoals.includes(goal)).length;
const goalCoverageValue = Math.round(((criticalCovered + nonCriticalCovered) * FIXED_POINT) / Math.max(1, input.criticalGoals.length + input.nonCriticalGoals.length));
const metadataCompleteness = composition.selected.length
? Math.round(composition.selected.reduce((sum, candidate) => sum + candidate.factors.metadataCompleteness, 0) / composition.selected.length)
: 0;
const evidenceStrength = composition.selected.length
? Math.round(composition.selected.filter((candidate) => candidate.eligibility.evidenceLevel === "evidence-backed").length * FIXED_POINT / composition.selected.length)
: 0;
const criticalComplete = criticalCovered === input.criticalGoals.length;
const nonCriticalRatio = input.nonCriticalGoals.length ? Math.round(nonCriticalCovered * FIXED_POINT / input.nonCriticalGoals.length) : FIXED_POINT;
const status = composition.selected.length === 0
? "insufficientCoverage"
: (criticalComplete && nonCriticalRatio >= input.minimumNonCriticalGoalCoverage ? "complete" : "partial");
const returnedRecommended = recommended.slice(0, MAX_RETURNED_CANDIDATES).map(publicCandidate);
const returnedDiscovery = discoveryCandidates.slice(0, MAX_RETURNED_CANDIDATES).map(publicCandidate);
const returnedExclusions = exclusions.slice(0, MAX_RETURNED_EXCLUSIONS);
return {
ok: true,
status,
...versions,
catalog: { package: catalog.package, version: catalog.version, digest: catalog.digest },
normalizedInput: input,
recommended: returnedRecommended,
discoveryCandidates: returnedDiscovery,
candidateCounts: {
recommendedTotal: recommended.length,
recommendedReturned: returnedRecommended.length,
discoveryTotal: discoveryCandidates.length,
discoveryReturned: returnedDiscovery.length,
excludedTotal: exclusions.length,
excludedReturned: returnedExclusions.length,
},
proposedStack: composition.selected.map((candidate) => candidate.id),
includedSkillIds: composition.selected.map((candidate) => candidate.id),
coveredGoals,
uncoveredGoals: composition.uncovered,
goalCapabilityMatrix: [...input.criticalGoals, ...input.nonCriticalGoals].map((goal) => ({
goal,
critical: input.criticalGoals.includes(goal),
skillIds: composition.selected.filter((candidate) => candidate.coveredGoals.includes(goal)).map((candidate) => candidate.id),
})),
exclusions: returnedExclusions,
exclusionReasonCounts: exclusionReasonCounts(exclusions),
hardPolicyViolations: [],
discoveryPromotions: [],
unknown: sortedUnique(discoveryCandidates.flatMap((candidate) => candidate.eligibility.unknownFields.map((field) => `${candidate.id}:${field}`))),
measures: { goalCoverage: goalCoverageValue, metadataCompleteness, evidenceStrength },
};
}
module.exports = {
MAX_RETURNED_CANDIDATES,
MAX_RETURNED_EXCLUSIONS,
eligibility,
bm25Fixed,
scoreCandidates,
composeStack,
publicCandidate,
recommendStack,
};