{"version":3,"file":"skills.mjs","names":[],"sources":["../../../../../../../ai/src/skills/skills.ts"],"sourcesContent":["import type { AgentToolEntry } from \"../tool/executable-as-tool\";\nimport {\n  buildCatalog,\n  loadRecord,\n  renderCatalogPrompt,\n  semanticPreselect,\n} from \"./catalog\";\nimport type {\n  SkillAnalyticsEvent,\n  SkillsConfig,\n} from \"./contracts/skills-config.type\";\nimport type {\n  SkillCatalogEntry,\n  SkillRecord,\n} from \"./contracts/skill-record.type\";\nimport type { SkillsContract } from \"./contracts/skills.contract\";\nimport type { SkillsStoreContract } from \"./contracts/skills-store.contract\";\nimport { loadSkillTool } from \"./load-skill-tool\";\nimport { saveSkillTool } from \"./save-skill-tool\";\nimport { resolveSource } from \"./sources\";\n\nconst DEFAULT_MAX_LOADS_PER_RUN = 5;\n\n/**\n * Create a runtime skills library — the **mechanism** behind the\n * first-class `skills` agent option.\n *\n * The returned {@link SkillsContract} produces the agent-facing wiring:\n * - `catalog` / `catalogPrompt` — the always-injected cheap metadata block\n *   (one line per in-scope, non-candidate skill).\n * - `preload` — the bodies to inject up front per `inject` (`[]` when\n *   `inject` is omitted — the default catalog-only progressive disclosure).\n * - `tools(runId)` — the `loadSkill` tool always; plus `saveSkill` ONLY\n *   when a `review` gate is configured (otherwise self-authoring is inert).\n *\n * Sources are merged in order; a later source wins on a name collision.\n * `maxLoadsPerRun` (default 5) caps `loadSkill` calls per run; exhaustion\n * is an error RESULT the model self-corrects from, never a throw.\n *\n * @example\n * const lib = skills({\n *   name: \"build-skills\",\n *   sources: [{ type: \"directory\", path: \"./agent-skills\" }],\n *   inject: { select: \"semantic\", topK: 2, embedder },\n *   maxLoadsPerRun: 4,\n *   scope: { tags: [\"frontend\"] },\n * });\n */\nexport function skills(config: SkillsConfig): SkillsContract {\n  if (!config.sources || config.sources.length === 0) {\n    throw new Error(\n      `skills(\"${config.name}\"): at least one source is required (directory / url / store)`,\n    );\n  }\n\n  const stores: SkillsStoreContract[] = config.sources.map(resolveSource);\n  const scope = config.scope;\n  const maxLoadsPerRun = config.maxLoadsPerRun ?? DEFAULT_MAX_LOADS_PER_RUN;\n  const reviewExposed = config.review !== undefined;\n\n  /** Fire an analytics event, swallowing any sink error (mirrors agent hooks). */\n  const emit = (event: SkillAnalyticsEvent): void => {\n    if (!config.analytics) {\n      return;\n    }\n\n    try {\n      void Promise.resolve(config.analytics(event)).catch(() => undefined);\n    } catch {\n      // Sink threw synchronously — swallowed; analytics never crash a run.\n    }\n  };\n\n  const catalog = async (): Promise<SkillCatalogEntry[]> => {\n    const entries = await buildCatalog(stores, scope);\n\n    for (const entry of entries) {\n      emit({ type: \"catalogued\", skill: entry.name, version: entry.version });\n    }\n\n    return entries;\n  };\n\n  return {\n    name: config.name,\n\n    catalog,\n\n    async catalogPrompt(): Promise<string> {\n      const entries = await catalog();\n\n      return renderCatalogPrompt(config.name, entries);\n    },\n\n    async preload(input: string): Promise<SkillRecord[]> {\n      if (!config.inject) {\n        return [];\n      }\n\n      if (config.inject === \"all\") {\n        const entries = await buildCatalog(stores, scope);\n        const records: SkillRecord[] = [];\n\n        for (const entry of entries) {\n          const record = await loadRecord(stores, entry.name, entry.version);\n\n          if (record) {\n            records.push(record);\n          }\n        }\n\n        return records;\n      }\n\n      // `{ select: \"semantic\", topK }`\n      return semanticPreselect(stores, input, config.inject.topK, {\n        embedder: config.inject.embedder,\n        threshold: config.inject.threshold,\n        scope,\n      });\n    },\n\n    tools(runId?: string): AgentToolEntry<any, any>[] {\n      const entries: AgentToolEntry<any, any>[] = [\n        loadSkillTool({\n          load: (name, version) => loadRecord(stores, name, version),\n          maxLoadsPerRun,\n          onLoaded: (record) =>\n            emit({ type: \"loaded\", skill: record.name, version: record.version, runId }),\n        }),\n      ];\n\n      // Phase 2 — `saveSkill` is exposed ONLY when a review gate is wired.\n      // Absent gate ⇒ self-authoring is inert: the tool is never registered\n      // and a candidate can never be written, let alone injected.\n      if (reviewExposed && config.review) {\n        const reviewStore = config.review.store;\n\n        entries.push(\n          saveSkillTool({\n            saveCandidate: (record) => reviewStore.saveCandidate(record),\n            onSaved: (record) =>\n              emit({ type: \"saved\", skill: record.name, version: record.version, runId }),\n          }),\n        );\n      }\n\n      return entries;\n    },\n  };\n}\n"],"mappings":";;;;;;AAqBA,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BlC,SAAgB,OAAO,QAAsC;CAC3D,IAAI,CAAC,OAAO,WAAW,OAAO,QAAQ,WAAW,GAC/C,MAAM,IAAI,MACR,WAAW,OAAO,KAAK,8DACzB;CAGF,MAAM,SAAgC,OAAO,QAAQ,IAAI,aAAa;CACtE,MAAM,QAAQ,OAAO;CACrB,MAAM,iBAAiB,OAAO,kBAAkB;CAChD,MAAM,gBAAgB,OAAO,WAAW;;CAGxC,MAAM,QAAQ,UAAqC;EACjD,IAAI,CAAC,OAAO,WACV;EAGF,IAAI;GACF,AAAK,QAAQ,QAAQ,OAAO,UAAU,KAAK,CAAC,CAAC,CAAC,YAAY,MAAS;EACrE,QAAQ,CAER;CACF;CAEA,MAAM,UAAU,YAA0C;EACxD,MAAM,UAAU,MAAM,aAAa,QAAQ,KAAK;EAEhD,KAAK,MAAM,SAAS,SAClB,KAAK;GAAE,MAAM;GAAc,OAAO,MAAM;GAAM,SAAS,MAAM;EAAQ,CAAC;EAGxE,OAAO;CACT;CAEA,OAAO;EACL,MAAM,OAAO;EAEb;EAEA,MAAM,gBAAiC;GACrC,MAAM,UAAU,MAAM,QAAQ;GAE9B,OAAO,oBAAoB,OAAO,MAAM,OAAO;EACjD;EAEA,MAAM,QAAQ,OAAuC;GACnD,IAAI,CAAC,OAAO,QACV,OAAO,CAAC;GAGV,IAAI,OAAO,WAAW,OAAO;IAC3B,MAAM,UAAU,MAAM,aAAa,QAAQ,KAAK;IAChD,MAAM,UAAyB,CAAC;IAEhC,KAAK,MAAM,SAAS,SAAS;KAC3B,MAAM,SAAS,MAAM,WAAW,QAAQ,MAAM,MAAM,MAAM,OAAO;KAEjE,IAAI,QACF,QAAQ,KAAK,MAAM;IAEvB;IAEA,OAAO;GACT;GAGA,OAAO,kBAAkB,QAAQ,OAAO,OAAO,OAAO,MAAM;IAC1D,UAAU,OAAO,OAAO;IACxB,WAAW,OAAO,OAAO;IACzB;GACF,CAAC;EACH;EAEA,MAAM,OAA4C;GAChD,MAAM,UAAsC,CAC1C,cAAc;IACZ,OAAO,MAAM,YAAY,WAAW,QAAQ,MAAM,OAAO;IACzD;IACA,WAAW,WACT,KAAK;KAAE,MAAM;KAAU,OAAO,OAAO;KAAM,SAAS,OAAO;KAAS;IAAM,CAAC;GAC/E,CAAC,CACH;GAKA,IAAI,iBAAiB,OAAO,QAAQ;IAClC,MAAM,cAAc,OAAO,OAAO;IAElC,QAAQ,KACN,cAAc;KACZ,gBAAgB,WAAW,YAAY,cAAc,MAAM;KAC3D,UAAU,WACR,KAAK;MAAE,MAAM;MAAS,OAAO,OAAO;MAAM,SAAS,OAAO;MAAS;KAAM,CAAC;IAC9E,CAAC,CACH;GACF;GAEA,OAAO;EACT;CACF;AACF"}