export type ToolDef = import("../types").ToolDef; export type Skill = { /** * - Unique skill name (also the tool-name prefix). */ name: string; /** * - One-line catalog entry shown in `skill_use`'s description. */ description: string; /** * - Returned as the `skill_use` tool result when the skill is activated. */ instructions: string; /** * - Tools unlocked on use; their names are auto-prefixed `${name}_${tool}`. */ tools?: import("../types").ToolDef[] | undefined; }; export class SkillRegistry { /** * @param {Object} [options] * @param {Iterable} [options.reserved] - Tool names already in use (native/MCP), so a prefixed * skill tool that would collide with one is rejected at `register` time. Tool names are globally unique * for DISPATCH (PRD §2.6, D6) — this is the collision check across native + MCP + skills, not security. * @param {string} [options.metaToolName='skill_use'] - Override the meta-tool name if `skill_use` is taken. * @when you want to expose operator-registered skill bundles to a model by progressive disclosure — one meta-tool whose catalog unlocks a skill's tools on demand * @fails register() rejects an unsafe or colliding name fail-fast and commits nothing on failure; governance is unchanged — discovery never authorizes. * @example * const skills = new SkillRegistry(); * skills.register({ name: 'deploy', description: 'ship a release', instructions: '...', tools: [] }); * const loop = new Loop({ provider, tools: skills.activeTools }); */ constructor(options?: { reserved?: Iterable | undefined; metaToolName?: string | undefined; }); /** @type {Map} */ _skills: Map; /** @type {Set} skill names the agent has activated this run */ _unlocked: Set; /** @type {Set} every tool name owned by the registry (meta + all prefixed skill tools) */ _toolNames: Set; /** @type {Set} externally-reserved names (native/MCP) for collision detection */ _reserved: Set; metaToolName: string; /** * The current active tool set: `[metaTool, ...tools of every unlocked skill]`. Pass this method (bound) * as the Loop `tools` thunk — it is re-evaluated each round, so tools unlocked this round are offered next. * @returns {ToolDef[]} */ activeTools(): ToolDef[]; /** * Register a skill. Throws on a duplicate skill name, or if a prefixed tool name collides with an * existing native/MCP/skill tool (or the meta-tool). Returns `this` for chaining. * @param {Skill} skill * @returns {SkillRegistry} */ register(skill: Skill): SkillRegistry; /** Build the catalog block (one line per registered skill) for the meta-tool description. */ _catalog(): string; /** * The `skill_use` meta-tool ToolDef, with the current catalog in its description. Always included by * `activeTools()`. Activating a skill injects its instructions (the tool result) and unlocks its tools. * @returns {ToolDef} */ get metaTool(): ToolDef; /** Names of skills activated so far this run. @returns {string[]} */ unlocked(): string[]; /** Reset the unlocked set (e.g. between runs). The registered skills are kept. */ reset(): void; }