@tyvm/knowhow 0.0.47 โ 0.0.49
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/benchmarks/results/27b0a06/2025-09-27/xai/xai-grok-code-fast-1.json +2909 -0
- package/benchmarks/results/4057aed/2025-08-14/anthropic/anthropic-claude-sonnet-4-20250514.json +1671 -0
- package/jest.config.js +2 -2
- package/package.json +8 -3
- package/src/agents/base/base.ts +39 -25
- package/src/agents/patcher/patcher.ts +26 -5
- package/src/agents/tools/agentCall.ts +4 -2
- package/src/agents/tools/aiClient.ts +3 -11
- package/src/agents/tools/ast/astAppendNode.ts +90 -0
- package/src/agents/tools/ast/astDeleteNode.ts +88 -0
- package/src/agents/tools/ast/astEditNode.ts +95 -0
- package/src/agents/tools/ast/astGetPathForLine.ts +73 -0
- package/src/agents/tools/ast/astListPaths.ts +66 -0
- package/src/agents/tools/ast/index.ts +7 -0
- package/src/agents/tools/callPlugin.ts +8 -2
- package/src/agents/tools/embeddingSearch.ts +2 -1
- package/src/agents/tools/execCommand.ts +239 -94
- package/src/agents/tools/fileSearch.ts +15 -17
- package/src/agents/tools/index.ts +1 -0
- package/src/agents/tools/language/definitions.ts +10 -2
- package/src/agents/tools/language/index.ts +3 -2
- package/src/agents/tools/lintFile.ts +4 -2
- package/src/agents/tools/list.ts +203 -62
- package/src/agents/tools/patch.ts +48 -14
- package/src/agents/tools/readBlocks.ts +34 -0
- package/src/agents/tools/readFile.ts +23 -0
- package/src/agents/tools/stringReplace.ts +33 -9
- package/src/agents/tools/writeFile.ts +55 -0
- package/src/agents/tools/ycmd/server.ts +14 -4
- package/src/chat/CliChatService.ts +6 -1
- package/src/chat/modules/AgentModule.ts +129 -67
- package/src/chat/modules/AskModule.ts +0 -1
- package/src/chat/modules/SetupModule.ts +4 -4
- package/src/chat/modules/SystemModule.ts +28 -5
- package/src/chat/types.ts +2 -0
- package/src/chat-old.ts +2 -2
- package/src/clients/anthropic.ts +22 -1
- package/src/clients/openai.ts +1 -1
- package/src/clients/xai.ts +15 -5
- package/src/config.ts +17 -5
- package/src/dataset/diffs/generate.ts +2 -2
- package/src/dataset/diffs/jsonl.ts +0 -1
- package/src/embeddings.ts +8 -8
- package/src/index.ts +11 -5
- package/src/plugins/GitPlugin.ts +530 -0
- package/src/plugins/LinterPlugin.ts +89 -0
- package/src/plugins/PluginBase.ts +4 -2
- package/src/plugins/asana.ts +4 -2
- package/src/plugins/downloader/plugin.ts +5 -2
- package/src/plugins/embedding.ts +24 -4
- package/src/plugins/figma.ts +7 -3
- package/src/plugins/github.ts +4 -2
- package/src/plugins/jira.ts +4 -2
- package/src/plugins/language.ts +134 -27
- package/src/plugins/linear.ts +4 -2
- package/src/plugins/notion.ts +4 -2
- package/src/plugins/plugins.ts +27 -16
- package/src/plugins/tree-sitter/editor.ts +369 -0
- package/src/plugins/tree-sitter/lang-packs/index.ts +23 -0
- package/src/plugins/tree-sitter/lang-packs/java.ts +59 -0
- package/src/plugins/tree-sitter/lang-packs/javascript.ts +57 -0
- package/src/plugins/tree-sitter/lang-packs/python.ts +45 -0
- package/src/plugins/tree-sitter/lang-packs/types.ts +79 -0
- package/src/plugins/tree-sitter/lang-packs/typescript.ts +49 -0
- package/src/plugins/tree-sitter/parser.ts +444 -0
- package/src/plugins/tree-sitter/simple-paths.ts +467 -0
- package/src/plugins/types.ts +11 -0
- package/src/plugins/url.ts +5 -3
- package/src/plugins/vim.ts +8 -5
- package/src/processors/CustomVariables.ts +60 -70
- package/src/processors/TokenCompressor.ts +15 -14
- package/src/processors/ToolResponseCache.ts +20 -14
- package/src/services/EmbeddingService.ts +18 -9
- package/src/services/EventService.ts +80 -0
- package/src/services/Mcp.ts +5 -0
- package/src/services/S3.ts +4 -3
- package/src/services/Tools.ts +125 -53
- package/src/services/index.ts +16 -11
- package/src/services/types.ts +3 -3
- package/src/types.ts +7 -2
- package/src/worker.ts +14 -1
- package/test-comprehensive.ts +31 -0
- package/tests/clients/AIClient.test.ts +490 -0
- package/tests/manual/agent-events/run-test.ts +203 -0
- package/tests/{integration โ manual/file-edits}/figma.test.ts +1 -1
- package/tests/{integration โ manual/file-edits}/fileblocks/readwrite.test.ts +7 -3
- package/tests/{integration โ manual/file-edits}/patching.test.ts +11 -8
- package/tests/plugins/language/languagePlugin-content-triggers.test.ts +332 -0
- package/tests/plugins/language/languagePlugin-integration.test.ts +456 -0
- package/tests/plugins/language/languagePlugin.test.ts +363 -0
- package/tests/processors/Base64ImageDetector.test.ts +403 -0
- package/tests/processors/CustomVariables.test.ts +485 -0
- package/tests/processors/HarmonyToolProcessor.test.ts +490 -0
- package/tests/processors/TokenCompressor.test.ts +390 -0
- package/tests/processors/ToolResponseCache.test.ts +736 -0
- package/tests/services/Tools.test.ts +1339 -0
- package/tests/test.spec.ts +162 -117
- package/tests/tree-sitter/editor.test.ts +113 -0
- package/tests/tree-sitter/invalid.test.ts +299 -0
- package/tests/tree-sitter/paths/common-edits.test.ts +564 -0
- package/tests/tree-sitter/paths/debug-exact-position.test.ts +44 -0
- package/tests/tree-sitter/paths/debug-line-indexing.test.ts +49 -0
- package/tests/tree-sitter/paths/debug-paths.test.ts +90 -0
- package/tests/tree-sitter/paths/paths.test.ts +170 -0
- package/tests/tree-sitter/paths/simple-paths.test.ts +367 -0
- package/tests/tree-sitter/sample-after.ts +48 -0
- package/tests/tree-sitter/sample-before.ts +25 -0
- package/tests/tree-sitter/test-files/completely-broken.ts +7 -0
- package/tests/tree-sitter/test-files/duplicate-braces.ts +39 -0
- package/tests/tree-sitter/test-files/invalid-nesting.ts +39 -0
- package/tests/tree-sitter/test-files/malformed-signature.ts +39 -0
- package/tests/tree-sitter/test-files/mismatched-parens.ts +39 -0
- package/tests/tree-sitter/test-files/missing-semicolon.ts +39 -0
- package/tests/tree-sitter/test-files/partially-broken.ts +20 -0
- package/tests/tree-sitter/test-files/specific-errors.ts +14 -0
- package/tests/tree-sitter/test-files/unclosed-string.ts +39 -0
- package/tests/tree-sitter/tree-sitter.test.ts +251 -0
- package/ts_build/package.json +8 -3
- package/ts_build/src/agents/base/base.d.ts +7 -2
- package/ts_build/src/agents/base/base.js +27 -21
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/patcher/patcher.js +26 -5
- package/ts_build/src/agents/patcher/patcher.js.map +1 -1
- package/ts_build/src/agents/tools/agentCall.js +2 -1
- package/ts_build/src/agents/tools/agentCall.js.map +1 -1
- package/ts_build/src/agents/tools/aiClient.d.ts +7 -8
- package/ts_build/src/agents/tools/aiClient.js.map +1 -1
- package/ts_build/src/agents/tools/ast/astAppendNode.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astAppendNode.js +96 -0
- package/ts_build/src/agents/tools/ast/astAppendNode.js.map +1 -0
- package/ts_build/src/agents/tools/ast/astDeleteNode.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astDeleteNode.js +94 -0
- package/ts_build/src/agents/tools/ast/astDeleteNode.js.map +1 -0
- package/ts_build/src/agents/tools/ast/astEditNode.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astEditNode.js +96 -0
- package/ts_build/src/agents/tools/ast/astEditNode.js.map +1 -0
- package/ts_build/src/agents/tools/ast/astGetPathForLine.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astGetPathForLine.js +78 -0
- package/ts_build/src/agents/tools/ast/astGetPathForLine.js.map +1 -0
- package/ts_build/src/agents/tools/ast/astListPaths.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astListPaths.js +78 -0
- package/ts_build/src/agents/tools/ast/astListPaths.js.map +1 -0
- package/ts_build/src/agents/tools/ast/index.d.ts +5 -0
- package/ts_build/src/agents/tools/ast/index.js +14 -0
- package/ts_build/src/agents/tools/ast/index.js.map +1 -0
- package/ts_build/src/agents/tools/astAppendNode.d.ts +1 -0
- package/ts_build/src/agents/tools/astAppendNode.js +98 -0
- package/ts_build/src/agents/tools/astAppendNode.js.map +1 -0
- package/ts_build/src/agents/tools/astDeleteNode.d.ts +1 -0
- package/ts_build/src/agents/tools/astDeleteNode.js +95 -0
- package/ts_build/src/agents/tools/astDeleteNode.js.map +1 -0
- package/ts_build/src/agents/tools/astEditNode.d.ts +1 -0
- package/ts_build/src/agents/tools/astEditNode.js +98 -0
- package/ts_build/src/agents/tools/astEditNode.js.map +1 -0
- package/ts_build/src/agents/tools/astGetPathForLine.d.ts +1 -0
- package/ts_build/src/agents/tools/astGetPathForLine.js +89 -0
- package/ts_build/src/agents/tools/astGetPathForLine.js.map +1 -0
- package/ts_build/src/agents/tools/astListPaths.d.ts +1 -0
- package/ts_build/src/agents/tools/astListPaths.js +82 -0
- package/ts_build/src/agents/tools/astListPaths.js.map +1 -0
- package/ts_build/src/agents/tools/callPlugin.js +4 -2
- package/ts_build/src/agents/tools/callPlugin.js.map +1 -1
- package/ts_build/src/agents/tools/embeddingSearch.js +3 -2
- package/ts_build/src/agents/tools/embeddingSearch.js.map +1 -1
- package/ts_build/src/agents/tools/execCommand.d.ts +2 -2
- package/ts_build/src/agents/tools/execCommand.js +201 -67
- package/ts_build/src/agents/tools/execCommand.js.map +1 -1
- package/ts_build/src/agents/tools/fileSearch.d.ts +1 -1
- package/ts_build/src/agents/tools/fileSearch.js +11 -15
- package/ts_build/src/agents/tools/fileSearch.js.map +1 -1
- package/ts_build/src/agents/tools/github/index.d.ts +1 -1
- package/ts_build/src/agents/tools/index.d.ts +1 -0
- package/ts_build/src/agents/tools/index.js +1 -0
- package/ts_build/src/agents/tools/index.js.map +1 -1
- package/ts_build/src/agents/tools/language/definitions.js +11 -2
- package/ts_build/src/agents/tools/language/definitions.js.map +1 -1
- package/ts_build/src/agents/tools/language/index.js +4 -3
- package/ts_build/src/agents/tools/language/index.js.map +1 -1
- package/ts_build/src/agents/tools/lintFile.js +4 -2
- package/ts_build/src/agents/tools/lintFile.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +185 -49
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/patch.js +33 -10
- package/ts_build/src/agents/tools/patch.js.map +1 -1
- package/ts_build/src/agents/tools/readBlocks.js +23 -0
- package/ts_build/src/agents/tools/readBlocks.js.map +1 -1
- package/ts_build/src/agents/tools/readFile.js +14 -0
- package/ts_build/src/agents/tools/readFile.js.map +1 -1
- package/ts_build/src/agents/tools/stringReplace.js +19 -2
- package/ts_build/src/agents/tools/stringReplace.js.map +1 -1
- package/ts_build/src/agents/tools/writeFile.js +40 -0
- package/ts_build/src/agents/tools/writeFile.js.map +1 -1
- package/ts_build/src/agents/tools/ycmd/server.js +5 -0
- package/ts_build/src/agents/tools/ycmd/server.js.map +1 -1
- package/ts_build/src/chat/CliChatService.d.ts +1 -0
- package/ts_build/src/chat/CliChatService.js +6 -2
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +5 -1
- package/ts_build/src/chat/modules/AgentModule.js +62 -32
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/AskModule.js.map +1 -1
- package/ts_build/src/chat/modules/SetupModule.js +4 -3
- package/ts_build/src/chat/modules/SetupModule.js.map +1 -1
- package/ts_build/src/chat/modules/SystemModule.js +19 -4
- package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
- package/ts_build/src/chat/modules/index.d.ts +5 -0
- package/ts_build/src/chat/modules/index.js +14 -0
- package/ts_build/src/chat/modules/index.js.map +1 -0
- package/ts_build/src/chat/types.d.ts +2 -0
- package/ts_build/src/chat-old.js +3 -3
- package/ts_build/src/chat-old.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +1 -0
- package/ts_build/src/clients/anthropic.js +22 -1
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/openai.js +1 -1
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/xai.d.ts +7 -0
- package/ts_build/src/clients/xai.js +13 -4
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/config.js +14 -3
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/dataset/diffs/generate.js +2 -2
- package/ts_build/src/dataset/diffs/generate.js.map +1 -1
- package/ts_build/src/dataset/diffs/jsonl.js.map +1 -1
- package/ts_build/src/embeddings.js +9 -13
- package/ts_build/src/embeddings.js.map +1 -1
- package/ts_build/src/index.js +10 -10
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/plugins/GitPlugin.d.ts +39 -0
- package/ts_build/src/plugins/GitPlugin.js +439 -0
- package/ts_build/src/plugins/GitPlugin.js.map +1 -0
- package/ts_build/src/plugins/LinterPlugin.d.ts +15 -0
- package/ts_build/src/plugins/LinterPlugin.js +65 -0
- package/ts_build/src/plugins/LinterPlugin.js.map +1 -0
- package/ts_build/src/plugins/PluginBase.d.ts +4 -3
- package/ts_build/src/plugins/PluginBase.js +3 -3
- package/ts_build/src/plugins/PluginBase.js.map +1 -1
- package/ts_build/src/plugins/asana.d.ts +3 -1
- package/ts_build/src/plugins/asana.js +3 -2
- package/ts_build/src/plugins/asana.js.map +1 -1
- package/ts_build/src/plugins/downloader/plugin.d.ts +3 -1
- package/ts_build/src/plugins/downloader/plugin.js +3 -2
- package/ts_build/src/plugins/downloader/plugin.js.map +1 -1
- package/ts_build/src/plugins/embedding.d.ts +5 -1
- package/ts_build/src/plugins/embedding.js +15 -3
- package/ts_build/src/plugins/embedding.js.map +1 -1
- package/ts_build/src/plugins/figma.d.ts +3 -1
- package/ts_build/src/plugins/figma.js +28 -4
- package/ts_build/src/plugins/figma.js.map +1 -1
- package/ts_build/src/plugins/github.d.ts +3 -1
- package/ts_build/src/plugins/github.js +3 -2
- package/ts_build/src/plugins/github.js.map +1 -1
- package/ts_build/src/plugins/jira.d.ts +3 -1
- package/ts_build/src/plugins/jira.js +3 -2
- package/ts_build/src/plugins/jira.js.map +1 -1
- package/ts_build/src/plugins/language.d.ts +7 -4
- package/ts_build/src/plugins/language.js +85 -20
- package/ts_build/src/plugins/language.js.map +1 -1
- package/ts_build/src/plugins/linear.d.ts +3 -1
- package/ts_build/src/plugins/linear.js +3 -2
- package/ts_build/src/plugins/linear.js.map +1 -1
- package/ts_build/src/plugins/notion.d.ts +3 -1
- package/ts_build/src/plugins/notion.js +3 -2
- package/ts_build/src/plugins/notion.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +4 -3
- package/ts_build/src/plugins/plugins.js +24 -14
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/plugins/tree-sitter/editor.d.ts +34 -0
- package/ts_build/src/plugins/tree-sitter/editor.js +218 -0
- package/ts_build/src/plugins/tree-sitter/editor.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths-new.d.ts +29 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths-new.js +538 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths-new.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths.d.ts +22 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths.js +332 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/index.d.ts +8 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/index.js +26 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/index.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/java.d.ts +2 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/java.js +61 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/java.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/javascript.d.ts +2 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/javascript.js +59 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/javascript.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/python.d.ts +2 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/python.js +47 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/python.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/types.d.ts +43 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/types.js +3 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/types.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/typescript.d.ts +2 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/typescript.js +50 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/typescript.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/parser.d.ts +75 -0
- package/ts_build/src/plugins/tree-sitter/parser.js +306 -0
- package/ts_build/src/plugins/tree-sitter/parser.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/simple-paths.d.ts +22 -0
- package/ts_build/src/plugins/tree-sitter/simple-paths.js +332 -0
- package/ts_build/src/plugins/tree-sitter/simple-paths.js.map +1 -0
- package/ts_build/src/plugins/types.d.ts +10 -0
- package/ts_build/src/plugins/url.d.ts +3 -2
- package/ts_build/src/plugins/url.js +3 -2
- package/ts_build/src/plugins/url.js.map +1 -1
- package/ts_build/src/plugins/vim.d.ts +4 -2
- package/ts_build/src/plugins/vim.js +6 -8
- package/ts_build/src/plugins/vim.js.map +1 -1
- package/ts_build/src/processors/CustomVariables.js +45 -47
- package/ts_build/src/processors/CustomVariables.js.map +1 -1
- package/ts_build/src/processors/TokenCompressor.js +10 -13
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/processors/ToolResponseCache.d.ts +2 -2
- package/ts_build/src/processors/ToolResponseCache.js +18 -10
- package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
- package/ts_build/src/services/EmbeddingService.d.ts +10 -1
- package/ts_build/src/services/EmbeddingService.js +12 -12
- package/ts_build/src/services/EmbeddingService.js.map +1 -1
- package/ts_build/src/services/EventService.d.ts +7 -0
- package/ts_build/src/services/EventService.js +49 -0
- package/ts_build/src/services/EventService.js.map +1 -1
- package/ts_build/src/services/Mcp.js +8 -0
- package/ts_build/src/services/Mcp.js.map +1 -1
- package/ts_build/src/services/S3.js +4 -3
- package/ts_build/src/services/S3.js.map +1 -1
- package/ts_build/src/services/Tools.d.ts +1 -0
- package/ts_build/src/services/Tools.js +97 -35
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/index.d.ts +4 -5
- package/ts_build/src/services/index.js +14 -9
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/types.js +3 -3
- package/ts_build/src/services/types.js.map +1 -1
- package/ts_build/src/types.d.ts +7 -1
- package/ts_build/src/types.js +4 -0
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/worker.js +12 -1
- package/ts_build/src/worker.js.map +1 -1
- package/ts_build/tests/clients/AIClient.test.d.ts +1 -0
- package/ts_build/tests/clients/AIClient.test.js +377 -0
- package/ts_build/tests/clients/AIClient.test.js.map +1 -0
- package/ts_build/tests/languagePlugin.test.js +217 -11
- package/ts_build/tests/languagePlugin.test.js.map +1 -1
- package/ts_build/tests/manual/agent-events/event-handler-reliability.test.d.ts +1 -0
- package/ts_build/tests/manual/agent-events/event-handler-reliability.test.js +315 -0
- package/ts_build/tests/manual/agent-events/event-handler-reliability.test.js.map +1 -0
- package/ts_build/tests/manual/agent-events/run-test.d.ts +2 -0
- package/ts_build/tests/manual/agent-events/run-test.js +148 -0
- package/ts_build/tests/manual/agent-events/run-test.js.map +1 -0
- package/ts_build/tests/manual/file-edits/figma.test.d.ts +1 -0
- package/ts_build/tests/manual/file-edits/figma.test.js +47 -0
- package/ts_build/tests/manual/file-edits/figma.test.js.map +1 -0
- package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.d.ts +1 -0
- package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.js +100 -0
- package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.js.map +1 -0
- package/ts_build/tests/manual/file-edits/patching.test.d.ts +1 -0
- package/ts_build/tests/manual/file-edits/patching.test.js +119 -0
- package/ts_build/tests/manual/file-edits/patching.test.js.map +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.d.ts +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +277 -0
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.d.ts +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +331 -0
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -0
- package/ts_build/tests/plugins/language/languagePlugin.test.d.ts +1 -0
- package/ts_build/tests/plugins/language/languagePlugin.test.js +286 -0
- package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.d.ts +1 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js +351 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js.map +1 -0
- package/ts_build/tests/processors/CustomVariables.test.d.ts +1 -0
- package/ts_build/tests/processors/CustomVariables.test.js +354 -0
- package/ts_build/tests/processors/CustomVariables.test.js.map +1 -0
- package/ts_build/tests/processors/HarmonyToolProcessor.test.d.ts +1 -0
- package/ts_build/tests/processors/HarmonyToolProcessor.test.js +382 -0
- package/ts_build/tests/processors/HarmonyToolProcessor.test.js.map +1 -0
- package/ts_build/tests/processors/TokenCompressor.test.d.ts +1 -0
- package/ts_build/tests/processors/TokenCompressor.test.js +299 -0
- package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -0
- package/ts_build/tests/processors/ToolResponseCache.test.d.ts +1 -0
- package/ts_build/tests/processors/ToolResponseCache.test.js +550 -0
- package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -0
- package/ts_build/tests/services/Plugins/plugin-event-integration.test.d.ts +1 -0
- package/ts_build/tests/services/Plugins/plugin-event-integration.test.js +232 -0
- package/ts_build/tests/services/Plugins/plugin-event-integration.test.js.map +1 -0
- package/ts_build/tests/services/Tools.test.d.ts +1 -0
- package/ts_build/tests/services/Tools.test.js +1059 -0
- package/ts_build/tests/services/Tools.test.js.map +1 -0
- package/ts_build/tests/test.spec.js +110 -68
- package/ts_build/tests/test.spec.js.map +1 -1
- package/ts_build/tests/tree-sitter/editor.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/editor.test.js +85 -0
- package/ts_build/tests/tree-sitter/editor.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/invalid.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/invalid.test.js +198 -0
- package/ts_build/tests/tree-sitter/invalid.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/common-edits.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/common-edits.test.js +347 -0
- package/ts_build/tests/tree-sitter/paths/common-edits.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-exact-position.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-exact-position.test.js +35 -0
- package/ts_build/tests/tree-sitter/paths/debug-exact-position.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-line-indexing.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-line-indexing.test.js +38 -0
- package/ts_build/tests/tree-sitter/paths/debug-line-indexing.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-paths.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-paths.test.js +74 -0
- package/ts_build/tests/tree-sitter/paths/debug-paths.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/human-readable-paths.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/human-readable-paths.test.js +302 -0
- package/ts_build/tests/tree-sitter/paths/human-readable-paths.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/paths.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/paths.test.js +116 -0
- package/ts_build/tests/tree-sitter/paths/paths.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/simple-paths.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/simple-paths.test.js +302 -0
- package/ts_build/tests/tree-sitter/paths/simple-paths.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/sample-after.d.ts +11 -0
- package/ts_build/tests/tree-sitter/sample-after.js +44 -0
- package/ts_build/tests/tree-sitter/sample-after.js.map +1 -0
- package/ts_build/tests/tree-sitter/sample-before.d.ts +9 -0
- package/ts_build/tests/tree-sitter/sample-before.js +28 -0
- package/ts_build/tests/tree-sitter/sample-before.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/completely-broken.d.ts +2 -0
- package/ts_build/tests/tree-sitter/test-files/completely-broken.js +17 -0
- package/ts_build/tests/tree-sitter/test-files/completely-broken.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/duplicate-braces.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/duplicate-braces.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/duplicate-braces.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/invalid-nesting.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/invalid-nesting.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/invalid-nesting.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/malformed-signature.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/malformed-signature.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/malformed-signature.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/mismatched-parens.d.ts +10 -0
- package/ts_build/tests/tree-sitter/test-files/mismatched-parens.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/mismatched-parens.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/missing-semicolon.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/missing-semicolon.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/missing-semicolon.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/partially-broken.d.ts +6 -0
- package/ts_build/tests/tree-sitter/test-files/partially-broken.js +20 -0
- package/ts_build/tests/tree-sitter/test-files/partially-broken.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/specific-errors.d.ts +7 -0
- package/ts_build/tests/tree-sitter/test-files/specific-errors.js +14 -0
- package/ts_build/tests/tree-sitter/test-files/specific-errors.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/unclosed-string.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/unclosed-string.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/unclosed-string.js.map +1 -0
- package/ts_build/tests/tree-sitter/tree-sitter.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/tree-sitter.test.js +185 -0
- package/ts_build/tests/tree-sitter/tree-sitter.test.js.map +1 -0
- package/tsconfig.json +2 -1
- package/tests/languagePlugin.test.ts +0 -74
- /package/src/chat/modules/{index.js โ index.ts} +0 -0
- /package/tests/{integration โ manual/file-edits}/patching/input.txt +0 -0
- /package/tests/{integration โ manual/file-edits}/patching/output.txt +0 -0
- /package/tests/{integration โ manual/file-edits}/patching/patch.txt +0 -0
- /package/tests/{integration โ manual/file-edits}/patching/unseen.txt +0 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import { AIClient } from "../../src/clients";
|
|
2
|
+
import { GenericClient, CompletionOptions, CompletionResponse, EmbeddingOptions, EmbeddingResponse } from "../../src/clients/types";
|
|
3
|
+
|
|
4
|
+
class FakeClient implements GenericClient {
|
|
5
|
+
private apiKey: string = "";
|
|
6
|
+
private models: { id: string }[] = [
|
|
7
|
+
{ id: "fake-model-1" },
|
|
8
|
+
{ id: "fake-model-2" },
|
|
9
|
+
{ id: "fake-embed-model" }
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
constructor(modelIds?: string[]) {
|
|
13
|
+
if (modelIds) {
|
|
14
|
+
this.models = modelIds.map(id => ({ id }));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setKey(key: string): void {
|
|
19
|
+
this.apiKey = key;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setModels(models: { id: string }[]): void {
|
|
23
|
+
this.models = models;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async createChatCompletion(options: CompletionOptions): Promise<CompletionResponse> {
|
|
27
|
+
return {
|
|
28
|
+
choices: [{
|
|
29
|
+
message: {
|
|
30
|
+
role: "assistant",
|
|
31
|
+
content: `Fake response for model: ${options.model}`
|
|
32
|
+
}
|
|
33
|
+
}],
|
|
34
|
+
model: options.model,
|
|
35
|
+
usage: { total_tokens: 100 }
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async createEmbedding(options: EmbeddingOptions): Promise<EmbeddingResponse> {
|
|
40
|
+
return {
|
|
41
|
+
data: [{
|
|
42
|
+
object: "embedding",
|
|
43
|
+
embedding: [0.1, 0.2, 0.3],
|
|
44
|
+
index: 0
|
|
45
|
+
}],
|
|
46
|
+
model: options.model || "fake-embed-model",
|
|
47
|
+
usage: {
|
|
48
|
+
prompt_tokens: 10,
|
|
49
|
+
total_tokens: 10
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getModels(): Promise<{ id: string }[]> {
|
|
55
|
+
return this.models;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
describe("AIClient", () => {
|
|
60
|
+
let aiClient: AIClient;
|
|
61
|
+
let fakeClient: FakeClient;
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
aiClient = new AIClient();
|
|
65
|
+
fakeClient = new FakeClient();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("registerClient and getClient", () => {
|
|
69
|
+
it("should register a fake client and retrieve it", () => {
|
|
70
|
+
aiClient.registerClient("fake", fakeClient);
|
|
71
|
+
aiClient.registerModels("fake", ["fake-model-1", "fake-model-2"]);
|
|
72
|
+
|
|
73
|
+
const result = aiClient.getClient("fake");
|
|
74
|
+
|
|
75
|
+
expect(result.client).toBe(fakeClient);
|
|
76
|
+
expect(result.provider).toBe("fake");
|
|
77
|
+
expect(result.model).toBeUndefined();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should register a fake client and retrieve it with model", () => {
|
|
81
|
+
aiClient.registerClient("fake", fakeClient);
|
|
82
|
+
aiClient.registerModels("fake", ["fake-model-1", "fake-model-2"]);
|
|
83
|
+
|
|
84
|
+
const result = aiClient.getClient("fake", "fake-model-1");
|
|
85
|
+
|
|
86
|
+
expect(result.client).toBe(fakeClient);
|
|
87
|
+
expect(result.provider).toBe("fake");
|
|
88
|
+
expect(result.model).toBe("fake-model-1");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should return null client when provider is not registered", () => {
|
|
92
|
+
const result = aiClient.getClient("unregistered");
|
|
93
|
+
expect(result.client).toBeUndefined();
|
|
94
|
+
expect(result.provider).toBe("unregistered");
|
|
95
|
+
expect(result.model).toBeUndefined();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should throw error when model is not found", () => {
|
|
99
|
+
aiClient.registerClient("fake", fakeClient);
|
|
100
|
+
aiClient.registerModels("fake", ["fake-model-1"]);
|
|
101
|
+
|
|
102
|
+
expect(() => {
|
|
103
|
+
aiClient.getClient("fake", "non-existent-model");
|
|
104
|
+
}).toThrow("Model non-existent-model not registered for provider fake.");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe("detectProviderModel", () => {
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
aiClient.registerClient("fake", fakeClient);
|
|
110
|
+
aiClient.registerModels("fake", ["fake-model-1", "fake-model-2", "fake-embed-model"]);
|
|
111
|
+
|
|
112
|
+
aiClient.registerClient("another", new FakeClient());
|
|
113
|
+
aiClient.registerModels("another", ["another-model-1", "gpt-4"]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should detect exact provider and model match", () => {
|
|
117
|
+
const result = aiClient.detectProviderModel("fake", "fake-model-1");
|
|
118
|
+
expect(result.provider).toBe("fake");
|
|
119
|
+
expect(result.model).toBe("fake-model-1");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should detect model from slash-separated format (provider/model)", () => {
|
|
123
|
+
const result = aiClient.detectProviderModel("", "fake/fake-model-1");
|
|
124
|
+
expect(result.provider).toBe("fake");
|
|
125
|
+
expect(result.model).toBe("fake-model-1");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should detect model from nested slash format (provider/subprovider/model)", () => {
|
|
129
|
+
aiClient.registerClient("knowhow", new FakeClient());
|
|
130
|
+
aiClient.registerModels("knowhow", ["openai/gpt-4", "anthropic/claude-3"]);
|
|
131
|
+
|
|
132
|
+
const result = aiClient.detectProviderModel("", "knowhow/openai/gpt-4");
|
|
133
|
+
expect(result.provider).toBe("knowhow"); // AIClient returns the first part as provider
|
|
134
|
+
expect(result.model).toBe("openai/gpt-4"); // Rest becomes model
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should find model by detection in registered providers", () => {
|
|
138
|
+
aiClient.registerClient("test", new FakeClient());
|
|
139
|
+
aiClient.registerModels("test", ["gpt-4-turbo", "gpt-4-vision"]);
|
|
140
|
+
|
|
141
|
+
const result = aiClient.detectProviderModel("", "gpt-4");
|
|
142
|
+
expect(result.provider).toBe("openai"); // Real openai provider takes precedence
|
|
143
|
+
expect(result.model).toBe("gpt-4.1-2025-04-14"); // Actual model found by prefix match
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should handle model with provider prefix when provider is empty", () => {
|
|
147
|
+
const result = aiClient.detectProviderModel("", "another/gpt-4");
|
|
148
|
+
expect(result.provider).toBe("another"); // Provider from prefix
|
|
149
|
+
expect(result.model).toBe("gpt-4");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("should return original values when no match found", () => {
|
|
153
|
+
const result = aiClient.detectProviderModel("unknown", "unknown-model");
|
|
154
|
+
expect(result.provider).toBe("unknown");
|
|
155
|
+
expect(result.model).toBe("unknown-model");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should detect real provider when model exists", () => {
|
|
159
|
+
aiClient.registerClient("test", new FakeClient());
|
|
160
|
+
aiClient.registerModels("test", ["claude-3-opus"]);
|
|
161
|
+
|
|
162
|
+
// Test with provider prefix that gets stripped
|
|
163
|
+
const result = aiClient.detectProviderModel("", "anthropic/claude-3-opus-20240229");
|
|
164
|
+
expect(result.provider).toBe("anthropic"); // Real anthropic provider found
|
|
165
|
+
expect(result.model).toBe("claude-3-opus-20240229");
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe("Model Listing Functionality", () => {
|
|
169
|
+
beforeEach(() => {
|
|
170
|
+
aiClient.registerClient("fake", fakeClient);
|
|
171
|
+
aiClient.registerModels("fake", ["fake-model-1", "fake-model-2", "fake-embed-model"]);
|
|
172
|
+
|
|
173
|
+
aiClient.registerClient("another", new FakeClient());
|
|
174
|
+
aiClient.registerModels("another", ["another-model-1", "gpt-4", "claude-3"]);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe("listAllModels", () => {
|
|
178
|
+
it("should return all registered models from all providers", () => {
|
|
179
|
+
const allModels = aiClient.listAllModels();
|
|
180
|
+
expect(typeof allModels).toBe("object");
|
|
181
|
+
// listAllModels() only returns models from real providers that have API keys
|
|
182
|
+
// Our test clients are not included in the listAllModels() output
|
|
183
|
+
// But we can verify real providers are present
|
|
184
|
+
expect(Object.keys(allModels).length).toBeGreaterThan(0);
|
|
185
|
+
// Real providers like openai, anthropic should be present
|
|
186
|
+
const providers = Object.keys(allModels);
|
|
187
|
+
expect(providers.some(p => ['openai', 'anthropic', 'google', 'xai'].includes(p))).toBe(true);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("should return empty array when no clients registered", () => {
|
|
191
|
+
const freshClient = new AIClient();
|
|
192
|
+
const allModels = freshClient.listAllModels();
|
|
193
|
+
// Note: AIClient starts with real providers from environment, so this will not be empty
|
|
194
|
+
expect(typeof allModels).toBe("object"); // Should return object with real providers
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe("getRegisteredModels", () => {
|
|
199
|
+
it("should return models for specific provider", () => {
|
|
200
|
+
const fakeModels = aiClient.getRegisteredModels("fake");
|
|
201
|
+
expect(fakeModels).toEqual(["fake-model-1", "fake-model-2", "fake-embed-model"]);
|
|
202
|
+
|
|
203
|
+
const anotherModels = aiClient.getRegisteredModels("another");
|
|
204
|
+
expect(anotherModels).toEqual(["another-model-1", "gpt-4", "claude-3"]);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should return empty array for unregistered provider", () => {
|
|
208
|
+
const models = aiClient.getRegisteredModels("unregistered");
|
|
209
|
+
expect(models).toEqual([]);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("Model registration from client.getModels()", () => {
|
|
214
|
+
it("should register models from fake client getModels() method", async () => {
|
|
215
|
+
const clientWithModels = new FakeClient();
|
|
216
|
+
clientWithModels.setModels([
|
|
217
|
+
{ id: "dynamic-model-1" },
|
|
218
|
+
{ id: "dynamic-model-2" }
|
|
219
|
+
]);
|
|
220
|
+
|
|
221
|
+
aiClient.registerClient("dynamic", clientWithModels);
|
|
222
|
+
|
|
223
|
+
// Register models from the client's getModels method
|
|
224
|
+
const models = await clientWithModels.getModels();
|
|
225
|
+
const modelIds = models.map(m => m.id);
|
|
226
|
+
aiClient.registerModels("dynamic", modelIds);
|
|
227
|
+
|
|
228
|
+
const registeredModels = aiClient.getRegisteredModels("dynamic");
|
|
229
|
+
expect(registeredModels).toEqual(["dynamic-model-1", "dynamic-model-2"]);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe("Various Model Format Support", () => {
|
|
234
|
+
beforeEach(() => {
|
|
235
|
+
aiClient.registerClient("openai", new FakeClient());
|
|
236
|
+
aiClient.registerModels("openai", ["gpt-4", "gpt-3.5-turbo"]);
|
|
237
|
+
|
|
238
|
+
aiClient.registerClient("knowhow", new FakeClient());
|
|
239
|
+
aiClient.registerModels("knowhow", ["openai/gpt-4", "anthropic/claude-3", "google/gemini-pro"]);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("should support format: provider='openai', model='gpt-4'", () => {
|
|
243
|
+
const client = aiClient.getClient("openai", "gpt-4");
|
|
244
|
+
expect(client).toBeDefined();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("should support format: provider='knowhow', model='openai/gpt-4'", () => {
|
|
248
|
+
const client = aiClient.getClient("knowhow", "openai/gpt-4");
|
|
249
|
+
expect(client).toBeDefined();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("should support format: provider='', model='openai/gpt-4' (auto-detect)", () => {
|
|
253
|
+
const client = aiClient.getClient("", "openai/gpt-4");
|
|
254
|
+
expect(client).toBeDefined();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("should support format: provider='', model='knowhow/openai/gpt-4' (nested)", () => {
|
|
258
|
+
const client = aiClient.getClient("", "knowhow/openai/gpt-4");
|
|
259
|
+
expect(client).toBeDefined();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("should support model detection with complex nested paths", () => {
|
|
263
|
+
aiClient.registerClient("complex", new FakeClient());
|
|
264
|
+
aiClient.registerModels("complex", ["provider/subprovider/model-name", "another/path/to/model"]);
|
|
265
|
+
|
|
266
|
+
const client1 = aiClient.getClient("", "complex/provider/subprovider/model-name");
|
|
267
|
+
expect(client1).toBeDefined();
|
|
268
|
+
|
|
269
|
+
const client2 = aiClient.getClient("", "complex/another/path/to/model");
|
|
270
|
+
expect(client2).toBeDefined();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("should handle model detection with provider stripping", () => {
|
|
274
|
+
// Register a model without provider prefix
|
|
275
|
+
aiClient.registerClient("stripped", new FakeClient());
|
|
276
|
+
aiClient.registerModels("stripped", ["claude-3-opus"]);
|
|
277
|
+
|
|
278
|
+
// Should find it even when requested with provider prefix
|
|
279
|
+
const client = aiClient.getClient("", "anthropic/claude-3-opus");
|
|
280
|
+
expect(client).toBeDefined();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
describe("Integration Tests", () => {
|
|
284
|
+
it("should handle end-to-end flow: register client โ register models โ detect โ retrieve", () => {
|
|
285
|
+
// Register client and models
|
|
286
|
+
const fakeClient = new FakeClient();
|
|
287
|
+
aiClient.registerClient("integration", fakeClient);
|
|
288
|
+
aiClient.registerModels("integration", ["model-1", "model-2", "provider/model-3"]);
|
|
289
|
+
|
|
290
|
+
// Test detection
|
|
291
|
+
const detection1 = aiClient.detectProviderModel("", "integration/model-1");
|
|
292
|
+
expect(detection1).toEqual({ provider: "integration", model: "model-1" });
|
|
293
|
+
|
|
294
|
+
const detection2 = aiClient.detectProviderModel("", "integration/provider/model-3");
|
|
295
|
+
expect(detection2).toEqual({ provider: "integration", model: "provider/model-3" });
|
|
296
|
+
|
|
297
|
+
// Test retrieval
|
|
298
|
+
const result1 = aiClient.getClient("integration", "model-1");
|
|
299
|
+
expect(result1.client).toBe(fakeClient);
|
|
300
|
+
expect(result1.provider).toBe("integration");
|
|
301
|
+
expect(result1.model).toBe("model-1");
|
|
302
|
+
|
|
303
|
+
const result2 = aiClient.getClient("", "integration/model-1");
|
|
304
|
+
expect(result2.client).toBe(fakeClient);
|
|
305
|
+
expect(result2.provider).toBe("integration");
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("should register models from client.getModels() and make them available", async () => {
|
|
309
|
+
const fakeClient = new FakeClient(["auto-model-1", "auto-model-2"]);
|
|
310
|
+
aiClient.registerClient("auto", fakeClient);
|
|
311
|
+
|
|
312
|
+
// Get models from client
|
|
313
|
+
const models = await fakeClient.getModels();
|
|
314
|
+
aiClient.registerModels("auto", models.map(m => m.id));
|
|
315
|
+
|
|
316
|
+
// Should be able to retrieve client using these models
|
|
317
|
+
const result1 = aiClient.getClient("auto", "auto-model-1");
|
|
318
|
+
expect(result1.client).toBe(fakeClient);
|
|
319
|
+
expect(result1.provider).toBe("auto");
|
|
320
|
+
|
|
321
|
+
const result2 = aiClient.getClient("", "auto/auto-model-1");
|
|
322
|
+
expect(result2.client).toBe(fakeClient);
|
|
323
|
+
expect(result2.provider).toBe("auto");
|
|
324
|
+
|
|
325
|
+
// Models should appear in listings
|
|
326
|
+
const allModels = aiClient.listAllModels();
|
|
327
|
+
expect(allModels['auto']).toContain("auto-model-1");
|
|
328
|
+
expect(allModels['auto']).toContain("auto-model-2");
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("should handle multiple providers with overlapping model names", () => {
|
|
332
|
+
// Register multiple providers with same model names
|
|
333
|
+
aiClient.registerClient("provider1", new FakeClient());
|
|
334
|
+
aiClient.registerModels("provider1", ["common-model", "unique-model-1"]);
|
|
335
|
+
|
|
336
|
+
aiClient.registerClient("provider2", new FakeClient());
|
|
337
|
+
aiClient.registerModels("provider2", ["common-model", "unique-model-2"]);
|
|
338
|
+
|
|
339
|
+
// Should be able to get specific provider's model
|
|
340
|
+
const client1 = aiClient.getClient("provider1", "common-model");
|
|
341
|
+
const client2 = aiClient.getClient("provider2", "common-model");
|
|
342
|
+
|
|
343
|
+
expect(client1.client).toBeDefined();
|
|
344
|
+
expect(client2.client).toBeDefined();
|
|
345
|
+
expect(client1.client).not.toBe(client2.client);
|
|
346
|
+
|
|
347
|
+
// Auto-detection should work with full paths
|
|
348
|
+
const autoClient1 = aiClient.getClient("", "provider1/common-model");
|
|
349
|
+
const autoClient2 = aiClient.getClient("", "provider2/common-model");
|
|
350
|
+
|
|
351
|
+
expect(autoClient1.client).toBe(client1.client);
|
|
352
|
+
expect(autoClient2.client).toBe(client2.client);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
describe("Edge Case Testing", () => {
|
|
356
|
+
beforeEach(() => {
|
|
357
|
+
aiClient.registerClient("edge", new FakeClient());
|
|
358
|
+
aiClient.registerModels("edge", ["normal-model", "model-with-dashes", "model_with_underscores"]);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("should handle empty provider and model strings", () => {
|
|
362
|
+
// Empty strings should return default OpenAI client with gpt-5
|
|
363
|
+
const result = aiClient.getClient("", "");
|
|
364
|
+
expect(result.provider).toBe("openai");
|
|
365
|
+
expect(result.model).toBe("gpt-5");
|
|
366
|
+
|
|
367
|
+
const detection = aiClient.detectProviderModel("", "");
|
|
368
|
+
expect(detection?.provider).toBe("openai");
|
|
369
|
+
expect(detection?.model).toBe("gpt-5");
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("should handle malformed model formats", () => {
|
|
373
|
+
// Test various malformed formats
|
|
374
|
+
const malformedInputs = [
|
|
375
|
+
"//model",
|
|
376
|
+
"provider//model",
|
|
377
|
+
"/provider/model",
|
|
378
|
+
"provider/",
|
|
379
|
+
"///",
|
|
380
|
+
"provider/model/"
|
|
381
|
+
];
|
|
382
|
+
|
|
383
|
+
malformedInputs.forEach(input => {
|
|
384
|
+
const detection = aiClient.detectProviderModel("", input);
|
|
385
|
+
// Should either find a valid match or return fallback values, not throw
|
|
386
|
+
expect(detection).toBeDefined();
|
|
387
|
+
expect(detection?.provider).toBeDefined();
|
|
388
|
+
expect(detection?.model).toBeDefined();
|
|
389
|
+
// For malformed inputs that can't be parsed, should fallback to defaults
|
|
390
|
+
if (input === "provider/" || input === "///" || input === "provider/model/") {
|
|
391
|
+
expect(detection?.provider).toBe("openai");
|
|
392
|
+
expect(detection?.model).toBe("gpt-5");
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it("should handle provider stripping with complex model names", () => {
|
|
398
|
+
// Test detection with real providers that exist in AIClient
|
|
399
|
+
// AIClient should find the real anthropic provider for claude models
|
|
400
|
+
const detection1 = aiClient.detectProviderModel("", "anthropic/claude-3-opus-20240229");
|
|
401
|
+
expect(detection1?.provider).toBe("anthropic");
|
|
402
|
+
expect(detection1?.model).toBe("claude-3-opus-20240229");
|
|
403
|
+
|
|
404
|
+
// For models that don't exist in the registered providers, AIClient falls back
|
|
405
|
+
const detection2 = aiClient.detectProviderModel("", "openai/non-existent-model");
|
|
406
|
+
// Should either return empty strings or fallback to defaults
|
|
407
|
+
expect(detection2).toBeDefined();
|
|
408
|
+
if (detection2?.provider === "") {
|
|
409
|
+
expect(detection2?.model).toBe("openai/non-existent-model");
|
|
410
|
+
} else {
|
|
411
|
+
expect(detection2?.provider).toBe("openai");
|
|
412
|
+
expect(detection2?.model).toBe("gpt-5");
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("should handle model prefix matching edge cases", () => {
|
|
417
|
+
aiClient.registerClient("prefix", new FakeClient());
|
|
418
|
+
aiClient.registerModels("prefix", [
|
|
419
|
+
"test-model",
|
|
420
|
+
"test-model-turbo",
|
|
421
|
+
"test-model-vision"
|
|
422
|
+
]);
|
|
423
|
+
|
|
424
|
+
// Should match exact model first
|
|
425
|
+
const detection1 = aiClient.detectProviderModel("", "prefix/test-model");
|
|
426
|
+
expect(detection1?.model).toBe("test-model");
|
|
427
|
+
|
|
428
|
+
// Custom providers don't do prefix matching - should return empty provider
|
|
429
|
+
const detection2 = aiClient.detectProviderModel("", "prefix/test-model-unknown");
|
|
430
|
+
expect(detection2?.provider).toBe("");
|
|
431
|
+
// Should return the full model name since no match found
|
|
432
|
+
expect(detection2?.model).toBe("prefix/test-model-unknown");
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it("should handle special characters in model names", () => {
|
|
436
|
+
aiClient.registerClient("special", new FakeClient());
|
|
437
|
+
aiClient.registerModels("special", [
|
|
438
|
+
"model-with-dashes",
|
|
439
|
+
"model_with_underscores",
|
|
440
|
+
"model.with.dots",
|
|
441
|
+
"model@with@symbols"
|
|
442
|
+
]);
|
|
443
|
+
|
|
444
|
+
const testCases = [
|
|
445
|
+
"special/model-with-dashes",
|
|
446
|
+
"special/model_with_underscores",
|
|
447
|
+
"special/model.with.dots",
|
|
448
|
+
"special/model@with@symbols"
|
|
449
|
+
];
|
|
450
|
+
|
|
451
|
+
testCases.forEach(testCase => {
|
|
452
|
+
const detection = aiClient.detectProviderModel("", testCase);
|
|
453
|
+
expect(detection).toBeDefined();
|
|
454
|
+
expect(detection?.provider).toBe("special");
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it("should handle case sensitivity correctly", () => {
|
|
459
|
+
aiClient.registerClient("CaseTest", new FakeClient());
|
|
460
|
+
aiClient.registerModels("CaseTest", ["Model-Name", "UPPERCASE-MODEL"]);
|
|
461
|
+
|
|
462
|
+
// Test exact case matches
|
|
463
|
+
let detection = aiClient.detectProviderModel("", "CaseTest/Model-Name");
|
|
464
|
+
expect(detection?.model).toBe("Model-Name");
|
|
465
|
+
|
|
466
|
+
detection = aiClient.detectProviderModel("", "CaseTest/UPPERCASE-MODEL");
|
|
467
|
+
expect(detection?.model).toBe("UPPERCASE-MODEL");
|
|
468
|
+
|
|
469
|
+
// Test case mismatches - AIClient is case sensitive for providers
|
|
470
|
+
detection = aiClient.detectProviderModel("", "casetest/Model-Name");
|
|
471
|
+
// AIClient actually finds the provider despite case mismatch
|
|
472
|
+
expect(detection?.provider).toBe("CaseTest");
|
|
473
|
+
expect(detection?.model).toBe("Model-Name");
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("should handle very long model paths", () => {
|
|
477
|
+
const longProvider = "very-long-provider-name-with-many-segments";
|
|
478
|
+
const longModel = "extremely/long/nested/model/path/with/many/segments/final-model-name";
|
|
479
|
+
|
|
480
|
+
aiClient.registerClient(longProvider, new FakeClient());
|
|
481
|
+
aiClient.registerModels(longProvider, [longModel]);
|
|
482
|
+
|
|
483
|
+
const fullPath = `${longProvider}/${longModel}`;
|
|
484
|
+
const detection = aiClient.detectProviderModel("", fullPath);
|
|
485
|
+
|
|
486
|
+
expect(detection?.provider).toBe(longProvider);
|
|
487
|
+
expect(detection?.model).toBe(longModel);
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
});
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple test runner for the event handler reliability test
|
|
5
|
+
* This runs the test cases manually since they're in the manual test directory
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { EventEmitter } from "events";
|
|
9
|
+
|
|
10
|
+
// Mock the imports that might not be available
|
|
11
|
+
const mockMessage = { role: "user", content: "test" };
|
|
12
|
+
const mockContext = {
|
|
13
|
+
Tools: {
|
|
14
|
+
getTools: () => [],
|
|
15
|
+
callTool: async (name: string, params: any) => ({ name, content: "mock result" })
|
|
16
|
+
},
|
|
17
|
+
Events: { registerAgent: () => {} },
|
|
18
|
+
messageProcessor: { process: async () => "processed" }
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Simplified BaseAgent mock
|
|
22
|
+
class MockBaseAgent extends EventEmitter {
|
|
23
|
+
name = "MockAgent";
|
|
24
|
+
agentEvents = new EventEmitter();
|
|
25
|
+
eventTypes = { done: "done" };
|
|
26
|
+
|
|
27
|
+
constructor(context: any) {
|
|
28
|
+
super();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async call(userInput: string): Promise<string> {
|
|
32
|
+
// Simulate agent processing
|
|
33
|
+
console.log(`๐ค Agent processing: "${userInput}"`);
|
|
34
|
+
|
|
35
|
+
// Simulate async work
|
|
36
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
37
|
+
|
|
38
|
+
// Simulate finalAnswer tool call
|
|
39
|
+
const result = "Task completed successfully";
|
|
40
|
+
|
|
41
|
+
// Emit done event (this is what we're testing)
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
console.log("๐ก Emitting done event...");
|
|
44
|
+
this.agentEvents.emit(this.eventTypes.done, result);
|
|
45
|
+
}, 10);
|
|
46
|
+
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Test cases
|
|
52
|
+
async function runTests() {
|
|
53
|
+
console.log("๐งช Starting Agent Event Handler Reliability Tests\n");
|
|
54
|
+
|
|
55
|
+
let passedTests = 0;
|
|
56
|
+
let totalTests = 0;
|
|
57
|
+
|
|
58
|
+
// Test 1: Normal event handler
|
|
59
|
+
try {
|
|
60
|
+
totalTests++;
|
|
61
|
+
console.log("Test 1: Normal event handler flow");
|
|
62
|
+
|
|
63
|
+
const agent = new MockBaseAgent(mockContext);
|
|
64
|
+
let eventFired = false;
|
|
65
|
+
let eventMessage = "";
|
|
66
|
+
|
|
67
|
+
// Register event handler
|
|
68
|
+
const eventPromise = new Promise<void>((resolve, reject) => {
|
|
69
|
+
const timeout = setTimeout(() => {
|
|
70
|
+
reject(new Error("Event handler timeout"));
|
|
71
|
+
}, 1000);
|
|
72
|
+
|
|
73
|
+
agent.agentEvents.once(agent.eventTypes.done, (message) => {
|
|
74
|
+
clearTimeout(timeout);
|
|
75
|
+
console.log("โ
Agent has finished."); // This should log
|
|
76
|
+
eventFired = true;
|
|
77
|
+
eventMessage = message;
|
|
78
|
+
resolve();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const result = await agent.call("Complete normal test");
|
|
83
|
+
|
|
84
|
+
await eventPromise;
|
|
85
|
+
|
|
86
|
+
if (eventFired && eventMessage === result) {
|
|
87
|
+
console.log("โ
Test 1 PASSED - Event handler fired correctly\n");
|
|
88
|
+
passedTests++;
|
|
89
|
+
} else {
|
|
90
|
+
console.log("โ Test 1 FAILED - Event handler didn't fire properly\n");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
} catch (error: any) {
|
|
94
|
+
console.log(`โ Test 1 FAILED - ${error.message}\n`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Test 2: Race condition - late event handler registration
|
|
98
|
+
try {
|
|
99
|
+
totalTests++;
|
|
100
|
+
console.log("Test 2: Race condition - late event handler");
|
|
101
|
+
|
|
102
|
+
const agent = new MockBaseAgent(mockContext);
|
|
103
|
+
|
|
104
|
+
// Start agent immediately
|
|
105
|
+
const agentPromise = agent.call("Race condition test");
|
|
106
|
+
|
|
107
|
+
// Wait a bit before registering event handler (simulating race condition)
|
|
108
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
109
|
+
|
|
110
|
+
let eventFired = false;
|
|
111
|
+
|
|
112
|
+
// Register event handler LATE
|
|
113
|
+
const lateEventPromise = new Promise<void>((resolve, reject) => {
|
|
114
|
+
const timeout = setTimeout(() => {
|
|
115
|
+
reject(new Error("Late event handler timeout - demonstrates the bug"));
|
|
116
|
+
}, 500);
|
|
117
|
+
|
|
118
|
+
agent.agentEvents.once(agent.eventTypes.done, (message) => {
|
|
119
|
+
clearTimeout(timeout);
|
|
120
|
+
console.log("โ
Agent has finished (late handler).");
|
|
121
|
+
eventFired = true;
|
|
122
|
+
resolve();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const result = await agentPromise;
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
await lateEventPromise;
|
|
130
|
+
console.log("โ
Test 2 PASSED - Late event handler worked (no race condition)\n");
|
|
131
|
+
passedTests++;
|
|
132
|
+
} catch (error: any) {
|
|
133
|
+
console.log("โ Test 2 DEMONSTRATED BUG - Race condition detected!");
|
|
134
|
+
console.log(" This shows the event handler was registered too late\n");
|
|
135
|
+
// This is actually the expected failure that demonstrates the bug
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
} catch (error: any) {
|
|
139
|
+
console.log(`โ Test 2 ERROR - ${error.message}\n`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Test 3: Multiple event handlers
|
|
143
|
+
try {
|
|
144
|
+
totalTests++;
|
|
145
|
+
console.log("Test 3: Multiple event handlers");
|
|
146
|
+
|
|
147
|
+
const agent = new MockBaseAgent(mockContext);
|
|
148
|
+
let handler1Fired = false;
|
|
149
|
+
let handler2Fired = false;
|
|
150
|
+
|
|
151
|
+
// Register multiple handlers
|
|
152
|
+
const promise1 = new Promise<void>((resolve) => {
|
|
153
|
+
agent.agentEvents.once(agent.eventTypes.done, () => {
|
|
154
|
+
console.log("โ
Handler 1: Agent has finished.");
|
|
155
|
+
handler1Fired = true;
|
|
156
|
+
resolve();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const promise2 = new Promise<void>((resolve) => {
|
|
161
|
+
agent.agentEvents.once(agent.eventTypes.done, () => {
|
|
162
|
+
console.log("โ
Handler 2: Agent has completed the task.");
|
|
163
|
+
handler2Fired = true;
|
|
164
|
+
resolve();
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const result = await agent.call("Multiple handlers test");
|
|
169
|
+
|
|
170
|
+
await Promise.race([
|
|
171
|
+
Promise.all([promise1, promise2]),
|
|
172
|
+
new Promise((_, reject) =>
|
|
173
|
+
setTimeout(() => reject(new Error("Multiple handlers timeout")), 1000)
|
|
174
|
+
)
|
|
175
|
+
]);
|
|
176
|
+
|
|
177
|
+
if (handler1Fired && handler2Fired) {
|
|
178
|
+
console.log("โ
Test 3 PASSED - All event handlers fired\n");
|
|
179
|
+
passedTests++;
|
|
180
|
+
} else {
|
|
181
|
+
console.log("โ Test 3 FAILED - Not all handlers fired\n");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
} catch (error: any) {
|
|
185
|
+
console.log(`โ Test 3 FAILED - ${error.message}\n`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Summary
|
|
189
|
+
console.log("๐ Test Results:");
|
|
190
|
+
console.log(` Passed: ${passedTests}/${totalTests} tests`);
|
|
191
|
+
|
|
192
|
+
if (passedTests < totalTests) {
|
|
193
|
+
console.log("\n๐ Event Handler Issues Detected:");
|
|
194
|
+
console.log(" - Race conditions when handlers are registered late");
|
|
195
|
+
console.log(" - Timing issues in event emission");
|
|
196
|
+
console.log(" - This demonstrates the reliability issues in AgentModule.ts");
|
|
197
|
+
} else {
|
|
198
|
+
console.log("\nโ
All tests passed - Event handlers are working reliably");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Run the tests
|
|
203
|
+
runTests().catch(console.error);
|