UNPKG

agents

Version:

A home for your AI agents

239 lines (238 loc) • 8.77 kB
import { c as isModelInvocable, d as err, f as ok, o as parseSkillResourcePath, t as InvalidSkillResourcePathError } from "./resource-B59OV2a2.js"; //#region src/skills/source.ts /** A source's backing store failed. */ var SkillSourceError = class extends Error { /** * @param sourceId - The source that failed. * @param operation - What the source was doing. * @param cause - The underlying store error. */ constructor(sourceId, operation, cause) { super(`Skill source "${sourceId}" failed to ${operation === "list" ? "list skills" : "read a resource"}: ${cause instanceof Error ? cause.message : String(cause)}`, { cause }); this.sourceId = sourceId; this.operation = operation; this._tag = "SkillSourceError"; this.name = "SkillSourceError"; } }; /** A skill has no file at the requested path. */ var SkillResourceNotFoundError = class extends Error { /** * @param skill - The skill that was searched. * @param path - The missing path. */ constructor(skill, path) { super(`Resource not found: ${skill}/${path}`); this.skill = skill; this.path = path; this._tag = "SkillResourceNotFoundError"; this.name = "SkillResourceNotFoundError"; } }; //#endregion //#region src/skills/catalog.ts /** The catalog has no skill with the requested name. */ var UnknownSkillError = class extends Error { /** @param skill - The requested name. */ constructor(skill) { super(`Skill not found: ${skill}`); this.skill = skill; this._tag = "UnknownSkillError"; this.name = "UnknownSkillError"; } }; /** * Successful listings behind each catalog, by source object. Lets a later * load fall back to the last good listing of a failing source without * putting listings on the public catalog shape. */ const catalogListings = /* @__PURE__ */ new WeakMap(); /** * Load every source and merge them into a catalog. * * Sources apply in order: the first source to list a name wins. A failing * source or an invalid skill becomes a diagnostic instead of an error, so * one bad source never hides the others. Skills that set * `disable-model-invocation: true` are left out. * * @param sources - Skill sources in priority order. * @param options - Pass `previous` to keep a failing source's last skills. * @returns The merged catalog. */ async function loadSkillCatalog(sources, options = {}) { const previousListings = options.previous ? catalogListings.get(options.previous) : void 0; const listings = await Promise.all(sources.map(async (source) => ({ source, listed: await source.list().catch((cause) => err(new SkillSourceError(source.id, "list", cause))) }))); const diagnostics = []; const entries = /* @__PURE__ */ new Map(); const sourceBySkill = /* @__PURE__ */ new Map(); const goodListings = /* @__PURE__ */ new Map(); for (const { source, listed } of listings) { let listing; if (listed.ok) listing = listed.value; else { const fallback = previousListings?.get(source); diagnostics.push({ _tag: "SourceFailed", sourceId: source.id, error: listed.error, keptPrevious: fallback !== void 0 }); if (!fallback) continue; listing = fallback; } goodListings.set(source, listing); for (const invalid of listing.invalid) diagnostics.push({ _tag: "InvalidSkill", sourceId: source.id, location: invalid.location, error: invalid.error }); for (const { skill, resources, location, warnings } of listing.skills) { const existing = entries.get(skill.name); if (existing) { diagnostics.push({ _tag: "DuplicateSkill", name: skill.name, sourceId: source.id, keptSourceId: existing.sourceId }); continue; } entries.set(skill.name, { skill, sourceId: source.id, resources, ...location === void 0 ? {} : { location } }); sourceBySkill.set(skill.name, source); for (const warning of warnings ?? []) diagnostics.push({ _tag: "SkillWarning", sourceId: source.id, name: skill.name, error: warning }); } } const skills = [...entries.values()].filter((entry) => isModelInvocable(entry.skill)); const visible = new Map(skills.map((entry) => [entry.skill.name, entry])); const prompt = renderCatalogPrompt(skills); const catalog = { fingerprint: fingerprintOf(prompt ?? ""), skills, diagnostics, prompt: () => prompt, find: (name) => visible.get(name), activate(name) { const entry = visible.get(name); return entry ? ok(renderSkillActivation(entry)) : err(new UnknownSkillError(name)); }, async readResource(request) { const target = resolveResourceTarget(visible, request); if (!target.ok) return target; const { entry, path } = target.value; const source = sourceBySkill.get(entry.skill.name); if (!source) return err(new UnknownSkillError(entry.skill.name)); const read = await source.readResource(entry.skill.name, path).catch((cause) => err(new SkillSourceError(source.id, "read-resource", cause))); if (!read.ok) return read; return ok({ entry, resource: read.value }); } }; catalogListings.set(catalog, goodListings); return catalog; } /** * Describe a diagnostic in one line, for logs. * * @param diagnostic - A diagnostic from {@link SkillCatalog.diagnostics}. */ function formatSkillDiagnostic(diagnostic) { switch (diagnostic._tag) { case "InvalidSkill": return `Skipped invalid skill ${diagnostic.location} from source "${diagnostic.sourceId}": ${diagnostic.error.message}`; case "SkillWarning": return `Skill "${diagnostic.name}" from source "${diagnostic.sourceId}" breaks the Agent Skills spec but was loaded: ${diagnostic.error.message}`; case "SourceFailed": return `${diagnostic.error.message}. ${diagnostic.keptPrevious ? "Its last listed skills were kept." : "Its skills were skipped."}`; case "DuplicateSkill": return `Duplicate skill "${diagnostic.name}" from source "${diagnostic.sourceId}" ignored; already provided by "${diagnostic.keptSourceId}".`; } } /** * Render a resource read for the model. * * @param read - A resource returned by {@link SkillCatalog.readResource}. */ function renderSkillResource(read) { const { entry, resource } = read; return [ `<skill_resource${attributes([ ["name", entry.skill.name], ["path", resource.path], ["kind", resource.kind], ["encoding", resource.encoding], ["mimeType", resource.mimeType] ])}>`, resource.content, "</skill_resource>" ].join("\n"); } function renderCatalogPrompt(skills) { if (skills.length === 0) return null; return [ "Available skills. When a task matches a skill, use activate_skill with its name before proceeding.", "", ...skills.map(({ skill }) => `- ${skill.name}: ${skill.description.replace(/\s+/g, " ")}`) ].join("\n"); } function renderSkillActivation(entry) { const lines = [`<skill_content${attributes([["name", entry.skill.name], ["location", entry.location]])}>`, entry.skill.body.trim()]; if (entry.resources.length > 0) { lines.push("", "<skill_resources>"); for (const resource of entry.resources) lines.push(` <file${attributes([ ["kind", resource.kind], ["encoding", resource.encoding], ["size", resource.size === void 0 ? void 0 : String(resource.size)] ])}>${resource.path}</file>`); lines.push("</skill_resources>"); } lines.push("</skill_content>"); return lines.join("\n"); } function resolveResourceTarget(skills, request) { let name = request.name; let relative = request.path; if (name === void 0) { const slash = relative.indexOf("/"); if (slash <= 0) return err(new InvalidSkillResourcePathError(relative)); name = relative.slice(0, slash); relative = relative.slice(slash + 1); } const entry = skills.get(name); if (!entry) return err(new UnknownSkillError(name)); const path = parseSkillResourcePath(relative); if (!path.ok) return path; if (!entry.resources.some((resource) => resource.path === path.value)) return err(new SkillResourceNotFoundError(entry.skill.name, path.value)); return ok({ entry, path: path.value }); } function attributes(pairs) { return pairs.filter((pair) => pair[1] !== void 0).map(([key, value]) => ` ${key}="${value.replace(/"/g, "&quot;")}"`).join(""); } /** Two FNV-1a passes with different seeds: cheap and stable across isolates. */ function fingerprintOf(text) { let a = 2166136261; let b = 16777619; for (let index = 0; index < text.length; index++) { const code = text.charCodeAt(index); a = Math.imul(a ^ code, 16777619); b = Math.imul(b ^ code, 2166136261); } return `${(a >>> 0).toString(16).padStart(8, "0")}${(b >>> 0).toString(16).padStart(8, "0")}`; } //#endregion export { SkillResourceNotFoundError as a, renderSkillResource as i, formatSkillDiagnostic as n, SkillSourceError as o, loadSkillCatalog as r, UnknownSkillError as t }; //# sourceMappingURL=catalog-BpOejOsE.js.map