@tyvm/knowhow 0.0.90 → 0.0.92

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 (272) hide show
  1. package/.depcheckrc +30 -0
  2. package/bin/knowhow.js +1 -1
  3. package/package.json +8 -34
  4. package/src/agents/configurable/ConfigAgent.ts +2 -2
  5. package/src/agents/tools/executeScript/index.ts +5 -0
  6. package/src/agents/tools/googleSearch.ts +2 -2
  7. package/src/agents/tools/index.ts +0 -3
  8. package/src/agents/tools/list.ts +0 -147
  9. package/src/agents/tools/loadWebpage.ts +3 -113
  10. package/src/auth/browserLogin.ts +10 -13
  11. package/src/chat/modules/AgentModule.ts +0 -1
  12. package/src/chat/types.ts +1 -1
  13. package/src/cli.ts +63 -3
  14. package/src/clients/gemini.ts +96 -25
  15. package/src/clients/http.ts +7 -11
  16. package/src/clients/pricing/google.ts +122 -26
  17. package/src/conversion.ts +24 -54
  18. package/src/index.ts +15 -20
  19. package/src/login.ts +5 -6
  20. package/src/plugins/language.ts +0 -4
  21. package/src/plugins/plugins.ts +0 -14
  22. package/src/plugins/url.ts +31 -12
  23. package/src/services/EmbeddingsService.ts +70 -0
  24. package/src/services/KnowhowClient.ts +34 -34
  25. package/src/{plugins/downloader/downloader.ts → services/MediaProcessorService.ts} +109 -267
  26. package/src/services/S3.ts +19 -87
  27. package/src/services/index.ts +8 -8
  28. package/src/services/modules/index.ts +12 -3
  29. package/src/services/modules/types.ts +8 -2
  30. package/src/services/script-execution/ScriptExecutor.ts +29 -10
  31. package/src/services/script-execution/ScriptPolicy.ts +6 -2
  32. package/src/types.ts +1 -0
  33. package/src/utils/http.ts +127 -0
  34. package/src/workers/auth/PasskeySetup.ts +7 -11
  35. package/tests/clients/AIClient.test.ts +24 -21
  36. package/tests/manual/file-edits/figma.test.ts +3 -70
  37. package/tests/plugins/language/languagePlugin-content-triggers.test.ts +2 -0
  38. package/tests/plugins/language/languagePlugin.test.ts +2 -0
  39. package/tests/processors/ToolResponseCache.test.ts +2 -2
  40. package/tests/test.spec.ts +0 -14
  41. package/tests/unit/modules/moduleLoading.test.ts +12 -4
  42. package/tests/unit/plugins/pluginLoading.test.ts +6 -6
  43. package/ts_build/package.json +8 -34
  44. package/ts_build/src/agents/tools/ast/astAppendNode.d.ts +1 -1
  45. package/ts_build/src/agents/tools/ast/astAppendNode.js +2 -90
  46. package/ts_build/src/agents/tools/ast/astAppendNode.js.map +1 -1
  47. package/ts_build/src/agents/tools/ast/astDeleteNode.d.ts +1 -1
  48. package/ts_build/src/agents/tools/ast/astDeleteNode.js +2 -88
  49. package/ts_build/src/agents/tools/ast/astDeleteNode.js.map +1 -1
  50. package/ts_build/src/agents/tools/ast/astEditNode.d.ts +1 -1
  51. package/ts_build/src/agents/tools/ast/astEditNode.js +2 -90
  52. package/ts_build/src/agents/tools/ast/astEditNode.js.map +1 -1
  53. package/ts_build/src/agents/tools/ast/astGetPathForLine.d.ts +1 -1
  54. package/ts_build/src/agents/tools/ast/astGetPathForLine.js +2 -72
  55. package/ts_build/src/agents/tools/ast/astGetPathForLine.js.map +1 -1
  56. package/ts_build/src/agents/tools/ast/astListPaths.d.ts +1 -1
  57. package/ts_build/src/agents/tools/ast/astListPaths.js +2 -72
  58. package/ts_build/src/agents/tools/ast/astListPaths.js.map +1 -1
  59. package/ts_build/src/agents/tools/executeScript/index.d.ts +3 -2
  60. package/ts_build/src/agents/tools/executeScript/index.js +4 -1
  61. package/ts_build/src/agents/tools/executeScript/index.js.map +1 -1
  62. package/ts_build/src/agents/tools/googleSearch.js +2 -2
  63. package/ts_build/src/agents/tools/googleSearch.js.map +1 -1
  64. package/ts_build/src/agents/tools/index.d.ts +0 -3
  65. package/ts_build/src/agents/tools/index.js +0 -3
  66. package/ts_build/src/agents/tools/index.js.map +1 -1
  67. package/ts_build/src/agents/tools/list.js +0 -138
  68. package/ts_build/src/agents/tools/list.js.map +1 -1
  69. package/ts_build/src/agents/tools/loadWebpage.js +1 -89
  70. package/ts_build/src/agents/tools/loadWebpage.js.map +1 -1
  71. package/ts_build/src/agents/tools/textSearch.d.ts +1 -1
  72. package/ts_build/src/auth/browserLogin.js +7 -7
  73. package/ts_build/src/auth/browserLogin.js.map +1 -1
  74. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  75. package/ts_build/src/chat/types.d.ts +1 -1
  76. package/ts_build/src/cli.d.ts +1 -1
  77. package/ts_build/src/cli.js +47 -1
  78. package/ts_build/src/cli.js.map +1 -1
  79. package/ts_build/src/clients/gemini.d.ts +1 -73
  80. package/ts_build/src/clients/gemini.js +57 -19
  81. package/ts_build/src/clients/gemini.js.map +1 -1
  82. package/ts_build/src/clients/http.js +5 -9
  83. package/ts_build/src/clients/http.js.map +1 -1
  84. package/ts_build/src/clients/pricing/google.d.ts +17 -73
  85. package/ts_build/src/clients/pricing/google.js +47 -10
  86. package/ts_build/src/clients/pricing/google.js.map +1 -1
  87. package/ts_build/src/conversion.d.ts +1 -4
  88. package/ts_build/src/conversion.js +12 -27
  89. package/ts_build/src/conversion.js.map +1 -1
  90. package/ts_build/src/index.d.ts +4 -0
  91. package/ts_build/src/index.js +15 -14
  92. package/ts_build/src/index.js.map +1 -1
  93. package/ts_build/src/login.js +5 -4
  94. package/ts_build/src/login.js.map +1 -1
  95. package/ts_build/src/plugins/downloader/downloader.js +3 -3
  96. package/ts_build/src/plugins/downloader/downloader.js.map +1 -1
  97. package/ts_build/src/plugins/language.js.map +1 -1
  98. package/ts_build/src/plugins/plugins.js +0 -14
  99. package/ts_build/src/plugins/plugins.js.map +1 -1
  100. package/ts_build/src/plugins/tree-sitter/editor.d.ts +3 -32
  101. package/ts_build/src/plugins/tree-sitter/editor.js +6 -208
  102. package/ts_build/src/plugins/tree-sitter/editor.js.map +1 -1
  103. package/ts_build/src/plugins/tree-sitter/parser.d.ts +19 -54
  104. package/ts_build/src/plugins/tree-sitter/parser.js +19 -293
  105. package/ts_build/src/plugins/tree-sitter/parser.js.map +1 -1
  106. package/ts_build/src/plugins/tree-sitter/simple-paths.d.ts +2 -15
  107. package/ts_build/src/plugins/tree-sitter/simple-paths.js +2 -324
  108. package/ts_build/src/plugins/tree-sitter/simple-paths.js.map +1 -1
  109. package/ts_build/src/plugins/url.js +27 -8
  110. package/ts_build/src/plugins/url.js.map +1 -1
  111. package/ts_build/src/services/EmbeddingsService.d.ts +14 -0
  112. package/ts_build/src/services/EmbeddingsService.js +33 -0
  113. package/ts_build/src/services/EmbeddingsService.js.map +1 -0
  114. package/ts_build/src/services/GitHub.js +2 -2
  115. package/ts_build/src/services/GitHub.js.map +1 -1
  116. package/ts_build/src/services/KnowhowClient.d.ts +29 -29
  117. package/ts_build/src/services/KnowhowClient.js +33 -33
  118. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  119. package/ts_build/src/services/MediaProcessorService.d.ts +22 -0
  120. package/ts_build/src/services/MediaProcessorService.js +215 -0
  121. package/ts_build/src/services/MediaProcessorService.js.map +1 -0
  122. package/ts_build/src/services/S3.d.ts +0 -4
  123. package/ts_build/src/services/S3.js +14 -60
  124. package/ts_build/src/services/S3.js.map +1 -1
  125. package/ts_build/src/services/index.d.ts +6 -5
  126. package/ts_build/src/services/index.js +6 -6
  127. package/ts_build/src/services/index.js.map +1 -1
  128. package/ts_build/src/services/modules/index.js +12 -3
  129. package/ts_build/src/services/modules/index.js.map +1 -1
  130. package/ts_build/src/services/modules/types.d.ts +8 -2
  131. package/ts_build/src/services/script-execution/ScriptExecutor.js +22 -7
  132. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +1 -1
  133. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +1 -1
  134. package/ts_build/src/services/script-execution/ScriptPolicy.js +4 -2
  135. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +1 -1
  136. package/ts_build/src/types.d.ts +1 -0
  137. package/ts_build/src/types.js +1 -0
  138. package/ts_build/src/types.js.map +1 -1
  139. package/ts_build/src/utils/http.d.ts +27 -0
  140. package/ts_build/src/utils/http.js +98 -0
  141. package/ts_build/src/utils/http.js.map +1 -0
  142. package/ts_build/src/workers/auth/PasskeySetup.js +6 -7
  143. package/ts_build/src/workers/auth/PasskeySetup.js.map +1 -1
  144. package/ts_build/tests/clients/AIClient.test.js +11 -14
  145. package/ts_build/tests/clients/AIClient.test.js.map +1 -1
  146. package/ts_build/tests/manual/file-edits/figma.test.d.ts +0 -1
  147. package/ts_build/tests/manual/file-edits/figma.test.js +1 -46
  148. package/ts_build/tests/manual/file-edits/figma.test.js.map +1 -1
  149. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +2 -0
  150. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -1
  151. package/ts_build/tests/plugins/language/languagePlugin.test.js +2 -0
  152. package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -1
  153. package/ts_build/tests/processors/ToolResponseCache.test.js +2 -2
  154. package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -1
  155. package/ts_build/tests/test.spec.js +0 -14
  156. package/ts_build/tests/test.spec.js.map +1 -1
  157. package/ts_build/tests/tree-sitter/tree-sitter.test.d.ts +0 -1
  158. package/ts_build/tests/tree-sitter/tree-sitter.test.js +2 -183
  159. package/ts_build/tests/tree-sitter/tree-sitter.test.js.map +1 -1
  160. package/ts_build/tests/unit/modules/moduleLoading.test.js +11 -4
  161. package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -1
  162. package/ts_build/tests/unit/plugins/pluginLoading.test.js +4 -4
  163. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
  164. package/benchmarks/.dockerignore +0 -7
  165. package/benchmarks/README.md +0 -166
  166. package/benchmarks/docker/Dockerfile +0 -68
  167. package/benchmarks/example-config.yml +0 -27
  168. package/benchmarks/jest.config.js +0 -13
  169. package/benchmarks/package-lock.json +0 -4297
  170. package/benchmarks/package.json +0 -39
  171. package/benchmarks/results/27b0a06/2025-09-27/xai/xai-grok-code-fast-1.json +0 -2909
  172. package/benchmarks/results/4057aed/2025-08-14/anthropic/anthropic-claude-sonnet-4-20250514.json +0 -1671
  173. package/benchmarks/results/4542435/2025-08-05/lms/lms-openai-gpt-oss-20b.json +0 -2814
  174. package/benchmarks/results/4542435/2025-08-05/lms/lms-qwen-qwen3-30b-a3b-2507.json +0 -2014
  175. package/benchmarks/results/4fb9125/2025-08-07/anthropic/anthropic-claude-sonnet-4-20250514.json +0 -3121
  176. package/benchmarks/results/5766aee/2025-08-02/lms-qwen/qwen3-coder-30b.json +0 -98
  177. package/benchmarks/results/6d73808/2025-08-07/openai/openai-gpt-5.json +0 -3256
  178. package/benchmarks/results/77bf0a6/2025-08-02/lms-qwen/qwen3-30b-a3b-2507.json +0 -4298
  179. package/benchmarks/results/8c0d445/2025-08-03/anthropic/anthropic-claude-sonnet-4-20250514.json +0 -3031
  180. package/benchmarks/results/8c0d445/2025-08-03/openai/openai-gpt-4.1-2025-04-14.json +0 -2990
  181. package/benchmarks/results/ac6b2ab/2025-08-03/anthropic/anthropic-claude-sonnet-4-20250514.json +0 -3256
  182. package/benchmarks/results/ac6b2ab/2025-08-03/lms/lms-qwen-qwen3-coder-30b.json +0 -3007
  183. package/benchmarks/results/ac6b2ab/2025-08-03/openai/openai-gpt-4.1-2025-04-14.json +0 -3256
  184. package/benchmarks/results/ac6b2ab/2025-08-03/openai/openai-gpt-4.1-mini-2025-04-14.json +0 -3036
  185. package/benchmarks/results/ac6b2ab/2025-08-03/openai/openai-gpt-4.1-nano-2025-04-14.json +0 -3280
  186. package/benchmarks/results/adff675/2025-08-04/lms/lms-qwen-qwen3-30b-a3b-2507.json +0 -1920
  187. package/benchmarks/results/adff675/2025-08-04/lms/lms-qwen-qwen3-coder-30b.json +0 -3281
  188. package/benchmarks/results/b502ed9/2025-08-03/lms-qwen/qwen3-coder-30b.json +0 -2896
  189. package/benchmarks/results/d1a8129/2025-08-03/lms/lms-qwen-qwen3-coder-30b.json +0 -3011
  190. package/benchmarks/results/e60471c/2025-08-03/lms/qwen3-30b-a3b-2507.json +0 -3003
  191. package/benchmarks/scripts/build-and-run.sh +0 -47
  192. package/benchmarks/scripts/clone-exercism.sh +0 -92
  193. package/benchmarks/scripts/validate.sh +0 -48
  194. package/benchmarks/src/__tests__/runner.test.ts +0 -27
  195. package/benchmarks/src/cli.ts +0 -90
  196. package/benchmarks/src/evaluators/EvaluatorRegistry.ts +0 -64
  197. package/benchmarks/src/evaluators/JavaScriptEvaluator.ts +0 -183
  198. package/benchmarks/src/evaluators/index.ts +0 -3
  199. package/benchmarks/src/evaluators/types.ts +0 -22
  200. package/benchmarks/src/index.ts +0 -3
  201. package/benchmarks/src/providers.ts +0 -13
  202. package/benchmarks/src/runner.ts +0 -824
  203. package/benchmarks/src/types.ts +0 -63
  204. package/benchmarks/tsconfig.json +0 -19
  205. package/leaderboard/README.md +0 -148
  206. package/leaderboard/app/api/benchmark-data/route.ts +0 -131
  207. package/leaderboard/app/api/benchmark-detail/route.ts +0 -172
  208. package/leaderboard/app/details/[model]/[provider]/[language]/page.tsx +0 -501
  209. package/leaderboard/app/exercise/[model]/[provider]/[language]/[exercise]/page.tsx +0 -375
  210. package/leaderboard/app/globals.css +0 -27
  211. package/leaderboard/app/layout.tsx +0 -21
  212. package/leaderboard/app/page.tsx +0 -170
  213. package/leaderboard/components/LeaderboardTable.tsx +0 -168
  214. package/leaderboard/components/PerformanceChart.tsx +0 -109
  215. package/leaderboard/next-env.d.ts +0 -5
  216. package/leaderboard/next.config.js +0 -4
  217. package/leaderboard/package-lock.json +0 -6363
  218. package/leaderboard/package.json +0 -28
  219. package/leaderboard/postcss.config.js +0 -6
  220. package/leaderboard/tailwind.config.js +0 -17
  221. package/leaderboard/tsconfig.json +0 -28
  222. package/leaderboard/types/benchmark.ts +0 -67
  223. package/leaderboard/utils/dataProcessor.ts +0 -33
  224. package/src/agents/tools/asana/definitions.ts +0 -199
  225. package/src/agents/tools/asana/index.ts +0 -108
  226. package/src/agents/tools/ast/astAppendNode.ts +0 -90
  227. package/src/agents/tools/ast/astDeleteNode.ts +0 -88
  228. package/src/agents/tools/ast/astEditNode.ts +0 -95
  229. package/src/agents/tools/ast/astGetPathForLine.ts +0 -73
  230. package/src/agents/tools/ast/astListPaths.ts +0 -66
  231. package/src/agents/tools/ast/index.ts +0 -7
  232. package/src/agents/tools/github/definitions.ts +0 -89
  233. package/src/agents/tools/github/index.ts +0 -67
  234. package/src/chat-old.ts +0 -446
  235. package/src/plugins/asana.ts +0 -146
  236. package/src/plugins/downloader/plugin.ts +0 -103
  237. package/src/plugins/downloader/types.ts +0 -92
  238. package/src/plugins/figma.ts +0 -158
  239. package/src/plugins/github.ts +0 -219
  240. package/src/plugins/jira.ts +0 -115
  241. package/src/plugins/linear.ts +0 -230
  242. package/src/plugins/notion.ts +0 -179
  243. package/src/plugins/tree-sitter/editor.ts +0 -369
  244. package/src/plugins/tree-sitter/lang-packs/index.ts +0 -23
  245. package/src/plugins/tree-sitter/lang-packs/java.ts +0 -59
  246. package/src/plugins/tree-sitter/lang-packs/javascript.ts +0 -57
  247. package/src/plugins/tree-sitter/lang-packs/python.ts +0 -45
  248. package/src/plugins/tree-sitter/lang-packs/types.ts +0 -79
  249. package/src/plugins/tree-sitter/lang-packs/typescript.ts +0 -49
  250. package/src/plugins/tree-sitter/parser.ts +0 -470
  251. package/src/plugins/tree-sitter/simple-paths.ts +0 -467
  252. package/src/services/GitHub.ts +0 -59
  253. package/tests/tree-sitter/editor.test.ts +0 -113
  254. package/tests/tree-sitter/invalid.test.ts +0 -299
  255. package/tests/tree-sitter/paths/common-edits.test.ts +0 -564
  256. package/tests/tree-sitter/paths/debug-exact-position.test.ts +0 -44
  257. package/tests/tree-sitter/paths/debug-line-indexing.test.ts +0 -49
  258. package/tests/tree-sitter/paths/debug-paths.test.ts +0 -90
  259. package/tests/tree-sitter/paths/paths.test.ts +0 -170
  260. package/tests/tree-sitter/paths/simple-paths.test.ts +0 -367
  261. package/tests/tree-sitter/sample-after.ts +0 -48
  262. package/tests/tree-sitter/sample-before.ts +0 -25
  263. package/tests/tree-sitter/test-files/completely-broken.ts +0 -7
  264. package/tests/tree-sitter/test-files/duplicate-braces.ts +0 -39
  265. package/tests/tree-sitter/test-files/invalid-nesting.ts +0 -39
  266. package/tests/tree-sitter/test-files/malformed-signature.ts +0 -39
  267. package/tests/tree-sitter/test-files/mismatched-parens.ts +0 -39
  268. package/tests/tree-sitter/test-files/missing-semicolon.ts +0 -39
  269. package/tests/tree-sitter/test-files/partially-broken.ts +0 -20
  270. package/tests/tree-sitter/test-files/specific-errors.ts +0 -14
  271. package/tests/tree-sitter/test-files/unclosed-string.ts +0 -39
  272. package/tests/tree-sitter/tree-sitter.test.ts +0 -251
