agentic-flow 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/agentic-flow-ecosystem/SKILL.md +50 -0
- package/.claude/skills/agentic-flow-ecosystem/skill.md +58 -0
- package/.claude/skills/agentic-flow-quickstart/skill.md +6 -6
- package/.claude/skills/hooks-automation/skill.md +11 -11
- package/.claude/skills/memory-patterns/skill.md +7 -7
- package/.claude/skills/sparc-methodology/skill.md +13 -13
- package/.claude/skills/swarm-coordination/skill.md +5 -5
- package/.claude/skills/worker-benchmarks/skill.md +5 -5
- package/.claude/skills/worker-integration/skill.md +5 -5
- package/.claude/statusline.mjs +22 -5
- package/CHANGELOG.md +48 -0
- package/README.md +83 -71
- package/dist/.tsbuildinfo +1 -1
- package/dist/cli/commands/hooks.d.ts.map +1 -1
- package/dist/cli/commands/hooks.js +17 -17
- package/dist/cli/commands/hooks.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -5
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +11 -15
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/swarm.d.ts.map +1 -1
- package/dist/cli/commands/swarm.js +3 -2
- package/dist/cli/commands/swarm.js.map +1 -1
- package/dist/core/agentdb-fast.d.ts +21 -11
- package/dist/core/agentdb-fast.d.ts.map +1 -1
- package/dist/core/agentdb-fast.js +39 -14
- package/dist/core/agentdb-fast.js.map +1 -1
- package/dist/integrations/agent-booster.d.ts +35 -0
- package/dist/integrations/agent-booster.d.ts.map +1 -0
- package/dist/integrations/agent-booster.js +45 -0
- package/dist/integrations/agent-booster.js.map +1 -0
- package/dist/integrations/index.d.ts +15 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +11 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/onnx-wasm-embedder.d.ts +59 -0
- package/dist/integrations/onnx-wasm-embedder.d.ts.map +1 -0
- package/dist/integrations/onnx-wasm-embedder.js +145 -0
- package/dist/integrations/onnx-wasm-embedder.js.map +1 -0
- package/dist/integrations/optional.d.ts +44 -0
- package/dist/integrations/optional.d.ts.map +1 -0
- package/dist/integrations/optional.js +107 -0
- package/dist/integrations/optional.js.map +1 -0
- package/dist/integrations/prompt-injection.d.ts +52 -0
- package/dist/integrations/prompt-injection.d.ts.map +1 -0
- package/dist/integrations/prompt-injection.js +126 -0
- package/dist/integrations/prompt-injection.js.map +1 -0
- package/dist/integrations/sublinear.d.ts +71 -0
- package/dist/integrations/sublinear.d.ts.map +1 -0
- package/dist/integrations/sublinear.js +227 -0
- package/dist/integrations/sublinear.js.map +1 -0
- package/dist/integrations/task-partition.d.ts +40 -0
- package/dist/integrations/task-partition.d.ts.map +1 -0
- package/dist/integrations/task-partition.js +173 -0
- package/dist/integrations/task-partition.js.map +1 -0
- package/dist/mcp/mcp-policy.d.ts.map +1 -1
- package/dist/mcp/mcp-policy.js +2 -0
- package/dist/mcp/mcp-policy.js.map +1 -1
- package/dist/mcp/standalone-stdio.js +12 -26
- package/dist/mcp/standalone-stdio.js.map +1 -1
- package/dist/mcp/tools/ecosystem-tools.d.ts +49 -0
- package/dist/mcp/tools/ecosystem-tools.d.ts.map +1 -0
- package/dist/mcp/tools/ecosystem-tools.js +126 -0
- package/dist/mcp/tools/ecosystem-tools.js.map +1 -0
- package/dist/proxy/anthropic-to-gemini.d.ts.map +1 -1
- package/dist/proxy/anthropic-to-gemini.js +10 -14
- package/dist/proxy/anthropic-to-gemini.js.map +1 -1
- package/dist/proxy/anthropic-to-openrouter.d.ts.map +1 -1
- package/dist/proxy/anthropic-to-openrouter.js +6 -25
- package/dist/proxy/anthropic-to-openrouter.js.map +1 -1
- package/dist/proxy/anthropic-to-requesty.d.ts.map +1 -1
- package/dist/proxy/anthropic-to-requesty.js +8 -10
- package/dist/proxy/anthropic-to-requesty.js.map +1 -1
- package/dist/proxy/sse-lines.d.ts +9 -0
- package/dist/proxy/sse-lines.d.ts.map +1 -0
- package/dist/proxy/sse-lines.js +37 -0
- package/dist/proxy/sse-lines.js.map +1 -0
- package/dist/reasoningbank/index.d.ts +2 -1
- package/dist/reasoningbank/index.d.ts.map +1 -1
- package/dist/reasoningbank/index.js +1 -1
- package/dist/reasoningbank/index.js.map +1 -1
- package/dist/reasoningbank/utils/embeddings.d.ts +17 -0
- package/dist/reasoningbank/utils/embeddings.d.ts.map +1 -1
- package/dist/reasoningbank/utils/embeddings.js +48 -17
- package/dist/reasoningbank/utils/embeddings.js.map +1 -1
- package/dist/routing/SemanticRouter.d.ts +25 -9
- package/dist/routing/SemanticRouter.d.ts.map +1 -1
- package/dist/routing/SemanticRouter.js +63 -12
- package/dist/routing/SemanticRouter.js.map +1 -1
- package/dist/utils/input-validator.d.ts +10 -2
- package/dist/utils/input-validator.d.ts.map +1 -1
- package/dist/utils/input-validator.js +30 -2
- package/dist/utils/input-validator.js.map +1 -1
- package/dist/utils/model-cache.d.ts.map +1 -1
- package/dist/utils/model-cache.js +6 -25
- package/dist/utils/model-cache.js.map +1 -1
- package/dist/utils/package-version.d.ts +9 -0
- package/dist/utils/package-version.d.ts.map +1 -0
- package/dist/utils/package-version.js +39 -0
- package/dist/utils/package-version.js.map +1 -0
- package/dist/wasm/onnx-embeddings-wasm.d.ts.map +1 -1
- package/dist/wasm/onnx-embeddings-wasm.js +21 -10
- package/dist/wasm/onnx-embeddings-wasm.js.map +1 -1
- package/docs/docker-tests/TEST-V1.7.8.Dockerfile +2 -2
- package/docs/docker-tests/TEST-V1.7.9-NODE20.Dockerfile +2 -2
- package/docs/docker-tests/TEST-V1.7.9.Dockerfile +2 -2
- package/docs/guides/CLAUDE-CODE-INTEGRATION.md +25 -8
- package/docs/guides/PROXY-ARCHITECTURE-AND-EXTENSION.md +22 -9
- package/docs/guides/STANDALONE_PROXY_GUIDE.md +16 -6
- package/docs/integrations/FASTMCP_CLI_INTEGRATION.md +1 -1
- package/docs/plans/requesty/02-architecture.md +5 -6
- package/docs/plans/requesty/03-implementation-phases.md +3 -4
- package/docs/plans/requesty/05-migration-guide.md +2 -2
- package/package.json +12 -7
- package/scripts/validate-providers.sh +7 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../../src/reasoningbank/utils/embeddings.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,iFAAiF;AACjF,kFAAkF;AAClF,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,6BAA6B;AACnE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,8BAA8B;AAErE,MAAM,QAAQ,GAAG,yBAAyB,CAAC;AAC3C,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAC/B,6EAA6E;AAC7E,uDAAuD;AACvD,EAAE;AACF,+EAA+E;AAC/E,4EAA4E;AAC5E,6EAA6E;AAC7E,8DAA8D;AAC9D,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,IAAI,iBAAiB,GAAQ,IAAI,CAAC;AAClC,IAAI,qBAAqB,GAAyB,IAAI,CAAC;AAUvD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAsB,CAAC;AAErD,SAAS,QAAQ,CAAC,GAAW;IAC3B,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,SAAS,CAAC;IACpD,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,6BAA6B;IAC7D,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW,EAAE,MAAoB,EAAE,UAAkB;IACrE,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,EAAE,CAAC,CAAC;IAC/E,OAAO,cAAc,CAAC,IAAI,GAAG,iBAAiB,EAAE,CAAC;QAC/C,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QAClD,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM;QAChC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,UAAU,EAAE,EAAE,UAAU,EAAE,iBAAiB,IAAI,IAAI,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,oBAAoB;IACjC,sBAAsB;IACtB,IAAI,iBAAiB;QAAE,OAAO;IAE9B,sDAAsD;IACtD,IAAI,qBAAqB,EAAE,CAAC;QAC1B,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAED,qEAAqE;IACrE,qBAAqB,GAAG,CAAC,KAAK,IAAI,EAAE;QAClC,OAAO,CAAC,KAAK,CAAC,oDAAoD,QAAQ,MAAM,CAAC,CAAC;QAClF,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;QAErE,IAAI,CAAC;YACH,iBAAiB,GAAG,MAAM,QAAQ,CAChC,oBAAoB,EACpB,QAAQ,EACR,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,oBAAoB;aACrC,CAAC;YACF,OAAO,CAAC,KAAK,CAAC,oCAAoC,UAAU,cAAc,CAAC,CAAC;QAC9E,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC;YAC7E,OAAO,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;YACnF,qCAAqC;YACrC,qBAAqB,GAAG,IAAI,CAAC;QAC/B,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,iBAAiB,KAAK,IAAI,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY;IACjD,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC,IAAI,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IACrE,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,uBAAuB;IACvB,MAAM,oBAAoB,EAAE,CAAC;IAE7B,IAAI,SAAuB,CAAC;IAC5B,IAAI,IAAI,GAAqB,OAAO,CAAC;IAErC,IAAI,iBAAiB,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE;gBAC3C,OAAO,EAAE,MAAM;gBACf,SAAS,EAAE,IAAI;aAChB,CAAC,CAAC;YACH,SAAS,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC;YAC1E,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YACxC,IAAI,GAAG,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,GAAG,UAAU,EAAE,EAAE,UAAU,EAAE,UAAU,IAAI,UAAU,CAAC;QAChE,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAClC,IAAI,GAAG,MAAM,CAAC;IAChB,CAAC;IAED,QAAQ,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;IAC1D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,KAAe;IACzD,MAAM,oBAAoB,EAAE,CAAC;IAC7B,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,OAAO,GAAmB,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC,CAAC,yBAAyB;IAEtE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;QACzC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;YACpB,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,OAAO;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;YACxB,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAE/C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,IAAI,gBAAgB,EAAE,CAAC;QAC1E,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,gBAAgB,CAAC,CAAC;QACjE,IAAI,OAAuB,CAAC;QAC5B,IAAI,IAAI,GAAqB,OAAO,CAAC;QAErC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACjD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAoB,CAAC;YACzC,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3F,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC;YAChF,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;YACzD,IAAI,GAAG,MAAM,CAAC;QAChB,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACxB,QAAQ,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAC7C,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAE;gBAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,UAAU,CAAC,CAAC,uCAAuC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,IAAY,EAAE,IAAY;IAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;IAEnC,wDAAwD;IACxD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAChD,IAAI,IAAI,CAAC,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,GAAiB;IAClC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAErB,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAE1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;IAChB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,cAAc,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC","sourcesContent":["/**\n * Embedding generation for semantic similarity\n * Uses local transformers.js - no API key required!\n *\n * Logs go to stderr so that stdout stays clean for callers that pipe\n * structured output (CLIs, MCP stdio servers).\n */\n\nimport { pipeline, env } from '@huggingface/transformers';\nimport { loadConfig } from './config.js';\n\n// Configure transformers.js to use WASM backend only (avoid ONNX runtime issues)\n// The native ONNX runtime causes \"DefaultLogger not registered\" errors in Node.js\nenv.backends.onnx.wasm.proxy = false; // Disable ONNX runtime proxy\nenv.backends.onnx.wasm.numThreads = 1; // Single thread for stability\n\nconst MODEL_ID = 'Xenova/all-MiniLM-L6-v2';\nconst MODEL_DIMS = 384;\nconst CACHE_MAX_ENTRIES = 1000;\n// Texts per pipeline call in computeEmbeddingBatch. Bounds padding waste and\n// peak memory when a batch mixes short and long texts.\n//\n// The q8 model quantizes activations dynamically with one scale per tensor, so\n// a batched vector depends slightly on its batch-mates: cosine vs. the same\n// text embedded alone is ~0.99 (min ~0.985 measured). fp32 weights are exact\n// but slower for single texts and change every stored vector.\nconst BATCH_CHUNK_SIZE = 32;\n\nlet embeddingPipeline: any = null;\nlet initializationPromise: Promise<void> | null = null;\n\n/**\n * LRU cache with lazy TTL expiry. No timers: an expired entry is dropped when\n * it is next read, so the cache never keeps the event loop alive.\n */\ninterface CacheEntry {\n vector: Float32Array;\n expiresAt: number;\n}\nconst embeddingCache = new Map<string, CacheEntry>();\n\nfunction cacheGet(key: string): Float32Array | undefined {\n const entry = embeddingCache.get(key);\n if (!entry) return undefined;\n embeddingCache.delete(key);\n if (entry.expiresAt <= Date.now()) return undefined;\n embeddingCache.set(key, entry); // move to most-recently-used\n return entry.vector;\n}\n\nfunction cacheSet(key: string, vector: Float32Array, ttlSeconds: number): void {\n embeddingCache.delete(key);\n embeddingCache.set(key, { vector, expiresAt: Date.now() + ttlSeconds * 1000 });\n while (embeddingCache.size > CACHE_MAX_ENTRIES) {\n const oldest = embeddingCache.keys().next().value;\n if (oldest === undefined) break;\n embeddingCache.delete(oldest);\n }\n}\n\nfunction cacheTtlSeconds(): number {\n return loadConfig()?.embeddings?.cache_ttl_seconds || 3600;\n}\n\n/**\n * Initialize the embedding pipeline (lazy load)\n * RACE CONDITION FIX: Use promise-based initialization instead of busy-wait\n */\nasync function initializeEmbeddings(): Promise<void> {\n // Already initialized\n if (embeddingPipeline) return;\n\n // Initialization in progress - await existing promise\n if (initializationPromise) {\n return initializationPromise;\n }\n\n // RACE CONDITION FIX: Create promise for concurrent callers to await\n initializationPromise = (async () => {\n console.error(`[Embeddings] Initializing local embedding model (${MODEL_ID})...`);\n console.error('[Embeddings] First run will download ~23MB model...');\n\n try {\n embeddingPipeline = await pipeline(\n 'feature-extraction',\n MODEL_ID,\n { dtype: 'q8' } // quantized weights\n );\n console.error(`[Embeddings] Local model ready! (${MODEL_DIMS} dimensions)`);\n } catch (error: any) {\n console.error('[Embeddings] Failed to initialize:', error?.message || error);\n console.error('[Embeddings] Falling back to hash-based embeddings (not semantic)');\n // Reset promise so retry is possible\n initializationPromise = null;\n }\n })();\n\n return initializationPromise;\n}\n\n/**\n * True when embeddings come from the transformer model; false when they come\n * from the deterministic hash fallback, whose vectors carry no semantic\n * similarity. Returns false until the first embedding call has initialized the\n * model.\n */\nexport function isSemanticEmbedding(): boolean {\n return embeddingPipeline !== null;\n}\n\n/**\n * Compute embedding for text using local model\n */\nexport async function computeEmbedding(text: string): Promise<Float32Array> {\n const cached = cacheGet(`local:${text}`) ?? cacheGet(`hash:${text}`);\n if (cached) return cached;\n\n // Initialize if needed\n await initializeEmbeddings();\n\n let embedding: Float32Array;\n let kind: 'local' | 'hash' = 'local';\n\n if (embeddingPipeline) {\n try {\n const output = await embeddingPipeline(text, {\n pooling: 'mean',\n normalize: true\n });\n embedding = new Float32Array(output.data);\n } catch (error: any) {\n console.error('[Embeddings] Generation failed:', error?.message || error);\n embedding = hashEmbed(text, MODEL_DIMS);\n kind = 'hash';\n }\n } else {\n const dims = loadConfig()?.embeddings?.dimensions || MODEL_DIMS;\n embedding = hashEmbed(text, dims);\n kind = 'hash';\n }\n\n cacheSet(`${kind}:${text}`, embedding, cacheTtlSeconds());\n return embedding;\n}\n\n/**\n * Batch compute embeddings. Cache misses are embedded with one pipeline call\n * per chunk of texts rather than one call per text.\n */\nexport async function computeEmbeddingBatch(texts: string[]): Promise<Float32Array[]> {\n await initializeEmbeddings();\n if (!embeddingPipeline) {\n return Promise.all(texts.map(text => computeEmbedding(text)));\n }\n\n const results: Float32Array[] = new Array(texts.length);\n const missing = new Map<string, number[]>(); // text -> result indices\n\n texts.forEach((text, i) => {\n const cached = cacheGet(`local:${text}`);\n if (cached) {\n results[i] = cached;\n return;\n }\n const indices = missing.get(text);\n if (indices) indices.push(i);\n else missing.set(text, [i]);\n });\n\n const ttl = cacheTtlSeconds();\n const uniqueTexts = Array.from(missing.keys());\n\n for (let start = 0; start < uniqueTexts.length; start += BATCH_CHUNK_SIZE) {\n const chunk = uniqueTexts.slice(start, start + BATCH_CHUNK_SIZE);\n let vectors: Float32Array[];\n let kind: 'local' | 'hash' = 'local';\n\n try {\n const output = await embeddingPipeline(chunk, { pooling: 'mean', normalize: true });\n const dims = output.dims[output.dims.length - 1];\n const data = output.data as Float32Array;\n vectors = chunk.map((_, j) => new Float32Array(data.subarray(j * dims, (j + 1) * dims)));\n } catch (error: any) {\n console.error('[Embeddings] Batch generation failed:', error?.message || error);\n vectors = chunk.map(text => hashEmbed(text, MODEL_DIMS));\n kind = 'hash';\n }\n\n chunk.forEach((text, j) => {\n cacheSet(`${kind}:${text}`, vectors[j], ttl);\n for (const i of missing.get(text)!) results[i] = vectors[j];\n });\n }\n\n return results;\n}\n\n/**\n * Get embedding dimensions\n */\nexport function getEmbeddingDimensions(): number {\n return MODEL_DIMS; // all-MiniLM-L6-v2 uses 384 dimensions\n}\n\n/**\n * Deterministic hash-based embedding (fallback)\n */\nfunction hashEmbed(text: string, dims: number): Float32Array {\n const hash = simpleHash(text);\n const vec = new Float32Array(dims);\n\n // Generate deterministic pseudo-random vector from hash\n for (let i = 0; i < dims; i++) {\n vec[i] = Math.sin(hash * (i + 1) * 0.01) + Math.cos(hash * i * 0.02);\n }\n\n return normalize(vec);\n}\n\n/**\n * Simple string hash function\n */\nfunction simpleHash(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) - hash) + str.charCodeAt(i);\n hash |= 0;\n }\n return Math.abs(hash);\n}\n\n/**\n * Normalize vector to unit length\n */\nfunction normalize(vec: Float32Array): Float32Array {\n let mag = 0;\n for (let i = 0; i < vec.length; i++) {\n mag += vec[i] * vec[i];\n }\n mag = Math.sqrt(mag);\n\n if (mag === 0) return vec;\n\n for (let i = 0; i < vec.length; i++) {\n vec[i] /= mag;\n }\n return vec;\n}\n\n/**\n * Clear embedding cache\n */\nexport function clearEmbeddingCache(): void {\n embeddingCache.clear();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../../src/reasoningbank/utils/embeddings.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Embedding generation for semantic similarity\n * Uses local transformers.js - no API key required!\n *\n * Logs go to stderr so that stdout stays clean for callers that pipe\n * structured output (CLIs, MCP stdio servers).\n */\n\nimport { pipeline, env } from '@huggingface/transformers';\nimport { loadConfig } from './config.js';\n\n// Configure transformers.js to use WASM backend only (avoid ONNX runtime issues)\n// The native ONNX runtime causes \"DefaultLogger not registered\" errors in Node.js\nenv.backends.onnx.wasm.proxy = false; // Disable ONNX runtime proxy\nenv.backends.onnx.wasm.numThreads = 1; // Single thread for stability\n\nconst MODEL_ID = 'Xenova/all-MiniLM-L6-v2';\nconst MODEL_DIMS = 384;\nconst CACHE_MAX_ENTRIES = 1000;\n// Texts per pipeline call in computeEmbeddingBatch. Bounds padding waste and\n// peak memory when a batch mixes short and long texts.\n//\n// The q8 model quantizes activations dynamically with one scale per tensor, so\n// a batched vector depends slightly on its batch-mates: cosine vs. the same\n// text embedded alone is ~0.99 (min ~0.985 measured). fp32 weights are exact\n// but slower for single texts and change every stored vector.\nconst BATCH_CHUNK_SIZE = 32;\n\nlet embeddingPipeline: any = null;\nlet initializationPromise: Promise<void> | null = null;\n\n/**\n * The space every vector in this process belongs to. Set by the first\n * embedding and kept until resetEmbeddingBackend(): model vectors and hash\n * vectors are not comparable, so a process never mixes them (issue #246).\n */\nexport type EmbeddingKind = 'local' | 'hash';\nlet embeddingKind: EmbeddingKind | null = null;\n\n/**\n * LRU cache with lazy TTL expiry. No timers: an expired entry is dropped when\n * it is next read, so the cache never keeps the event loop alive.\n */\ninterface CacheEntry {\n vector: Float32Array;\n expiresAt: number;\n}\nconst embeddingCache = new Map<string, CacheEntry>();\n\nfunction cacheGet(key: string): Float32Array | undefined {\n const entry = embeddingCache.get(key);\n if (!entry) return undefined;\n embeddingCache.delete(key);\n if (entry.expiresAt <= Date.now()) return undefined;\n embeddingCache.set(key, entry); // move to most-recently-used\n return entry.vector;\n}\n\nfunction cacheSet(key: string, vector: Float32Array, ttlSeconds: number): void {\n embeddingCache.delete(key);\n embeddingCache.set(key, { vector, expiresAt: Date.now() + ttlSeconds * 1000 });\n while (embeddingCache.size > CACHE_MAX_ENTRIES) {\n const oldest = embeddingCache.keys().next().value;\n if (oldest === undefined) break;\n embeddingCache.delete(oldest);\n }\n}\n\nfunction cacheTtlSeconds(): number {\n return loadConfig()?.embeddings?.cache_ttl_seconds || 3600;\n}\n\n/**\n * Initialize the embedding pipeline (lazy load)\n * RACE CONDITION FIX: Use promise-based initialization instead of busy-wait\n */\nasync function initializeEmbeddings(): Promise<void> {\n // Already initialized\n if (embeddingPipeline) return;\n\n // Initialization in progress - await existing promise\n if (initializationPromise) {\n return initializationPromise;\n }\n\n // RACE CONDITION FIX: Create promise for concurrent callers to await\n initializationPromise = (async () => {\n console.error(`[Embeddings] Initializing local embedding model (${MODEL_ID})...`);\n console.error('[Embeddings] First run will download ~23MB model...');\n\n try {\n embeddingPipeline = await pipeline(\n 'feature-extraction',\n MODEL_ID,\n { dtype: 'q8' } // quantized weights\n );\n console.error(`[Embeddings] Local model ready! (${MODEL_DIMS} dimensions)`);\n } catch (error: any) {\n console.error('[Embeddings] Failed to initialize:', error?.message || error);\n console.error('[Embeddings] Falling back to hash-based embeddings (not semantic)');\n // No automatic retry: a later successful load would put new vectors in a\n // different space from the hash vectors already handed out. Call\n // resetEmbeddingBackend() to try the model again.\n }\n })();\n\n return initializationPromise;\n}\n\n/**\n * True when embeddings come from the transformer model; false when they come\n * from the deterministic hash fallback, whose vectors carry no semantic\n * similarity. Returns false until the first embedding call has initialized the\n * model.\n */\nexport function isSemanticEmbedding(): boolean {\n return embeddingKind === 'local' || (embeddingKind === null && embeddingPipeline !== null);\n}\n\n/**\n * The space this process's embeddings are in, or null before the first one.\n * Vectors of different kinds must never be compared.\n */\nexport function getEmbeddingKind(): EmbeddingKind | null {\n return embeddingKind;\n}\n\n/**\n * Forget the backend choice and the cache, so the next embedding tries to load\n * the model again. Vectors produced before the reset may be in a different\n * space from those produced after it; re-embed anything you keep.\n */\nexport function resetEmbeddingBackend(): void {\n embeddingPipeline = null;\n initializationPromise = null;\n embeddingKind = null;\n embeddingCache.clear();\n}\n\n/** Fix the process to one space on first use; refuse to switch afterwards. */\nfunction commitKind(kind: EmbeddingKind): void {\n if (embeddingKind === null) embeddingKind = kind;\n else if (embeddingKind !== kind) {\n throw new Error(\n `[Embeddings] refusing to produce a ${kind} vector: this process already embeds with ${embeddingKind}`\n );\n }\n}\n\nfunction modelFailure(error: any): Error {\n return new Error(\n `[Embeddings] model failed (${error?.message || error}); not substituting a hash vector, which would not be comparable with the model vectors already produced`\n );\n}\n\n/**\n * Compute embedding for text using local model\n */\nexport async function computeEmbedding(text: string): Promise<Float32Array> {\n const cached = cacheGet(text);\n if (cached) return cached;\n\n // A process already in the hash space stays there; don't load the model.\n if (embeddingKind !== 'hash') await initializeEmbeddings();\n\n let embedding: Float32Array;\n if (embeddingPipeline) {\n commitKind('local');\n try {\n const output = await embeddingPipeline(text, {\n pooling: 'mean',\n normalize: true\n });\n embedding = new Float32Array(output.data);\n } catch (error: any) {\n console.error('[Embeddings] Generation failed:', error?.message || error);\n throw modelFailure(error);\n }\n } else {\n commitKind('hash');\n const dims = loadConfig()?.embeddings?.dimensions || MODEL_DIMS;\n embedding = hashEmbed(text, dims);\n }\n\n cacheSet(text, embedding, cacheTtlSeconds());\n return embedding;\n}\n\n/**\n * Batch compute embeddings. Cache misses are embedded with one pipeline call\n * per chunk of texts rather than one call per text.\n */\nexport async function computeEmbeddingBatch(texts: string[]): Promise<Float32Array[]> {\n if (embeddingKind !== 'hash') await initializeEmbeddings();\n if (!embeddingPipeline) {\n return Promise.all(texts.map(text => computeEmbedding(text)));\n }\n commitKind('local');\n\n const results: Float32Array[] = new Array(texts.length);\n const missing = new Map<string, number[]>(); // text -> result indices\n\n texts.forEach((text, i) => {\n const cached = cacheGet(text);\n if (cached) {\n results[i] = cached;\n return;\n }\n const indices = missing.get(text);\n if (indices) indices.push(i);\n else missing.set(text, [i]);\n });\n\n const ttl = cacheTtlSeconds();\n const uniqueTexts = Array.from(missing.keys());\n\n for (let start = 0; start < uniqueTexts.length; start += BATCH_CHUNK_SIZE) {\n const chunk = uniqueTexts.slice(start, start + BATCH_CHUNK_SIZE);\n let vectors: Float32Array[];\n\n try {\n const output = await embeddingPipeline(chunk, { pooling: 'mean', normalize: true });\n const dims = output.dims[output.dims.length - 1];\n const data = output.data as Float32Array;\n vectors = chunk.map((_, j) => new Float32Array(data.subarray(j * dims, (j + 1) * dims)));\n } catch (error: any) {\n console.error('[Embeddings] Batch generation failed:', error?.message || error);\n throw modelFailure(error);\n }\n\n chunk.forEach((text, j) => {\n cacheSet(text, vectors[j], ttl);\n for (const i of missing.get(text)!) results[i] = vectors[j];\n });\n }\n\n return results;\n}\n\n/**\n * Get embedding dimensions\n */\nexport function getEmbeddingDimensions(): number {\n return MODEL_DIMS; // all-MiniLM-L6-v2 uses 384 dimensions\n}\n\n/**\n * Deterministic hash-based embedding (fallback)\n */\nfunction hashEmbed(text: string, dims: number): Float32Array {\n const hash = simpleHash(text);\n const vec = new Float32Array(dims);\n\n // Generate deterministic pseudo-random vector from hash\n for (let i = 0; i < dims; i++) {\n vec[i] = Math.sin(hash * (i + 1) * 0.01) + Math.cos(hash * i * 0.02);\n }\n\n return normalize(vec);\n}\n\n/**\n * Simple string hash function\n */\nfunction simpleHash(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) - hash) + str.charCodeAt(i);\n hash |= 0;\n }\n return Math.abs(hash);\n}\n\n/**\n * Normalize vector to unit length\n */\nfunction normalize(vec: Float32Array): Float32Array {\n let mag = 0;\n for (let i = 0; i < vec.length; i++) {\n mag += vec[i] * vec[i];\n }\n mag = Math.sqrt(mag);\n\n if (mag === 0) return vec;\n\n for (let i = 0; i < vec.length; i++) {\n vec[i] /= mag;\n }\n return vec;\n}\n\n/**\n * Clear embedding cache\n */\nexport function clearEmbeddingCache(): void {\n embeddingCache.clear();\n}\n"]}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Semantic Router - HNSW-Powered Intent Matching
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Uses @ruvector/router's native HNSW index (VectorDb) when the package and
|
|
5
|
+
* its platform binary are installed, and an exact brute-force cosine scan
|
|
6
|
+
* otherwise. Both return the same ranking contract: similarity in [-1, 1],
|
|
7
|
+
* highest first.
|
|
5
8
|
*
|
|
6
9
|
* Features:
|
|
7
10
|
* - HNSW (Hierarchical Navigable Small World) index
|
|
@@ -17,6 +20,7 @@
|
|
|
17
20
|
* - O(log N) search complexity
|
|
18
21
|
*/
|
|
19
22
|
import type { EmbeddingService } from 'agentdb';
|
|
23
|
+
export type SemanticIndexBackend = 'ruvector-router' | 'brute-force';
|
|
20
24
|
export interface AgentIntent {
|
|
21
25
|
/** Agent type identifier */
|
|
22
26
|
agentType: string;
|
|
@@ -73,8 +77,17 @@ export declare class SemanticRouter {
|
|
|
73
77
|
private agentIntents;
|
|
74
78
|
private intentEmbeddings;
|
|
75
79
|
private indexBuilt;
|
|
80
|
+
private nativeIndex;
|
|
81
|
+
private backend;
|
|
82
|
+
private readonly useNative;
|
|
76
83
|
private routingStats;
|
|
77
|
-
|
|
84
|
+
/**
|
|
85
|
+
* @param embedder - Embedding service for intents and tasks
|
|
86
|
+
* @param options.native - Use @ruvector/router when installed (default true)
|
|
87
|
+
*/
|
|
88
|
+
constructor(embedder: EmbeddingService, options?: {
|
|
89
|
+
native?: boolean;
|
|
90
|
+
});
|
|
78
91
|
/**
|
|
79
92
|
* Register agent intent for routing
|
|
80
93
|
*
|
|
@@ -88,12 +101,15 @@ export declare class SemanticRouter {
|
|
|
88
101
|
*/
|
|
89
102
|
registerAgents(intents: AgentIntent[]): Promise<void>;
|
|
90
103
|
/**
|
|
91
|
-
* Build
|
|
104
|
+
* Build the routing index.
|
|
92
105
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
106
|
+
* Loads @ruvector/router's native HNSW (VectorDb, cosine metric) when it is
|
|
107
|
+
* installed; otherwise routing uses an exact brute-force scan, which is
|
|
108
|
+
* still fast at the ~66-agent scale.
|
|
95
109
|
*/
|
|
96
110
|
buildIndex(): void;
|
|
111
|
+
/** Which index answered the last build: native HNSW or brute force. */
|
|
112
|
+
getIndexBackend(): SemanticIndexBackend;
|
|
97
113
|
/**
|
|
98
114
|
* Route task to best agent using semantic similarity
|
|
99
115
|
*
|
|
@@ -130,14 +146,14 @@ export declare class SemanticRouter {
|
|
|
130
146
|
*/
|
|
131
147
|
getRegisteredAgents(): AgentIntent[];
|
|
132
148
|
/**
|
|
133
|
-
* Search
|
|
149
|
+
* Search the index for nearest neighbours.
|
|
134
150
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
151
|
+
* @ruvector/router reports cosine *distance* (0 = identical), so it is
|
|
152
|
+
* converted to similarity (1 - distance) to keep "higher is better".
|
|
137
153
|
*
|
|
138
154
|
* @param queryEmbedding - Query vector
|
|
139
155
|
* @param k - Number of results
|
|
140
|
-
* @returns Top k candidates with similarity scores
|
|
156
|
+
* @returns Top k candidates with similarity scores, highest first
|
|
141
157
|
*/
|
|
142
158
|
private searchHNSW;
|
|
143
159
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SemanticRouter.d.ts","sourceRoot":"","sources":["../../src/routing/SemanticRouter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"SemanticRouter.d.ts","sourceRoot":"","sources":["../../src/routing/SemanticRouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Semantic Router - HNSW-Powered Intent Matching
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Uses @ruvector/router's native HNSW index (VectorDb) when the package and
|
|
5
|
+
* its platform binary are installed, and an exact brute-force cosine scan
|
|
6
|
+
* otherwise. Both return the same ranking contract: similarity in [-1, 1],
|
|
7
|
+
* highest first.
|
|
5
8
|
*
|
|
6
9
|
* Features:
|
|
7
10
|
* - HNSW (Hierarchical Navigable Small World) index
|
|
@@ -16,6 +19,7 @@
|
|
|
16
19
|
* - Support for 66+ agent types
|
|
17
20
|
* - O(log N) search complexity
|
|
18
21
|
*/
|
|
22
|
+
import { requireOptional } from '../integrations/optional.js';
|
|
19
23
|
/**
|
|
20
24
|
* Semantic Router
|
|
21
25
|
*
|
|
@@ -30,12 +34,20 @@ export class SemanticRouter {
|
|
|
30
34
|
// Agent registry
|
|
31
35
|
agentIntents;
|
|
32
36
|
intentEmbeddings;
|
|
33
|
-
// HNSW index
|
|
37
|
+
// Native HNSW index (@ruvector/router) or null for the brute-force scan
|
|
34
38
|
indexBuilt = false;
|
|
39
|
+
nativeIndex = null;
|
|
40
|
+
backend = 'brute-force';
|
|
41
|
+
useNative;
|
|
35
42
|
// Performance tracking
|
|
36
43
|
routingStats;
|
|
37
|
-
|
|
44
|
+
/**
|
|
45
|
+
* @param embedder - Embedding service for intents and tasks
|
|
46
|
+
* @param options.native - Use @ruvector/router when installed (default true)
|
|
47
|
+
*/
|
|
48
|
+
constructor(embedder, options = {}) {
|
|
38
49
|
this.embedder = embedder;
|
|
50
|
+
this.useNative = options.native ?? true;
|
|
39
51
|
this.agentIntents = new Map();
|
|
40
52
|
this.intentEmbeddings = new Map();
|
|
41
53
|
this.routingStats = {
|
|
@@ -71,16 +83,43 @@ export class SemanticRouter {
|
|
|
71
83
|
await Promise.all(intents.map(intent => this.registerAgent(intent)));
|
|
72
84
|
}
|
|
73
85
|
/**
|
|
74
|
-
* Build
|
|
86
|
+
* Build the routing index.
|
|
75
87
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
88
|
+
* Loads @ruvector/router's native HNSW (VectorDb, cosine metric) when it is
|
|
89
|
+
* installed; otherwise routing uses an exact brute-force scan, which is
|
|
90
|
+
* still fast at the ~66-agent scale.
|
|
78
91
|
*/
|
|
79
92
|
buildIndex() {
|
|
80
|
-
|
|
81
|
-
|
|
93
|
+
this.nativeIndex = null;
|
|
94
|
+
this.backend = 'brute-force';
|
|
95
|
+
const first = this.intentEmbeddings.values().next().value;
|
|
96
|
+
const router = this.useNative && first
|
|
97
|
+
? requireOptional('@ruvector/router')
|
|
98
|
+
: null;
|
|
99
|
+
if (router && first) {
|
|
100
|
+
try {
|
|
101
|
+
const db = new router.VectorDb({
|
|
102
|
+
dimensions: first.length,
|
|
103
|
+
distanceMetric: router.DistanceMetric.Cosine,
|
|
104
|
+
});
|
|
105
|
+
for (const [agentType, embedding] of Array.from(this.intentEmbeddings.entries())) {
|
|
106
|
+
db.insert(agentType, embedding instanceof Float32Array ? embedding : Float32Array.from(embedding));
|
|
107
|
+
}
|
|
108
|
+
this.nativeIndex = db;
|
|
109
|
+
this.backend = 'ruvector-router';
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Native binary missing for this platform, or a dimension mismatch:
|
|
113
|
+
// keep the exact brute-force path.
|
|
114
|
+
this.nativeIndex = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
82
117
|
this.indexBuilt = true;
|
|
83
118
|
}
|
|
119
|
+
/** Which index answered the last build: native HNSW or brute force. */
|
|
120
|
+
getIndexBackend() {
|
|
121
|
+
return this.backend;
|
|
122
|
+
}
|
|
84
123
|
/**
|
|
85
124
|
* Route task to best agent using semantic similarity
|
|
86
125
|
*
|
|
@@ -194,16 +233,28 @@ export class SemanticRouter {
|
|
|
194
233
|
// Private Helper Methods
|
|
195
234
|
// ========================================================================
|
|
196
235
|
/**
|
|
197
|
-
* Search
|
|
236
|
+
* Search the index for nearest neighbours.
|
|
198
237
|
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
238
|
+
* @ruvector/router reports cosine *distance* (0 = identical), so it is
|
|
239
|
+
* converted to similarity (1 - distance) to keep "higher is better".
|
|
201
240
|
*
|
|
202
241
|
* @param queryEmbedding - Query vector
|
|
203
242
|
* @param k - Number of results
|
|
204
|
-
* @returns Top k candidates with similarity scores
|
|
243
|
+
* @returns Top k candidates with similarity scores, highest first
|
|
205
244
|
*/
|
|
206
245
|
searchHNSW(queryEmbedding, k) {
|
|
246
|
+
if (this.nativeIndex) {
|
|
247
|
+
try {
|
|
248
|
+
const query = queryEmbedding instanceof Float32Array ? queryEmbedding : Float32Array.from(queryEmbedding);
|
|
249
|
+
return this.nativeIndex
|
|
250
|
+
.search(query, Math.min(k, this.intentEmbeddings.size))
|
|
251
|
+
.map((r) => ({ agentType: r.id, similarity: 1 - r.score }))
|
|
252
|
+
.sort((a, b) => b.similarity - a.similarity);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// fall through to the exact scan
|
|
256
|
+
}
|
|
257
|
+
}
|
|
207
258
|
const candidates = [];
|
|
208
259
|
for (const [agentType, embedding] of Array.from(this.intentEmbeddings.entries())) {
|
|
209
260
|
const similarity = this.cosineSimilarity(queryEmbedding, embedding);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SemanticRouter.js","sourceRoot":"","sources":["../../src/routing/SemanticRouter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAkDH;;;;;;;;GAQG;AACH,MAAM,OAAO,cAAc;IACjB,QAAQ,CAAmB;IAEnC,iBAAiB;IACT,YAAY,CAA2B;IACvC,gBAAgB,CAA4B;IAEpD,gEAAgE;IACxD,UAAU,GAAY,KAAK,CAAC;IAEpC,uBAAuB;IACf,YAAY,CAIlB;IAEF,YAAY,QAA0B;QACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,EAAE,CAAC;QAC9B,IAAI,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC,YAAY,GAAG;YAClB,WAAW,EAAE,CAAC;YACd,gBAAgB,EAAE,CAAC;YACnB,YAAY,EAAE,CAAC;SAChB,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,aAAa,CAAC,MAAmB;QACrC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAEhD,iDAAiD;QACjD,MAAM,UAAU,GAAG;YACjB,MAAM,CAAC,WAAW;YAClB,GAAG,MAAM,CAAC,QAAQ;YAClB,GAAG,MAAM,CAAC,IAAI;SACf,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEZ,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACxD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAEvD,gCAAgC;QAChC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,cAAc,CAAC,OAAsB;QACzC,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAED;;;;;OAKG;IACH,UAAU;QACR,uDAAuD;QACvD,mEAAmE;QACnE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,KAAK,CAAC,eAAuB,EAAE,IAAY,CAAC;QAChD,MAAM,gBAAgB,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAE3C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC;QAED,qBAAqB;QACrB,MAAM,kBAAkB,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAC7C,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACjE,MAAM,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,kBAAkB,CAAC;QAE/D,4BAA4B;QAC5B,MAAM,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACzD,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC;QAEzD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;QAED,0BAA0B;QAC1B,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QAC5C,MAAM,YAAY,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACxD,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,UAAU,EAAE,CAAC,CAAC,UAAU;SACzB,CAAC,CAAC,CAAC;QAEJ,MAAM,cAAc,GAAG,UAAU;aAC9B,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;aACf,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC;QAEnE,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC;QAE3D,eAAe;QACf,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;QAEhC,OAAO;YACL,YAAY;YACZ,UAAU;YACV,YAAY;YACZ,cAAc;YACd,OAAO,EAAE;gBACP,aAAa;gBACb,eAAe;gBACf,YAAY;gBACZ,mBAAmB,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI;aAC5C;SACF,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,iBAAiB,CACrB,eAAuB,EACvB,YAAoB,GAAG;QAEvB,uCAAuC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;QAEnD,qBAAqB;QACrB,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,GAAG,CACtC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAC,OAAO,EAAC,EAAE;YAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACjD,OAAO;gBACL,GAAG,MAAM;gBACT,WAAW,EAAE,OAAO,CAAC,IAAI;aAC1B,CAAC;QACJ,CAAC,CAAC,CACH,CAAC;QAEF,kCAAkC;QAClC,MAAM,OAAO,GAAG,cAAc;aAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,SAAS,CAAC;aACtC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACT,SAAS,EAAE,CAAC,CAAC,YAAY;YACzB,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,WAAW,EAAE,CAAC,CAAC,WAAW;SAC3B,CAAC,CAAC,CAAC;QAEN,sCAAsC;QACtC,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACvD,MAAM,kBAAkB,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;QAEpD,gDAAgD;QAChD,MAAM,cAAc,GAAG,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;QAEhF,OAAO;YACL,OAAO,EAAE,aAAa;YACtB,kBAAkB;YAClB,cAAc;SACf,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,QAAQ;QACN,OAAO,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;IAClC,CAAC;IAED;;;;OAIG;IACH,mBAAmB;QACjB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,2EAA2E;IAC3E,yBAAyB;IACzB,2EAA2E;IAE3E;;;;;;;;;OASG;IACK,UAAU,CAAC,cAA4B,EAAE,CAAS;QAIxD,MAAM,UAAU,GAAqD,EAAE,CAAC;QAExE,KAAK,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;YACjF,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;YACpE,UAAU,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,kCAAkC;QAClC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QAEvD,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,CAAe,EAAE,CAAe;QACvD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAClC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACrB,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACvB,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,KAAK,CAAC;IAC9C,CAAC;IAED;;OAEG;IACK,WAAW,CAAC,eAAuB;QACzC,mDAAmD;QACnD,MAAM,SAAS,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAExE,MAAM,QAAQ,GAA2C,EAAE,CAAC;QAE5D,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE;YAClC,uCAAuC;YACvC,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;YAEnE,WAAW;iBACR,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB;iBAClD,OAAO,CAAC,OAAO,CAAC,EAAE;gBACjB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;gBAC/B,IAAI,OAAO,EAAE,CAAC;oBACZ,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;gBAC/C,CAAC;YACH,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC;IAED;;OAEG;IACK,kBAAkB,CACxB,OAA8E;QAE9E,MAAM,IAAI,GAAG,IAAI,GAAG,EAA6B,CAAC;QAElD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC5C,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACzD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;IAC/E,CAAC;IAED;;OAEG;IACK,mBAAmB,CACzB,OAA8E,EAC9E,eAAuB;QAEvB,uDAAuD;QACvD,MAAM,SAAS,GAAG,eAAe,CAAC,WAAW,EAAE,CAAC;QAEhD,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;YACzC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;YACrE,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAEtD,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACK,WAAW,CAAC,aAAqB;QACvC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;QAEhC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,gBAAgB;QACnC,IAAI,CAAC,YAAY,CAAC,gBAAgB;YAChC,IAAI,CAAC,YAAY,CAAC,gBAAgB,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,aAAa,GAAG,KAAK,CAAC;IAC7E,CAAC;CACF","sourcesContent":["/**\n * Semantic Router - HNSW-Powered Intent Matching\n *\n * Integrates @ruvector/router for sub-10ms semantic routing.\n *\n * Features:\n * - HNSW (Hierarchical Navigable Small World) index\n * - Intent classification for 66+ agents\n * - Sub-10ms routing latency\n * - Automatic intent embedding and indexing\n * - Multi-intent detection\n *\n * Performance:\n * - <10ms routing time\n * - >85% routing accuracy\n * - Support for 66+ agent types\n * - O(log N) search complexity\n */\n\n// Import from agentdb package\nimport type { EmbeddingService } from 'agentdb';\n\nexport interface AgentIntent {\n /** Agent type identifier */\n agentType: string;\n /** Natural language description of agent capabilities */\n description: string;\n /** Example tasks for this agent */\n examples: string[];\n /** Agent specialty tags */\n tags: string[];\n}\n\nexport interface RoutingResult {\n /** Primary agent selection */\n primaryAgent: string;\n /** Confidence score (0-1) */\n confidence: number;\n /** Secondary agent suggestions */\n alternatives: Array<{\n agentType: string;\n confidence: number;\n }>;\n /** Matched intent descriptions */\n matchedIntents: string[];\n /** Routing metrics */\n metrics: {\n routingTimeMs: number;\n embeddingTimeMs: number;\n searchTimeMs: number;\n candidatesEvaluated: number;\n };\n}\n\nexport interface MultiIntentResult {\n /** Detected intents in order of confidence */\n intents: Array<{\n agentType: string;\n confidence: number;\n matchedText: string;\n }>;\n /** Whether task requires multiple agents */\n requiresMultiAgent: boolean;\n /** Suggested execution order */\n executionOrder: string[];\n}\n\n/**\n * Semantic Router\n *\n * Provides intent-based agent routing:\n * 1. Register agent intents with descriptions\n * 2. Build HNSW index for fast semantic search\n * 3. Route tasks to agents based on intent similarity\n * 4. Support multi-intent detection for complex tasks\n */\nexport class SemanticRouter {\n private embedder: EmbeddingService;\n\n // Agent registry\n private agentIntents: Map<string, AgentIntent>;\n private intentEmbeddings: Map<string, Float32Array>;\n\n // HNSW index simulation (production would use @ruvector/router)\n private indexBuilt: boolean = false;\n\n // Performance tracking\n private routingStats: {\n totalRoutes: number;\n avgRoutingTimeMs: number;\n accuracyRate: number; // Requires feedback\n };\n\n constructor(embedder: EmbeddingService) {\n this.embedder = embedder;\n this.agentIntents = new Map();\n this.intentEmbeddings = new Map();\n this.routingStats = {\n totalRoutes: 0,\n avgRoutingTimeMs: 0,\n accuracyRate: 0,\n };\n }\n\n /**\n * Register agent intent for routing\n *\n * @param intent - Agent intent configuration\n */\n async registerAgent(intent: AgentIntent): Promise<void> {\n this.agentIntents.set(intent.agentType, intent);\n\n // Generate embedding from description + examples\n const intentText = [\n intent.description,\n ...intent.examples,\n ...intent.tags,\n ].join(' ');\n\n const embedding = await this.embedder.embed(intentText);\n this.intentEmbeddings.set(intent.agentType, embedding);\n\n // Mark index as needing rebuild\n this.indexBuilt = false;\n }\n\n /**\n * Register multiple agents in batch\n *\n * @param intents - Array of agent intents\n */\n async registerAgents(intents: AgentIntent[]): Promise<void> {\n await Promise.all(intents.map(intent => this.registerAgent(intent)));\n }\n\n /**\n * Build HNSW index for fast routing\n *\n * In production, this would use @ruvector/router's native HNSW.\n * For this implementation, we use a simplified version.\n */\n buildIndex(): void {\n // In production: Initialize HNSW with intentEmbeddings\n // For now, we'll use brute-force search (still fast for 66 agents)\n this.indexBuilt = true;\n }\n\n /**\n * Route task to best agent using semantic similarity\n *\n * Process:\n * 1. Embed task description\n * 2. Search HNSW index for nearest intents\n * 3. Return top matches with confidence scores\n *\n * @param taskDescription - Natural language task description\n * @param k - Number of alternatives to return (default: 3)\n * @returns Routing result with primary agent and alternatives\n */\n async route(taskDescription: string, k: number = 3): Promise<RoutingResult> {\n const overallStartTime = performance.now();\n\n if (!this.indexBuilt) {\n this.buildIndex();\n }\n\n // Step 1: Embed task\n const embeddingStartTime = performance.now();\n const taskEmbedding = await this.embedder.embed(taskDescription);\n const embeddingTimeMs = performance.now() - embeddingStartTime;\n\n // Step 2: Search HNSW index\n const searchStartTime = performance.now();\n const candidates = this.searchHNSW(taskEmbedding, k + 1);\n const searchTimeMs = performance.now() - searchStartTime;\n\n if (candidates.length === 0) {\n throw new Error('No agents registered for routing');\n }\n\n // Step 3: Extract results\n const primaryAgent = candidates[0].agentType;\n const confidence = candidates[0].similarity;\n const alternatives = candidates.slice(1, k + 1).map(c => ({\n agentType: c.agentType,\n confidence: c.similarity,\n }));\n\n const matchedIntents = candidates\n .slice(0, k + 1)\n .map(c => this.agentIntents.get(c.agentType)?.description ?? '');\n\n const routingTimeMs = performance.now() - overallStartTime;\n\n // Update stats\n this.updateStats(routingTimeMs);\n\n return {\n primaryAgent,\n confidence,\n alternatives,\n matchedIntents,\n metrics: {\n routingTimeMs,\n embeddingTimeMs,\n searchTimeMs,\n candidatesEvaluated: this.agentIntents.size,\n },\n };\n }\n\n /**\n * Detect multiple intents in complex task\n *\n * Useful for tasks requiring coordination of multiple agents.\n *\n * @param taskDescription - Task that may require multiple agents\n * @param threshold - Minimum confidence for intent detection (default: 0.6)\n * @returns Multi-intent result with suggested execution order\n */\n async detectMultiIntent(\n taskDescription: string,\n threshold: number = 0.6\n ): Promise<MultiIntentResult> {\n // Split task into sentences or clauses\n const segments = this.segmentTask(taskDescription);\n\n // Route each segment\n const segmentResults = await Promise.all(\n segments.map(async segment => {\n const result = await this.route(segment.text, 1);\n return {\n ...result,\n matchedText: segment.text,\n };\n })\n );\n\n // Collect high-confidence intents\n const intents = segmentResults\n .filter(r => r.confidence >= threshold)\n .map(r => ({\n agentType: r.primaryAgent,\n confidence: r.confidence,\n matchedText: r.matchedText,\n }));\n\n // Deduplicate and order by confidence\n const uniqueIntents = this.deduplicateIntents(intents);\n const requiresMultiAgent = uniqueIntents.length > 1;\n\n // Suggest execution order based on dependencies\n const executionOrder = this.inferExecutionOrder(uniqueIntents, taskDescription);\n\n return {\n intents: uniqueIntents,\n requiresMultiAgent,\n executionOrder,\n };\n }\n\n /**\n * Get routing statistics\n *\n * @returns Cumulative routing metrics\n */\n getStats(): typeof this.routingStats {\n return { ...this.routingStats };\n }\n\n /**\n * Get all registered agents\n *\n * @returns Array of registered agent intents\n */\n getRegisteredAgents(): AgentIntent[] {\n return Array.from(this.agentIntents.values());\n }\n\n // ========================================================================\n // Private Helper Methods\n // ========================================================================\n\n /**\n * Search HNSW index for nearest neighbors\n *\n * In production, this would use @ruvector/router's native HNSW.\n * For this implementation, we use brute-force cosine similarity.\n *\n * @param queryEmbedding - Query vector\n * @param k - Number of results\n * @returns Top k candidates with similarity scores\n */\n private searchHNSW(queryEmbedding: Float32Array, k: number): Array<{\n agentType: string;\n similarity: number;\n }> {\n const candidates: Array<{ agentType: string; similarity: number }> = [];\n\n for (const [agentType, embedding] of Array.from(this.intentEmbeddings.entries())) {\n const similarity = this.cosineSimilarity(queryEmbedding, embedding);\n candidates.push({ agentType, similarity });\n }\n\n // Sort by similarity (descending)\n candidates.sort((a, b) => b.similarity - a.similarity);\n\n return candidates.slice(0, k);\n }\n\n /**\n * Calculate cosine similarity\n */\n private cosineSimilarity(a: Float32Array, b: Float32Array): number {\n if (a.length !== b.length) {\n throw new Error('Vectors must have same length');\n }\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n\n const denom = Math.sqrt(normA) * Math.sqrt(normB);\n return denom === 0 ? 0 : dotProduct / denom;\n }\n\n /**\n * Segment task into independent clauses\n */\n private segmentTask(taskDescription: string): Array<{ text: string; index: number }> {\n // Split by sentences and coordination conjunctions\n const sentences = taskDescription.split(/[.!?]+/).filter(s => s.trim());\n\n const segments: Array<{ text: string; index: number }> = [];\n\n sentences.forEach((sentence, idx) => {\n // Further split by \"and\", \"then\", etc.\n const subSegments = sentence.split(/\\b(and|then|after|before)\\b/i);\n\n subSegments\n .filter((_, i) => i % 2 === 0) // Skip conjunctions\n .forEach(segment => {\n const trimmed = segment.trim();\n if (trimmed) {\n segments.push({ text: trimmed, index: idx });\n }\n });\n });\n\n return segments.length > 0 ? segments : [{ text: taskDescription, index: 0 }];\n }\n\n /**\n * Deduplicate intents by agent type\n */\n private deduplicateIntents(\n intents: Array<{ agentType: string; confidence: number; matchedText: string }>\n ): Array<{ agentType: string; confidence: number; matchedText: string }> {\n const seen = new Map<string, typeof intents[0]>();\n\n for (const intent of intents) {\n const existing = seen.get(intent.agentType);\n if (!existing || intent.confidence > existing.confidence) {\n seen.set(intent.agentType, intent);\n }\n }\n\n return Array.from(seen.values()).sort((a, b) => b.confidence - a.confidence);\n }\n\n /**\n * Infer execution order from intents and task description\n */\n private inferExecutionOrder(\n intents: Array<{ agentType: string; confidence: number; matchedText: string }>,\n taskDescription: string\n ): string[] {\n // Simple heuristic: order by position in original text\n const taskLower = taskDescription.toLowerCase();\n\n const withPositions = intents.map(intent => {\n const position = taskLower.indexOf(intent.matchedText.toLowerCase());\n return { ...intent, position };\n });\n\n withPositions.sort((a, b) => a.position - b.position);\n\n return withPositions.map(i => i.agentType);\n }\n\n /**\n * Update routing statistics\n */\n private updateStats(routingTimeMs: number): void {\n this.routingStats.totalRoutes++;\n\n const alpha = 0.1; // EMA smoothing\n this.routingStats.avgRoutingTimeMs =\n this.routingStats.avgRoutingTimeMs * (1 - alpha) + routingTimeMs * alpha;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"SemanticRouter.js","sourceRoot":"","sources":["../../src/routing/SemanticRouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Semantic Router - HNSW-Powered Intent Matching\n *\n * Uses @ruvector/router's native HNSW index (VectorDb) when the package and\n * its platform binary are installed, and an exact brute-force cosine scan\n * otherwise. Both return the same ranking contract: similarity in [-1, 1],\n * highest first.\n *\n * Features:\n * - HNSW (Hierarchical Navigable Small World) index\n * - Intent classification for 66+ agents\n * - Sub-10ms routing latency\n * - Automatic intent embedding and indexing\n * - Multi-intent detection\n *\n * Performance:\n * - <10ms routing time\n * - >85% routing accuracy\n * - Support for 66+ agent types\n * - O(log N) search complexity\n */\n\n// Import from agentdb package\nimport type { EmbeddingService } from 'agentdb';\nimport { requireOptional } from '../integrations/optional.js';\n\n/** Structural subset of @ruvector/router's native VectorDb. */\ninterface NativeVectorDb {\n insert(id: string, vector: Float32Array): string;\n search(query: Float32Array, k: number): Array<{ id: string; score: number }>;\n}\ninterface RuvectorRouterModule {\n VectorDb: new (options: { dimensions: number; distanceMetric?: number }) => NativeVectorDb;\n DistanceMetric: { Cosine: number };\n}\n\nexport type SemanticIndexBackend = 'ruvector-router' | 'brute-force';\n\nexport interface AgentIntent {\n /** Agent type identifier */\n agentType: string;\n /** Natural language description of agent capabilities */\n description: string;\n /** Example tasks for this agent */\n examples: string[];\n /** Agent specialty tags */\n tags: string[];\n}\n\nexport interface RoutingResult {\n /** Primary agent selection */\n primaryAgent: string;\n /** Confidence score (0-1) */\n confidence: number;\n /** Secondary agent suggestions */\n alternatives: Array<{\n agentType: string;\n confidence: number;\n }>;\n /** Matched intent descriptions */\n matchedIntents: string[];\n /** Routing metrics */\n metrics: {\n routingTimeMs: number;\n embeddingTimeMs: number;\n searchTimeMs: number;\n candidatesEvaluated: number;\n };\n}\n\nexport interface MultiIntentResult {\n /** Detected intents in order of confidence */\n intents: Array<{\n agentType: string;\n confidence: number;\n matchedText: string;\n }>;\n /** Whether task requires multiple agents */\n requiresMultiAgent: boolean;\n /** Suggested execution order */\n executionOrder: string[];\n}\n\n/**\n * Semantic Router\n *\n * Provides intent-based agent routing:\n * 1. Register agent intents with descriptions\n * 2. Build HNSW index for fast semantic search\n * 3. Route tasks to agents based on intent similarity\n * 4. Support multi-intent detection for complex tasks\n */\nexport class SemanticRouter {\n private embedder: EmbeddingService;\n\n // Agent registry\n private agentIntents: Map<string, AgentIntent>;\n private intentEmbeddings: Map<string, Float32Array>;\n\n // Native HNSW index (@ruvector/router) or null for the brute-force scan\n private indexBuilt: boolean = false;\n private nativeIndex: NativeVectorDb | null = null;\n private backend: SemanticIndexBackend = 'brute-force';\n private readonly useNative: boolean;\n\n // Performance tracking\n private routingStats: {\n totalRoutes: number;\n avgRoutingTimeMs: number;\n accuracyRate: number; // Requires feedback\n };\n\n /**\n * @param embedder - Embedding service for intents and tasks\n * @param options.native - Use @ruvector/router when installed (default true)\n */\n constructor(embedder: EmbeddingService, options: { native?: boolean } = {}) {\n this.embedder = embedder;\n this.useNative = options.native ?? true;\n this.agentIntents = new Map();\n this.intentEmbeddings = new Map();\n this.routingStats = {\n totalRoutes: 0,\n avgRoutingTimeMs: 0,\n accuracyRate: 0,\n };\n }\n\n /**\n * Register agent intent for routing\n *\n * @param intent - Agent intent configuration\n */\n async registerAgent(intent: AgentIntent): Promise<void> {\n this.agentIntents.set(intent.agentType, intent);\n\n // Generate embedding from description + examples\n const intentText = [\n intent.description,\n ...intent.examples,\n ...intent.tags,\n ].join(' ');\n\n const embedding = await this.embedder.embed(intentText);\n this.intentEmbeddings.set(intent.agentType, embedding);\n\n // Mark index as needing rebuild\n this.indexBuilt = false;\n }\n\n /**\n * Register multiple agents in batch\n *\n * @param intents - Array of agent intents\n */\n async registerAgents(intents: AgentIntent[]): Promise<void> {\n await Promise.all(intents.map(intent => this.registerAgent(intent)));\n }\n\n /**\n * Build the routing index.\n *\n * Loads @ruvector/router's native HNSW (VectorDb, cosine metric) when it is\n * installed; otherwise routing uses an exact brute-force scan, which is\n * still fast at the ~66-agent scale.\n */\n buildIndex(): void {\n this.nativeIndex = null;\n this.backend = 'brute-force';\n const first = this.intentEmbeddings.values().next().value as Float32Array | undefined;\n const router = this.useNative && first\n ? requireOptional<RuvectorRouterModule>('@ruvector/router')\n : null;\n if (router && first) {\n try {\n const db = new router.VectorDb({\n dimensions: first.length,\n distanceMetric: router.DistanceMetric.Cosine,\n });\n for (const [agentType, embedding] of Array.from(this.intentEmbeddings.entries())) {\n db.insert(agentType, embedding instanceof Float32Array ? embedding : Float32Array.from(embedding));\n }\n this.nativeIndex = db;\n this.backend = 'ruvector-router';\n } catch {\n // Native binary missing for this platform, or a dimension mismatch:\n // keep the exact brute-force path.\n this.nativeIndex = null;\n }\n }\n this.indexBuilt = true;\n }\n\n /** Which index answered the last build: native HNSW or brute force. */\n getIndexBackend(): SemanticIndexBackend {\n return this.backend;\n }\n\n /**\n * Route task to best agent using semantic similarity\n *\n * Process:\n * 1. Embed task description\n * 2. Search HNSW index for nearest intents\n * 3. Return top matches with confidence scores\n *\n * @param taskDescription - Natural language task description\n * @param k - Number of alternatives to return (default: 3)\n * @returns Routing result with primary agent and alternatives\n */\n async route(taskDescription: string, k: number = 3): Promise<RoutingResult> {\n const overallStartTime = performance.now();\n\n if (!this.indexBuilt) {\n this.buildIndex();\n }\n\n // Step 1: Embed task\n const embeddingStartTime = performance.now();\n const taskEmbedding = await this.embedder.embed(taskDescription);\n const embeddingTimeMs = performance.now() - embeddingStartTime;\n\n // Step 2: Search HNSW index\n const searchStartTime = performance.now();\n const candidates = this.searchHNSW(taskEmbedding, k + 1);\n const searchTimeMs = performance.now() - searchStartTime;\n\n if (candidates.length === 0) {\n throw new Error('No agents registered for routing');\n }\n\n // Step 3: Extract results\n const primaryAgent = candidates[0].agentType;\n const confidence = candidates[0].similarity;\n const alternatives = candidates.slice(1, k + 1).map(c => ({\n agentType: c.agentType,\n confidence: c.similarity,\n }));\n\n const matchedIntents = candidates\n .slice(0, k + 1)\n .map(c => this.agentIntents.get(c.agentType)?.description ?? '');\n\n const routingTimeMs = performance.now() - overallStartTime;\n\n // Update stats\n this.updateStats(routingTimeMs);\n\n return {\n primaryAgent,\n confidence,\n alternatives,\n matchedIntents,\n metrics: {\n routingTimeMs,\n embeddingTimeMs,\n searchTimeMs,\n candidatesEvaluated: this.agentIntents.size,\n },\n };\n }\n\n /**\n * Detect multiple intents in complex task\n *\n * Useful for tasks requiring coordination of multiple agents.\n *\n * @param taskDescription - Task that may require multiple agents\n * @param threshold - Minimum confidence for intent detection (default: 0.6)\n * @returns Multi-intent result with suggested execution order\n */\n async detectMultiIntent(\n taskDescription: string,\n threshold: number = 0.6\n ): Promise<MultiIntentResult> {\n // Split task into sentences or clauses\n const segments = this.segmentTask(taskDescription);\n\n // Route each segment\n const segmentResults = await Promise.all(\n segments.map(async segment => {\n const result = await this.route(segment.text, 1);\n return {\n ...result,\n matchedText: segment.text,\n };\n })\n );\n\n // Collect high-confidence intents\n const intents = segmentResults\n .filter(r => r.confidence >= threshold)\n .map(r => ({\n agentType: r.primaryAgent,\n confidence: r.confidence,\n matchedText: r.matchedText,\n }));\n\n // Deduplicate and order by confidence\n const uniqueIntents = this.deduplicateIntents(intents);\n const requiresMultiAgent = uniqueIntents.length > 1;\n\n // Suggest execution order based on dependencies\n const executionOrder = this.inferExecutionOrder(uniqueIntents, taskDescription);\n\n return {\n intents: uniqueIntents,\n requiresMultiAgent,\n executionOrder,\n };\n }\n\n /**\n * Get routing statistics\n *\n * @returns Cumulative routing metrics\n */\n getStats(): typeof this.routingStats {\n return { ...this.routingStats };\n }\n\n /**\n * Get all registered agents\n *\n * @returns Array of registered agent intents\n */\n getRegisteredAgents(): AgentIntent[] {\n return Array.from(this.agentIntents.values());\n }\n\n // ========================================================================\n // Private Helper Methods\n // ========================================================================\n\n /**\n * Search the index for nearest neighbours.\n *\n * @ruvector/router reports cosine *distance* (0 = identical), so it is\n * converted to similarity (1 - distance) to keep \"higher is better\".\n *\n * @param queryEmbedding - Query vector\n * @param k - Number of results\n * @returns Top k candidates with similarity scores, highest first\n */\n private searchHNSW(queryEmbedding: Float32Array, k: number): Array<{\n agentType: string;\n similarity: number;\n }> {\n if (this.nativeIndex) {\n try {\n const query = queryEmbedding instanceof Float32Array ? queryEmbedding : Float32Array.from(queryEmbedding);\n return this.nativeIndex\n .search(query, Math.min(k, this.intentEmbeddings.size))\n .map((r) => ({ agentType: r.id, similarity: 1 - r.score }))\n .sort((a, b) => b.similarity - a.similarity);\n } catch {\n // fall through to the exact scan\n }\n }\n\n const candidates: Array<{ agentType: string; similarity: number }> = [];\n\n for (const [agentType, embedding] of Array.from(this.intentEmbeddings.entries())) {\n const similarity = this.cosineSimilarity(queryEmbedding, embedding);\n candidates.push({ agentType, similarity });\n }\n\n // Sort by similarity (descending)\n candidates.sort((a, b) => b.similarity - a.similarity);\n\n return candidates.slice(0, k);\n }\n\n /**\n * Calculate cosine similarity\n */\n private cosineSimilarity(a: Float32Array, b: Float32Array): number {\n if (a.length !== b.length) {\n throw new Error('Vectors must have same length');\n }\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n\n const denom = Math.sqrt(normA) * Math.sqrt(normB);\n return denom === 0 ? 0 : dotProduct / denom;\n }\n\n /**\n * Segment task into independent clauses\n */\n private segmentTask(taskDescription: string): Array<{ text: string; index: number }> {\n // Split by sentences and coordination conjunctions\n const sentences = taskDescription.split(/[.!?]+/).filter(s => s.trim());\n\n const segments: Array<{ text: string; index: number }> = [];\n\n sentences.forEach((sentence, idx) => {\n // Further split by \"and\", \"then\", etc.\n const subSegments = sentence.split(/\\b(and|then|after|before)\\b/i);\n\n subSegments\n .filter((_, i) => i % 2 === 0) // Skip conjunctions\n .forEach(segment => {\n const trimmed = segment.trim();\n if (trimmed) {\n segments.push({ text: trimmed, index: idx });\n }\n });\n });\n\n return segments.length > 0 ? segments : [{ text: taskDescription, index: 0 }];\n }\n\n /**\n * Deduplicate intents by agent type\n */\n private deduplicateIntents(\n intents: Array<{ agentType: string; confidence: number; matchedText: string }>\n ): Array<{ agentType: string; confidence: number; matchedText: string }> {\n const seen = new Map<string, typeof intents[0]>();\n\n for (const intent of intents) {\n const existing = seen.get(intent.agentType);\n if (!existing || intent.confidence > existing.confidence) {\n seen.set(intent.agentType, intent);\n }\n }\n\n return Array.from(seen.values()).sort((a, b) => b.confidence - a.confidence);\n }\n\n /**\n * Infer execution order from intents and task description\n */\n private inferExecutionOrder(\n intents: Array<{ agentType: string; confidence: number; matchedText: string }>,\n taskDescription: string\n ): string[] {\n // Simple heuristic: order by position in original text\n const taskLower = taskDescription.toLowerCase();\n\n const withPositions = intents.map(intent => {\n const position = taskLower.indexOf(intent.matchedText.toLowerCase());\n return { ...intent, position };\n });\n\n withPositions.sort((a, b) => a.position - b.position);\n\n return withPositions.map(i => i.agentType);\n }\n\n /**\n * Update routing statistics\n */\n private updateStats(routingTimeMs: number): void {\n this.routingStats.totalRoutes++;\n\n const alpha = 0.1; // EMA smoothing\n this.routingStats.avgRoutingTimeMs =\n this.routingStats.avgRoutingTimeMs * (1 - alpha) + routingTimeMs * alpha;\n }\n}\n"]}
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Provides secure input validation for RuVector integration:
|
|
5
5
|
* - Task description validation
|
|
6
6
|
* - Configuration validation
|
|
7
|
-
* - Injection attack prevention
|
|
7
|
+
* - Injection attack prevention (prompt injection via aidefence when installed)
|
|
8
8
|
* - Resource exhaustion prevention
|
|
9
9
|
*/
|
|
10
|
+
import { type InjectionScan } from '../integrations/prompt-injection.js';
|
|
10
11
|
export interface ValidationOptions {
|
|
11
12
|
maxLength?: number;
|
|
12
13
|
minLength?: number;
|
|
@@ -17,16 +18,23 @@ export declare class ValidationError extends Error {
|
|
|
17
18
|
field?: string;
|
|
18
19
|
constructor(message: string, field?: string);
|
|
19
20
|
}
|
|
21
|
+
/** Thrown when task text is rejected by the prompt-injection policy. */
|
|
22
|
+
export declare class PromptInjectionError extends ValidationError {
|
|
23
|
+
scan: InjectionScan;
|
|
24
|
+
constructor(scan: InjectionScan, field?: string);
|
|
25
|
+
}
|
|
20
26
|
/**
|
|
21
27
|
* Input Validator
|
|
22
28
|
*
|
|
23
29
|
* Validates all external inputs to prevent:
|
|
24
|
-
* - Injection attacks (XSS, SQL injection, prompt injection
|
|
30
|
+
* - Injection attacks (XSS, SQL injection, and prompt injection — the latter
|
|
31
|
+
* through `scanPromptInjection`, see integrations/prompt-injection.ts)
|
|
25
32
|
* - Resource exhaustion (excessive length, recursion)
|
|
26
33
|
* - Malicious content (scripts, control characters)
|
|
27
34
|
*/
|
|
28
35
|
export declare class InputValidator {
|
|
29
36
|
private static readonly SUSPICIOUS_PATTERNS;
|
|
37
|
+
private static readonly warnedInjections;
|
|
30
38
|
private static readonly CONTROL_CHARS_REGEX;
|
|
31
39
|
/**
|
|
32
40
|
* Validate task description
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-validator.d.ts","sourceRoot":"","sources":["../../src/utils/input-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACJ,KAAK,CAAC,EAAE,MAAM;gBAAtC,OAAO,EAAE,MAAM,EAAS,KAAK,CAAC,EAAE,MAAM;CAInD;AAED
|
|
1
|
+
{"version":3,"file":"input-validator.d.ts","sourceRoot":"","sources":["../../src/utils/input-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAE9F,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACJ,KAAK,CAAC,EAAE,MAAM;gBAAtC,OAAO,EAAE,MAAM,EAAS,KAAK,CAAC,EAAE,MAAM;CAInD;AAED,wEAAwE;AACxE,qBAAa,oBAAqB,SAAQ,eAAe;IACpC,IAAI,EAAE,aAAa;gBAAnB,IAAI,EAAE,aAAa,EAAE,KAAK,SAAoB;CASlE;AAED;;;;;;;;GAQG;AACH,qBAAa,cAAc;IAEzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAUzC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IAG7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAsB;IAEjE;;;;;;;OAOG;IACH,MAAM,CAAC,uBAAuB,CAC5B,eAAe,EAAE,MAAM,EACvB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,MAAM;IA6ET;;;;;;OAMG;IACH,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAuBnD;;;;;;OAMG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAmBrD;;;;;;;;OAQG;IACH,MAAM,CAAC,eAAe,CACpB,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAY,EACjB,GAAG,GAAE,MAAe,GACnB,MAAM;IAuBT;;;;;;;;;OASG;IACH,MAAM,CAAC,mBAAmB,CACxB,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAY,EACtB,SAAS,GAAE,MAAa,GACvB,MAAM,EAAE;IAoCX;;;;;;;OAOG;IACH,MAAM,CAAC,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjD,MAAM,EAAE,OAAO,EACf,MAAM,EAAE;SACL,CAAC,IAAI,MAAM,CAAC,GAAG;YACd,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;YACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;YACnB,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC;SACrC;KACF,GACA,CAAC;IAmEJ;;;;;OAKG;IACH,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAezC;;;;;;OAMG;IACH,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAiB5C;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAE/B,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,mBAmB5C"}
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Provides secure input validation for RuVector integration:
|
|
5
5
|
* - Task description validation
|
|
6
6
|
* - Configuration validation
|
|
7
|
-
* - Injection attack prevention
|
|
7
|
+
* - Injection attack prevention (prompt injection via aidefence when installed)
|
|
8
8
|
* - Resource exhaustion prevention
|
|
9
9
|
*/
|
|
10
|
+
import { scanPromptInjection } from '../integrations/prompt-injection.js';
|
|
10
11
|
export class ValidationError extends Error {
|
|
11
12
|
field;
|
|
12
13
|
constructor(message, field) {
|
|
@@ -15,11 +16,23 @@ export class ValidationError extends Error {
|
|
|
15
16
|
this.name = 'ValidationError';
|
|
16
17
|
}
|
|
17
18
|
}
|
|
19
|
+
/** Thrown when task text is rejected by the prompt-injection policy. */
|
|
20
|
+
export class PromptInjectionError extends ValidationError {
|
|
21
|
+
scan;
|
|
22
|
+
constructor(scan, field = 'taskDescription') {
|
|
23
|
+
const top = scan.threats[0];
|
|
24
|
+
super(`Task description rejected: possible prompt injection (${scan.maxSeverity}` +
|
|
25
|
+
(top ? `, ${top.type}` : '') + `; engine ${scan.engine})`, field);
|
|
26
|
+
this.scan = scan;
|
|
27
|
+
this.name = 'PromptInjectionError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
18
30
|
/**
|
|
19
31
|
* Input Validator
|
|
20
32
|
*
|
|
21
33
|
* Validates all external inputs to prevent:
|
|
22
|
-
* - Injection attacks (XSS, SQL injection, prompt injection
|
|
34
|
+
* - Injection attacks (XSS, SQL injection, and prompt injection — the latter
|
|
35
|
+
* through `scanPromptInjection`, see integrations/prompt-injection.ts)
|
|
23
36
|
* - Resource exhaustion (excessive length, recursion)
|
|
24
37
|
* - Malicious content (scripts, control characters)
|
|
25
38
|
*/
|
|
@@ -36,6 +49,7 @@ export class InputValidator {
|
|
|
36
49
|
/\.\.\//, // Path traversal
|
|
37
50
|
/[;\x00]/, // SQL injection chars
|
|
38
51
|
];
|
|
52
|
+
static warnedInjections = new Set();
|
|
39
53
|
// Control characters that should be removed
|
|
40
54
|
static CONTROL_CHARS_REGEX = /[\x00-\x1F\x7F]/g;
|
|
41
55
|
/**
|
|
@@ -83,6 +97,20 @@ export class InputValidator {
|
|
|
83
97
|
throw new ValidationError('Task description contains suspicious content', 'taskDescription');
|
|
84
98
|
}
|
|
85
99
|
}
|
|
100
|
+
// Prompt injection (aidefence when installed, built-in patterns otherwise)
|
|
101
|
+
const scan = scanPromptInjection(sanitized);
|
|
102
|
+
if (scan.decision === 'block') {
|
|
103
|
+
throw new PromptInjectionError(scan);
|
|
104
|
+
}
|
|
105
|
+
if (scan.decision === 'warn') {
|
|
106
|
+
const message = `possible prompt injection in task description (${scan.maxSeverity}: ` +
|
|
107
|
+
`${scan.threats.map((t) => t.type).join(', ')}; engine ${scan.engine})`;
|
|
108
|
+
// Once per distinct finding: an orchestrator loop would otherwise repeat it.
|
|
109
|
+
if (!InputValidator.warnedInjections.has(message)) {
|
|
110
|
+
InputValidator.warnedInjections.add(message);
|
|
111
|
+
process.emitWarning(message, { code: 'AGENTIC_FLOW_PROMPT_INJECTION' });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
86
114
|
}
|
|
87
115
|
return sanitized.trim();
|
|
88
116
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-validator.js","sourceRoot":"","sources":["../../src/utils/input-validator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Input Validation Utilities\n *\n * Provides secure input validation for RuVector integration:\n * - Task description validation\n * - Configuration validation\n * - Injection attack prevention\n * - Resource exhaustion prevention\n */\n\nexport interface ValidationOptions {\n maxLength?: number;\n minLength?: number;\n allowEmpty?: boolean;\n sanitize?: boolean;\n}\n\nexport class ValidationError extends Error {\n constructor(message: string, public field?: string) {\n super(message);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Input Validator\n *\n * Validates all external inputs to prevent:\n * - Injection attacks (XSS, SQL injection, prompt injection)\n * - Resource exhaustion (excessive length, recursion)\n * - Malicious content (scripts, control characters)\n */\nexport class InputValidator {\n // Suspicious patterns that could indicate attacks\n private static readonly SUSPICIOUS_PATTERNS = [\n /<script/i, // XSS attempt\n /javascript:/i, // JavaScript protocol\n /data:text\\/html/i, // Data URI XSS\n /on\\w+\\s*=/i, // Event handlers\n /\\beval\\s*\\(/i, // eval() calls\n /\\bFunction\\s*\\(/i, // Function constructor\n /__proto__/, // Prototype pollution\n /\\.\\.\\//, // Path traversal\n /[;\\x00]/, // SQL injection chars\n ];\n\n // Control characters that should be removed\n private static readonly CONTROL_CHARS_REGEX = /[\\x00-\\x1F\\x7F]/g;\n\n /**\n * Validate task description\n *\n * @param taskDescription - User-provided task description\n * @param options - Validation options\n * @returns Sanitized task description\n * @throws ValidationError if invalid\n */\n static validateTaskDescription(\n taskDescription: string,\n options?: ValidationOptions\n ): string {\n const opts = {\n maxLength: options?.maxLength ?? 10000,\n minLength: options?.minLength ?? 1,\n allowEmpty: options?.allowEmpty ?? false,\n sanitize: options?.sanitize ?? true,\n };\n\n // Check for null/undefined\n if (taskDescription === null || taskDescription === undefined) {\n throw new ValidationError('Task description is required', 'taskDescription');\n }\n\n // Check type\n if (typeof taskDescription !== 'string') {\n throw new ValidationError(\n `Task description must be a string, got ${typeof taskDescription}`,\n 'taskDescription'\n );\n }\n\n // Check empty\n if (!opts.allowEmpty && taskDescription.trim().length === 0) {\n throw new ValidationError('Task description cannot be empty', 'taskDescription');\n }\n\n // Check length\n if (taskDescription.length < opts.minLength) {\n throw new ValidationError(\n `Task description too short (min ${opts.minLength} chars)`,\n 'taskDescription'\n );\n }\n\n if (taskDescription.length > opts.maxLength) {\n throw new ValidationError(\n `Task description too long (max ${opts.maxLength} chars)`,\n 'taskDescription'\n );\n }\n\n // Sanitize if requested\n let sanitized = taskDescription;\n if (opts.sanitize) {\n // Remove control characters\n sanitized = sanitized.replace(InputValidator.CONTROL_CHARS_REGEX, '');\n\n // Check for suspicious patterns\n for (const pattern of InputValidator.SUSPICIOUS_PATTERNS) {\n if (pattern.test(sanitized)) {\n throw new ValidationError(\n 'Task description contains suspicious content',\n 'taskDescription'\n );\n }\n }\n }\n\n return sanitized.trim();\n }\n\n /**\n * Validate agent name\n *\n * @param agentName - Agent identifier\n * @returns Sanitized agent name\n * @throws ValidationError if invalid\n */\n static validateAgentName(agentName: string): string {\n if (!agentName || typeof agentName !== 'string') {\n throw new ValidationError('Agent name is required', 'agentName');\n }\n\n if (agentName.length < 1 || agentName.length > 100) {\n throw new ValidationError(\n 'Agent name must be between 1-100 characters',\n 'agentName'\n );\n }\n\n // Only allow alphanumeric, dash, underscore\n if (!/^[a-zA-Z0-9_-]+$/.test(agentName)) {\n throw new ValidationError(\n 'Agent name can only contain letters, numbers, dash, and underscore',\n 'agentName'\n );\n }\n\n return agentName.toLowerCase();\n }\n\n /**\n * Validate confidence score\n *\n * @param confidence - Confidence score (0-1)\n * @returns Validated confidence\n * @throws ValidationError if invalid\n */\n static validateConfidence(confidence: number): number {\n if (typeof confidence !== 'number') {\n throw new ValidationError(\n `Confidence must be a number, got ${typeof confidence}`,\n 'confidence'\n );\n }\n\n if (Number.isNaN(confidence) || !Number.isFinite(confidence)) {\n throw new ValidationError('Confidence must be a valid number', 'confidence');\n }\n\n if (confidence < 0 || confidence > 1) {\n throw new ValidationError('Confidence must be between 0 and 1', 'confidence');\n }\n\n return confidence;\n }\n\n /**\n * Validate timeout value\n *\n * @param timeout - Timeout in milliseconds\n * @param min - Minimum allowed timeout (default: 100ms)\n * @param max - Maximum allowed timeout (default: 5 minutes)\n * @returns Validated timeout\n * @throws ValidationError if invalid\n */\n static validateTimeout(\n timeout: number,\n min: number = 100,\n max: number = 300000\n ): number {\n if (typeof timeout !== 'number') {\n throw new ValidationError(\n `Timeout must be a number, got ${typeof timeout}`,\n 'timeout'\n );\n }\n\n if (Number.isNaN(timeout) || !Number.isFinite(timeout)) {\n throw new ValidationError('Timeout must be a valid number', 'timeout');\n }\n\n if (timeout < min) {\n throw new ValidationError(`Timeout too short (min ${min}ms)`, 'timeout');\n }\n\n if (timeout > max) {\n throw new ValidationError(`Timeout too long (max ${max}ms)`, 'timeout');\n }\n\n return Math.floor(timeout);\n }\n\n /**\n * Validate array of strings\n *\n * @param array - Array to validate\n * @param fieldName - Field name for error messages\n * @param maxItems - Maximum number of items\n * @param maxLength - Maximum length per item\n * @returns Validated array\n * @throws ValidationError if invalid\n */\n static validateStringArray(\n array: unknown,\n fieldName: string,\n maxItems: number = 100,\n maxLength: number = 1000\n ): string[] {\n if (!Array.isArray(array)) {\n throw new ValidationError(`${fieldName} must be an array`, fieldName);\n }\n\n if (array.length > maxItems) {\n throw new ValidationError(\n `${fieldName} has too many items (max ${maxItems})`,\n fieldName\n );\n }\n\n const validated: string[] = [];\n for (let i = 0; i < array.length; i++) {\n const item = array[i];\n\n if (typeof item !== 'string') {\n throw new ValidationError(\n `${fieldName}[${i}] must be a string`,\n fieldName\n );\n }\n\n if (item.length > maxLength) {\n throw new ValidationError(\n `${fieldName}[${i}] too long (max ${maxLength} chars)`,\n fieldName\n );\n }\n\n validated.push(item);\n }\n\n return validated;\n }\n\n /**\n * Validate configuration object\n *\n * @param config - Configuration to validate\n * @param schema - Validation schema\n * @returns Validated configuration\n * @throws ValidationError if invalid\n */\n static validateConfig<T extends Record<string, any>>(\n config: unknown,\n schema: {\n [K in keyof T]: {\n type: 'string' | 'number' | 'boolean' | 'object';\n required?: boolean;\n min?: number;\n max?: number;\n validator?: (value: any) => boolean;\n };\n }\n ): T {\n if (!config || typeof config !== 'object') {\n throw new ValidationError('Configuration must be an object', 'config');\n }\n\n const validated: any = {};\n const configObj = config as Record<string, any>;\n\n for (const [key, rules] of Object.entries(schema)) {\n const value = configObj[key];\n\n // Check required\n if (rules.required && (value === undefined || value === null)) {\n throw new ValidationError(`Configuration field '${key}' is required`, key);\n }\n\n // Skip if optional and not provided\n if (!rules.required && (value === undefined || value === null)) {\n continue;\n }\n\n // Check type\n if (typeof value !== rules.type) {\n throw new ValidationError(\n `Configuration field '${key}' must be ${rules.type}, got ${typeof value}`,\n key\n );\n }\n\n // Validate numbers\n if (rules.type === 'number') {\n if (Number.isNaN(value) || !Number.isFinite(value)) {\n throw new ValidationError(\n `Configuration field '${key}' must be a valid number`,\n key\n );\n }\n\n if (rules.min !== undefined && value < rules.min) {\n throw new ValidationError(\n `Configuration field '${key}' must be >= ${rules.min}`,\n key\n );\n }\n\n if (rules.max !== undefined && value > rules.max) {\n throw new ValidationError(\n `Configuration field '${key}' must be <= ${rules.max}`,\n key\n );\n }\n }\n\n // Custom validator\n if (rules.validator && !rules.validator(value)) {\n throw new ValidationError(\n `Configuration field '${key}' failed validation`,\n key\n );\n }\n\n validated[key] = value;\n }\n\n return validated as T;\n }\n\n /**\n * Sanitize HTML to prevent XSS\n *\n * @param html - HTML string to sanitize\n * @returns Sanitized HTML (text only)\n */\n static sanitizeHtml(html: string): string {\n if (typeof html !== 'string') {\n return '';\n }\n\n // Strip all HTML tags, keep text only\n return html\n .replace(/<[^>]*>/g, '')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n }\n\n /**\n * Validate email address\n *\n * @param email - Email to validate\n * @returns Normalized email\n * @throws ValidationError if invalid\n */\n static validateEmail(email: string): string {\n if (!email || typeof email !== 'string') {\n throw new ValidationError('Email is required', 'email');\n }\n\n // Basic email regex\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(email)) {\n throw new ValidationError('Invalid email format', 'email');\n }\n\n if (email.length > 254) {\n throw new ValidationError('Email too long (max 254 chars)', 'email');\n }\n\n return email.toLowerCase().trim();\n }\n}\n\n/**\n * Validation middleware factory\n *\n * Creates validation middleware for Express/Fastify routes\n */\nexport function createValidationMiddleware(\n validator: (req: any) => void | Promise<void>\n) {\n return async (req: any, res: any, next: any) => {\n try {\n await validator(req);\n next();\n } catch (error) {\n if (error instanceof ValidationError) {\n res.status(400).json({\n error: 'Validation Error',\n message: error.message,\n field: error.field,\n });\n } else {\n res.status(500).json({\n error: 'Internal Server Error',\n message: 'Validation failed',\n });\n }\n }\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"input-validator.js","sourceRoot":"","sources":["../../src/utils/input-validator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Input Validation Utilities\n *\n * Provides secure input validation for RuVector integration:\n * - Task description validation\n * - Configuration validation\n * - Injection attack prevention (prompt injection via aidefence when installed)\n * - Resource exhaustion prevention\n */\n\nimport { scanPromptInjection, type InjectionScan } from '../integrations/prompt-injection.js';\n\nexport interface ValidationOptions {\n maxLength?: number;\n minLength?: number;\n allowEmpty?: boolean;\n sanitize?: boolean;\n}\n\nexport class ValidationError extends Error {\n constructor(message: string, public field?: string) {\n super(message);\n this.name = 'ValidationError';\n }\n}\n\n/** Thrown when task text is rejected by the prompt-injection policy. */\nexport class PromptInjectionError extends ValidationError {\n constructor(public scan: InjectionScan, field = 'taskDescription') {\n const top = scan.threats[0];\n super(\n `Task description rejected: possible prompt injection (${scan.maxSeverity}` +\n (top ? `, ${top.type}` : '') + `; engine ${scan.engine})`,\n field,\n );\n this.name = 'PromptInjectionError';\n }\n}\n\n/**\n * Input Validator\n *\n * Validates all external inputs to prevent:\n * - Injection attacks (XSS, SQL injection, and prompt injection — the latter\n * through `scanPromptInjection`, see integrations/prompt-injection.ts)\n * - Resource exhaustion (excessive length, recursion)\n * - Malicious content (scripts, control characters)\n */\nexport class InputValidator {\n // Suspicious patterns that could indicate attacks\n private static readonly SUSPICIOUS_PATTERNS = [\n /<script/i, // XSS attempt\n /javascript:/i, // JavaScript protocol\n /data:text\\/html/i, // Data URI XSS\n /on\\w+\\s*=/i, // Event handlers\n /\\beval\\s*\\(/i, // eval() calls\n /\\bFunction\\s*\\(/i, // Function constructor\n /__proto__/, // Prototype pollution\n /\\.\\.\\//, // Path traversal\n /[;\\x00]/, // SQL injection chars\n ];\n\n private static readonly warnedInjections = new Set<string>();\n\n // Control characters that should be removed\n private static readonly CONTROL_CHARS_REGEX = /[\\x00-\\x1F\\x7F]/g;\n\n /**\n * Validate task description\n *\n * @param taskDescription - User-provided task description\n * @param options - Validation options\n * @returns Sanitized task description\n * @throws ValidationError if invalid\n */\n static validateTaskDescription(\n taskDescription: string,\n options?: ValidationOptions\n ): string {\n const opts = {\n maxLength: options?.maxLength ?? 10000,\n minLength: options?.minLength ?? 1,\n allowEmpty: options?.allowEmpty ?? false,\n sanitize: options?.sanitize ?? true,\n };\n\n // Check for null/undefined\n if (taskDescription === null || taskDescription === undefined) {\n throw new ValidationError('Task description is required', 'taskDescription');\n }\n\n // Check type\n if (typeof taskDescription !== 'string') {\n throw new ValidationError(\n `Task description must be a string, got ${typeof taskDescription}`,\n 'taskDescription'\n );\n }\n\n // Check empty\n if (!opts.allowEmpty && taskDescription.trim().length === 0) {\n throw new ValidationError('Task description cannot be empty', 'taskDescription');\n }\n\n // Check length\n if (taskDescription.length < opts.minLength) {\n throw new ValidationError(\n `Task description too short (min ${opts.minLength} chars)`,\n 'taskDescription'\n );\n }\n\n if (taskDescription.length > opts.maxLength) {\n throw new ValidationError(\n `Task description too long (max ${opts.maxLength} chars)`,\n 'taskDescription'\n );\n }\n\n // Sanitize if requested\n let sanitized = taskDescription;\n if (opts.sanitize) {\n // Remove control characters\n sanitized = sanitized.replace(InputValidator.CONTROL_CHARS_REGEX, '');\n\n // Check for suspicious patterns\n for (const pattern of InputValidator.SUSPICIOUS_PATTERNS) {\n if (pattern.test(sanitized)) {\n throw new ValidationError(\n 'Task description contains suspicious content',\n 'taskDescription'\n );\n }\n }\n\n // Prompt injection (aidefence when installed, built-in patterns otherwise)\n const scan = scanPromptInjection(sanitized);\n if (scan.decision === 'block') {\n throw new PromptInjectionError(scan);\n }\n if (scan.decision === 'warn') {\n const message =\n `possible prompt injection in task description (${scan.maxSeverity}: ` +\n `${scan.threats.map((t) => t.type).join(', ')}; engine ${scan.engine})`;\n // Once per distinct finding: an orchestrator loop would otherwise repeat it.\n if (!InputValidator.warnedInjections.has(message)) {\n InputValidator.warnedInjections.add(message);\n process.emitWarning(message, { code: 'AGENTIC_FLOW_PROMPT_INJECTION' });\n }\n }\n }\n\n return sanitized.trim();\n }\n\n /**\n * Validate agent name\n *\n * @param agentName - Agent identifier\n * @returns Sanitized agent name\n * @throws ValidationError if invalid\n */\n static validateAgentName(agentName: string): string {\n if (!agentName || typeof agentName !== 'string') {\n throw new ValidationError('Agent name is required', 'agentName');\n }\n\n if (agentName.length < 1 || agentName.length > 100) {\n throw new ValidationError(\n 'Agent name must be between 1-100 characters',\n 'agentName'\n );\n }\n\n // Only allow alphanumeric, dash, underscore\n if (!/^[a-zA-Z0-9_-]+$/.test(agentName)) {\n throw new ValidationError(\n 'Agent name can only contain letters, numbers, dash, and underscore',\n 'agentName'\n );\n }\n\n return agentName.toLowerCase();\n }\n\n /**\n * Validate confidence score\n *\n * @param confidence - Confidence score (0-1)\n * @returns Validated confidence\n * @throws ValidationError if invalid\n */\n static validateConfidence(confidence: number): number {\n if (typeof confidence !== 'number') {\n throw new ValidationError(\n `Confidence must be a number, got ${typeof confidence}`,\n 'confidence'\n );\n }\n\n if (Number.isNaN(confidence) || !Number.isFinite(confidence)) {\n throw new ValidationError('Confidence must be a valid number', 'confidence');\n }\n\n if (confidence < 0 || confidence > 1) {\n throw new ValidationError('Confidence must be between 0 and 1', 'confidence');\n }\n\n return confidence;\n }\n\n /**\n * Validate timeout value\n *\n * @param timeout - Timeout in milliseconds\n * @param min - Minimum allowed timeout (default: 100ms)\n * @param max - Maximum allowed timeout (default: 5 minutes)\n * @returns Validated timeout\n * @throws ValidationError if invalid\n */\n static validateTimeout(\n timeout: number,\n min: number = 100,\n max: number = 300000\n ): number {\n if (typeof timeout !== 'number') {\n throw new ValidationError(\n `Timeout must be a number, got ${typeof timeout}`,\n 'timeout'\n );\n }\n\n if (Number.isNaN(timeout) || !Number.isFinite(timeout)) {\n throw new ValidationError('Timeout must be a valid number', 'timeout');\n }\n\n if (timeout < min) {\n throw new ValidationError(`Timeout too short (min ${min}ms)`, 'timeout');\n }\n\n if (timeout > max) {\n throw new ValidationError(`Timeout too long (max ${max}ms)`, 'timeout');\n }\n\n return Math.floor(timeout);\n }\n\n /**\n * Validate array of strings\n *\n * @param array - Array to validate\n * @param fieldName - Field name for error messages\n * @param maxItems - Maximum number of items\n * @param maxLength - Maximum length per item\n * @returns Validated array\n * @throws ValidationError if invalid\n */\n static validateStringArray(\n array: unknown,\n fieldName: string,\n maxItems: number = 100,\n maxLength: number = 1000\n ): string[] {\n if (!Array.isArray(array)) {\n throw new ValidationError(`${fieldName} must be an array`, fieldName);\n }\n\n if (array.length > maxItems) {\n throw new ValidationError(\n `${fieldName} has too many items (max ${maxItems})`,\n fieldName\n );\n }\n\n const validated: string[] = [];\n for (let i = 0; i < array.length; i++) {\n const item = array[i];\n\n if (typeof item !== 'string') {\n throw new ValidationError(\n `${fieldName}[${i}] must be a string`,\n fieldName\n );\n }\n\n if (item.length > maxLength) {\n throw new ValidationError(\n `${fieldName}[${i}] too long (max ${maxLength} chars)`,\n fieldName\n );\n }\n\n validated.push(item);\n }\n\n return validated;\n }\n\n /**\n * Validate configuration object\n *\n * @param config - Configuration to validate\n * @param schema - Validation schema\n * @returns Validated configuration\n * @throws ValidationError if invalid\n */\n static validateConfig<T extends Record<string, any>>(\n config: unknown,\n schema: {\n [K in keyof T]: {\n type: 'string' | 'number' | 'boolean' | 'object';\n required?: boolean;\n min?: number;\n max?: number;\n validator?: (value: any) => boolean;\n };\n }\n ): T {\n if (!config || typeof config !== 'object') {\n throw new ValidationError('Configuration must be an object', 'config');\n }\n\n const validated: any = {};\n const configObj = config as Record<string, any>;\n\n for (const [key, rules] of Object.entries(schema)) {\n const value = configObj[key];\n\n // Check required\n if (rules.required && (value === undefined || value === null)) {\n throw new ValidationError(`Configuration field '${key}' is required`, key);\n }\n\n // Skip if optional and not provided\n if (!rules.required && (value === undefined || value === null)) {\n continue;\n }\n\n // Check type\n if (typeof value !== rules.type) {\n throw new ValidationError(\n `Configuration field '${key}' must be ${rules.type}, got ${typeof value}`,\n key\n );\n }\n\n // Validate numbers\n if (rules.type === 'number') {\n if (Number.isNaN(value) || !Number.isFinite(value)) {\n throw new ValidationError(\n `Configuration field '${key}' must be a valid number`,\n key\n );\n }\n\n if (rules.min !== undefined && value < rules.min) {\n throw new ValidationError(\n `Configuration field '${key}' must be >= ${rules.min}`,\n key\n );\n }\n\n if (rules.max !== undefined && value > rules.max) {\n throw new ValidationError(\n `Configuration field '${key}' must be <= ${rules.max}`,\n key\n );\n }\n }\n\n // Custom validator\n if (rules.validator && !rules.validator(value)) {\n throw new ValidationError(\n `Configuration field '${key}' failed validation`,\n key\n );\n }\n\n validated[key] = value;\n }\n\n return validated as T;\n }\n\n /**\n * Sanitize HTML to prevent XSS\n *\n * @param html - HTML string to sanitize\n * @returns Sanitized HTML (text only)\n */\n static sanitizeHtml(html: string): string {\n if (typeof html !== 'string') {\n return '';\n }\n\n // Strip all HTML tags, keep text only\n return html\n .replace(/<[^>]*>/g, '')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n }\n\n /**\n * Validate email address\n *\n * @param email - Email to validate\n * @returns Normalized email\n * @throws ValidationError if invalid\n */\n static validateEmail(email: string): string {\n if (!email || typeof email !== 'string') {\n throw new ValidationError('Email is required', 'email');\n }\n\n // Basic email regex\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(email)) {\n throw new ValidationError('Invalid email format', 'email');\n }\n\n if (email.length > 254) {\n throw new ValidationError('Email too long (max 254 chars)', 'email');\n }\n\n return email.toLowerCase().trim();\n }\n}\n\n/**\n * Validation middleware factory\n *\n * Creates validation middleware for Express/Fastify routes\n */\nexport function createValidationMiddleware(\n validator: (req: any) => void | Promise<void>\n) {\n return async (req: any, res: any, next: any) => {\n try {\n await validator(req);\n next();\n } catch (error) {\n if (error instanceof ValidationError) {\n res.status(400).json({\n error: 'Validation Error',\n message: error.message,\n field: error.field,\n });\n } else {\n res.status(500).json({\n error: 'Internal Server Error',\n message: 'Validation failed',\n });\n }\n }\n };\n}\n"]}
|