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