@@ -1,9 +1,8 @@
1
- import { DownloaderService } from "../plugins/downloader/downloader";
2
1
  import { AIClient, Clients } from "../clients";
3
2
  import { AgentService } from "./AgentService";
4
3
  import { EventService } from "./EventService";
5
4
  import { FlagsService } from "./flags";
6
- import { GitHubService } from "./GitHub";
5
+ import { EmbeddingsService } from "./EmbeddingsService";
7
6
  import { KnowhowSimpleClient } from "./KnowhowClient";
8
7
  import { McpService } from "./Mcp";
9
8
  import { S3Service } from "./S3";
@@ -14,17 +13,19 @@ import { AgentSyncKnowhowWeb } from "./AgentSyncKnowhowWeb";
14
13
  import { AgentSyncFs } from "./AgentSyncFs";
15
14
  import { SessionManager } from "./SessionManager";
16
15
  import { TaskRegistry } from "./TaskRegistry";
16
+ import { MediaProcessorService } from "./MediaProcessorService";
17
17
 
18
18
  export * from "./AgentService";
19
19
  export * from "./EventService";
20
20
  export * from "./flags";
21
- export * from "./GitHub";
21
+ export * from "./EmbeddingsService";
22
22
  export * from "./S3";
23
23
  export * from "./Tools";
