{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/core/capabilities/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,MAAM,cAAc,GACvB,UAAU,GACV,OAAO,GACP,SAAS,GACT,OAAO,GACP,kBAAkB,GAClB,kBAAkB;AACpB,oEAAoE;GAClE,KAAK,CAAC;AAET,MAAM,WAAW,aAAa;IAC7B,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,cAAc,CAAC;IACrB,yEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,QAAQ,EAAE,OAAO,CAAC;CAClB;AAWD,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAG/F;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,GAAG,aAAa,EAAE,CAQlF;AAED,wDAAwD;AACxD,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,SAAS,aAAa,EAAsB,GAAG,MAAM,CAI5F","sourcesContent":["/**\n * One index of everything the agent can *acquire* — MCP tools, skills, slash\n * commands, subagents, and plugins available or installed.\n *\n * The problem this exists for: deferral already withholds MCP tool schemas, but\n * the model still needs some way to find a withheld tool, and the only one it\n * had was an exact name match against a catalog dumped in full into the\n * resolver's description. Those two are locked together — an exact-only matcher\n * *forces* the full dump, because a name you cannot guess is a tool you cannot\n * reach. Give the matcher retrieval and the dump becomes optional, which is\n * where the context saving actually lives.\n *\n * The producers are deliberately not coupled to it: each one hands over a flat\n * list of documents for its own kind and knows nothing about retrieval. That is\n * what lets skills and commands join later (§6.4 leaves them eager for now)\n * without touching the search side.\n *\n * See docs/plugin-system-architecture.md §6.\n */\n\nimport { createHash } from \"node:crypto\";\n\nexport type CapabilityKind =\n\t| \"mcp-tool\"\n\t| \"skill\"\n\t| \"command\"\n\t| \"agent\"\n\t| \"plugin-available\"\n\t| \"plugin-installed\"\n\t/** A heading-level slice of hoocode's own shipped documentation. */\n\t| \"doc\";\n\nexport interface CapabilityDoc {\n\t/** Unique and stable within a session; `<kind>:<name>` by convention. */\n\tid: string;\n\tkind: CapabilityKind;\n\t/** How the model refers to it — a tool name, skill name, plugin id. */\n\tname: string;\n\tdescription: string;\n\t/** Where it came from: MCP server, plugin id, marketplace name. */\n\tsource?: string;\n\t/**\n\t * Whether the expensive part (a JSON schema, a skill body) is currently\n\t * withheld from context.\n\t *\n\t * Recorded rather than derived because it is the policy knob of §6.3: it says\n\t * which entries retrieval is actually *for*. An eager capability is already\n\t * visible to the model, so surfacing it in search results is a convenience;\n\t * a deferred one is otherwise unreachable.\n\t */\n\tdeferred: boolean;\n}\n\n/**\n * Registered documents, by kind.\n *\n * Keyed by kind rather than a flat map because a producer owns its whole kind:\n * the MCP loader knows every MCP tool there is, and on reload it should replace\n * that set, not merge into a pile where a removed server's tools linger.\n */\nconst byKind = new Map<CapabilityKind, CapabilityDoc[]>();\n\n/** Replace every document of `kind`. Producers call this on load and on reload. */\nexport function registerCapabilities(kind: CapabilityKind, docs: readonly CapabilityDoc[]): void {\n\tif (docs.length === 0) byKind.delete(kind);\n\telse byKind.set(kind, [...docs]);\n}\n\n/** Every registered document, in a stable order (kind, then id). */\nexport function getCapabilities(kinds?: readonly CapabilityKind[]): CapabilityDoc[] {\n\tconst wanted = kinds && kinds.length > 0 ? new Set(kinds) : undefined;\n\tconst out: CapabilityDoc[] = [];\n\tfor (const kind of [...byKind.keys()].sort()) {\n\t\tif (wanted && !wanted.has(kind)) continue;\n\t\tout.push(...(byKind.get(kind) ?? []).slice().sort((a, b) => a.id.localeCompare(b.id)));\n\t}\n\treturn out;\n}\n\n/** Drop everything. Tests, and a full session reset. */\nexport function clearCapabilities(): void {\n\tbyKind.clear();\n}\n\n/**\n * Content hash of the current capability set — the key a persistent index is\n * stored under.\n *\n * Hashes the text that gets embedded, not the count: two sessions with the same\n * tools should share an index, and one where a server changed a description\n * should not silently reuse vectors describing the old one.\n */\nexport function capabilitySetHash(docs: readonly CapabilityDoc[] = getCapabilities()): string {\n\tconst h = createHash(\"sha256\");\n\tfor (const d of docs) h.update(`${d.id}\u0000${d.name}\u0000${d.description}\u0000`);\n\treturn h.digest(\"hex\").slice(0, 16);\n}\n"]}