onbuzz 4.9.13 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/node_modules/glob/README.md +31 -5
  2. package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
  3. package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
  4. package/node_modules/glob/dist/commonjs/glob.js +2 -1
  5. package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
  6. package/node_modules/glob/dist/commonjs/index.min.js +3 -3
  7. package/node_modules/glob/dist/commonjs/index.min.js.map +4 -4
  8. package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
  9. package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
  10. package/node_modules/glob/dist/commonjs/pattern.js +4 -0
  11. package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
  12. package/node_modules/glob/dist/esm/glob.d.ts +8 -0
  13. package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
  14. package/node_modules/glob/dist/esm/glob.js +2 -1
  15. package/node_modules/glob/dist/esm/glob.js.map +1 -1
  16. package/node_modules/glob/dist/esm/index.min.js +3 -3
  17. package/node_modules/glob/dist/esm/index.min.js.map +4 -4
  18. package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
  19. package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
  20. package/node_modules/glob/dist/esm/pattern.js +4 -0
  21. package/node_modules/glob/dist/esm/pattern.js.map +1 -1
  22. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/README.md +7 -10
  23. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/package.json +7 -18
  24. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/README.md +3 -6
  25. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.js +6 -4
  26. package/node_modules/glob/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -0
  27. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.js +6 -4
  28. package/node_modules/glob/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
  29. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/package.json +11 -7
  30. package/node_modules/glob/node_modules/minimatch/README.md +76 -1
  31. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts +1 -1
  32. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map +1 -1
  33. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map +1 -1
  34. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts +4 -2
  35. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -1
  36. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js +309 -55
  37. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js.map +1 -1
  38. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -1
  39. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js +2 -4
  40. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -1
  41. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts +1 -1
  42. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts.map +1 -1
  43. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js +4 -4
  44. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js.map +1 -1
  45. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts +81 -1
  46. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -1
  47. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js +232 -134
  48. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js.map +1 -1
  49. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts +1 -1
  50. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts.map +1 -1
  51. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js +8 -8
  52. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -1
  53. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts +1 -1
  54. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map +1 -1
  55. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -1
  56. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts +4 -2
  57. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -1
  58. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js +309 -55
  59. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js.map +1 -1
  60. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -1
  61. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js +2 -4
  62. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -1
  63. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts +1 -1
  64. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts.map +1 -1
  65. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js +4 -4
  66. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js.map +1 -1
  67. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts +81 -1
  68. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts.map +1 -1
  69. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js +232 -134
  70. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js.map +1 -1
  71. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts +1 -1
  72. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts.map +1 -1
  73. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js +8 -8
  74. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js.map +1 -1
  75. package/node_modules/glob/node_modules/minimatch/package.json +17 -11
  76. package/node_modules/glob/package.json +10 -13
  77. package/node_modules/minipass/LICENSE.md +55 -0
  78. package/node_modules/minipass/dist/commonjs/index.d.ts +12 -16
  79. package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -1
  80. package/node_modules/minipass/dist/commonjs/index.js +13 -3
  81. package/node_modules/minipass/dist/commonjs/index.js.map +1 -1
  82. package/node_modules/minipass/dist/esm/index.d.ts +12 -16
  83. package/node_modules/minipass/dist/esm/index.d.ts.map +1 -1
  84. package/node_modules/minipass/dist/esm/index.js +3 -1
  85. package/node_modules/minipass/dist/esm/index.js.map +1 -1
  86. package/node_modules/minipass/package.json +9 -14
  87. package/node_modules/path-scurry/node_modules/lru-cache/README.md +96 -10
  88. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.d.ts.map +1 -0
  89. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.js.map +1 -0
  90. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.d.ts +5 -0
  91. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
  92. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts +1400 -0
  93. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -0
  94. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js +1726 -0
  95. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -0
  96. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
  97. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +7 -0
  98. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts +12 -0
  99. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts.map +1 -0
  100. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
  101. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js.map +1 -0
  102. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.cjs.map +1 -0
  103. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
  104. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
  105. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
  106. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts +109 -32
  107. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -1
  108. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +334 -197
  109. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js.map +1 -1
  110. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
  111. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js.map +4 -4
  112. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.d.ts.map +1 -0
  113. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.js.map +1 -0
  114. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.d.ts +5 -0
  115. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
  116. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts +1400 -0
  117. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -0
  118. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js +1726 -0
  119. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -0
  120. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
  121. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +7 -0
  122. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts +12 -0
  123. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts.map +1 -0
  124. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
  125. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js.map +1 -0
  126. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts +12 -0
  127. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts.map +1 -0
  128. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
  129. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js.map +1 -0
  130. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.d.ts.map +1 -0
  131. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.js.map +1 -0
  132. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
  133. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
  134. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts +1400 -0
  135. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -0
  136. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js +1722 -0
  137. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -0
  138. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
  139. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js.map +7 -0
  140. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts +12 -0
  141. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts.map +1 -0
  142. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
  143. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js.map +1 -0
  144. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
  145. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.mjs.map +1 -0
  146. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
  147. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
  148. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts +109 -32
  149. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -1
  150. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +333 -196
  151. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js.map +1 -1
  152. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +1 -1
  153. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js.map +4 -4
  154. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.d.ts.map +1 -0
  155. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.js.map +1 -0
  156. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
  157. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
  158. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts +1400 -0
  159. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -0
  160. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js +1722 -0
  161. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js.map +1 -0
  162. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
  163. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js.map +7 -0
  164. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts +12 -0
  165. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts.map +1 -0
  166. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
  167. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js.map +1 -0
  168. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts +12 -0
  169. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts.map +1 -0
  170. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js +7 -0
  171. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js.map +1 -0
  172. package/node_modules/path-scurry/node_modules/lru-cache/package.json +71 -18
  173. package/node_modules/path-scurry/package.json +8 -24
  174. package/package.json +1 -1
  175. package/scripts/debug-balance-probe.mjs +35 -35
  176. package/scripts/push-image.sh +43 -43
  177. package/scripts/setup-acr.sh +65 -65
  178. package/scripts/verify-optional-deps.js +96 -1
  179. package/src/__tests__/composioCliFlags.test.js +239 -239
  180. package/src/analyzers/CSSAnalyzer.js +298 -297
  181. package/src/analyzers/ConfigValidator.js +691 -690
  182. package/src/analyzers/ESLintAnalyzer.js +320 -320
  183. package/src/analyzers/JavaScriptAnalyzer.js +260 -261
  184. package/src/analyzers/PrettierFormatter.js +246 -247
  185. package/src/analyzers/PythonAnalyzer.js +283 -283
  186. package/src/analyzers/SecurityAnalyzer.js +729 -729
  187. package/src/analyzers/SparrowAnalyzer.js +341 -341
  188. package/src/analyzers/TypeScriptAnalyzer.js +247 -247
  189. package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -41
  190. package/src/analyzers/__tests__/ConfigValidator.test.js +362 -362
  191. package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -40
  192. package/src/analyzers/__tests__/PythonAnalyzer.test.js +205 -208
  193. package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -303
  194. package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -187
  195. package/src/analyzers/codeCloneDetector/analyzer.js +344 -344
  196. package/src/analyzers/codeCloneDetector/detector.js +250 -250
  197. package/src/analyzers/codeCloneDetector/index.js +194 -192
  198. package/src/analyzers/codeCloneDetector/parser.js +199 -199
  199. package/src/core/__tests__/agentPool.test.js +866 -866
  200. package/src/core/__tests__/agentPoolAutoResume.test.js +209 -209
  201. package/src/core/__tests__/agentPoolWakeOnMessage.test.js +315 -315
  202. package/src/core/__tests__/agentScheduler.emptyResponseChatStall.test.js +213 -213
  203. package/src/core/__tests__/agentScheduler.errorCategorisation.test.js +246 -246
  204. package/src/core/__tests__/agentScheduler.firstChunkTimeout.test.js +138 -138
  205. package/src/core/__tests__/agentScheduler.modeTransitions.test.js +233 -233
  206. package/src/core/__tests__/agentScheduler.nativePromptPick.test.js +319 -319
  207. package/src/core/__tests__/agentScheduler.taskLifecycleInstruction.test.js +78 -78
  208. package/src/core/__tests__/agentScheduler.visualizer.test.js +258 -258
  209. package/src/core/__tests__/flowCheckpointStore.test.js +140 -140
  210. package/src/core/__tests__/flowEndToEnd.test.js +565 -565
  211. package/src/core/__tests__/flowFieldMapping.test.js +188 -189
  212. package/src/core/__tests__/flowLintClientMirror.test.js +96 -98
  213. package/src/core/__tests__/flowSavePayload.test.js +170 -169
  214. package/src/core/__tests__/flowTemplates.test.js +311 -311
  215. package/src/core/__tests__/flowVersionStore.test.js +123 -123
  216. package/src/core/__tests__/messageProcessor.test.js +669 -669
  217. package/src/core/__tests__/stateManager.test.js +0 -1
  218. package/src/core/agentPool.js +2474 -2475
  219. package/src/core/agentScheduler.js +1 -4
  220. package/src/core/contextManager.js +708 -708
  221. package/src/core/flowExecutor.js +1510 -1510
  222. package/src/core/flowFieldMapping.js +136 -138
  223. package/src/core/messageProcessor.js +953 -954
  224. package/src/core/orchestrator.js +593 -595
  225. package/src/core/stateManager.js +1765 -1752
  226. package/src/index.js +1221 -1221
  227. package/src/interfaces/__tests__/archivedAgentDelete.test.js +207 -207
  228. package/src/interfaces/__tests__/bulkAgentRoute.test.js +361 -361
  229. package/src/interfaces/__tests__/imageServing.test.js +228 -228
  230. package/src/interfaces/__tests__/remoteSessionAuth.test.js +308 -308
  231. package/src/interfaces/__tests__/videoJobsRoutes.test.js +178 -179
  232. package/src/interfaces/__tests__/webServer.marketplace.test.js +629 -629
  233. package/src/interfaces/schedulerRoutes.js +50 -50
  234. package/src/interfaces/terminal/__tests__/smoke/connection.test.js +341 -350
  235. package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +156 -156
  236. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +325 -330
  237. package/src/interfaces/terminal/__tests__/smoke/tools.test.js +385 -388
  238. package/src/interfaces/terminal/api/session.js +265 -266
  239. package/src/interfaces/terminal/api/websocket.js +496 -497
  240. package/src/interfaces/terminal/components/AgentCreator.js +691 -705
  241. package/src/interfaces/terminal/components/AgentEditor.js +676 -678
  242. package/src/interfaces/terminal/components/AgentSwitcher.js +331 -330
  243. package/src/interfaces/terminal/components/ErrorPanel.js +263 -264
  244. package/src/interfaces/terminal/components/Header.js +28 -28
  245. package/src/interfaces/terminal/components/Layout.js +598 -603
  246. package/src/interfaces/terminal/components/MessageList.js +280 -281
  247. package/src/interfaces/terminal/components/SettingsPanel.js +410 -415
  248. package/src/interfaces/terminal/components/StatusBar.js +2 -0
  249. package/src/interfaces/terminal/index.js +168 -168
  250. package/src/interfaces/terminal/state/useAgentControl.js +496 -496
  251. package/src/interfaces/terminal/state/useAgents.js +537 -537
  252. package/src/interfaces/terminal/state/useMessages.js +629 -630
  253. package/src/interfaces/terminal/state/useTools.js +554 -554
  254. package/src/interfaces/terminal/utils/debugLogger.js +44 -44
  255. package/src/interfaces/terminal/utils/settingsStorage.js +232 -232
  256. package/src/interfaces/webServer.js +7578 -7579
  257. package/src/interfaces/webServer.js.bak +7046 -7046
  258. package/src/modules/fileExplorer/__tests__/zipDownload.test.js +237 -237
  259. package/src/modules/fileExplorer/controller.js +470 -469
  260. package/src/modules/fileExplorer/routes.js +285 -286
  261. package/src/modules/widget/__tests__/isDisabled.test.js +41 -41
  262. package/src/modules/widget/__tests__/routes.test.js +677 -678
  263. package/src/modules/widget/__tests__/runtime.test.js +401 -401
  264. package/src/modules/widget/__tests__/versioning.test.js +309 -309
  265. package/src/modules/widget/__tests__/webComponentRuntime.test.js +565 -565
  266. package/src/modules/widget/__tests__/widgetTool.test.js +316 -316
  267. package/src/modules/widget/routes.js +435 -435
  268. package/src/modules/widget/runtime/bundle.js +640 -640
  269. package/src/modules/widget/runtime/webComponentBundle.js +470 -470
  270. package/src/modules/widget/schema.js +182 -181
  271. package/src/modules/widget/widgetTool.js +1389 -1389
  272. package/src/services/__tests__/agentActivityService.test.js +401 -402
  273. package/src/services/__tests__/benchmarkService.test.js +184 -184
  274. package/src/services/__tests__/contextInjectionService.test.js +246 -246
  275. package/src/services/__tests__/conversationQuery.test.js +721 -723
  276. package/src/services/__tests__/credentialVault.test.js +469 -469
  277. package/src/services/__tests__/discordService.integration.test.js +638 -639
  278. package/src/services/__tests__/flowContextService.test.js +590 -590
  279. package/src/services/__tests__/memoryService.test.js +1 -1
  280. package/src/services/__tests__/messageSource.test.js +380 -380
  281. package/src/services/__tests__/modelRouterNaming.test.js +111 -111
  282. package/src/services/__tests__/projectDetector.test.js +34 -34
  283. package/src/services/__tests__/promptService.test.js +242 -242
  284. package/src/services/__tests__/telegramService.test.js +941 -941
  285. package/src/services/__tests__/tokenCountingService.test.js +48 -48
  286. package/src/services/agentActivityService.js +419 -420
  287. package/src/services/aiService.js +2997 -3001
  288. package/src/services/apiKeyManager.js +359 -359
  289. package/src/services/benchmarkService.js +196 -196
  290. package/src/services/codebaseKnowledgeService.js +2 -2
  291. package/src/services/composioService.js +738 -738
  292. package/src/services/conversationCompactionService.js +1258 -1257
  293. package/src/services/credentialVault.js +685 -685
  294. package/src/services/discordService.js +792 -793
  295. package/src/services/embeddings/__tests__/azureCustomProvider.test.js +232 -232
  296. package/src/services/embeddings/__tests__/embeddingService.test.js +417 -417
  297. package/src/services/embeddings/__tests__/localProvider.test.js +263 -263
  298. package/src/services/embeddings/autoRecall.js +218 -219
  299. package/src/services/embeddings/indexers/__tests__/agentIndexer.test.js +232 -232
  300. package/src/services/embeddings/indexers/__tests__/memoryIndexer.test.js +418 -418
  301. package/src/services/embeddings/indexers/__tests__/reminisceIndexer.test.js +356 -357
  302. package/src/services/embeddings/indexers/__tests__/skillsIndexer.test.js +145 -145
  303. package/src/services/embeddings/indexers/__tests__/taskIndexer.test.js +146 -146
  304. package/src/services/embeddings/indexers/composioIndexer.js +279 -279
  305. package/src/services/embeddings/providerInterface.js +206 -206
  306. package/src/services/embeddings/providers/localProvider.js +11 -7
  307. package/src/services/embeddings/providers/openaiProvider.js +101 -101
  308. package/src/services/embeddings/vectorStore/inMemoryJsonStore.js +356 -356
  309. package/src/services/errorHandler.js +809 -809
  310. package/src/services/flowContextService.js +586 -586
  311. package/src/services/grounding/MockAdapter.js +125 -125
  312. package/src/services/modelRouterService.js +26 -31
  313. package/src/services/modelsService.js +322 -322
  314. package/src/services/ollamaService.js +452 -452
  315. package/src/services/projectDetector.js +403 -404
  316. package/src/services/promptService.js +418 -418
  317. package/src/services/qualityInspector.js +795 -795
  318. package/src/services/scheduleService.js +726 -726
  319. package/src/services/serviceRegistry.js +386 -386
  320. package/src/services/telegrafBot.js +174 -174
  321. package/src/services/telegramService.js +1972 -1972
  322. package/src/services/visualEditorBridge.js +1033 -1033
  323. package/src/services/visualEditorServer.js +1769 -1774
  324. package/src/services/whatsappService.js +667 -668
  325. package/src/tools/__tests__/agentCommunicationTool.findAgent.test.js +226 -226
  326. package/src/tools/__tests__/agentCommunicationTool.test.js +3 -3
  327. package/src/tools/__tests__/agentDelayTool.test.js +342 -342
  328. package/src/tools/__tests__/baseTool.test.js +3 -3
  329. package/src/tools/__tests__/codeMapTool.test.js +915 -915
  330. package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -309
  331. package/src/tools/__tests__/fileTreeTool.test.js +274 -274
  332. package/src/tools/__tests__/filesystemTool.test.js +815 -815
  333. package/src/tools/__tests__/foundryWebSearchTool.test.js +252 -252
  334. package/src/tools/__tests__/imageTool.validator.test.js +194 -194
  335. package/src/tools/__tests__/jobDoneTool.test.js +580 -581
  336. package/src/tools/__tests__/memoryTool.forgetStale.test.js +272 -272
  337. package/src/tools/__tests__/memoryTool.reminisce.test.js +2 -2
  338. package/src/tools/__tests__/memoryTool.reminisceSemanticSearch.test.js +301 -301
  339. package/src/tools/__tests__/memoryTool.semanticSearch.test.js +405 -405
  340. package/src/tools/__tests__/memoryTool.teamPool.test.js +293 -293
  341. package/src/tools/__tests__/memoryTool.test.js +1 -1
  342. package/src/tools/__tests__/seekTool.test.js +282 -282
  343. package/src/tools/__tests__/skillsTool.search.test.js +164 -164
  344. package/src/tools/__tests__/skillsTool.test.js +226 -226
  345. package/src/tools/__tests__/staticAnalysisTool.test.js +509 -509
  346. package/src/tools/__tests__/taskManagerTool.discipline.test.js +137 -137
  347. package/src/tools/__tests__/taskManagerTool.search.test.js +143 -143
  348. package/src/tools/__tests__/taskManagerTool.test.js +866 -866
  349. package/src/tools/__tests__/terminalTool.test.js +448 -448
  350. package/src/tools/__tests__/toolShapeForgiveness.test.js +259 -260
  351. package/src/tools/__tests__/userPromptTool.test.js +297 -297
  352. package/src/tools/__tests__/videoTool.jobs.test.js +147 -147
  353. package/src/tools/__tests__/webTool.e2e.test.js +609 -603
  354. package/src/tools/__tests__/webTool.unit.test.js +195 -195
  355. package/src/tools/__tests__/webTool.visionModel.test.js +75 -75
  356. package/src/tools/agentCommunicationTool.js +8 -10
  357. package/src/tools/agentDelayTool.js +496 -497
  358. package/src/tools/asyncToolManager.js +602 -603
  359. package/src/tools/baseTool.js +12 -11
  360. package/src/tools/cloneDetectionTool.js +576 -581
  361. package/src/tools/codeMapTool.js +0 -6
  362. package/src/tools/composioTool.js +617 -617
  363. package/src/tools/dependencyResolverTool.js +1211 -1212
  364. package/src/tools/desktop/DesktopTool.js +629 -638
  365. package/src/tools/desktop/__tests__/DesktopTool.e2e.test.js +306 -306
  366. package/src/tools/desktop/__tests__/DesktopTool.test.js +507 -507
  367. package/src/tools/desktop/__tests__/osController.test.js +364 -364
  368. package/src/tools/desktop/osController.js +491 -491
  369. package/src/tools/docxTool.js +623 -623
  370. package/src/tools/excelTool.js +636 -636
  371. package/src/tools/fileContentReplaceTool.js +5 -7
  372. package/src/tools/fileSystemTool.js +12 -19
  373. package/src/tools/fileTreeTool.js +840 -840
  374. package/src/tools/foundryWebSearchTool.js +273 -273
  375. package/src/tools/helpTool.js +198 -198
  376. package/src/tools/imageTool.js +1397 -1397
  377. package/src/tools/importAnalyzerTool.js +1056 -1056
  378. package/src/tools/jobDoneTool.js +495 -495
  379. package/src/tools/memoryTool.js +1 -1
  380. package/src/tools/office/pres/__tests__/presSystem.test.js +365 -365
  381. package/src/tools/office/pres/archetypes/agenda.js +61 -61
  382. package/src/tools/office/pres/archetypes/bentoGrid.js +218 -219
  383. package/src/tools/office/pres/archetypes/bigStat.js +140 -142
  384. package/src/tools/office/pres/archetypes/closing.js +70 -70
  385. package/src/tools/office/pres/archetypes/hero.js +70 -70
  386. package/src/tools/office/pres/archetypes/productHero.js +93 -94
  387. package/src/tools/office/pres/archetypes/table.js +73 -74
  388. package/src/tools/office/pres/backgrounds/orb.js +66 -66
  389. package/src/tools/office/pres/components.js +422 -423
  390. package/src/tools/officeTool.js +441 -441
  391. package/src/tools/pdfTool.js +625 -627
  392. package/src/tools/platformControlTool.js +1081 -1081
  393. package/src/tools/seekTool.js +917 -918
  394. package/src/tools/skillsTool.js +1 -1
  395. package/src/tools/staticAnalysisTool.js +2143 -2146
  396. package/src/tools/taskManagerTool.js +3324 -3324
  397. package/src/tools/terminalTool.js +2615 -2618
  398. package/src/tools/videoTool.js +1303 -1303
  399. package/src/tools/visionTool.js +508 -508
  400. package/src/tools/visualEditorTool.js +1289 -1290
  401. package/src/tools/webTool.js +3368 -3368
  402. package/src/tools/whatsappTool.js +464 -464
  403. package/src/types/__tests__/agent.test.js +499 -499
  404. package/src/types/__tests__/contextReference.test.js +606 -606
  405. package/src/types/__tests__/conversation.test.js +555 -555
  406. package/src/types/__tests__/toolCommand.test.js +584 -584
  407. package/src/types/contextReference.js +974 -971
  408. package/src/types/conversation.js +729 -729
  409. package/src/types/toolCommand.js +746 -746
  410. package/src/utilities/__tests__/attachmentValidator.test.js +80 -80
  411. package/src/utilities/__tests__/auditReport.test.js +328 -328
  412. package/src/utilities/__tests__/directoryAccessManager.test.js +388 -388
  413. package/src/utilities/__tests__/jsonRepair.test.js +103 -104
  414. package/src/utilities/__tests__/modeTransitionReasons.test.js +105 -105
  415. package/src/utilities/__tests__/platformUtils.test.js +80 -87
  416. package/src/utilities/__tests__/structuredFileValidator.test.js +261 -263
  417. package/src/utilities/__tests__/toolConstants.test.js +92 -94
  418. package/src/utilities/__tests__/useIsTouchDevice.detect.test.js +114 -114
  419. package/src/utilities/__tests__/webUiUtilSync.test.js +117 -117
  420. package/src/utilities/attachmentValidator.js +284 -288
  421. package/src/utilities/authCache.js.backup-1779570472481 +121 -121
  422. package/src/utilities/browserStealth.js +631 -630
  423. package/src/utilities/configManager.js +616 -617
  424. package/src/utilities/directoryAccessManager.js +564 -565
  425. package/src/utilities/fileProcessor.js +308 -307
  426. package/src/utilities/humanBehavior.js +454 -453
  427. package/src/utilities/logger.js +479 -479
  428. package/src/utilities/structuredFileValidator.js +696 -699
  429. package/src/utilities/tagParser.js +5 -10
  430. package/src/utilities/userDataDir.js +308 -308
  431. package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js.map +0 -1
  432. package/node_modules/@isaacs/brace-expansion/dist/esm/index.js.map +0 -1
  433. package/node_modules/minipass/LICENSE +0 -15
  434. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/LICENSE.md +0 -0
  435. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts +0 -0
  436. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts.map +0 -0
  437. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js +0 -0
  438. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js.map +0 -0
  439. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/package.json +0 -0
  440. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts +0 -0
  441. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts.map +0 -0
  442. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js +0 -0
  443. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js.map +0 -0
  444. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/package.json +0 -0
  445. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/LICENSE +0 -0
  446. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts +0 -0
  447. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts.map +0 -0
  448. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/package.json +0 -0
  449. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts +0 -0
  450. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts.map +0 -0
  451. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/package.json +0 -0