24
24
  export * from "./LazyToolsService";
25
25
  export * as MCP from "./Mcp";
26
26
  export * from "./EmbeddingService";
27
27
  export * from "./DockerService";
28
+ export * from "./MediaProcessorService";
28
29
  export * from "./AgentSyncKnowhowWeb";
29
30
  export * from "./AgentSyncFs";
30
31
  export * from "./SessionManager";
@@ -37,15 +38,15 @@ let Singletons = {} as {
37
38
  Tools: ToolsService;
38
39
  Events: EventService;
39
40
  Agents: AgentService;
41
+ Embeddings: EmbeddingsService;
40
42
  Flags: FlagsService;
41
- GitHub: GitHubService;
42
43
  Mcp: McpService;
43
44
  AwsS3: S3Service;
44
45
  Docker: DockerService;
45
46
  knowhowApiClient: KnowhowSimpleClient;
46
47
  Plugins: PluginService;
47
48
  Clients: AIClient;
48
- Downloader: DownloaderService;
49
+ MediaProcessor: MediaProcessorService;
49
50
  };
50
51
 
51
52
  export const services = (): typeof Singletons => {
@@ -53,7 +54,6 @@ export const services = (): typeof Singletons => {
53
54
  const Tools = new ToolsService();
54
55
  const Events = new EventService();
55
56
  const Agents = new AgentService(Tools, Events);
56
- const Downloader = new DownloaderService(Clients);
57
57
  const Plugins = new PluginService({
58
58
  Agents,
59
59
  Events,
@@ -66,11 +66,11 @@ export const services = (): typeof Singletons => {
66
66
  AwsS3: new S3Service(),
67
67
  Clients,
68
68
  Docker: new DockerService(),
69
- Downloader,
70
69
  Events,
70
+ Embeddings: new EmbeddingsService(),
71
71
  Flags: new FlagsService(),
72
- GitHub: new GitHubService(),
73
72
  Mcp: new McpService(),
73
+ MediaProcessor: new MediaProcessorService(Clients),
74
74
  Plugins,
75
75
  Tools,
76
76
  knowhowApiClient: new KnowhowSimpleClient(),
@@ -10,12 +10,14 @@ export class ModulesService {
10
10
 
11
11
  // If no context provided, fall back to global singletons
12
12
  if (!context) {
13
- const { Clients, Plugins, Agents, Tools } = services();
13
+ const { Clients, Plugins, Agents, Tools, Embeddings, MediaProcessor } = services();
14
14
  context = {
15
15
  Agents,
16
+ Embeddings,
16
17
  Plugins,
17
18
  Clients,
18
19
  Tools,
20
+ MediaProcessor,
19
21
  };
20
22
  }
21
23
 
@@ -34,7 +36,7 @@ export class ModulesService {
34
36
 
35
37
  for (const modulePath of allModulePaths) {
36
38
  const importedModule = require(modulePath) as KnowhowModule;
37
- await importedModule.init({ config, cwd: process.cwd() });
39
+ await importedModule.init({ config, cwd: process.cwd(), context });
38
40
 
39
41
  for (const agent of importedModule.agents) {
40
42
  agentService.registerAgent(agent);
@@ -46,7 +48,14 @@ export class ModulesService {
46
48
  }
47
49
 
48
50
  for (const plugin of importedModule.plugins) {
49
- pluginService.registerPlugin(plugin.name, plugin.plugin);
51
+ const pluginContext = {
52
+ Agents: agentService,
53
+ Clients: clients,
54
+ Tools: toolsService,
55
+ Plugins: pluginService,
56
+ ...(context.MediaProcessor ? { MediaProcessor: context.MediaProcessor } : {}),
57
+ };
58
+ pluginService.registerPlugin(plugin.name, new plugin.plugin(pluginContext as any));
50
59
  }
51
60
 
52
61
  for (const client of importedModule.clients) {
@@ -1,12 +1,14 @@
1
- import { Plugin } from "../../plugins/types";
1
+ import { Plugin, PluginContext } from "../../plugins/types";
2
2
  import { IAgent } from "../../agents/interface";
3
3
  import { Tool } from "../../clients/types";
4
4
  import { Config } from "../../types";
5
5
  import { GenericClient } from "../../clients/types";
6
6
  import { AgentService } from "../AgentService";
7
+ import { EmbeddingsService } from "../EmbeddingsService";
7
8
  import { PluginService } from "../../plugins/plugins";
8
9
  import { AIClient } from "../../clients";
9
10
  import { ToolsService } from "../Tools";
11
+ import { MediaProcessorService } from "../MediaProcessorService";
10
12
 
11
13
  /*
12
14
  *
@@ -29,7 +31,8 @@ export interface ModuleTool {
29
31
 
30
32
  export type ModuleAgent = IAgent;
31
33
 
32
- export type ModulePlugin = { name: string; plugin: Plugin };
34
+ export type PluginConstructor = new (context: PluginContext) => Plugin;
35
+ export type ModulePlugin = { name: string; plugin: PluginConstructor };
33
36
 
34
37
  export type ModuleClient = {
35
38
  client: GenericClient;
@@ -40,13 +43,16 @@ export type ModuleClient = {
40
43
  export type InitParams = {
41
44
  config: Config;
42
45
  cwd: string;
46
+ context?: ModuleContext;
43
47
  };
44
48
 
45
49
  export interface ModuleContext {
46
50
  Agents: AgentService;
51
+ Embeddings: EmbeddingsService;
47
52
  Plugins: PluginService;
48
53
  Clients: AIClient;
49
54
  Tools: ToolsService;
55
+ MediaProcessor?: MediaProcessorService;
50
56
  }
51
57
 
52
58
  export interface KnowhowModule {
@@ -96,7 +96,7 @@ export class ScriptExecutor {
96
96
 
97
97
  try {
98
98
  // Validate script
99
- const validation = policyEnforcer.validateScript(request.script);
99
+ const validation = policyEnforcer.validateScript(request.script, policy.allowNetworkAccess);
100
100
  if (!validation.valid) {
101
101
  tracer.emitEvent("script_validation_failed", {
102
102
  issues: validation.issues,
@@ -240,8 +240,9 @@ export class ScriptExecutor {
240
240
  tracer.emitEvent("script_execution_start", {});
241
241
 
242
242
  // Execute the script and get the result
243
+ // Note: do NOT set timeout here — it kills the isolate while awaiting host async promises.
244
+ // The outer executeWithTimeout wrapper handles wall-clock timeout instead.
243
245
  const result = await compiledScript.run(vmContext, {
244
- timeout: policyEnforcer.getQuotas().maxExecutionTimeMs,
245
246
  promise: true,
246
247
  copy: true,
247
248
  });
@@ -280,13 +281,29 @@ export class ScriptExecutor {
280
281
  `__host_${name}`,
281
282
  new ivm.Reference(async (...args: any[]) => {
282
283
  const result = await fn(...args);
283
- return new ivm.ExternalCopy(result).copyInto();
284
+ const safeResult = result !== undefined ? result : null;
285
+ const plainResult =
286
+ safeResult !== null && typeof safeResult === 'object'
287
+ ? JSON.parse(JSON.stringify(safeResult))
288
+ : safeResult;
289
+ // copyInto() transfers the value into the isolate heap so it's directly usable
290
+ return new ivm.ExternalCopy(plainResult).copyInto();
284
291
  })
285
292
  );
293
+ // Use applySyncPromise so the script isolate suspends and yields the Node.js event loop
294
+ // back to the host while waiting for async host operations (MCP stdio calls etc.).
295
+ // Without this, the ivm isolate blocks the event loop and stdio-based MCP transports
296
+ // can never deliver their responses → deadlock.
286
297
  await vmContext.eval(`
287
298
  globalThis.${name} = (...a) =>
288
- __host_${name}.apply(undefined, a,
289
- { arguments: { copy: true }, result: { promise: true, copy: true } });
299
+ new Promise((resolve, reject) => {
300
+ try {
301
+ // applySyncPromise does not support result options — the Reference fn returns ExternalCopy
302
+ const result = __host_${name}.applySyncPromise(undefined, a,
303
+ { arguments: { copy: true } });
304
+ resolve(result);
305
+ } catch(e) { reject(e); }
306
+ });
290
307
  `);
291
308
  };
292
309
 
@@ -308,11 +325,13 @@ export class ScriptExecutor {
308
325
 
309
326
  // Expose async sandbox functions
310
327
  await exposeAsync("callTool", async (tool, params) => {
311
- const { functionResp } = await sandboxContext.callTool(
312
- tool as string,
313
- params
314
- );
315
- return functionResp;
328
+ try {
329
+ const result = await sandboxContext.callTool(tool as string, params);
330
+ const { functionResp } = result;
331
+ return functionResp !== undefined ? functionResp : null;
332
+ } catch (err) {
333
+ throw err;
334
+ }
316
335
  });
317
336
  await exposeAsync("llm", (messages, options) =>
318
337
  sandboxContext.llm(messages, options || {})
@@ -163,7 +163,7 @@ export class ScriptPolicyEnforcer {
163
163
  /**
164
164
  * Validate script content for security issues
165
165
  */
166
- validateScript(scriptContent: string): { valid: boolean; issues: string[] } {
166
+ validateScript(scriptContent: string, allowNetworkAccess?: boolean): { valid: boolean; issues: string[] } {
167
167
  const issues: string[] = [];
168
168
 
169
169
  // Check for dangerous patterns
@@ -176,11 +176,15 @@ export class ScriptPolicyEnforcer {
176
176
  /Function\s*\(/gi, // Function constructor
177
177
  /setTimeout/gi, // setTimeout
178
178
  /setInterval/gi, // setInterval
179
- /fetch\s*\(/gi, // Direct fetch calls
180
179
  /XMLHttpRequest/gi, // XHR
181
180
  /WebSocket/gi, // WebSocket
182
181
  ];
183
182
 
183
+ // Block direct fetch calls when network access is not explicitly allowed
184
+ if (!allowNetworkAccess) {
185
+ dangerousPatterns.push(/fetch\s*\(/gi);
186
+ }
187
+
184
188
  for (const pattern of dangerousPatterns) {
185
189
  if (pattern.test(scriptContent)) {
186
190
  issues.push(`Potentially dangerous pattern detected: ${pattern.source}`);
package/src/types.ts CHANGED
@@ -265,6 +265,7 @@ export const Models = {
265
265
  Gemini_31_Pro_Preview: "gemini-3.1-pro-preview",
266
266
  Gemini_31_Flash_Image_Preview: "gemini-3.1-flash-image-preview",
267
267
  Gemini_31_Flash_Lite_Preview: "gemini-3.1-flash-lite-preview",
268
+ Gemini_31_Flash_Live_Preview: "gemini-3.1-flash-live-preview",
268
269
  Gemini_3_Flash_Preview: "gemini-3-flash-preview",
269
270
  Gemini_3_Pro_Image_Preview: "gemini-3-pro-image-preview",
270
271
  // Gemini 2.5
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Thin native-fetch wrapper that returns { data: T } for backward compatibility.
3
+ * Replaces axios throughout the codebase.
4
+ */
5
+
6
+ export interface HttpResponse<T = any> {
7
+ data: T;
8
+ status: number;
9
+ headers: Headers;
10
+ }
11
+
12
+ export class HttpError extends Error {
13
+ constructor(
14
+ public status: number,
15
+ public response: Response,
16
+ message: string
17
+ ) {
18
+ super(message);
19
+ this.name = "HttpError";
20
+ }
21
+ }
22
+
23
+ async function parseBody<T>(response: Response, responseType?: string): Promise<T> {
24
+ if (responseType === "arraybuffer") {
25
+ return (await response.arrayBuffer()) as unknown as T;
26
+ }
27
+ if (responseType === "stream") {
28
+ return response.body as unknown as T;
29
+ }
30
+ const text = await response.text();
31
+ if (!text) return undefined as unknown as T;
32
+ try {
33
+ return JSON.parse(text) as T;
34
+ } catch {
35
+ return text as unknown as T;
36
+ }
37
+ }
38
+
39
+ async function request<T = any>(
40
+ url: string,
41
+ options: {
42
+ method?: string;
43
+ headers?: Record<string, string>;
44
+ body?: any;
45
+ responseType?: "json" | "arraybuffer" | "stream" | "text";
46
+ params?: Record<string, any>;
47
+ /** Timeout in milliseconds. Default: 30000 (30s). Use 0 to disable. */
48
+ timeout?: number;
49
+ } = {}
50
+ ): Promise<HttpResponse<T>> {
51
+ const { method = "GET", headers = {}, body, responseType, params, timeout = 30000 } = options;
52
+
53
+ let fullUrl = url;
54
+ if (params && Object.keys(params).length > 0) {
55
+ const searchParams = new URLSearchParams();
56
+ for (const [k, v] of Object.entries(params)) {
57
+ if (v !== undefined && v !== null) {
58
+ searchParams.set(k, String(v));
59
+ }
60
+ }
61
+ fullUrl = `${url}?${searchParams.toString()}`;
62
+ }
63
+
64
+ const fetchOptions: RequestInit = {
65
+ method,
66
+ headers: { ...headers },
67
+ };
68
+
69
+ if (body !== undefined && body !== null) {
70
+ if (body instanceof Buffer || body instanceof Uint8Array || typeof (body as any).pipe === "function") {
71
+ // Stream or buffer — pass directly, Node fetch supports ReadableStream
72
+ fetchOptions.body = body;
73
+ } else if (typeof body === "object" && !(body instanceof FormData)) {
74
+ fetchOptions.body = JSON.stringify(body);
75
+ (fetchOptions.headers as Record<string, string>)["Content-Type"] = "application/json";
76
+ } else {
77
+ fetchOptions.body = body;
78
+ }
79
+ }
80
+
81
+ // Apply timeout via AbortController
82
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
83
+ if (timeout > 0) {
84
+ const controller = new AbortController();
85
+ fetchOptions.signal = controller.signal;
86
+ timeoutId = setTimeout(() => {
87
+ controller.abort();
88
+ }, timeout);
89
+ }
90
+
91
+ let response: Response;
92
+ try {
93
+ response = await fetch(fullUrl, fetchOptions);
94
+ } catch (e: any) {
95
+ if (e?.name === "AbortError") {
96
+ throw new HttpError(408, undefined as any, `Request timeout after ${timeout}ms: ${url}`);
97
+ }
98
+ throw e;
99
+ } finally {
100
+ if (timeoutId !== undefined) clearTimeout(timeoutId);
101
+ }
102
+
103
+ if (!response.ok) {
104
+ const text = await response.text().catch(() => "");
105
+ throw new HttpError(response.status, response, `HTTP ${response.status}: ${text}`);
106
+ }
107
+
108
+ const data = await parseBody<T>(response, responseType);
109
+
110
+ return { data, status: response.status, headers: response.headers };
111
+ }
112
+
113
+ export const http = {
114
+ get: <T = any>(url: string, options?: Omit<Parameters<typeof request>[1], "method">) =>
115
+ request<T>(url, { ...options, method: "GET" }),
116
+ post: <T = any>(url: string, body?: any, options?: Omit<Parameters<typeof request>[1], "method" | "body">) =>
117
+ request<T>(url, { ...options, method: "POST", body }),
118
+ put: <T = any>(url: string, body?: any, options?: Omit<Parameters<typeof request>[1], "method" | "body">) =>
119
+ request<T>(url, { ...options, method: "PUT", body }),
120
+ patch: <T = any>(url: string, body?: any, options?: Omit<Parameters<typeof request>[1], "method" | "body">) =>
121
+ request<T>(url, { ...options, method: "PATCH", body }),
122
+ delete: <T = any>(url: string, options?: Omit<Parameters<typeof request>[1], "method">) =>
123
+ request<T>(url, { ...options, method: "DELETE" }),
124
+ isHttpError: (e: any): e is HttpError => e instanceof HttpError,
125
+ };
126
+
127
+ export default http;
@@ -1,4 +1,4 @@
1
- import axios from "axios";
1
+ import http from "../../utils/http";
2
2
  import { KNOWHOW_API_URL } from "../../services/KnowhowClient";
3
3
  import { openBrowser } from "../../auth/browserLogin";
4
4
  import { Spinner } from "../../auth/spinner";
@@ -93,21 +93,18 @@ export class PasskeySetupService {
93
93
 
94
94
  private async createSetupSession(jwt: string): Promise<PasskeySetupSession> {
95
95
  try {
96
- const response = await axios.post<PasskeySetupSession>(
96
+ const response = await http.post<PasskeySetupSession>(
97
97
  `${this.baseUrl}/api/worker/passkey/setup/session`,
98
98
  {},
99
99
  {
100
100
  headers: { Authorization: `Bearer ${jwt}` },
101
- timeout: 10000,
102
101
  }
103
102
  );
104
103
  return response.data;
105
104
  } catch (error) {
106
- if (axios.isAxiosError(error)) {
105
+ if (http.isHttpError(error)) {
107
106
  throw new Error(
108
- `Failed to create passkey setup session: ${
109
- error.response?.data?.message || error.message
110
- }`
107
+ `Failed to create passkey setup session: ${error.message}`
111
108
  );
112
109
  }
113
110
  throw error;
@@ -125,9 +122,8 @@ export class PasskeySetupService {
125
122
  attempt++;
126
123
 
127
124
  try {
128
- const response = await axios.get<PasskeySetupStatus>(
129
- `${this.baseUrl}/api/worker/passkey/setup/status/${sessionId}`,
130
- { timeout: 10000 }
125
+ const response = await http.get<PasskeySetupStatus>(
126
+ `${this.baseUrl}/api/worker/passkey/setup/status/${sessionId}`
131
127
  );
132
128
 
133
129
  const { status, credential } = response.data;
@@ -140,7 +136,7 @@ export class PasskeySetupService {
140
136
  );
141
137
  }
142
138
  } catch (error) {
143
- if (axios.isAxiosError(error) && error.code === "ECONNABORTED") {
139
+ if (http.isHttpError(error) && error.status === 408) {
144
140
  // Timeout — keep polling
145
141
  } else if (!(error instanceof Error && error.message.includes("expired"))) {
146
142
  // Re-throw non-timeout, non-expected errors
@@ -154,12 +154,18 @@ describe("AIClient", () => {
154
154
  });
155
155
 
156
156
  it("should find model by detection in registered providers", () => {
157
- aiClient.registerClient("test", new FakeClient());
158
- aiClient.registerModels("test", ["gpt-4-turbo", "gpt-4-vision"]);
157
+ // Register an explicit myopenai provider with a gpt-4 prefix model
158
+ aiClient.registerClient("myopenai", new FakeClient());
159
+ aiClient.registerModels("myopenai", ["gpt-4-turbo", "gpt-4-vision"]);
159
160
 
161
+ // "another" provider (registered in beforeEach) has exact "gpt-4" match
162
+ // "myopenai" has prefix-matches "gpt-4-turbo" and "gpt-4-vision"
163
+ // findModel finds the first provider with a model matching the prefix "gpt-4"
160
164
  const result = aiClient.detectProviderModel("", "gpt-4");
161
- expect(result.provider).toBe("openai"); // Real openai provider takes precedence
162
- expect(result.model).toBe("gpt-4.1-2025-04-14"); // Actual model found by prefix match
165
+ // Some provider should be found that has a gpt-4 model
166
+ expect(result.provider).toBeTruthy();
167
+ // The found model should start with "gpt-4"
168
+ expect(result.model).toMatch(/^gpt-4/);
163
169
  });
164
170
 
165
171
  it("should handle model with provider prefix when provider is empty", () => {
@@ -175,15 +181,16 @@ describe("AIClient", () => {
175
181
  });
176
182
 
177
183
  it("should detect real provider when model exists", () => {
178
- aiClient.registerClient("test", new FakeClient());
179
- aiClient.registerModels("test", ["claude-3-opus"]);
184
+ // Register an anthropic provider explicitly so we don't rely on env vars
185
+ aiClient.registerClient("anthropic", new FakeClient());
186
+ aiClient.registerModels("anthropic", ["claude-3-opus-20240229"]);
180
187
 
181
188
  // Test with provider prefix that gets stripped
182
189
  const result = aiClient.detectProviderModel(
183
190
  "",
184
191
  "anthropic/claude-3-opus-20240229"
185
192
  );
186
- expect(result.provider).toBe("anthropic"); // Real anthropic provider found
193
+ expect(result.provider).toBe("anthropic"); // anthropic provider found
187
194
  expect(result.model).toBe("claude-3-opus-20240229");
188
195
  });
189
196
  });
@@ -208,15 +215,13 @@ describe("AIClient", () => {
208
215
  it("should return all registered models from all providers", () => {
209
216
  const allModels = aiClient.listAllModels();
210
217
  expect(typeof allModels).toBe("object");
211
- // listAllModels() only returns models from real providers that have API keys
212
- // Our test clients are not included in the listAllModels() output
213
- // But we can verify real providers are present
218
+ // Verify our registered test providers are present
214
219
  expect(Object.keys(allModels).length).toBeGreaterThan(0);
215
- // Real providers like openai, anthropic should be present
220
+ // The test providers registered in beforeEach should be present
216
221
  const providers = Object.keys(allModels);
217
222
  expect(
218
223
  providers.some((p) =>
219
- ["openai", "anthropic", "google", "xai"].includes(p)
224
+ ["fake", "another"].includes(p)
220
225
  )
221
226
  ).toBe(true);
222
227
  });
@@ -470,8 +475,10 @@ describe("AIClient", () => {
470
475
  });
471
476
 
472
477
  it("should handle provider stripping with complex model names", () => {
473
- // Test detection with real providers that exist in AIClient
474
- // AIClient should find the real anthropic provider for claude models
478
+ // Register providers explicitly so we don't rely on env vars
479
+ aiClient.registerClient("anthropic", new FakeClient());
480
+ aiClient.registerModels("anthropic", ["claude-3-opus-20240229"]);
481
+
475
482
  const detection1 = aiClient.detectProviderModel(
476
483
  "",
477
484
  "anthropic/claude-3-opus-20240229"
@@ -484,14 +491,10 @@ describe("AIClient", () => {
484
491
  "",
485
492
  "openai/non-existent-model"
486
493
  );
487
- // Should either return empty strings or fallback to defaults
494
+ // Should return original values when no match found
488
495
  expect(detection2).toBeDefined();
489
- if (detection2?.provider === "") {
490
- expect(detection2?.model).toBe("openai/non-existent-model");
491
- } else {
492
- expect(detection2?.provider).toBe("openai");
493
- expect(detection2?.model).toBe("gpt-5");
494
- }
496
+ expect(detection2?.provider).toBe("");
497
+ expect(detection2?.model).toBe("openai/non-existent-model");
495
498
  });
496
499
 
497
500
  it("should handle model prefix matching edge cases", () => {
@@ -1,70 +1,3 @@
1
- import { FigmaPlugin } from "../../../src/plugins/figma";
2
-
3
- const figmaToken = "test_token"; // This should be the actual testing token
4
- const figmaPlugin = new FigmaPlugin();
5
-
6
- const exampleFigmaFileUrl =
7
- "https://www.figma.com/file/iK2ggDLxl94Q4q0FKUdlmM/Guilds";
8
- const exampleNodeId = "52-717";
9
- const exampleNodeIds = [exampleNodeId];
10
-
11
- const expectedApiResponse = {
12
- name: "Guilds",
13
- nodes: { "52-717": { document: {} } },
14
- };
15
-
16
- describe("FigmaPlugin", () => {
17
- describe("extractUrls", () => {
18
- it("should extract Figma file URLs from a given text", () => {
19
- const text = `Design can be found here: ${exampleFigmaFileUrl}`;
20
- expect(figmaPlugin.extractUrls(text)).toEqual([exampleFigmaFileUrl]);
21
- });
22
- });
23
-
24
- describe("extractFileIdFromUrl", () => {
25
- it("should extract file ID from Figma URL", () => {
26
- expect(figmaPlugin.extractFileIdFromUrl(exampleFigmaFileUrl)).toBe(
27
- "iK2ggDLxl94Q4q0FKUdlmM"
28
- );
29
- });
30
- });
31
-
32
- describe("parseNodeIdsFromUrl", () => {
33
- it("should extract node IDs from Figma URL", () => {
34
- const urlWithNode = exampleFigmaFileUrl + "?node-id=" + exampleNodeId;
35
- expect(figmaPlugin.parseNodeIdsFromUrl(urlWithNode)).toEqual(
36
- exampleNodeIds
37
- );
38
- });
39
- });
40
-
41
- describe("loadFigmaData", () => {
42
- it("should make an API call and return Figma data", async () => {
43
- const data = await figmaPlugin.loadFigmaData(
44
- exampleFigmaFileUrl + "?node-id=" + exampleNodeId
45
- );
46
- expect(data).toBeDefined;
47
- });
48
- });
49
-
50
- describe("embed", () => {
51
- it("should return a minimal embedding array", async () => {
52
- const embeddings = await figmaPlugin.embed(
53
- `Here's the design file: ${exampleFigmaFileUrl}?node-id=${exampleNodeId}`
54
- );
55
- expect(embeddings).toBeInstanceOf(Array);
56
- expect(embeddings).toHaveLength(1);
57
- const nodeId = exampleNodeId.replace("-", ":");
58
- console.log(JSON.parse(embeddings[0].text));
59
- /*
60
- *expect(embeddings[0].text).toEqual(
61
- * JSON.stringify({
62
- * id: expectedApiResponse.name,
63
- * metadata: {},
64
- * text: JSON.stringify(expectedApiResponse),
65
- * })
66
- *);
67
- */
68
- });
69
- });
70
- });
1
+ // FigmaPlugin tests moved to @tyvm/knowhow-plugin-figma package
2
+ // Install and configure @tyvm/knowhow-plugin-figma to use Figma integration
3
+ describe.skip("FigmaPlugin (moved to @tyvm/knowhow-plugin-figma)", () => {});
@@ -18,6 +18,7 @@ jest.mock("../../../src/services/EventService", () => ({
18
18
  EventService: jest.fn().mockImplementation(() => ({
19
19
  on: jest.fn(),
20
20
  emit: jest.fn(),
21
+ log: jest.fn(),
21
22
  })),
22
23
  }));
23
24
 
@@ -54,6 +55,7 @@ describe("LanguagePlugin - Content-Based Triggering", () => {
54
55
  eventHandlers.set(event, handler);
55
56
  }),
56
57
  emit: jest.fn(),
58
+ log: jest.fn(),
57
59
  };
58
60
 
59
61
  mockPluginService = {
@@ -73,6 +73,7 @@ describe("LanguagePlugin", () => {
73
73
  const mockEventService = {
74
74
  on: jest.fn(),
75
75
  emit: jest.fn(),
76
+ log: jest.fn(),
76
77
  };
77
78
  const mockListPlugins = jest.fn().mockReturnValue(["github", "asana"]);
78
79
  const mockCall = jest.fn().mockResolvedValue(["mocked plugin response"]);
@@ -362,6 +363,7 @@ describe("LanguagePlugin", () => {
362
363
  const mockEventService = {
363
364
  on: jest.fn(),
364
365
  emit: jest.fn(),
366
+ log: jest.fn(),
365
367
  };
366
368
  const mockPluginService = new MockedPluginService({} as any);
367
369
  const mockListPlugins = jest.fn().mockReturnValue(["github"]);
@@ -371,7 +371,7 @@ describe("ToolResponseCache", () => {
371
371
  const result = await cache.queryToolResponse("missing_id", ".test");
372
372
  expect(result).toContain("Error: No tool response found");
373
373
  expect(result).toContain("missing_id");
374
- expect(result).toContain("Available IDs:");
374
+ expect(result).toContain("Available toolCallIds:");
375
375
  });
376
376
 
377
377
  it("should handle invalid JQ queries with error message", async () => {
@@ -698,7 +698,7 @@ describe("ToolResponseCache", () => {
698
698
  const result = await cache.tailToolResponse("missing_id");
699
699
  expect(result).toContain("Error: No tool response found");
700
700
  expect(result).toContain("missing_id");
701
- expect(result).toContain("Available IDs:");
701
+ expect(result).toContain("Available toolCallIds:");
702
702
  });
703
703
 
704
704
  it("should handle lines option of 0", async () => {