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.
- package/node_modules/glob/README.md +31 -5
- package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
- package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
- package/node_modules/glob/dist/commonjs/glob.js +2 -1
- package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
- package/node_modules/glob/dist/commonjs/index.min.js +3 -3
- package/node_modules/glob/dist/commonjs/index.min.js.map +4 -4
- package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
- package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
- package/node_modules/glob/dist/commonjs/pattern.js +4 -0
- package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
- package/node_modules/glob/dist/esm/glob.d.ts +8 -0
- package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
- package/node_modules/glob/dist/esm/glob.js +2 -1
- package/node_modules/glob/dist/esm/glob.js.map +1 -1
- package/node_modules/glob/dist/esm/index.min.js +3 -3
- package/node_modules/glob/dist/esm/index.min.js.map +4 -4
- package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
- package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
- package/node_modules/glob/dist/esm/pattern.js +4 -0
- package/node_modules/glob/dist/esm/pattern.js.map +1 -1
- package/node_modules/{@isaacs → glob/node_modules}/balanced-match/README.md +7 -10
- package/node_modules/{@isaacs → glob/node_modules}/balanced-match/package.json +7 -18
- package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/README.md +3 -6
- package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.js +6 -4
- package/node_modules/glob/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -0
- package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.js +6 -4
- package/node_modules/glob/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
- package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/package.json +11 -7
- package/node_modules/glob/node_modules/minimatch/README.md +76 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts +4 -2
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js +309 -55
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js +2 -4
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js +4 -4
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts +81 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js +232 -134
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js +8 -8
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts +4 -2
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js +309 -55
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js +2 -4
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js +4 -4
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts +81 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.js +232 -134
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js +8 -8
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/package.json +17 -11
- package/node_modules/glob/package.json +10 -13
- package/node_modules/minipass/LICENSE.md +55 -0
- package/node_modules/minipass/dist/commonjs/index.d.ts +12 -16
- package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/minipass/dist/commonjs/index.js +13 -3
- package/node_modules/minipass/dist/commonjs/index.js.map +1 -1
- package/node_modules/minipass/dist/esm/index.d.ts +12 -16
- package/node_modules/minipass/dist/esm/index.d.ts.map +1 -1
- package/node_modules/minipass/dist/esm/index.js +3 -1
- package/node_modules/minipass/dist/esm/index.js.map +1 -1
- package/node_modules/minipass/package.json +9 -14
- package/node_modules/path-scurry/node_modules/lru-cache/README.md +96 -10
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts +1400 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js +1726 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.cjs.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts +109 -32
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +334 -197
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js.map +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js.map +4 -4
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts +1400 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js +1726 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts +1400 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js +1722 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.mjs.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts +109 -32
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +333 -196
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js.map +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js.map +4 -4
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts +1400 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js +1722 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/package.json +71 -18
- package/node_modules/path-scurry/package.json +8 -24
- package/package.json +1 -1
- package/scripts/debug-balance-probe.mjs +35 -35
- package/scripts/push-image.sh +43 -43
- package/scripts/setup-acr.sh +65 -65
- package/scripts/verify-optional-deps.js +96 -1
- package/src/__tests__/composioCliFlags.test.js +239 -239
- package/src/analyzers/CSSAnalyzer.js +298 -297
- package/src/analyzers/ConfigValidator.js +691 -690
- package/src/analyzers/ESLintAnalyzer.js +320 -320
- package/src/analyzers/JavaScriptAnalyzer.js +260 -261
- package/src/analyzers/PrettierFormatter.js +246 -247
- package/src/analyzers/PythonAnalyzer.js +283 -283
- package/src/analyzers/SecurityAnalyzer.js +729 -729
- package/src/analyzers/SparrowAnalyzer.js +341 -341
- package/src/analyzers/TypeScriptAnalyzer.js +247 -247
- package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -41
- package/src/analyzers/__tests__/ConfigValidator.test.js +362 -362
- package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -40
- package/src/analyzers/__tests__/PythonAnalyzer.test.js +205 -208
- package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -303
- package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -187
- package/src/analyzers/codeCloneDetector/analyzer.js +344 -344
- package/src/analyzers/codeCloneDetector/detector.js +250 -250
- package/src/analyzers/codeCloneDetector/index.js +194 -192
- package/src/analyzers/codeCloneDetector/parser.js +199 -199
- package/src/core/__tests__/agentPool.test.js +866 -866
- package/src/core/__tests__/agentPoolAutoResume.test.js +209 -209
- package/src/core/__tests__/agentPoolWakeOnMessage.test.js +315 -315
- package/src/core/__tests__/agentScheduler.emptyResponseChatStall.test.js +213 -213
- package/src/core/__tests__/agentScheduler.errorCategorisation.test.js +246 -246
- package/src/core/__tests__/agentScheduler.firstChunkTimeout.test.js +138 -138
- package/src/core/__tests__/agentScheduler.modeTransitions.test.js +233 -233
- package/src/core/__tests__/agentScheduler.nativePromptPick.test.js +319 -319
- package/src/core/__tests__/agentScheduler.taskLifecycleInstruction.test.js +78 -78
- package/src/core/__tests__/agentScheduler.visualizer.test.js +258 -258
- package/src/core/__tests__/flowCheckpointStore.test.js +140 -140
- package/src/core/__tests__/flowEndToEnd.test.js +565 -565
- package/src/core/__tests__/flowFieldMapping.test.js +188 -189
- package/src/core/__tests__/flowLintClientMirror.test.js +96 -98
- package/src/core/__tests__/flowSavePayload.test.js +170 -169
- package/src/core/__tests__/flowTemplates.test.js +311 -311
- package/src/core/__tests__/flowVersionStore.test.js +123 -123
- package/src/core/__tests__/messageProcessor.test.js +669 -669
- package/src/core/__tests__/stateManager.test.js +0 -1
- package/src/core/agentPool.js +2474 -2475
- package/src/core/agentScheduler.js +1 -4
- package/src/core/contextManager.js +708 -708
- package/src/core/flowExecutor.js +1510 -1510
- package/src/core/flowFieldMapping.js +136 -138
- package/src/core/messageProcessor.js +953 -954
- package/src/core/orchestrator.js +593 -595
- package/src/core/stateManager.js +1765 -1752
- package/src/index.js +1221 -1221
- package/src/interfaces/__tests__/archivedAgentDelete.test.js +207 -207
- package/src/interfaces/__tests__/bulkAgentRoute.test.js +361 -361
- package/src/interfaces/__tests__/imageServing.test.js +228 -228
- package/src/interfaces/__tests__/remoteSessionAuth.test.js +308 -308
- package/src/interfaces/__tests__/videoJobsRoutes.test.js +178 -179
- package/src/interfaces/__tests__/webServer.marketplace.test.js +629 -629
- package/src/interfaces/schedulerRoutes.js +50 -50
- package/src/interfaces/terminal/__tests__/smoke/connection.test.js +341 -350
- package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +156 -156
- package/src/interfaces/terminal/__tests__/smoke/imports.test.js +325 -330
- package/src/interfaces/terminal/__tests__/smoke/tools.test.js +385 -388
- package/src/interfaces/terminal/api/session.js +265 -266
- package/src/interfaces/terminal/api/websocket.js +496 -497
- package/src/interfaces/terminal/components/AgentCreator.js +691 -705
- package/src/interfaces/terminal/components/AgentEditor.js +676 -678
- package/src/interfaces/terminal/components/AgentSwitcher.js +331 -330
- package/src/interfaces/terminal/components/ErrorPanel.js +263 -264
- package/src/interfaces/terminal/components/Header.js +28 -28
- package/src/interfaces/terminal/components/Layout.js +598 -603
- package/src/interfaces/terminal/components/MessageList.js +280 -281
- package/src/interfaces/terminal/components/SettingsPanel.js +410 -415
- package/src/interfaces/terminal/components/StatusBar.js +2 -0
- package/src/interfaces/terminal/index.js +168 -168
- package/src/interfaces/terminal/state/useAgentControl.js +496 -496
- package/src/interfaces/terminal/state/useAgents.js +537 -537
- package/src/interfaces/terminal/state/useMessages.js +629 -630
- package/src/interfaces/terminal/state/useTools.js +554 -554
- package/src/interfaces/terminal/utils/debugLogger.js +44 -44
- package/src/interfaces/terminal/utils/settingsStorage.js +232 -232
- package/src/interfaces/webServer.js +7578 -7579
- package/src/interfaces/webServer.js.bak +7046 -7046
- package/src/modules/fileExplorer/__tests__/zipDownload.test.js +237 -237
- package/src/modules/fileExplorer/controller.js +470 -469
- package/src/modules/fileExplorer/routes.js +285 -286
- package/src/modules/widget/__tests__/isDisabled.test.js +41 -41
- package/src/modules/widget/__tests__/routes.test.js +677 -678
- package/src/modules/widget/__tests__/runtime.test.js +401 -401
- package/src/modules/widget/__tests__/versioning.test.js +309 -309
- package/src/modules/widget/__tests__/webComponentRuntime.test.js +565 -565
- package/src/modules/widget/__tests__/widgetTool.test.js +316 -316
- package/src/modules/widget/routes.js +435 -435
- package/src/modules/widget/runtime/bundle.js +640 -640
- package/src/modules/widget/runtime/webComponentBundle.js +470 -470
- package/src/modules/widget/schema.js +182 -181
- package/src/modules/widget/widgetTool.js +1389 -1389
- package/src/services/__tests__/agentActivityService.test.js +401 -402
- package/src/services/__tests__/benchmarkService.test.js +184 -184
- package/src/services/__tests__/contextInjectionService.test.js +246 -246
- package/src/services/__tests__/conversationQuery.test.js +721 -723
- package/src/services/__tests__/credentialVault.test.js +469 -469
- package/src/services/__tests__/discordService.integration.test.js +638 -639
- package/src/services/__tests__/flowContextService.test.js +590 -590
- package/src/services/__tests__/memoryService.test.js +1 -1
- package/src/services/__tests__/messageSource.test.js +380 -380
- package/src/services/__tests__/modelRouterNaming.test.js +111 -111
- package/src/services/__tests__/projectDetector.test.js +34 -34
- package/src/services/__tests__/promptService.test.js +242 -242
- package/src/services/__tests__/telegramService.test.js +941 -941
- package/src/services/__tests__/tokenCountingService.test.js +48 -48
- package/src/services/agentActivityService.js +419 -420
- package/src/services/aiService.js +2997 -3001
- package/src/services/apiKeyManager.js +359 -359
- package/src/services/benchmarkService.js +196 -196
- package/src/services/codebaseKnowledgeService.js +2 -2
- package/src/services/composioService.js +738 -738
- package/src/services/conversationCompactionService.js +1258 -1257
- package/src/services/credentialVault.js +685 -685
- package/src/services/discordService.js +792 -793
- package/src/services/embeddings/__tests__/azureCustomProvider.test.js +232 -232
- package/src/services/embeddings/__tests__/embeddingService.test.js +417 -417
- package/src/services/embeddings/__tests__/localProvider.test.js +263 -263
- package/src/services/embeddings/autoRecall.js +218 -219
- package/src/services/embeddings/indexers/__tests__/agentIndexer.test.js +232 -232
- package/src/services/embeddings/indexers/__tests__/memoryIndexer.test.js +418 -418
- package/src/services/embeddings/indexers/__tests__/reminisceIndexer.test.js +356 -357
- package/src/services/embeddings/indexers/__tests__/skillsIndexer.test.js +145 -145
- package/src/services/embeddings/indexers/__tests__/taskIndexer.test.js +146 -146
- package/src/services/embeddings/indexers/composioIndexer.js +279 -279
- package/src/services/embeddings/providerInterface.js +206 -206
- package/src/services/embeddings/providers/localProvider.js +11 -7
- package/src/services/embeddings/providers/openaiProvider.js +101 -101
- package/src/services/embeddings/vectorStore/inMemoryJsonStore.js +356 -356
- package/src/services/errorHandler.js +809 -809
- package/src/services/flowContextService.js +586 -586
- package/src/services/grounding/MockAdapter.js +125 -125
- package/src/services/modelRouterService.js +26 -31
- package/src/services/modelsService.js +322 -322
- package/src/services/ollamaService.js +452 -452
- package/src/services/projectDetector.js +403 -404
- package/src/services/promptService.js +418 -418
- package/src/services/qualityInspector.js +795 -795
- package/src/services/scheduleService.js +726 -726
- package/src/services/serviceRegistry.js +386 -386
- package/src/services/telegrafBot.js +174 -174
- package/src/services/telegramService.js +1972 -1972
- package/src/services/visualEditorBridge.js +1033 -1033
- package/src/services/visualEditorServer.js +1769 -1774
- package/src/services/whatsappService.js +667 -668
- package/src/tools/__tests__/agentCommunicationTool.findAgent.test.js +226 -226
- package/src/tools/__tests__/agentCommunicationTool.test.js +3 -3
- package/src/tools/__tests__/agentDelayTool.test.js +342 -342
- package/src/tools/__tests__/baseTool.test.js +3 -3
- package/src/tools/__tests__/codeMapTool.test.js +915 -915
- package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -309
- package/src/tools/__tests__/fileTreeTool.test.js +274 -274
- package/src/tools/__tests__/filesystemTool.test.js +815 -815
- package/src/tools/__tests__/foundryWebSearchTool.test.js +252 -252
- package/src/tools/__tests__/imageTool.validator.test.js +194 -194
- package/src/tools/__tests__/jobDoneTool.test.js +580 -581
- package/src/tools/__tests__/memoryTool.forgetStale.test.js +272 -272
- package/src/tools/__tests__/memoryTool.reminisce.test.js +2 -2
- package/src/tools/__tests__/memoryTool.reminisceSemanticSearch.test.js +301 -301
- package/src/tools/__tests__/memoryTool.semanticSearch.test.js +405 -405
- package/src/tools/__tests__/memoryTool.teamPool.test.js +293 -293
- package/src/tools/__tests__/memoryTool.test.js +1 -1
- package/src/tools/__tests__/seekTool.test.js +282 -282
- package/src/tools/__tests__/skillsTool.search.test.js +164 -164
- package/src/tools/__tests__/skillsTool.test.js +226 -226
- package/src/tools/__tests__/staticAnalysisTool.test.js +509 -509
- package/src/tools/__tests__/taskManagerTool.discipline.test.js +137 -137
- package/src/tools/__tests__/taskManagerTool.search.test.js +143 -143
- package/src/tools/__tests__/taskManagerTool.test.js +866 -866
- package/src/tools/__tests__/terminalTool.test.js +448 -448
- package/src/tools/__tests__/toolShapeForgiveness.test.js +259 -260
- package/src/tools/__tests__/userPromptTool.test.js +297 -297
- package/src/tools/__tests__/videoTool.jobs.test.js +147 -147
- package/src/tools/__tests__/webTool.e2e.test.js +609 -603
- package/src/tools/__tests__/webTool.unit.test.js +195 -195
- package/src/tools/__tests__/webTool.visionModel.test.js +75 -75
- package/src/tools/agentCommunicationTool.js +8 -10
- package/src/tools/agentDelayTool.js +496 -497
- package/src/tools/asyncToolManager.js +602 -603
- package/src/tools/baseTool.js +12 -11
- package/src/tools/cloneDetectionTool.js +576 -581
- package/src/tools/codeMapTool.js +0 -6
- package/src/tools/composioTool.js +617 -617
- package/src/tools/dependencyResolverTool.js +1211 -1212
- package/src/tools/desktop/DesktopTool.js +629 -638
- package/src/tools/desktop/__tests__/DesktopTool.e2e.test.js +306 -306
- package/src/tools/desktop/__tests__/DesktopTool.test.js +507 -507
- package/src/tools/desktop/__tests__/osController.test.js +364 -364
- package/src/tools/desktop/osController.js +491 -491
- package/src/tools/docxTool.js +623 -623
- package/src/tools/excelTool.js +636 -636
- package/src/tools/fileContentReplaceTool.js +5 -7
- package/src/tools/fileSystemTool.js +12 -19
- package/src/tools/fileTreeTool.js +840 -840
- package/src/tools/foundryWebSearchTool.js +273 -273
- package/src/tools/helpTool.js +198 -198
- package/src/tools/imageTool.js +1397 -1397
- package/src/tools/importAnalyzerTool.js +1056 -1056
- package/src/tools/jobDoneTool.js +495 -495
- package/src/tools/memoryTool.js +1 -1
- package/src/tools/office/pres/__tests__/presSystem.test.js +365 -365
- package/src/tools/office/pres/archetypes/agenda.js +61 -61
- package/src/tools/office/pres/archetypes/bentoGrid.js +218 -219
- package/src/tools/office/pres/archetypes/bigStat.js +140 -142
- package/src/tools/office/pres/archetypes/closing.js +70 -70
- package/src/tools/office/pres/archetypes/hero.js +70 -70
- package/src/tools/office/pres/archetypes/productHero.js +93 -94
- package/src/tools/office/pres/archetypes/table.js +73 -74
- package/src/tools/office/pres/backgrounds/orb.js +66 -66
- package/src/tools/office/pres/components.js +422 -423
- package/src/tools/officeTool.js +441 -441
- package/src/tools/pdfTool.js +625 -627
- package/src/tools/platformControlTool.js +1081 -1081
- package/src/tools/seekTool.js +917 -918
- package/src/tools/skillsTool.js +1 -1
- package/src/tools/staticAnalysisTool.js +2143 -2146
- package/src/tools/taskManagerTool.js +3324 -3324
- package/src/tools/terminalTool.js +2615 -2618
- package/src/tools/videoTool.js +1303 -1303
- package/src/tools/visionTool.js +508 -508
- package/src/tools/visualEditorTool.js +1289 -1290
- package/src/tools/webTool.js +3368 -3368
- package/src/tools/whatsappTool.js +464 -464
- package/src/types/__tests__/agent.test.js +499 -499
- package/src/types/__tests__/contextReference.test.js +606 -606
- package/src/types/__tests__/conversation.test.js +555 -555
- package/src/types/__tests__/toolCommand.test.js +584 -584
- package/src/types/contextReference.js +974 -971
- package/src/types/conversation.js +729 -729
- package/src/types/toolCommand.js +746 -746
- package/src/utilities/__tests__/attachmentValidator.test.js +80 -80
- package/src/utilities/__tests__/auditReport.test.js +328 -328
- package/src/utilities/__tests__/directoryAccessManager.test.js +388 -388
- package/src/utilities/__tests__/jsonRepair.test.js +103 -104
- package/src/utilities/__tests__/modeTransitionReasons.test.js +105 -105
- package/src/utilities/__tests__/platformUtils.test.js +80 -87
- package/src/utilities/__tests__/structuredFileValidator.test.js +261 -263
- package/src/utilities/__tests__/toolConstants.test.js +92 -94
- package/src/utilities/__tests__/useIsTouchDevice.detect.test.js +114 -114
- package/src/utilities/__tests__/webUiUtilSync.test.js +117 -117
- package/src/utilities/attachmentValidator.js +284 -288
- package/src/utilities/authCache.js.backup-1779570472481 +121 -121
- package/src/utilities/browserStealth.js +631 -630
- package/src/utilities/configManager.js +616 -617
- package/src/utilities/directoryAccessManager.js +564 -565
- package/src/utilities/fileProcessor.js +308 -307
- package/src/utilities/humanBehavior.js +454 -453
- package/src/utilities/logger.js +479 -479
- package/src/utilities/structuredFileValidator.js +696 -699
- package/src/utilities/tagParser.js +5 -10
- package/src/utilities/userDataDir.js +308 -308
- package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js.map +0 -1
- package/node_modules/@isaacs/brace-expansion/dist/esm/index.js.map +0 -1
- package/node_modules/minipass/LICENSE +0 -15
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/LICENSE.md +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/package.json +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/package.json +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/LICENSE +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/package.json +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts.map +0 -0
- /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;
|