@@ -1,617 +1,617 @@
1
- /**
2
- * Composio Tool — third-party integration hub (Gmail, Slack, GitHub,
3
- * Linear, Notion, Drive, Sheets, Calendar, ~250 apps).
4
- *
5
- * Architecture: thin wrapper around the official `@composio/core` SDK
6
- * (v0.6.7+). The SDK is in optionalDependencies so the rest of the
7
- * runtime works even if it's not installed — agents without the
8
- * `composio` capability never see this tool.
9
- *
10
- * Auth model: per-user. We map agentId → Composio userId. Composio
11
- * holds the OAuth tokens server-side and auto-refreshes them. Our
12
- * code never sees raw tokens.
13
- *
14
- * API key: resolved by composioService.getClient() — first checks the
15
- * encrypted vault (where the web-ui's Integrations page saves it),
16
- * then falls back to process.env.COMPOSIO_API_KEY for ops who set it
17
- * out-of-band. Obtain at dashboard.composio.dev.
18
- *
19
- * Security note: Composio had a security incident in May 2026 that
20
- * leaked ~5K GitHub OAuth tokens. Key rotation should be automatable
21
- * on the operator's side (env-var refresh + restart). Keep this tool
22
- * GATED behind an explicit `composio` capability — never default-on.
23
- *
24
- * Plural-canonical envelope (cross-tool convention):
25
- * { toolId: "composio", actions: [{ type: "<op>", ...fields }] }
26
- * Singular {action,...} accepted via reverse-forgiveness.
27
- */
28
-
29
- import { BaseTool } from './baseTool.js';
30
- import { ComposioIndexer } from '../services/embeddings/indexers/composioIndexer.js';
31
- import * as composioService from '../services/composioService.js';
32
-
33
- const SUPPORTED_ACTIONS = [
34
- 'list-toolkits', // discover available integrations
35
- 'list-tools', // for a specific toolkit, list its action slugs + schemas
36
- 'connect', // generate a Connect Link URL for the user to OAuth into a toolkit
37
- 'connection-status', // check whether the current user has an active connection to a toolkit
38
- 'execute', // invoke a Composio action with arguments
39
- 'find-action', // semantic search over the Composio catalog (needs index)
40
- 'backfill-actions-index', // (re)build the catalog vector index for find-action
41
- ];
42
-
43
- class ComposioTool extends BaseTool {
44
- constructor(config = {}, logger = null) {
45
- super(config, logger);
46
- this.id = 'composio';
47
- this.requiresProject = false;
48
- this.isAsync = true;
49
- this.timeout = config.timeout || 60_000;
50
- // SDK client + key resolution are owned by composioService; we
51
- // don't keep our own instance cache here because the service's
52
- // cache is already keyed by API-key fingerprint and gets
53
- // invalidated when the UI rotates the key. Holding a parallel
54
- // cache here would mean "agent sees old key" after a rotation —
55
- // which is exactly the regression that caused the
56
- // "COMPOSIO_API_KEY env var is not set" bug report.
57
- }
58
-
59
- getDescription() {
60
- return `
61
- Composio Tool: third-party integration hub. Connects agents to ~250 apps
62
- (Gmail, Slack, GitHub, Linear, Notion, Drive, Sheets, Calendar, etc.)
63
- via Composio's hosted OAuth and unified action API.
64
-
65
- USAGE (plural canonical — same shape as other tools):
66
- \`\`\`json
67
- {
68
- "toolId": "composio",
69
- "actions": [
70
- {"type": "execute", "toolkitSlug": "github", "actionSlug": "GITHUB_CREATE_AN_ISSUE",
71
- "arguments": {"owner": "acme", "repo": "api", "title": "Bug X", "body": "..."}}
72
- ]
73
- }
74
- \`\`\`
75
-
76
- ACTIONS:
77
-
78
- 1. **list-toolkits** — discover integrations the org has enabled
79
- \`\`\`json
80
- {"toolId": "composio", "actions": [{"type": "list-toolkits"}]}
81
- \`\`\`
82
- Returns: { toolkits: [{slug, name, authConfigured, ...}] }
83
-
84
- 2. **list-tools** — list a toolkit's available actions + their schemas
85
- \`\`\`json
86
- {"toolId": "composio", "actions": [{"type": "list-tools", "toolkitSlug": "github"}]}
87
- \`\`\`
88
- Returns: { tools: [{slug, displayName, description, inputSchema, outputSchema}] }
89
-
90
- 3. **connect** — start an OAuth connection for the current user. Returns
91
- a Connect Link URL the user opens to complete the flow.
92
- \`\`\`json
93
- {"toolId": "composio", "actions": [{"type": "connect", "toolkitSlug": "github"}]}
94
- \`\`\`
95
- Returns: { connectLink: "https://connect.composio.dev/link/...", connectionId }
96
-
97
- 4. **connection-status** — has the current user connected this toolkit?
98
- \`\`\`json
99
- {"toolId": "composio", "actions": [{"type": "connection-status", "toolkitSlug": "github"}]}
100
- \`\`\`
101
- Returns: { connected: true|false, status: "ACTIVE"|"INITIATED"|"FAILED", connectionId? }
102
-
103
- 5. **execute** — invoke a Composio action
104
- \`\`\`json
105
- {
106
- "toolId": "composio",
107
- "actions": [{
108
- "type": "execute",
109
- "toolkitSlug": "slack",
110
- "actionSlug": "SLACK_SEND_MESSAGE",
111
- "arguments": {"channel": "#general", "text": "hello"}
112
- }]
113
- }
114
- \`\`\`
115
- Returns: { success: true|false, output: <upstream result>, error? }
116
-
117
- NOTES:
118
- - Each agent is mapped to a distinct Composio userId (agentId is reused).
119
- Tokens are isolated per-agent.
120
- - The org-level API key is resolved by composioService: encrypted vault
121
- first (saved via Settings → Integrations), then \`COMPOSIO_API_KEY\`
122
- env var as an operator fallback.
123
- - If neither source has a key OR the SDK isn't installed, every action
124
- returns a structured error so the agent can recover gracefully.
125
- - Composio holds OAuth tokens server-side. We never see raw tokens.
126
- `.trim();
127
- }
128
-
129
- getRequiredParameters() {
130
- // Validation happens per-action in customValidateParameters.
131
- return [];
132
- }
133
-
134
- customValidateParameters(params) {
135
- const errors = [];
136
- // Plural-canonical with singular fallback (reverse-forgiveness).
137
- // The forgiveness handles {action,...} → {actions:[{type,...}]}.
138
- let actions = Array.isArray(params?.actions) ? params.actions : null;
139
- if (!actions && params?.action) {
140
- const { action, parameters, ...rest } = params;
141
- actions = [{ type: action, ...(parameters && typeof parameters === 'object' ? parameters : {}), ...rest }];
142
- }
143
- if (!actions || actions.length === 0) {
144
- errors.push('action is required (or actions: [...] for batched calls)');
145
- return { valid: false, errors };
146
- }
147
- for (let i = 0; i < actions.length; i++) {
148
- const a = actions[i];
149
- const tag = actions.length > 1 ? `actions[${i}]: ` : '';
150
- const t = a?.type || a?.action;
151
- if (!t) {
152
- errors.push(`${tag}type is required`);
153
- continue;
154
- }
155
- if (!SUPPORTED_ACTIONS.includes(t)) {
156
- errors.push(`${tag}invalid type "${t}". Must be one of: ${SUPPORTED_ACTIONS.join(', ')}`);
157
- continue;
158
- }
159
- switch (t) {
160
- case 'list-tools':
161
- case 'connect':
162
- case 'connection-status':
163
- if (!a.toolkitSlug) errors.push(`${tag}toolkitSlug is required for ${t}`);
164
- break;
165
- case 'execute':
166
- if (!a.toolkitSlug) errors.push(`${tag}toolkitSlug is required for execute`);
167
- if (!a.actionSlug) errors.push(`${tag}actionSlug is required for execute`);
168
- break;
169
- case 'find-action':
170
- if (!a.query || typeof a.query !== 'string' || !a.query.trim()) {
171
- errors.push(`${tag}query is required for find-action`);
172
- }
173
- if (a.topK != null && (!Number.isInteger(a.topK) || a.topK <= 0)) {
174
- errors.push(`${tag}topK must be a positive integer when provided`);
175
- }
176
- break;
177
- // backfill-actions-index needs no params (full rebuild of the catalog).
178
- }
179
- }
180
- return { valid: errors.length === 0, errors };
181
- }
182
-
183
- /**
184
- * Delegate SDK loading to composioService — single source of truth for
185
- * key resolution (vault → env-var fallback) and client caching. The
186
- * service's cache invalidates on key rotation (UI save / forget), so
187
- * the next invocation here picks up the new key without a restart.
188
- *
189
- * Returns { client } on success or { error } on failure — never throws.
190
- * Tests can still stub this method to inject a mock client.
191
- */
192
- async _loadSdk() {
193
- return composioService.getClient();
194
- }
195
-
196
- async execute(params, context = {}) {
197
- // Reverse-forgiveness: accept both envelopes.
198
- const actions = this._normalizeToActions(params);
199
- if (actions.length === 0) {
200
- return { success: false, error: 'composio requires `action` (singular) or `actions` (plural array).' };
201
- }
202
-
203
- const userId = context.agentId || 'default-user';
204
- const { client, error: sdkErr } = await this._loadSdk();
205
- if (sdkErr) {
206
- return {
207
- success: false,
208
- error: sdkErr,
209
- hint: 'Add a Composio API key on the Integrations page (Settings → Integrations) or set COMPOSIO_API_KEY in the environment. Also ensure `@composio/core` is installed.',
210
- };
211
- }
212
-
213
- // Single-action: return its result directly (back-compat with other
214
- // tools' single-action shape). Multi-action: return batched results.
215
- if (actions.length === 1) {
216
- return await this._dispatch(client, userId, actions[0]);
217
- }
218
- const results = [];
219
- for (const a of actions) {
220
- try { results.push(await this._dispatch(client, userId, a)); }
221
- catch (err) { results.push({ success: false, action: a.type, error: err.message }); }
222
- }
223
- return {
224
- success: results.every(r => r.success !== false),
225
- batched: true,
226
- actionCount: results.length,
227
- results,
228
- };
229
- }
230
-
231
- /** @private */
232
- _normalizeToActions(params) {
233
- if (!params || typeof params !== 'object') return [];
234
- if (Array.isArray(params.actions)) {
235
- return params.actions.map(a => {
236
- if (a && !a.type && a.action) return { type: a.action, ...a };
237
- return a;
238
- });
239
- }
240
- if (params.action) {
241
- const { action, parameters, ...rest } = params;
242
- return [{ type: action, ...(parameters && typeof parameters === 'object' ? parameters : {}), ...rest }];
243
- }
244
- return [];
245
- }
246
-
247
- /**
248
- * Lazy ComposioIndexer accessor. Mirrors the memoryTool / reminisce
249
- * pattern: cached on the instance after first use; null when no
250
- * embedding service is attached to aiService.
251
- *
252
- * @private
253
- * @returns {ComposioIndexer | null}
254
- */
255
- _getComposioIndexer() {
256
- if (this._composioIndexer) return this._composioIndexer;
257
- const svc = this.aiService?.getEmbeddingService?.();
258
- if (!svc) return null;
259
- this._composioIndexer = new ComposioIndexer({ embeddingService: svc, logger: this.logger });
260
- return this._composioIndexer;
261
- }
262
-
263
- /**
264
- * Execute find-action: semantic search across the indexed catalog.
265
- * Returns ranked candidates with their toolkitSlug, actionSlug,
266
- * displayName, description, and inputSchema — everything the agent
267
- * needs to immediately call `execute` without a second round-trip.
268
- *
269
- * When the index is empty / stale, surfaces a clear hint to run
270
- * `backfill-actions-index` first.
271
- *
272
- * @private
273
- */
274
- async _executeFindAction(action, context = {}) {
275
- const query = String(action.query || '').trim();
276
- if (!query) {
277
- return { success: false, action: 'find-action', error: 'query is required' };
278
- }
279
- const topK = Number.isInteger(action.topK) && action.topK > 0 ? action.topK : 10;
280
- const hybrid = action.hybrid !== false;
281
- const toolkitSlug = typeof action.toolkitSlug === 'string' && action.toolkitSlug ? action.toolkitSlug : null;
282
- const userId = context.agentId || 'default-user';
283
-
284
- // Try the embedding path first. We DEGRADE GRACEFULLY: if embeddings
285
- // are missing, disabled, the index is empty, or search throws, we
286
- // fall back to a substring-match scan via the Composio SDK so the
287
- // agent always gets *something* useful. The fallback comes with a
288
- // warning so the agent + operator know quality is degraded and how
289
- // to upgrade it.
290
- const indexer = this._getComposioIndexer();
291
- const embeddingsLive = !!(indexer && indexer.isEnabled);
292
- // Track WHY we fell back so the warning is accurate. Three distinct
293
- // causes — each routes the operator to a different remediation.
294
- let fallbackCause = 'embeddings-unavailable';
295
- let embeddingErrorDetail = null;
296
-
297
- if (embeddingsLive) {
298
- fallbackCause = 'index-empty';
299
- let indexCount = 0;
300
- try {
301
- const stats = await indexer.stats();
302
- indexCount = stats?.count || 0;
303
- } catch { /* fall through to fallback */ }
304
-
305
- if (indexCount > 0) {
306
- try {
307
- const { hits, error } = await indexer.search(query, { topK, hybrid, toolkitSlug });
308
- if (!error) {
309
- return {
310
- success: true,
311
- action: 'find-action',
312
- mode: hybrid ? 'hybrid' : 'semantic',
313
- query, topK, toolkitSlug, hits,
314
- message: hits.length === 0
315
- ? `No actions matched "${query}"`
316
- : `Found ${hits.length} candidate actions ranked by similarity to "${query}".`,
317
- };
318
- }
319
- // Embedding search returned an error — fall through to substring.
320
- fallbackCause = 'embedding-search-failed';
321
- embeddingErrorDetail = error;
322
- this.logger?.warn?.('[composio] embedding find-action failed; falling back to substring', { error });
323
- } catch (err) {
324
- fallbackCause = 'embedding-search-failed';
325
- embeddingErrorDetail = err?.message || String(err);
326
- this.logger?.warn?.('[composio] embedding find-action threw; falling back to substring', { error: err?.message });
327
- }
328
- }
329
- // else: index empty → fall through with a "build me" hint.
330
- }
331
-
332
- // ─── Substring fallback ────────────────────────────────────────
333
- let reason, upgradeHint;
334
- if (fallbackCause === 'embeddings-unavailable') {
335
- reason = 'Embeddings unavailable.';
336
- upgradeHint = 'Enable an embedding provider in Settings → Embeddings for semantic ranking.';
337
- } else if (fallbackCause === 'index-empty') {
338
- reason = 'Catalog index is empty.';
339
- upgradeHint = 'Run `composio.backfill-actions-index` once to enable semantic ranking.';
340
- } else {
341
- // 'embedding-search-failed'
342
- reason = `Embedding search failed${embeddingErrorDetail ? ` (${embeddingErrorDetail})` : ''}.`;
343
- upgradeHint = 'Check the embedding provider status; using substring search in the meantime.';
344
- }
345
-
346
- const fallback = await this._substringFindAction(query, { topK, toolkitSlug, userId });
347
- return {
348
- ...fallback,
349
- warning: `${reason} Used substring search (lower quality). ${upgradeHint}`,
350
- fallbackCause,
351
- };
352
- }
353
-
354
- /**
355
- * Brute-force substring/keyword search across the Composio catalog.
356
- * Used when the embedding index is unavailable or empty so the agent
357
- * still gets ranked candidates.
358
- *
359
- * Strategy:
360
- * 1. List toolkits (or use the supplied toolkitSlug as the only one).
361
- * 2. Score each by token overlap with the query against name/slug/desc.
362
- * 3. For the top-5 toolkits (by score), list their tools and score
363
- * each tool the same way. Top-5 cap keeps API fan-out bounded —
364
- * a query against 250 toolkits should not produce 250 list-tools
365
- * calls in the worst case.
366
- * 4. Rank tools by score, return top-K with the same hit shape as
367
- * the embedding path (toolkitSlug, actionSlug, displayName,
368
- * description, inputSchema, score).
369
- *
370
- * @private
371
- */
372
- async _substringFindAction(query, { topK, toolkitSlug, userId }) {
373
- const tokenize = (s) => String(s || '')
374
- .toLowerCase()
375
- .split(/[\s_,.\-/()\[\]{}]+/)
376
- .filter(t => t.length > 1);
377
- const qTokens = tokenize(query);
378
- if (qTokens.length === 0) {
379
- return { success: true, action: 'find-action', mode: 'substring-fallback', query, topK, toolkitSlug, hits: [], message: 'No searchable tokens in query.' };
380
- }
381
- const scoreText = (text) => {
382
- const tTokens = tokenize(text);
383
- let s = 0;
384
- for (const q of qTokens) {
385
- for (const t of tTokens) {
386
- if (t === q) s += 3; // exact token match
387
- else if (t.includes(q) || q.includes(t)) s += 1; // substring either way
388
- }
389
- }
390
- return s;
391
- };
392
-
393
- // Step 1: get the candidate toolkit list.
394
- let toolkits;
395
- if (toolkitSlug) {
396
- toolkits = [{ slug: toolkitSlug, name: toolkitSlug }];
397
- } else {
398
- const tk = await composioService.listToolkits();
399
- if (!tk.success) {
400
- return { success: false, action: 'find-action', mode: 'substring-fallback', error: tk.error };
401
- }
402
- toolkits = tk.toolkits;
403
- }
404
-
405
- if (toolkits.length === 0) {
406
- return {
407
- success: true,
408
- action: 'find-action',
409
- mode: 'substring-fallback',
410
- query, topK, toolkitSlug,
411
- hits: [],
412
- message: 'Composio returned no toolkits to search.',
413
- };
414
- }
415
-
416
- // Step 2: rank toolkits, take top-5 (or all if toolkitSlug is pinned).
417
- const toolkitMaxFanout = toolkitSlug ? 1 : 5;
418
- const toolkitsScored = toolkits
419
- .map(tk => ({ tk, score: scoreText(`${tk.slug || ''} ${tk.name || ''} ${tk.description || ''}`) }))
420
- .sort((a, b) => b.score - a.score);
421
- // If at least one toolkit scored, scan those. Otherwise scan the first
422
- // N alphabetically as a last-ditch — the query may not match any
423
- // toolkit name but might match action names within a toolkit.
424
- const toolkitsToScan = (toolkitsScored[0]?.score > 0
425
- ? toolkitsScored.filter(x => x.score > 0)
426
- : toolkitsScored
427
- ).slice(0, toolkitMaxFanout);
428
-
429
- // Step 3: scan tools in those toolkits.
430
- const allHits = [];
431
- for (const { tk } of toolkitsToScan) {
432
- const r = await composioService.listTools(tk.slug, userId);
433
- if (!r.success) continue;
434
- for (const tool of (r.tools || [])) {
435
- const text = `${tool.slug || ''} ${tool.displayName || tool.name || ''} ${tool.description || ''}`;
436
- const score = scoreText(text);
437
- if (score > 0) {
438
- allHits.push({
439
- toolkitSlug: tk.slug,
440
- actionSlug: tool.slug,
441
- displayName: tool.displayName || tool.name || tool.slug,
442
- description: tool.description || '',
443
- inputSchema: tool.input || tool.parameters || tool.inputParameters || null,
444
- score,
445
- });
446
- }
447
- }
448
- }
449
- allHits.sort((a, b) => b.score - a.score);
450
- const hits = allHits.slice(0, topK);
451
-
452
- return {
453
- success: true,
454
- action: 'find-action',
455
- mode: 'substring-fallback',
456
- query, topK, toolkitSlug,
457
- hits,
458
- message: hits.length === 0
459
- ? `No actions matched "${query}" via substring search.`
460
- : `Found ${hits.length} candidate actions via substring search.`,
461
- };
462
- }
463
-
464
- /**
465
- * Execute backfill-actions-index: walk every Composio toolkit, fetch
466
- * its actions, embed them all in batched calls, write the global
467
- * catalog index. Drops any prior index first (full rebuild semantics).
468
- *
469
- * @private
470
- */
471
- async _executeBackfillActionsIndex(client, userId) {
472
- const indexer = this._getComposioIndexer();
473
- if (!indexer || !indexer.isEnabled) {
474
- return {
475
- success: false,
476
- action: 'backfill-actions-index',
477
- message: 'Embeddings are not enabled — nothing to backfill. Enable a provider in Settings → Embeddings first.',
478
- };
479
- }
480
-
481
- // Delegate to composioService — same code path everything else uses.
482
- // The previous implementation called `client.toolkits.list()` and
483
- // `client.tools.list()` directly, which silently returned undefined
484
- // because those methods don't exist on the @composio/core SDK
485
- // (it's `toolkits.get` / `tools.get`, and toolkits.get drops
486
- // pagination so even that wouldn't be enough). The service now
487
- // paginates via REST and returns the full ~1043-toolkit catalog.
488
- const tkResult = await composioService.listToolkits();
489
- if (!tkResult.success) {
490
- return {
491
- success: false,
492
- action: 'backfill-actions-index',
493
- error: `Could not list toolkits: ${tkResult.error}`,
494
- };
495
- }
496
- const toolkits = tkResult.toolkits;
497
- if (toolkits.length === 0) {
498
- return {
499
- success: false,
500
- action: 'backfill-actions-index',
501
- error: 'Composio returned no toolkits — check the Composio API key and that the org has at least one integration enabled.',
502
- };
503
- }
504
-
505
- const fetcher = async (toolkitSlug) => {
506
- const r = await composioService.listTools(toolkitSlug, userId);
507
- return r.success ? (r.tools || []) : [];
508
- };
509
-
510
- const r = await indexer.indexCatalog(toolkits, fetcher);
511
- return {
512
- success: r.ok !== false,
513
- action: 'backfill-actions-index',
514
- indexedActions: r.indexedActions || 0,
515
- indexedToolkits: r.indexedToolkits || 0,
516
- failedToolkits: r.failedToolkits || 0,
517
- ...(r.error ? { error: r.error } : {}),
518
- message: r.ok === false
519
- ? `Backfill failed: ${r.reason || r.error || 'unknown'}`
520
- : `Indexed ${r.indexedActions || 0} actions across ${r.indexedToolkits || 0} toolkits` +
521
- (r.failedToolkits ? ` (${r.failedToolkits} toolkits failed to load)` : '') + '.',
522
- };
523
- }
524
-
525
- /** @private */
526
- async _dispatch(client, userId, action) {
527
- const type = action.type || action.action;
528
- try {
529
- switch (type) {
530
- case 'list-toolkits': {
531
- // Delegate to composioService — single source of truth for
532
- // the SDK's paginated/wrapped response shape (`{items:[]}` vs
533
- // `{data:[]}` vs Array). Direct `await client.toolkits.list()`
534
- // here used to return the wrapper object as "the list" or
535
- // collapse to `[]` via the `|| []` fallback, which is how
536
- // agents saw "0 toolkits" even when integrations existed.
537
- const r = await composioService.listToolkits();
538
- return r.success
539
- ? { success: true, action: 'list-toolkits', toolkits: r.toolkits }
540
- : { success: false, action: 'list-toolkits', error: r.error };
541
- }
542
- case 'list-tools': {
543
- const r = await composioService.listTools(action.toolkitSlug, userId);
544
- return r.success
545
- ? { success: true, action: 'list-tools', toolkitSlug: action.toolkitSlug, tools: r.tools }
546
- : { success: false, action: 'list-tools', toolkitSlug: action.toolkitSlug, error: r.error };
547
- }
548
- case 'connect': {
549
- // composioService.connect maps the cryptic SDK error
550
- // "authConfigIds.0 should be a string, but you provided
551
- // undefined" into a human message that names the actual
552
- // remediation (go to Composio dashboard → Integrations,
553
- // add the toolkit's integration).
554
- const r = await composioService.connect(userId, action.toolkitSlug, {
555
- authConfigId: action.authConfigId,
556
- });
557
- return r.success
558
- ? {
559
- success: true,
560
- action: 'connect',
561
- connectLink: r.connectLink,
562
- connectionId: r.connectionId,
563
- raw: r.raw,
564
- }
565
- : {
566
- success: false,
567
- action: 'connect',
568
- error: r.error,
569
- ...(r.code ? { code: r.code } : {}),
570
- };
571
- }
572
- case 'connection-status': {
573
- // composioService.connectionStatus uses `.list({userId,
574
- // toolkit})` + client-side filter — the old `.get({…})`
575
- // path passed an object as a connectionId and got back the
576
- // "Path parameters result in path with invalid segments"
577
- // SDK error.
578
- const r = await composioService.connectionStatus(userId, action.toolkitSlug);
579
- return {
580
- success: r.success,
581
- action: 'connection-status',
582
- toolkitSlug: action.toolkitSlug,
583
- connected: r.connected,
584
- status: r.status,
585
- connectionId: r.connectionId,
586
- ...(r.error ? { error: r.error } : {}),
587
- };
588
- }
589
- case 'execute': {
590
- const result = await client.tools?.execute?.(action.actionSlug, {
591
- userId,
592
- arguments: action.arguments || {},
593
- });
594
- return {
595
- success: result?.successful !== false,
596
- action: 'execute',
597
- actionSlug: action.actionSlug,
598
- toolkitSlug: action.toolkitSlug,
599
- output: result?.data ?? result?.output ?? result,
600
- error: result?.error || null,
601
- };
602
- }
603
- case 'find-action':
604
- return await this._executeFindAction(action, { agentId: userId });
605
- case 'backfill-actions-index':
606
- return await this._executeBackfillActionsIndex(client, userId);
607
- default:
608
- return { success: false, error: `Unknown composio action: ${type}` };
609
- }
610
- } catch (err) {
611
- this.logger?.error?.('[ComposioTool] action failed', { type, userId, error: err.message });
612
- return { success: false, action: type, error: err.message };
613
- }
614
- }
615
- }
616
-
617
- export default ComposioTool;
1
+ /**
2
+ * Composio Tool — third-party integration hub (Gmail, Slack, GitHub,
3
+ * Linear, Notion, Drive, Sheets, Calendar, ~250 apps).
4
+ *
5
+ * Architecture: thin wrapper around the official `@composio/core` SDK
6
+ * (v0.6.7+). The SDK is in optionalDependencies so the rest of the
7
+ * runtime works even if it's not installed — agents without the
8
+ * `composio` capability never see this tool.
9
+ *
10
+ * Auth model: per-user. We map agentId → Composio userId. Composio
11
+ * holds the OAuth tokens server-side and auto-refreshes them. Our
12
+ * code never sees raw tokens.
13
+ *
14
+ * API key: resolved by composioService.getClient() — first checks the
15
+ * encrypted vault (where the web-ui's Integrations page saves it),
16
+ * then falls back to process.env.COMPOSIO_API_KEY for ops who set it
17
+ * out-of-band. Obtain at dashboard.composio.dev.
18
+ *
19
+ * Security note: Composio had a security incident in May 2026 that
20
+ * leaked ~5K GitHub OAuth tokens. Key rotation should be automatable
21
+ * on the operator's side (env-var refresh + restart). Keep this tool
22
+ * GATED behind an explicit `composio` capability — never default-on.
23
+ *
24
+ * Plural-canonical envelope (cross-tool convention):
25
+ * { toolId: "composio", actions: [{ type: "<op>", ...fields }] }
26
+ * Singular {action,...} accepted via reverse-forgiveness.
27
+ */
28
+
29
+ import { BaseTool } from './baseTool.js';
30
+ import { ComposioIndexer } from '../services/embeddings/indexers/composioIndexer.js';
31
+ import * as composioService from '../services/composioService.js';
32
+
33
+ const SUPPORTED_ACTIONS = [
34
+ 'list-toolkits', // discover available integrations
35
+ 'list-tools', // for a specific toolkit, list its action slugs + schemas
36
+ 'connect', // generate a Connect Link URL for the user to OAuth into a toolkit
37
+ 'connection-status', // check whether the current user has an active connection to a toolkit
38
+ 'execute', // invoke a Composio action with arguments
39
+ 'find-action', // semantic search over the Composio catalog (needs index)
40
+ 'backfill-actions-index', // (re)build the catalog vector index for find-action
41
+ ];
42
+
43
+ class ComposioTool extends BaseTool {
44
+ constructor(config = {}, logger = null) {
45
+ super(config, logger);
46
+ this.id = 'composio';
47
+ this.requiresProject = false;
48
+ this.isAsync = true;
49
+ this.timeout = config.timeout || 60_000;
50
+ // SDK client + key resolution are owned by composioService; we
51
+ // don't keep our own instance cache here because the service's
52
+ // cache is already keyed by API-key fingerprint and gets
53
+ // invalidated when the UI rotates the key. Holding a parallel
54
+ // cache here would mean "agent sees old key" after a rotation —
55
+ // which is exactly the regression that caused the
56
+ // "COMPOSIO_API_KEY env var is not set" bug report.
57
+ }
58
+
59
+ getDescription() {
60
+ return `
61
+ Composio Tool: third-party integration hub. Connects agents to ~250 apps
62
+ (Gmail, Slack, GitHub, Linear, Notion, Drive, Sheets, Calendar, etc.)
63
+ via Composio's hosted OAuth and unified action API.
64
+
65
+ USAGE (plural canonical — same shape as other tools):
66
+ \`\`\`json
67
+ {
68
+ "toolId": "composio",
69
+ "actions": [
70
+ {"type": "execute", "toolkitSlug": "github", "actionSlug": "GITHUB_CREATE_AN_ISSUE",
71
+ "arguments": {"owner": "acme", "repo": "api", "title": "Bug X", "body": "..."}}
72
+ ]
73
+ }
74
+ \`\`\`
75
+
76
+ ACTIONS:
77
+
78
+ 1. **list-toolkits** — discover integrations the org has enabled
79
+ \`\`\`json
80
+ {"toolId": "composio", "actions": [{"type": "list-toolkits"}]}
81
+ \`\`\`
82
+ Returns: { toolkits: [{slug, name, authConfigured, ...}] }
83
+
84
+ 2. **list-tools** — list a toolkit's available actions + their schemas
85
+ \`\`\`json
86
+ {"toolId": "composio", "actions": [{"type": "list-tools", "toolkitSlug": "github"}]}
87
+ \`\`\`
88
+ Returns: { tools: [{slug, displayName, description, inputSchema, outputSchema}] }
89
+
90
+ 3. **connect** — start an OAuth connection for the current user. Returns
91
+ a Connect Link URL the user opens to complete the flow.
92
+ \`\`\`json
93
+ {"toolId": "composio", "actions": [{"type": "connect", "toolkitSlug": "github"}]}
94
+ \`\`\`
95
+ Returns: { connectLink: "https://connect.composio.dev/link/...", connectionId }
96
+
97
+ 4. **connection-status** — has the current user connected this toolkit?
98
+ \`\`\`json
99
+ {"toolId": "composio", "actions": [{"type": "connection-status", "toolkitSlug": "github"}]}
100
+ \`\`\`
101
+ Returns: { connected: true|false, status: "ACTIVE"|"INITIATED"|"FAILED", connectionId? }
102
+
103
+ 5. **execute** — invoke a Composio action
104
+ \`\`\`json
105
+ {
106
+ "toolId": "composio",
107
+ "actions": [{
108
+ "type": "execute",
109
+ "toolkitSlug": "slack",
110
+ "actionSlug": "SLACK_SEND_MESSAGE",
111
+ "arguments": {"channel": "#general", "text": "hello"}
112
+ }]
113
+ }
114
+ \`\`\`
115
+ Returns: { success: true|false, output: <upstream result>, error? }
116
+
117
+ NOTES:
118
+ - Each agent is mapped to a distinct Composio userId (agentId is reused).
119
+ Tokens are isolated per-agent.
120
+ - The org-level API key is resolved by composioService: encrypted vault
121
+ first (saved via Settings → Integrations), then \`COMPOSIO_API_KEY\`
122
+ env var as an operator fallback.
123
+ - If neither source has a key OR the SDK isn't installed, every action
124
+ returns a structured error so the agent can recover gracefully.
125
+ - Composio holds OAuth tokens server-side. We never see raw tokens.
126
+ `.trim();
127
+ }
128
+
129
+ getRequiredParameters() {
130
+ // Validation happens per-action in customValidateParameters.
131
+ return [];
132
+ }
133
+
134
+ customValidateParameters(params) {
135
+ const errors = [];
136
+ // Plural-canonical with singular fallback (reverse-forgiveness).
137
+ // The forgiveness handles {action,...} → {actions:[{type,...}]}.
138
+ let actions = Array.isArray(params?.actions) ? params.actions : null;
139
+ if (!actions && params?.action) {
140
+ const { action, parameters, ...rest } = params;
141
+ actions = [{ type: action, ...(parameters && typeof parameters === 'object' ? parameters : {}), ...rest }];
142
+ }
143
+ if (!actions || actions.length === 0) {
144
+ errors.push('action is required (or actions: [...] for batched calls)');
145
+ return { valid: false, errors };
146
+ }
147
+ for (let i = 0; i < actions.length; i++) {
148
+ const a = actions[i];
149
+ const tag = actions.length > 1 ? `actions[${i}]: ` : '';
150
+ const t = a?.type || a?.action;
151
+ if (!t) {
152
+ errors.push(`${tag}type is required`);
153
+ continue;
154
+ }
155
+ if (!SUPPORTED_ACTIONS.includes(t)) {
156
+ errors.push(`${tag}invalid type "${t}". Must be one of: ${SUPPORTED_ACTIONS.join(', ')}`);
157
+ continue;
158
+ }
159
+ switch (t) {
160
+ case 'list-tools':
161
+ case 'connect':
162
+ case 'connection-status':
163
+ if (!a.toolkitSlug) errors.push(`${tag}toolkitSlug is required for ${t}`);
164
+ break;
165
+ case 'execute':
166
+ if (!a.toolkitSlug) errors.push(`${tag}toolkitSlug is required for execute`);
167
+ if (!a.actionSlug) errors.push(`${tag}actionSlug is required for execute`);
168
+ break;
169
+ case 'find-action':
170
+ if (!a.query || typeof a.query !== 'string' || !a.query.trim()) {
171
+ errors.push(`${tag}query is required for find-action`);
172
+ }
173
+ if (a.topK != null && (!Number.isInteger(a.topK) || a.topK <= 0)) {
174
+ errors.push(`${tag}topK must be a positive integer when provided`);
175
+ }
176
+ break;
177
+ // backfill-actions-index needs no params (full rebuild of the catalog).
178
+ }
179
+ }
180
+ return { valid: errors.length === 0, errors };
181
+ }
182
+
183
+ /**
184
+ * Delegate SDK loading to composioService — single source of truth for
185
+ * key resolution (vault → env-var fallback) and client caching. The
186
+ * service's cache invalidates on key rotation (UI save / forget), so
187
+ * the next invocation here picks up the new key without a restart.
188
+ *
189
+ * Returns { client } on success or { error } on failure — never throws.
190
+ * Tests can still stub this method to inject a mock client.
191
+ */
192
+ async _loadSdk() {
193
+ return composioService.getClient();
194
+ }
195
+
196
+ async execute(params, context = {}) {
197
+ // Reverse-forgiveness: accept both envelopes.
198
+ const actions = this._normalizeToActions(params);
199
+ if (actions.length === 0) {
200
+ return { success: false, error: 'composio requires `action` (singular) or `actions` (plural array).' };
201
+ }
202
+
203
+ const userId = context.agentId || 'default-user';
204
+ const { client, error: sdkErr } = await this._loadSdk();
205
+ if (sdkErr) {
206
+ return {
207
+ success: false,
208
+ error: sdkErr,
209
+ hint: 'Add a Composio API key on the Integrations page (Settings → Integrations) or set COMPOSIO_API_KEY in the environment. Also ensure `@composio/core` is installed.',
210
+ };
211
+ }
212
+
213
+ // Single-action: return its result directly (back-compat with other
214
+ // tools' single-action shape). Multi-action: return batched results.
215
+ if (actions.length === 1) {
216
+ return await this._dispatch(client, userId, actions[0]);
217
+ }
218
+ const results = [];
219
+ for (const a of actions) {
220
+ try { results.push(await this._dispatch(client, userId, a)); }
221
+ catch (err) { results.push({ success: false, action: a.type, error: err.message }); }
222
+ }
223
+ return {
224
+ success: results.every(r => r.success !== false),
225
+ batched: true,
226
+ actionCount: results.length,
227
+ results,
228
+ };
229
+ }
230
+
231
+ /** @private */
232
+ _normalizeToActions(params) {
233
+ if (!params || typeof params !== 'object') return [];
234
+ if (Array.isArray(params.actions)) {
235
+ return params.actions.map(a => {
236
+ if (a && !a.type && a.action) return { type: a.action, ...a };
237
+ return a;
238
+ });
239
+ }
240
+ if (params.action) {
241
+ const { action, parameters, ...rest } = params;
242
+ return [{ type: action, ...(parameters && typeof parameters === 'object' ? parameters : {}), ...rest }];
243
+ }
244
+ return [];
245
+ }
246
+
247
+ /**
248
+ * Lazy ComposioIndexer accessor. Mirrors the memoryTool / reminisce
249
+ * pattern: cached on the instance after first use; null when no
250
+ * embedding service is attached to aiService.
251
+ *
252
+ * @private
253
+ * @returns {ComposioIndexer | null}
254
+ */
255
+ _getComposioIndexer() {
256
+ if (this._composioIndexer) return this._composioIndexer;
257
+ const svc = this.aiService?.getEmbeddingService?.();
258
+ if (!svc) return null;
259
+ this._composioIndexer = new ComposioIndexer({ embeddingService: svc, logger: this.logger });
260
+ return this._composioIndexer;
261
+ }
262
+
263
+ /**
264
+ * Execute find-action: semantic search across the indexed catalog.
265
+ * Returns ranked candidates with their toolkitSlug, actionSlug,
266
+ * displayName, description, and inputSchema — everything the agent
267
+ * needs to immediately call `execute` without a second round-trip.
268
+ *
269
+ * When the index is empty / stale, surfaces a clear hint to run
270
+ * `backfill-actions-index` first.
271
+ *
272
+ * @private
273
+ */
274
+ async _executeFindAction(action, context = {}) {
275
+ const query = String(action.query || '').trim();
276
+ if (!query) {
277
+ return { success: false, action: 'find-action', error: 'query is required' };
278
+ }
279
+ const topK = Number.isInteger(action.topK) && action.topK > 0 ? action.topK : 10;
280
+ const hybrid = action.hybrid !== false;
281
+ const toolkitSlug = typeof action.toolkitSlug === 'string' && action.toolkitSlug ? action.toolkitSlug : null;
282
+ const userId = context.agentId || 'default-user';
283
+
284
+ // Try the embedding path first. We DEGRADE GRACEFULLY: if embeddings
285
+ // are missing, disabled, the index is empty, or search throws, we
286
+ // fall back to a substring-match scan via the Composio SDK so the
287
+ // agent always gets *something* useful. The fallback comes with a
288
+ // warning so the agent + operator know quality is degraded and how
289
+ // to upgrade it.
290
+ const indexer = this._getComposioIndexer();
291
+ const embeddingsLive = !!(indexer && indexer.isEnabled);
292
+ // Track WHY we fell back so the warning is accurate. Three distinct
293
+ // causes — each routes the operator to a different remediation.
294
+ let fallbackCause = 'embeddings-unavailable';
295
+ let embeddingErrorDetail = null;
296
+
297
+ if (embeddingsLive) {
298
+ fallbackCause = 'index-empty';
299
+ let indexCount = 0;
300
+ try {
301
+ const stats = await indexer.stats();
302
+ indexCount = stats?.count || 0;
303
+ } catch { /* fall through to fallback */ }
304
+
305
+ if (indexCount > 0) {
306
+ try {
307
+ const { hits, error } = await indexer.search(query, { topK, hybrid, toolkitSlug });
308
+ if (!error) {
309
+ return {
310
+ success: true,
311
+ action: 'find-action',
312
+ mode: hybrid ? 'hybrid' : 'semantic',
313
+ query, topK, toolkitSlug, hits,
314
+ message: hits.length === 0
315
+ ? `No actions matched "${query}"`
316
+ : `Found ${hits.length} candidate actions ranked by similarity to "${query}".`,
317
+ };
318
+ }
319
+ // Embedding search returned an error — fall through to substring.
320
+ fallbackCause = 'embedding-search-failed';
321
+ embeddingErrorDetail = error;
322
+ this.logger?.warn?.('[composio] embedding find-action failed; falling back to substring', { error });
323
+ } catch (err) {
324
+ fallbackCause = 'embedding-search-failed';
325
+ embeddingErrorDetail = err?.message || String(err);
326
+ this.logger?.warn?.('[composio] embedding find-action threw; falling back to substring', { error: err?.message });
327
+ }
328
+ }
329
+ // else: index empty → fall through with a "build me" hint.
330
+ }
331
+
332
+ // ─── Substring fallback ────────────────────────────────────────
333
+ let reason, upgradeHint;
334
+ if (fallbackCause === 'embeddings-unavailable') {
335
+ reason = 'Embeddings unavailable.';
336
+ upgradeHint = 'Enable an embedding provider in Settings → Embeddings for semantic ranking.';
337
+ } else if (fallbackCause === 'index-empty') {
338
+ reason = 'Catalog index is empty.';
339
+ upgradeHint = 'Run `composio.backfill-actions-index` once to enable semantic ranking.';
340
+ } else {
341
+ // 'embedding-search-failed'
342
+ reason = `Embedding search failed${embeddingErrorDetail ? ` (${embeddingErrorDetail})` : ''}.`;
343
+ upgradeHint = 'Check the embedding provider status; using substring search in the meantime.';
344
+ }
345
+
346
+ const fallback = await this._substringFindAction(query, { topK, toolkitSlug, userId });
347
+ return {
348
+ ...fallback,
349
+ warning: `${reason} Used substring search (lower quality). ${upgradeHint}`,
350
+ fallbackCause,
351
+ };
352
+ }
353
+
354
+ /**
355
+ * Brute-force substring/keyword search across the Composio catalog.
356
+ * Used when the embedding index is unavailable or empty so the agent
357
+ * still gets ranked candidates.
358
+ *
359
+ * Strategy:
360
+ * 1. List toolkits (or use the supplied toolkitSlug as the only one).
361
+ * 2. Score each by token overlap with the query against name/slug/desc.
362
+ * 3. For the top-5 toolkits (by score), list their tools and score
363
+ * each tool the same way. Top-5 cap keeps API fan-out bounded —
364
+ * a query against 250 toolkits should not produce 250 list-tools
365
+ * calls in the worst case.
366
+ * 4. Rank tools by score, return top-K with the same hit shape as
367
+ * the embedding path (toolkitSlug, actionSlug, displayName,
368
+ * description, inputSchema, score).
369
+ *
370
+ * @private
371
+ */
372
+ async _substringFindAction(query, { topK, toolkitSlug, userId }) {
373
+ const tokenize = (s) => String(s || '')
374
+ .toLowerCase()
375
+ .split(/[\s_,.\-/()[\]{}]+/)
376
+ .filter(t => t.length > 1);
377
+ const qTokens = tokenize(query);
378
+ if (qTokens.length === 0) {
379
+ return { success: true, action: 'find-action', mode: 'substring-fallback', query, topK, toolkitSlug, hits: [], message: 'No searchable tokens in query.' };
380
+ }
381
+ const scoreText = (text) => {
382
+ const tTokens = tokenize(text);
383
+ let s = 0;
384
+ for (const q of qTokens) {
385
+ for (const t of tTokens) {
386
+ if (t === q) s += 3; // exact token match
387
+ else if (t.includes(q) || q.includes(t)) s += 1; // substring either way
388
+ }
389
+ }
390
+ return s;
391
+ };
392
+
393
+ // Step 1: get the candidate toolkit list.
394
+ let toolkits;
395
+ if (toolkitSlug) {
396
+ toolkits = [{ slug: toolkitSlug, name: toolkitSlug }];
397
+ } else {
398
+ const tk = await composioService.listToolkits();
399
+ if (!tk.success) {
400
+ return { success: false, action: 'find-action', mode: 'substring-fallback', error: tk.error };
401
+ }
402
+ toolkits = tk.toolkits;
403
+ }
404
+
405
+ if (toolkits.length === 0) {
406
+ return {
407
+ success: true,
408
+ action: 'find-action',
409
+ mode: 'substring-fallback',
410
+ query, topK, toolkitSlug,
411
+ hits: [],
412
+ message: 'Composio returned no toolkits to search.',
413
+ };
414
+ }
415
+
416
+ // Step 2: rank toolkits, take top-5 (or all if toolkitSlug is pinned).
417
+ const toolkitMaxFanout = toolkitSlug ? 1 : 5;
418
+ const toolkitsScored = toolkits
419
+ .map(tk => ({ tk, score: scoreText(`${tk.slug || ''} ${tk.name || ''} ${tk.description || ''}`) }))
420
+ .sort((a, b) => b.score - a.score);
421
+ // If at least one toolkit scored, scan those. Otherwise scan the first
422
+ // N alphabetically as a last-ditch — the query may not match any
423
+ // toolkit name but might match action names within a toolkit.
424
+ const toolkitsToScan = (toolkitsScored[0]?.score > 0
425
+ ? toolkitsScored.filter(x => x.score > 0)
426
+ : toolkitsScored
427
+ ).slice(0, toolkitMaxFanout);
428
+
429
+ // Step 3: scan tools in those toolkits.
430
+ const allHits = [];
431
+ for (const { tk } of toolkitsToScan) {
432
+ const r = await composioService.listTools(tk.slug, userId);
433
+ if (!r.success) continue;
434
+ for (const tool of (r.tools || [])) {
435
+ const text = `${tool.slug || ''} ${tool.displayName || tool.name || ''} ${tool.description || ''}`;
436
+ const score = scoreText(text);
437
+ if (score > 0) {
438
+ allHits.push({
439
+ toolkitSlug: tk.slug,
440
+ actionSlug: tool.slug,
441
+ displayName: tool.displayName || tool.name || tool.slug,
442
+ description: tool.description || '',
443
+ inputSchema: tool.input || tool.parameters || tool.inputParameters || null,
444
+ score,
445
+ });
446
+ }
447
+ }
448
+ }
449
+ allHits.sort((a, b) => b.score - a.score);
450
+ const hits = allHits.slice(0, topK);
451
+
452
+ return {
453
+ success: true,
454
+ action: 'find-action',
455
+ mode: 'substring-fallback',
456
+ query, topK, toolkitSlug,
457
+ hits,
458
+ message: hits.length === 0
459
+ ? `No actions matched "${query}" via substring search.`
460
+ : `Found ${hits.length} candidate actions via substring search.`,
461
+ };
462
+ }
463
+
464
+ /**
465
+ * Execute backfill-actions-index: walk every Composio toolkit, fetch
466
+ * its actions, embed them all in batched calls, write the global
467
+ * catalog index. Drops any prior index first (full rebuild semantics).
468
+ *
469
+ * @private
470
+ */
471
+ async _executeBackfillActionsIndex(client, userId) {
472
+ const indexer = this._getComposioIndexer();
473
+ if (!indexer || !indexer.isEnabled) {
474
+ return {
475
+ success: false,
476
+ action: 'backfill-actions-index',
477
+ message: 'Embeddings are not enabled — nothing to backfill. Enable a provider in Settings → Embeddings first.',
478
+ };
479
+ }
480
+
481
+ // Delegate to composioService — same code path everything else uses.
482
+ // The previous implementation called `client.toolkits.list()` and
483
+ // `client.tools.list()` directly, which silently returned undefined
484
+ // because those methods don't exist on the @composio/core SDK
485
+ // (it's `toolkits.get` / `tools.get`, and toolkits.get drops
486
+ // pagination so even that wouldn't be enough). The service now
487
+ // paginates via REST and returns the full ~1043-toolkit catalog.
488
+ const tkResult = await composioService.listToolkits();
489
+ if (!tkResult.success) {
490
+ return {
491
+ success: false,
492
+ action: 'backfill-actions-index',
493
+ error: `Could not list toolkits: ${tkResult.error}`,
494
+ };
495
+ }
496
+ const toolkits = tkResult.toolkits;
497
+ if (toolkits.length === 0) {
498
+ return {
499
+ success: false,
500
+ action: 'backfill-actions-index',
501
+ error: 'Composio returned no toolkits — check the Composio API key and that the org has at least one integration enabled.',
502
+ };
503
+ }
504
+
505
+ const fetcher = async (toolkitSlug) => {
506
+ const r = await composioService.listTools(toolkitSlug, userId);
507
+ return r.success ? (r.tools || []) : [];
508
+ };
509
+
510
+ const r = await indexer.indexCatalog(toolkits, fetcher);
511
+ return {
512
+ success: r.ok !== false,
513
+ action: 'backfill-actions-index',
514
+ indexedActions: r.indexedActions || 0,
515
+ indexedToolkits: r.indexedToolkits || 0,
516
+ failedToolkits: r.failedToolkits || 0,
517
+ ...(r.error ? { error: r.error } : {}),
518
+ message: r.ok === false
519
+ ? `Backfill failed: ${r.reason || r.error || 'unknown'}`
520
+ : `Indexed ${r.indexedActions || 0} actions across ${r.indexedToolkits || 0} toolkits` +
521
+ (r.failedToolkits ? ` (${r.failedToolkits} toolkits failed to load)` : '') + '.',
522
+ };
523
+ }
524
+
525
+ /** @private */
526
+ async _dispatch(client, userId, action) {
527
+ const type = action.type || action.action;
528
+ try {
529
+ switch (type) {
530
+ case 'list-toolkits': {
531
+ // Delegate to composioService — single source of truth for
532
+ // the SDK's paginated/wrapped response shape (`{items:[]}` vs
533
+ // `{data:[]}` vs Array). Direct `await client.toolkits.list()`
534
+ // here used to return the wrapper object as "the list" or
535
+ // collapse to `[]` via the `|| []` fallback, which is how
536
+ // agents saw "0 toolkits" even when integrations existed.
537
+ const r = await composioService.listToolkits();
538
+ return r.success
539
+ ? { success: true, action: 'list-toolkits', toolkits: r.toolkits }
540
+ : { success: false, action: 'list-toolkits', error: r.error };
541
+ }
542
+ case 'list-tools': {
543
+ const r = await composioService.listTools(action.toolkitSlug, userId);
544
+ return r.success
545
+ ? { success: true, action: 'list-tools', toolkitSlug: action.toolkitSlug, tools: r.tools }
546
+ : { success: false, action: 'list-tools', toolkitSlug: action.toolkitSlug, error: r.error };
547
+ }
548
+ case 'connect': {
549
+ // composioService.connect maps the cryptic SDK error
550
+ // "authConfigIds.0 should be a string, but you provided
551
+ // undefined" into a human message that names the actual
552
+ // remediation (go to Composio dashboard → Integrations,
553
+ // add the toolkit's integration).
554
+ const r = await composioService.connect(userId, action.toolkitSlug, {
555
+ authConfigId: action.authConfigId,
556
+ });
557
+ return r.success
558
+ ? {
559
+ success: true,
560
+ action: 'connect',
561
+ connectLink: r.connectLink,
562
+ connectionId: r.connectionId,
563
+ raw: r.raw,
564
+ }
565
+ : {
566
+ success: false,
567
+ action: 'connect',
568
+ error: r.error,
569
+ ...(r.code ? { code: r.code } : {}),
570
+ };
571
+ }
572
+ case 'connection-status': {
573
+ // composioService.connectionStatus uses `.list({userId,
574
+ // toolkit})` + client-side filter — the old `.get({…})`
575
+ // path passed an object as a connectionId and got back the
576
+ // "Path parameters result in path with invalid segments"
577
+ // SDK error.
578
+ const r = await composioService.connectionStatus(userId, action.toolkitSlug);
579
+ return {
580
+ success: r.success,
581
+ action: 'connection-status',
582
+ toolkitSlug: action.toolkitSlug,
583
+ connected: r.connected,
584
+ status: r.status,
585
+ connectionId: r.connectionId,
586
+ ...(r.error ? { error: r.error } : {}),
587
+ };
588
+ }
589
+ case 'execute': {
590
+ const result = await client.tools?.execute?.(action.actionSlug, {
591
+ userId,
592
+ arguments: action.arguments || {},
593
+ });
594
+ return {
595
+ success: result?.successful !== false,
596
+ action: 'execute',
597
+ actionSlug: action.actionSlug,
598
+ toolkitSlug: action.toolkitSlug,
599
+ output: result?.data ?? result?.output ?? result,
600
+ error: result?.error || null,
601
+ };
602
+ }
603
+ case 'find-action':
604
+ return await this._executeFindAction(action, { agentId: userId });
605
+ case 'backfill-actions-index':
606
+ return await this._executeBackfillActionsIndex(client, userId);
607
+ default:
608
+ return { success: false, error: `Unknown composio action: ${type}` };
609
+ }
610
+ } catch (err) {
611
+ this.logger?.error?.('[ComposioTool] action failed', { type, userId, error: err.message });
612
+ return { success: false, action: type, error: err.message };
613
+ }
614
+ }
615
+ }
616
+
617
+ export default ComposioTool;