{"version":3,"file":"load-skill-tool.mjs","names":[],"sources":["../../../../../../../ai/src/skills/load-skill-tool.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ToolContract } from \"../tool/tool\";\nimport { tool } from \"../tool/tool\";\nimport type {\n  LoadSkillInput,\n  SkillRecord,\n} from \"./contracts/skill-record.type\";\n\n/** Result the `loadSkill` tool feeds back to the model. */\nexport type LoadSkillResult =\n  | { body: string; name: string; version: number }\n  | { error: string };\n\n/**\n * Hand-built, schema-library-agnostic Standard Schema for\n * `{ name: string; version?: number }` — built without `seal` / `zod` so\n * the skills feature stays dependency-free, matching the framework's own\n * `ragToolSchema` style. Bad shapes return `{ issues }` so the tool\n * runtime surfaces a `SchemaValidationError` like any other tool.\n */\nfunction loadSkillSchema(): StandardSchemaV1<LoadSkillInput> {\n  return {\n    \"~standard\": {\n      version: 1,\n      vendor: \"warlock-ai-skills\",\n      validate: (value: unknown) => {\n        const candidate = value as { name?: unknown; version?: unknown } | null;\n\n        if (!candidate || typeof candidate.name !== \"string\") {\n          return { issues: [{ message: \"loadSkill input must be { name: string; version?: number }\" }] };\n        }\n\n        if (candidate.version !== undefined && typeof candidate.version !== \"number\") {\n          return { issues: [{ message: \"loadSkill `version` must be a number when provided\" }] };\n        }\n\n        return {\n          value: {\n            name: candidate.name,\n            ...(candidate.version !== undefined ? { version: candidate.version } : {}),\n          },\n        };\n      },\n    },\n  };\n}\n\n/** Dependencies the `loadSkill` tool closes over — kept narrow for testing. */\nexport type LoadSkillToolDeps = {\n  /** Resolve a skill's full record across the merged sources. */\n  load: (name: string, version?: number) => Promise<SkillRecord | undefined>;\n  /** Per-run budget cap on `loadSkill` calls (default 5, enforced by caller-supplied counter). */\n  maxLoadsPerRun: number;\n  /** Fired on each successful load (`type: \"loaded\"`); errors swallowed by the sink wrapper. */\n  onLoaded?: (record: SkillRecord) => void;\n};\n\n/**\n * Build the `loadSkill` tool for one run. Returns the skill **body** as\n * the tool result, which the agent loop feeds straight back to the model\n * (the standard `role:\"tool\"` message path) — making the loaded procedure\n * visible on the next trip.\n *\n * The per-run counter is closed over here (one tool instance per run), so\n * the budget is naturally scoped to this execution:\n * - Past `maxLoadsPerRun` ⇒ returns `{ error: \"skill load budget exhausted\" }`\n *   as a RESULT, never a throw — the model self-corrects, exactly how the\n *   agent loop treats any tool error.\n * - Unknown skill (`load` ⇒ undefined) ⇒ `{ error: \"unknown skill: <name>\" }`.\n *\n * `execute` itself never throws — both failure modes are error results, so\n * the run continues.\n */\nexport function loadSkillTool(deps: LoadSkillToolDeps): ToolContract<LoadSkillInput, LoadSkillResult> {\n  let loads = 0;\n\n  return tool<LoadSkillInput, LoadSkillResult>({\n    name: \"loadSkill\",\n    description:\n      \"Load the full instructions of a named skill from the catalog into context. Call it with the skill's `name` (and optional `version`) when you need its detailed procedure.\",\n    input: loadSkillSchema(),\n    execute: async ({ name, version }) => {\n      if (loads >= deps.maxLoadsPerRun) {\n        return { error: \"skill load budget exhausted\" };\n      }\n\n      loads += 1;\n\n      const record = await deps.load(name, version);\n\n      if (!record) {\n        return { error: `unknown skill: ${name}` };\n      }\n\n      deps.onLoaded?.(record);\n\n      return { body: record.body, name: record.name, version: record.version };\n    },\n  });\n}\n"],"mappings":";;;;;;;;;;AAoBA,SAAS,kBAAoD;CAC3D,OAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,WAAW,UAAmB;GAC5B,MAAM,YAAY;GAElB,IAAI,CAAC,aAAa,OAAO,UAAU,SAAS,UAC1C,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,6DAA6D,CAAC,EAAE;GAG/F,IAAI,UAAU,YAAY,UAAa,OAAO,UAAU,YAAY,UAClE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,qDAAqD,CAAC,EAAE;GAGvF,OAAO,EACL,OAAO;IACL,MAAM,UAAU;IAChB,GAAI,UAAU,YAAY,SAAY,EAAE,SAAS,UAAU,QAAQ,IAAI,CAAC;GAC1E,EACF;EACF;CACF,EACF;AACF;;;;;;;;;;;;;;;;;AA4BA,SAAgB,cAAc,MAAwE;CACpG,IAAI,QAAQ;CAEZ,OAAO,KAAsC;EAC3C,MAAM;EACN,aACE;EACF,OAAO,gBAAgB;EACvB,SAAS,OAAO,EAAE,MAAM,cAAc;GACpC,IAAI,SAAS,KAAK,gBAChB,OAAO,EAAE,OAAO,8BAA8B;GAGhD,SAAS;GAET,MAAM,SAAS,MAAM,KAAK,KAAK,MAAM,OAAO;GAE5C,IAAI,CAAC,QACH,OAAO,EAAE,OAAO,kBAAkB,OAAO;GAG3C,KAAK,WAAW,MAAM;GAEtB,OAAO;IAAE,MAAM,OAAO;IAAM,MAAM,OAAO;IAAM,SAAS,OAAO;GAAQ;EACzE;CACF,CAAC;AACH"}