@tyvm/knowhow 0.0.46 → 0.0.48

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.
Files changed (461) hide show
  1. package/benchmarks/results/27b0a06/2025-09-27/xai/xai-grok-code-fast-1.json +2909 -0
  2. package/benchmarks/results/4057aed/2025-08-14/anthropic/anthropic-claude-sonnet-4-20250514.json +1671 -0
  3. package/jest.config.js +2 -2
  4. package/package.json +8 -3
  5. package/src/agents/base/base.ts +30 -24
  6. package/src/agents/patcher/patcher.ts +26 -5
  7. package/src/agents/tools/agentCall.ts +4 -2
  8. package/src/agents/tools/aiClient.ts +3 -11
  9. package/src/agents/tools/ast/astAppendNode.ts +90 -0
  10. package/src/agents/tools/ast/astDeleteNode.ts +88 -0
  11. package/src/agents/tools/ast/astEditNode.ts +95 -0
  12. package/src/agents/tools/ast/astGetPathForLine.ts +73 -0
  13. package/src/agents/tools/ast/astListPaths.ts +66 -0
  14. package/src/agents/tools/ast/index.ts +7 -0
  15. package/src/agents/tools/callPlugin.ts +8 -2
  16. package/src/agents/tools/embeddingSearch.ts +2 -1
  17. package/src/agents/tools/execCommand.ts +239 -94
  18. package/src/agents/tools/fileSearch.ts +15 -17
  19. package/src/agents/tools/index.ts +1 -0
  20. package/src/agents/tools/language/definitions.ts +10 -2
  21. package/src/agents/tools/language/index.ts +3 -2
  22. package/src/agents/tools/lintFile.ts +4 -2
  23. package/src/agents/tools/list.ts +203 -62
  24. package/src/agents/tools/patch.ts +48 -14
  25. package/src/agents/tools/readBlocks.ts +34 -0
  26. package/src/agents/tools/readFile.ts +23 -0
  27. package/src/agents/tools/stringReplace.ts +33 -9
  28. package/src/agents/tools/writeFile.ts +55 -0
  29. package/src/agents/tools/ycmd/server.ts +14 -4
  30. package/src/chat/CliChatService.ts +6 -1
  31. package/src/chat/modules/AgentModule.ts +107 -64
  32. package/src/chat/modules/AskModule.ts +0 -1
  33. package/src/chat/modules/SetupModule.ts +4 -4
  34. package/src/chat/modules/SystemModule.ts +28 -5
  35. package/src/chat/types.ts +2 -0
  36. package/src/chat-old.ts +2 -2
  37. package/src/clients/anthropic.ts +22 -1
  38. package/src/clients/openai.ts +1 -1
  39. package/src/clients/types.ts +1 -1
  40. package/src/clients/xai.ts +15 -5
  41. package/src/config.ts +17 -5
  42. package/src/dataset/diffs/generate.ts +2 -2
  43. package/src/dataset/diffs/jsonl.ts +0 -1
  44. package/src/embeddings.ts +6 -4
  45. package/src/index.ts +11 -5
  46. package/src/plugins/GitPlugin.ts +530 -0
  47. package/src/plugins/LinterPlugin.ts +89 -0
  48. package/src/plugins/PluginBase.ts +4 -2
  49. package/src/plugins/asana.ts +4 -2
  50. package/src/plugins/downloader/plugin.ts +5 -2
  51. package/src/plugins/embedding.ts +24 -4
  52. package/src/plugins/figma.ts +7 -3
  53. package/src/plugins/github.ts +4 -2
  54. package/src/plugins/jira.ts +4 -2
  55. package/src/plugins/language.ts +134 -27
  56. package/src/plugins/linear.ts +4 -2
  57. package/src/plugins/notion.ts +4 -2
  58. package/src/plugins/plugins.ts +27 -16
  59. package/src/plugins/tree-sitter/editor.ts +369 -0
  60. package/src/plugins/tree-sitter/lang-packs/index.ts +23 -0
  61. package/src/plugins/tree-sitter/lang-packs/java.ts +59 -0
  62. package/src/plugins/tree-sitter/lang-packs/javascript.ts +57 -0
  63. package/src/plugins/tree-sitter/lang-packs/python.ts +45 -0
  64. package/src/plugins/tree-sitter/lang-packs/types.ts +79 -0
  65. package/src/plugins/tree-sitter/lang-packs/typescript.ts +49 -0
  66. package/src/plugins/tree-sitter/parser.ts +444 -0
  67. package/src/plugins/tree-sitter/simple-paths.ts +467 -0
  68. package/src/plugins/types.ts +11 -0
  69. package/src/plugins/url.ts +5 -3
  70. package/src/plugins/vim.ts +8 -5
  71. package/src/processors/CustomVariables.ts +19 -7
  72. package/src/processors/TokenCompressor.ts +13 -13
  73. package/src/processors/ToolResponseCache.ts +15 -6
  74. package/src/services/EmbeddingService.ts +18 -9
  75. package/src/services/EventService.ts +80 -0
  76. package/src/services/Mcp.ts +5 -0
  77. package/src/services/S3.ts +4 -3
  78. package/src/services/Tools.ts +125 -53
  79. package/src/services/index.ts +16 -11
  80. package/src/services/types.ts +3 -3
  81. package/src/types.ts +7 -2
  82. package/src/worker.ts +14 -1
  83. package/test-comprehensive.ts +31 -0
  84. package/tests/clients/AIClient.test.ts +490 -0
  85. package/tests/manual/agent-events/run-test.ts +203 -0
  86. package/tests/{integration → manual/file-edits}/figma.test.ts +1 -1
  87. package/tests/{integration → manual/file-edits}/fileblocks/readwrite.test.ts +7 -3
  88. package/tests/{integration → manual/file-edits}/patching.test.ts +11 -8
  89. package/tests/plugins/language/languagePlugin-content-triggers.test.ts +332 -0
  90. package/tests/plugins/language/languagePlugin-integration.test.ts +456 -0
  91. package/tests/plugins/language/languagePlugin.test.ts +363 -0
  92. package/tests/processors/Base64ImageDetector.test.ts +403 -0
  93. package/tests/processors/CustomVariables.test.ts +430 -0
  94. package/tests/processors/HarmonyToolProcessor.test.ts +490 -0
  95. package/tests/processors/TokenCompressor.test.ts +391 -0
  96. package/tests/processors/ToolResponseCache.test.ts +688 -0
  97. package/tests/services/Tools.test.ts +1339 -0
  98. package/tests/test.spec.ts +162 -117
  99. package/tests/tree-sitter/editor.test.ts +113 -0
  100. package/tests/tree-sitter/invalid.test.ts +299 -0
  101. package/tests/tree-sitter/paths/common-edits.test.ts +564 -0
  102. package/tests/tree-sitter/paths/debug-exact-position.test.ts +44 -0
  103. package/tests/tree-sitter/paths/debug-line-indexing.test.ts +49 -0
  104. package/tests/tree-sitter/paths/debug-paths.test.ts +90 -0
  105. package/tests/tree-sitter/paths/paths.test.ts +170 -0
  106. package/tests/tree-sitter/paths/simple-paths.test.ts +367 -0
  107. package/tests/tree-sitter/sample-after.ts +48 -0
  108. package/tests/tree-sitter/sample-before.ts +25 -0
  109. package/tests/tree-sitter/test-files/completely-broken.ts +7 -0
  110. package/tests/tree-sitter/test-files/duplicate-braces.ts +39 -0
  111. package/tests/tree-sitter/test-files/invalid-nesting.ts +39 -0
  112. package/tests/tree-sitter/test-files/malformed-signature.ts +39 -0
  113. package/tests/tree-sitter/test-files/mismatched-parens.ts +39 -0
  114. package/tests/tree-sitter/test-files/missing-semicolon.ts +39 -0
  115. package/tests/tree-sitter/test-files/partially-broken.ts +20 -0
  116. package/tests/tree-sitter/test-files/specific-errors.ts +14 -0
  117. package/tests/tree-sitter/test-files/unclosed-string.ts +39 -0
  118. package/tests/tree-sitter/tree-sitter.test.ts +251 -0
  119. package/ts_build/package.json +8 -3
  120. package/ts_build/src/agents/base/base.d.ts +2 -2
  121. package/ts_build/src/agents/base/base.js +24 -20
  122. package/ts_build/src/agents/base/base.js.map +1 -1
  123. package/ts_build/src/agents/patcher/patcher.js +26 -5
  124. package/ts_build/src/agents/patcher/patcher.js.map +1 -1
  125. package/ts_build/src/agents/tools/agentCall.js +2 -1
  126. package/ts_build/src/agents/tools/agentCall.js.map +1 -1
  127. package/ts_build/src/agents/tools/aiClient.d.ts +7 -8
  128. package/ts_build/src/agents/tools/aiClient.js.map +1 -1
  129. package/ts_build/src/agents/tools/ast/astAppendNode.d.ts +1 -0
  130. package/ts_build/src/agents/tools/ast/astAppendNode.js +96 -0
  131. package/ts_build/src/agents/tools/ast/astAppendNode.js.map +1 -0
  132. package/ts_build/src/agents/tools/ast/astDeleteNode.d.ts +1 -0
  133. package/ts_build/src/agents/tools/ast/astDeleteNode.js +94 -0
  134. package/ts_build/src/agents/tools/ast/astDeleteNode.js.map +1 -0
  135. package/ts_build/src/agents/tools/ast/astEditNode.d.ts +1 -0
  136. package/ts_build/src/agents/tools/ast/astEditNode.js +96 -0
  137. package/ts_build/src/agents/tools/ast/astEditNode.js.map +1 -0
  138. package/ts_build/src/agents/tools/ast/astGetPathForLine.d.ts +1 -0
  139. package/ts_build/src/agents/tools/ast/astGetPathForLine.js +78 -0
  140. package/ts_build/src/agents/tools/ast/astGetPathForLine.js.map +1 -0
  141. package/ts_build/src/agents/tools/ast/astListPaths.d.ts +1 -0
  142. package/ts_build/src/agents/tools/ast/astListPaths.js +78 -0
  143. package/ts_build/src/agents/tools/ast/astListPaths.js.map +1 -0
  144. package/ts_build/src/agents/tools/ast/index.d.ts +5 -0
  145. package/ts_build/src/agents/tools/ast/index.js +14 -0
  146. package/ts_build/src/agents/tools/ast/index.js.map +1 -0
  147. package/ts_build/src/agents/tools/astAppendNode.d.ts +1 -0
  148. package/ts_build/src/agents/tools/astAppendNode.js +98 -0
  149. package/ts_build/src/agents/tools/astAppendNode.js.map +1 -0
  150. package/ts_build/src/agents/tools/astDeleteNode.d.ts +1 -0
  151. package/ts_build/src/agents/tools/astDeleteNode.js +95 -0
  152. package/ts_build/src/agents/tools/astDeleteNode.js.map +1 -0
  153. package/ts_build/src/agents/tools/astEditNode.d.ts +1 -0
  154. package/ts_build/src/agents/tools/astEditNode.js +98 -0
  155. package/ts_build/src/agents/tools/astEditNode.js.map +1 -0
  156. package/ts_build/src/agents/tools/astGetPathForLine.d.ts +1 -0
  157. package/ts_build/src/agents/tools/astGetPathForLine.js +89 -0
  158. package/ts_build/src/agents/tools/astGetPathForLine.js.map +1 -0
  159. package/ts_build/src/agents/tools/astListPaths.d.ts +1 -0
  160. package/ts_build/src/agents/tools/astListPaths.js +82 -0
  161. package/ts_build/src/agents/tools/astListPaths.js.map +1 -0
  162. package/ts_build/src/agents/tools/callPlugin.js +4 -2
  163. package/ts_build/src/agents/tools/callPlugin.js.map +1 -1
  164. package/ts_build/src/agents/tools/embeddingSearch.js +3 -2
  165. package/ts_build/src/agents/tools/embeddingSearch.js.map +1 -1
  166. package/ts_build/src/agents/tools/execCommand.d.ts +2 -2
  167. package/ts_build/src/agents/tools/execCommand.js +201 -67
  168. package/ts_build/src/agents/tools/execCommand.js.map +1 -1
  169. package/ts_build/src/agents/tools/fileSearch.d.ts +1 -1
  170. package/ts_build/src/agents/tools/fileSearch.js +11 -15
  171. package/ts_build/src/agents/tools/fileSearch.js.map +1 -1
  172. package/ts_build/src/agents/tools/github/index.d.ts +1 -1
  173. package/ts_build/src/agents/tools/index.d.ts +1 -0
  174. package/ts_build/src/agents/tools/index.js +1 -0
  175. package/ts_build/src/agents/tools/index.js.map +1 -1
  176. package/ts_build/src/agents/tools/language/definitions.js +11 -2
  177. package/ts_build/src/agents/tools/language/definitions.js.map +1 -1
  178. package/ts_build/src/agents/tools/language/index.js +4 -3
  179. package/ts_build/src/agents/tools/language/index.js.map +1 -1
  180. package/ts_build/src/agents/tools/lintFile.js +4 -2
  181. package/ts_build/src/agents/tools/lintFile.js.map +1 -1
  182. package/ts_build/src/agents/tools/list.js +185 -49
  183. package/ts_build/src/agents/tools/list.js.map +1 -1
  184. package/ts_build/src/agents/tools/patch.js +33 -10
  185. package/ts_build/src/agents/tools/patch.js.map +1 -1
  186. package/ts_build/src/agents/tools/readBlocks.js +23 -0
  187. package/ts_build/src/agents/tools/readBlocks.js.map +1 -1
  188. package/ts_build/src/agents/tools/readFile.js +14 -0
  189. package/ts_build/src/agents/tools/readFile.js.map +1 -1
  190. package/ts_build/src/agents/tools/stringReplace.js +19 -2
  191. package/ts_build/src/agents/tools/stringReplace.js.map +1 -1
  192. package/ts_build/src/agents/tools/writeFile.js +40 -0
  193. package/ts_build/src/agents/tools/writeFile.js.map +1 -1
  194. package/ts_build/src/agents/tools/ycmd/server.js +5 -0
  195. package/ts_build/src/agents/tools/ycmd/server.js.map +1 -1
  196. package/ts_build/src/chat/CliChatService.d.ts +1 -0
  197. package/ts_build/src/chat/CliChatService.js +6 -2
  198. package/ts_build/src/chat/CliChatService.js.map +1 -1
  199. package/ts_build/src/chat/modules/AgentModule.d.ts +5 -1
  200. package/ts_build/src/chat/modules/AgentModule.js +53 -31
  201. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  202. package/ts_build/src/chat/modules/AskModule.js.map +1 -1
  203. package/ts_build/src/chat/modules/SetupModule.js +4 -3
  204. package/ts_build/src/chat/modules/SetupModule.js.map +1 -1
  205. package/ts_build/src/chat/modules/SystemModule.js +19 -4
  206. package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
  207. package/ts_build/src/chat/modules/index.d.ts +5 -0
  208. package/ts_build/src/chat/modules/index.js +14 -0
  209. package/ts_build/src/chat/modules/index.js.map +1 -0
  210. package/ts_build/src/chat/types.d.ts +2 -0
  211. package/ts_build/src/chat-old.js +3 -3
  212. package/ts_build/src/chat-old.js.map +1 -1
  213. package/ts_build/src/clients/anthropic.d.ts +1 -0
  214. package/ts_build/src/clients/anthropic.js +22 -1
  215. package/ts_build/src/clients/anthropic.js.map +1 -1
  216. package/ts_build/src/clients/openai.js +1 -1
  217. package/ts_build/src/clients/openai.js.map +1 -1
  218. package/ts_build/src/clients/types.d.ts +1 -1
  219. package/ts_build/src/clients/xai.d.ts +7 -0
  220. package/ts_build/src/clients/xai.js +13 -4
  221. package/ts_build/src/clients/xai.js.map +1 -1
  222. package/ts_build/src/config.js +14 -3
  223. package/ts_build/src/config.js.map +1 -1
  224. package/ts_build/src/dataset/diffs/generate.js +2 -2
  225. package/ts_build/src/dataset/diffs/generate.js.map +1 -1
  226. package/ts_build/src/dataset/diffs/jsonl.js.map +1 -1
  227. package/ts_build/src/embeddings.js +7 -9
  228. package/ts_build/src/embeddings.js.map +1 -1
  229. package/ts_build/src/index.js +10 -10
  230. package/ts_build/src/index.js.map +1 -1
  231. package/ts_build/src/plugins/GitPlugin.d.ts +39 -0
  232. package/ts_build/src/plugins/GitPlugin.js +439 -0
  233. package/ts_build/src/plugins/GitPlugin.js.map +1 -0
  234. package/ts_build/src/plugins/LinterPlugin.d.ts +15 -0
  235. package/ts_build/src/plugins/LinterPlugin.js +65 -0
  236. package/ts_build/src/plugins/LinterPlugin.js.map +1 -0
  237. package/ts_build/src/plugins/PluginBase.d.ts +4 -3
  238. package/ts_build/src/plugins/PluginBase.js +3 -3
  239. package/ts_build/src/plugins/PluginBase.js.map +1 -1
  240. package/ts_build/src/plugins/asana.d.ts +3 -1
  241. package/ts_build/src/plugins/asana.js +3 -2
  242. package/ts_build/src/plugins/asana.js.map +1 -1
  243. package/ts_build/src/plugins/downloader/plugin.d.ts +3 -1
  244. package/ts_build/src/plugins/downloader/plugin.js +3 -2
  245. package/ts_build/src/plugins/downloader/plugin.js.map +1 -1
  246. package/ts_build/src/plugins/embedding.d.ts +5 -1
  247. package/ts_build/src/plugins/embedding.js +15 -3
  248. package/ts_build/src/plugins/embedding.js.map +1 -1
  249. package/ts_build/src/plugins/figma.d.ts +3 -1
  250. package/ts_build/src/plugins/figma.js +28 -4
  251. package/ts_build/src/plugins/figma.js.map +1 -1
  252. package/ts_build/src/plugins/github.d.ts +3 -1
  253. package/ts_build/src/plugins/github.js +3 -2
  254. package/ts_build/src/plugins/github.js.map +1 -1
  255. package/ts_build/src/plugins/jira.d.ts +3 -1
  256. package/ts_build/src/plugins/jira.js +3 -2
  257. package/ts_build/src/plugins/jira.js.map +1 -1
  258. package/ts_build/src/plugins/language.d.ts +7 -4
  259. package/ts_build/src/plugins/language.js +85 -20
  260. package/ts_build/src/plugins/language.js.map +1 -1
  261. package/ts_build/src/plugins/linear.d.ts +3 -1
  262. package/ts_build/src/plugins/linear.js +3 -2
  263. package/ts_build/src/plugins/linear.js.map +1 -1
  264. package/ts_build/src/plugins/notion.d.ts +3 -1
  265. package/ts_build/src/plugins/notion.js +3 -2
  266. package/ts_build/src/plugins/notion.js.map +1 -1
  267. package/ts_build/src/plugins/plugins.d.ts +4 -3
  268. package/ts_build/src/plugins/plugins.js +24 -14
  269. package/ts_build/src/plugins/plugins.js.map +1 -1
  270. package/ts_build/src/plugins/tree-sitter/editor.d.ts +34 -0
  271. package/ts_build/src/plugins/tree-sitter/editor.js +218 -0
  272. package/ts_build/src/plugins/tree-sitter/editor.js.map +1 -0
  273. package/ts_build/src/plugins/tree-sitter/human-readable-paths-new.d.ts +29 -0
  274. package/ts_build/src/plugins/tree-sitter/human-readable-paths-new.js +538 -0
  275. package/ts_build/src/plugins/tree-sitter/human-readable-paths-new.js.map +1 -0
  276. package/ts_build/src/plugins/tree-sitter/human-readable-paths.d.ts +22 -0
  277. package/ts_build/src/plugins/tree-sitter/human-readable-paths.js +332 -0
  278. package/ts_build/src/plugins/tree-sitter/human-readable-paths.js.map +1 -0
  279. package/ts_build/src/plugins/tree-sitter/lang-packs/index.d.ts +8 -0
  280. package/ts_build/src/plugins/tree-sitter/lang-packs/index.js +26 -0
  281. package/ts_build/src/plugins/tree-sitter/lang-packs/index.js.map +1 -0
  282. package/ts_build/src/plugins/tree-sitter/lang-packs/java.d.ts +2 -0
  283. package/ts_build/src/plugins/tree-sitter/lang-packs/java.js +61 -0
  284. package/ts_build/src/plugins/tree-sitter/lang-packs/java.js.map +1 -0
  285. package/ts_build/src/plugins/tree-sitter/lang-packs/javascript.d.ts +2 -0
  286. package/ts_build/src/plugins/tree-sitter/lang-packs/javascript.js +59 -0
  287. package/ts_build/src/plugins/tree-sitter/lang-packs/javascript.js.map +1 -0
  288. package/ts_build/src/plugins/tree-sitter/lang-packs/python.d.ts +2 -0
  289. package/ts_build/src/plugins/tree-sitter/lang-packs/python.js +47 -0
  290. package/ts_build/src/plugins/tree-sitter/lang-packs/python.js.map +1 -0
  291. package/ts_build/src/plugins/tree-sitter/lang-packs/types.d.ts +43 -0
  292. package/ts_build/src/plugins/tree-sitter/lang-packs/types.js +3 -0
  293. package/ts_build/src/plugins/tree-sitter/lang-packs/types.js.map +1 -0
  294. package/ts_build/src/plugins/tree-sitter/lang-packs/typescript.d.ts +2 -0
  295. package/ts_build/src/plugins/tree-sitter/lang-packs/typescript.js +50 -0
  296. package/ts_build/src/plugins/tree-sitter/lang-packs/typescript.js.map +1 -0
  297. package/ts_build/src/plugins/tree-sitter/parser.d.ts +75 -0
  298. package/ts_build/src/plugins/tree-sitter/parser.js +306 -0
  299. package/ts_build/src/plugins/tree-sitter/parser.js.map +1 -0
  300. package/ts_build/src/plugins/tree-sitter/simple-paths.d.ts +22 -0
  301. package/ts_build/src/plugins/tree-sitter/simple-paths.js +332 -0
  302. package/ts_build/src/plugins/tree-sitter/simple-paths.js.map +1 -0
  303. package/ts_build/src/plugins/types.d.ts +10 -0
  304. package/ts_build/src/plugins/url.d.ts +3 -2
  305. package/ts_build/src/plugins/url.js +3 -2
  306. package/ts_build/src/plugins/url.js.map +1 -1
  307. package/ts_build/src/plugins/vim.d.ts +4 -2
  308. package/ts_build/src/plugins/vim.js +6 -8
  309. package/ts_build/src/plugins/vim.js.map +1 -1
  310. package/ts_build/src/processors/CustomVariables.js +12 -3
  311. package/ts_build/src/processors/CustomVariables.js.map +1 -1
  312. package/ts_build/src/processors/TokenCompressor.js +8 -11
  313. package/ts_build/src/processors/TokenCompressor.js.map +1 -1
  314. package/ts_build/src/processors/ToolResponseCache.d.ts +2 -2
  315. package/ts_build/src/processors/ToolResponseCache.js +12 -2
  316. package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
  317. package/ts_build/src/services/EmbeddingService.d.ts +10 -1
  318. package/ts_build/src/services/EmbeddingService.js +12 -12
  319. package/ts_build/src/services/EmbeddingService.js.map +1 -1
  320. package/ts_build/src/services/EventService.d.ts +7 -0
  321. package/ts_build/src/services/EventService.js +49 -0
  322. package/ts_build/src/services/EventService.js.map +1 -1
  323. package/ts_build/src/services/Mcp.js +8 -0
  324. package/ts_build/src/services/Mcp.js.map +1 -1
  325. package/ts_build/src/services/S3.js +4 -3
  326. package/ts_build/src/services/S3.js.map +1 -1
  327. package/ts_build/src/services/Tools.d.ts +1 -0
  328. package/ts_build/src/services/Tools.js +97 -35
  329. package/ts_build/src/services/Tools.js.map +1 -1
  330. package/ts_build/src/services/index.d.ts +4 -5
  331. package/ts_build/src/services/index.js +14 -9
  332. package/ts_build/src/services/index.js.map +1 -1
  333. package/ts_build/src/services/types.js +3 -3
  334. package/ts_build/src/services/types.js.map +1 -1
  335. package/ts_build/src/types.d.ts +7 -1
  336. package/ts_build/src/types.js +4 -0
  337. package/ts_build/src/types.js.map +1 -1
  338. package/ts_build/src/worker.js +12 -1
  339. package/ts_build/src/worker.js.map +1 -1
  340. package/ts_build/tests/clients/AIClient.test.d.ts +1 -0
  341. package/ts_build/tests/clients/AIClient.test.js +377 -0
  342. package/ts_build/tests/clients/AIClient.test.js.map +1 -0
  343. package/ts_build/tests/languagePlugin.test.js +217 -11
  344. package/ts_build/tests/languagePlugin.test.js.map +1 -1
  345. package/ts_build/tests/manual/agent-events/event-handler-reliability.test.d.ts +1 -0
  346. package/ts_build/tests/manual/agent-events/event-handler-reliability.test.js +315 -0
  347. package/ts_build/tests/manual/agent-events/event-handler-reliability.test.js.map +1 -0
  348. package/ts_build/tests/manual/agent-events/run-test.d.ts +2 -0
  349. package/ts_build/tests/manual/agent-events/run-test.js +148 -0
  350. package/ts_build/tests/manual/agent-events/run-test.js.map +1 -0
  351. package/ts_build/tests/manual/file-edits/figma.test.d.ts +1 -0
  352. package/ts_build/tests/manual/file-edits/figma.test.js +47 -0
  353. package/ts_build/tests/manual/file-edits/figma.test.js.map +1 -0
  354. package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.d.ts +1 -0
  355. package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.js +100 -0
  356. package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.js.map +1 -0
  357. package/ts_build/tests/manual/file-edits/patching.test.d.ts +1 -0
  358. package/ts_build/tests/manual/file-edits/patching.test.js +119 -0
  359. package/ts_build/tests/manual/file-edits/patching.test.js.map +1 -0
  360. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.d.ts +1 -0
  361. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +277 -0
  362. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -0
  363. package/ts_build/tests/plugins/language/languagePlugin-integration.test.d.ts +1 -0
  364. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +331 -0
  365. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -0
  366. package/ts_build/tests/plugins/language/languagePlugin.test.d.ts +1 -0
  367. package/ts_build/tests/plugins/language/languagePlugin.test.js +286 -0
  368. package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -0
  369. package/ts_build/tests/processors/Base64ImageDetector.test.d.ts +1 -0
  370. package/ts_build/tests/processors/Base64ImageDetector.test.js +351 -0
  371. package/ts_build/tests/processors/Base64ImageDetector.test.js.map +1 -0
  372. package/ts_build/tests/processors/CustomVariables.test.d.ts +1 -0
  373. package/ts_build/tests/processors/CustomVariables.test.js +351 -0
  374. package/ts_build/tests/processors/CustomVariables.test.js.map +1 -0
  375. package/ts_build/tests/processors/HarmonyToolProcessor.test.d.ts +1 -0
  376. package/ts_build/tests/processors/HarmonyToolProcessor.test.js +382 -0
  377. package/ts_build/tests/processors/HarmonyToolProcessor.test.js.map +1 -0
  378. package/ts_build/tests/processors/TokenCompressor.test.d.ts +1 -0
  379. package/ts_build/tests/processors/TokenCompressor.test.js +300 -0
  380. package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -0
  381. package/ts_build/tests/processors/ToolResponseCache.test.d.ts +1 -0
  382. package/ts_build/tests/processors/ToolResponseCache.test.js +539 -0
  383. package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -0
  384. package/ts_build/tests/services/Plugins/plugin-event-integration.test.d.ts +1 -0
  385. package/ts_build/tests/services/Plugins/plugin-event-integration.test.js +232 -0
  386. package/ts_build/tests/services/Plugins/plugin-event-integration.test.js.map +1 -0
  387. package/ts_build/tests/services/Tools.test.d.ts +1 -0
  388. package/ts_build/tests/services/Tools.test.js +1059 -0
  389. package/ts_build/tests/services/Tools.test.js.map +1 -0
  390. package/ts_build/tests/test.spec.js +110 -68
  391. package/ts_build/tests/test.spec.js.map +1 -1
  392. package/ts_build/tests/tree-sitter/editor.test.d.ts +1 -0
  393. package/ts_build/tests/tree-sitter/editor.test.js +85 -0
  394. package/ts_build/tests/tree-sitter/editor.test.js.map +1 -0
  395. package/ts_build/tests/tree-sitter/invalid.test.d.ts +1 -0
  396. package/ts_build/tests/tree-sitter/invalid.test.js +198 -0
  397. package/ts_build/tests/tree-sitter/invalid.test.js.map +1 -0
  398. package/ts_build/tests/tree-sitter/paths/common-edits.test.d.ts +1 -0
  399. package/ts_build/tests/tree-sitter/paths/common-edits.test.js +347 -0
  400. package/ts_build/tests/tree-sitter/paths/common-edits.test.js.map +1 -0
  401. package/ts_build/tests/tree-sitter/paths/debug-exact-position.test.d.ts +1 -0
  402. package/ts_build/tests/tree-sitter/paths/debug-exact-position.test.js +35 -0
  403. package/ts_build/tests/tree-sitter/paths/debug-exact-position.test.js.map +1 -0
  404. package/ts_build/tests/tree-sitter/paths/debug-line-indexing.test.d.ts +1 -0
  405. package/ts_build/tests/tree-sitter/paths/debug-line-indexing.test.js +38 -0
  406. package/ts_build/tests/tree-sitter/paths/debug-line-indexing.test.js.map +1 -0
  407. package/ts_build/tests/tree-sitter/paths/debug-paths.test.d.ts +1 -0
  408. package/ts_build/tests/tree-sitter/paths/debug-paths.test.js +74 -0
  409. package/ts_build/tests/tree-sitter/paths/debug-paths.test.js.map +1 -0
  410. package/ts_build/tests/tree-sitter/paths/human-readable-paths.test.d.ts +1 -0
  411. package/ts_build/tests/tree-sitter/paths/human-readable-paths.test.js +302 -0
  412. package/ts_build/tests/tree-sitter/paths/human-readable-paths.test.js.map +1 -0
  413. package/ts_build/tests/tree-sitter/paths/paths.test.d.ts +1 -0
  414. package/ts_build/tests/tree-sitter/paths/paths.test.js +116 -0
  415. package/ts_build/tests/tree-sitter/paths/paths.test.js.map +1 -0
  416. package/ts_build/tests/tree-sitter/paths/simple-paths.test.d.ts +1 -0
  417. package/ts_build/tests/tree-sitter/paths/simple-paths.test.js +302 -0
  418. package/ts_build/tests/tree-sitter/paths/simple-paths.test.js.map +1 -0
  419. package/ts_build/tests/tree-sitter/sample-after.d.ts +11 -0
  420. package/ts_build/tests/tree-sitter/sample-after.js +44 -0
  421. package/ts_build/tests/tree-sitter/sample-after.js.map +1 -0
  422. package/ts_build/tests/tree-sitter/sample-before.d.ts +9 -0
  423. package/ts_build/tests/tree-sitter/sample-before.js +28 -0
  424. package/ts_build/tests/tree-sitter/sample-before.js.map +1 -0
  425. package/ts_build/tests/tree-sitter/test-files/completely-broken.d.ts +2 -0
  426. package/ts_build/tests/tree-sitter/test-files/completely-broken.js +17 -0
  427. package/ts_build/tests/tree-sitter/test-files/completely-broken.js.map +1 -0
  428. package/ts_build/tests/tree-sitter/test-files/duplicate-braces.d.ts +8 -0
  429. package/ts_build/tests/tree-sitter/test-files/duplicate-braces.js +38 -0
  430. package/ts_build/tests/tree-sitter/test-files/duplicate-braces.js.map +1 -0
  431. package/ts_build/tests/tree-sitter/test-files/invalid-nesting.d.ts +8 -0
  432. package/ts_build/tests/tree-sitter/test-files/invalid-nesting.js +38 -0
  433. package/ts_build/tests/tree-sitter/test-files/invalid-nesting.js.map +1 -0
  434. package/ts_build/tests/tree-sitter/test-files/malformed-signature.d.ts +8 -0
  435. package/ts_build/tests/tree-sitter/test-files/malformed-signature.js +38 -0
  436. package/ts_build/tests/tree-sitter/test-files/malformed-signature.js.map +1 -0
  437. package/ts_build/tests/tree-sitter/test-files/mismatched-parens.d.ts +10 -0
  438. package/ts_build/tests/tree-sitter/test-files/mismatched-parens.js +38 -0
  439. package/ts_build/tests/tree-sitter/test-files/mismatched-parens.js.map +1 -0
  440. package/ts_build/tests/tree-sitter/test-files/missing-semicolon.d.ts +8 -0
  441. package/ts_build/tests/tree-sitter/test-files/missing-semicolon.js +38 -0
  442. package/ts_build/tests/tree-sitter/test-files/missing-semicolon.js.map +1 -0
  443. package/ts_build/tests/tree-sitter/test-files/partially-broken.d.ts +6 -0
  444. package/ts_build/tests/tree-sitter/test-files/partially-broken.js +20 -0
  445. package/ts_build/tests/tree-sitter/test-files/partially-broken.js.map +1 -0
  446. package/ts_build/tests/tree-sitter/test-files/specific-errors.d.ts +7 -0
  447. package/ts_build/tests/tree-sitter/test-files/specific-errors.js +14 -0
  448. package/ts_build/tests/tree-sitter/test-files/specific-errors.js.map +1 -0
  449. package/ts_build/tests/tree-sitter/test-files/unclosed-string.d.ts +8 -0
  450. package/ts_build/tests/tree-sitter/test-files/unclosed-string.js +38 -0
  451. package/ts_build/tests/tree-sitter/test-files/unclosed-string.js.map +1 -0
  452. package/ts_build/tests/tree-sitter/tree-sitter.test.d.ts +1 -0
  453. package/ts_build/tests/tree-sitter/tree-sitter.test.js +185 -0
  454. package/ts_build/tests/tree-sitter/tree-sitter.test.js.map +1 -0
  455. package/tsconfig.json +2 -1
  456. package/tests/languagePlugin.test.ts +0 -74
  457. /package/src/chat/modules/{index.js → index.ts} +0 -0
  458. /package/tests/{integration → manual/file-edits}/patching/input.txt +0 -0
  459. /package/tests/{integration → manual/file-edits}/patching/output.txt +0 -0
  460. /package/tests/{integration → manual/file-edits}/patching/patch.txt +0 -0
  461. /package/tests/{integration → manual/file-edits}/patching/unseen.txt +0 -0
@@ -0,0 +1,31 @@
1
+ interface User {
2
+ id: number;
3
+ name: string;
4
+ email: string;
5
+ }
6
+
7
+ class UserService {
8
+ private users: User[] = [];
9
+
10
+ addUser(user: User): void {
11
+ this.users.push(user);
12
+ }
13
+
14
+ findUser(id: number): User | undefined {
15
+ return this.users.find(u => u.id === id);
16
+ }
17
+ }
18
+
19
+ const service = new UserService();
20
+ service.addUser({
21
+ id: 1,
22
+ name: "John",
23
+ email: "john@example.com"
24
+ });
25
+
26
+ // Test completion on service methods
27
+ const result = service.fi; // Should complete to findUser
28
+
29
+ // Test completion on User interface properties
30
+ const user: User = { id: 1, name: "Test", email: "test@test.com" };
31
+ console.log(user.na); // Should complete to name
@@ -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
+ });