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,361 +1,361 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for POST /api/agents/bulk — multi-agent action fanout.
|
|
3
|
-
*
|
|
4
|
-
* Pattern: mini Express harness via http.createServer + fetch, mirroring
|
|
5
|
-
* archivedAgentDelete.test.js. The route handler under test is inlined
|
|
6
|
-
* from src/interfaces/webServer.js — when the production handler
|
|
7
|
-
* changes, this inline copy MUST be updated in the same commit.
|
|
8
|
-
*/
|
|
9
|
-
import { describe, it, expect,
|
|
10
|
-
import express from 'express';
|
|
11
|
-
import { createServer } from 'http';
|
|
12
|
-
|
|
13
|
-
const SILENT_LOGGER = { info() {}, warn() {}, error() {}, debug() {} };
|
|
14
|
-
|
|
15
|
-
let server, baseUrl, orch;
|
|
16
|
-
|
|
17
|
-
function makeOrchestrator(overrides = {}) {
|
|
18
|
-
const agentPool = {
|
|
19
|
-
getAgent: jest.fn(async (id) => (id.startsWith('missing-') ? null : { id, capabilities: ['memory', 'tools'] })),
|
|
20
|
-
...(overrides.agentPool || {}),
|
|
21
|
-
};
|
|
22
|
-
const processRequest = overrides.processRequest || jest.fn(async ({ action, payload }) => ({ success: true, data: { action, payload } }));
|
|
23
|
-
return {
|
|
24
|
-
config: { project: { directory: '/tmp/test' } },
|
|
25
|
-
agentPool,
|
|
26
|
-
processRequest,
|
|
27
|
-
stateManager: overrides.stateManager || {},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Inlined handler — keep in sync with src/interfaces/webServer.js.
|
|
32
|
-
function bulkHandler(orchestrator, logger) {
|
|
33
|
-
return async (req, res) => {
|
|
34
|
-
const BULK_MAX_AGENTS = 100;
|
|
35
|
-
const VALID_ACTIONS = ['delete', 'unload', 'pause', 'resume', 'set-model', 'enable-tool', 'disable-tool', 'archive'];
|
|
36
|
-
try {
|
|
37
|
-
const { action, agentIds, params = {}, sessionId } = req.body || {};
|
|
38
|
-
if (!action || !VALID_ACTIONS.includes(action)) {
|
|
39
|
-
return res.status(400).json({ success: false, error: `Invalid action: ${action}. Must be one of: ${VALID_ACTIONS.join(', ')}` });
|
|
40
|
-
}
|
|
41
|
-
if (!Array.isArray(agentIds) || agentIds.length === 0) {
|
|
42
|
-
return res.status(400).json({ success: false, error: 'agentIds must be a non-empty array' });
|
|
43
|
-
}
|
|
44
|
-
if (agentIds.length > BULK_MAX_AGENTS) {
|
|
45
|
-
return res.status(422).json({ success: false, error: `Too many agents in one call: ${agentIds.length}. Maximum is ${BULK_MAX_AGENTS}.` });
|
|
46
|
-
}
|
|
47
|
-
if (action === 'set-model' && (!params.model || typeof params.model !== 'string')) {
|
|
48
|
-
return res.status(400).json({ success: false, error: 'set-model requires params.model (string)' });
|
|
49
|
-
}
|
|
50
|
-
if ((action === 'enable-tool' || action === 'disable-tool') && (!params.tool || typeof params.tool !== 'string')) {
|
|
51
|
-
return res.status(400).json({ success: false, error: `${action} requires params.tool (string)` });
|
|
52
|
-
}
|
|
53
|
-
const projectDir = orchestrator.config.project?.directory || process.cwd();
|
|
54
|
-
const effectiveSessionId = sessionId || 'bulk-action';
|
|
55
|
-
const actionMap = { 'delete': 'delete_agent', 'unload': 'unload_agent', 'pause': 'pause_agent', 'resume': 'resume_agent', 'set-model': 'switch_model' };
|
|
56
|
-
const runOne = async (agentId) => {
|
|
57
|
-
try {
|
|
58
|
-
const agent = await orchestrator.agentPool.getAgent(agentId).catch(() => null);
|
|
59
|
-
if (!agent && action !== 'delete') return { agentId, success: false, error: 'Agent not found' };
|
|
60
|
-
if (actionMap[action]) {
|
|
61
|
-
const payload = action === 'set-model' ? { agentId, model: params.model } : { agentId };
|
|
62
|
-
const r = await orchestrator.processRequest({ sessionId: effectiveSessionId, interface: 'web', action: actionMap[action], payload, projectDir });
|
|
63
|
-
return { agentId, success: r?.success !== false, error: r?.error || null };
|
|
64
|
-
}
|
|
65
|
-
if (action === 'enable-tool' || action === 'disable-tool') {
|
|
66
|
-
if (!agent) return { agentId, success: false, error: 'Agent not loaded' };
|
|
67
|
-
const caps = Array.isArray(agent.capabilities) ? [...agent.capabilities] : [];
|
|
68
|
-
const tool = params.tool;
|
|
69
|
-
const i = caps.indexOf(tool);
|
|
70
|
-
if (action === 'enable-tool' && i === -1) caps.push(tool);
|
|
71
|
-
if (action === 'disable-tool' && i !== -1) caps.splice(i, 1);
|
|
72
|
-
const r = await orchestrator.processRequest({ sessionId: effectiveSessionId, interface: 'web', action: 'update_agent', payload: { agentId, updates: { capabilities: caps } }, projectDir });
|
|
73
|
-
return { agentId, success: r?.success !== false, error: r?.error || null };
|
|
74
|
-
}
|
|
75
|
-
if (action === 'archive') {
|
|
76
|
-
if (orchestrator.stateManager?.archiveAgent) {
|
|
77
|
-
await orchestrator.stateManager.archiveAgent(agentId, projectDir);
|
|
78
|
-
return { agentId, success: true };
|
|
79
|
-
}
|
|
80
|
-
const r = await orchestrator.processRequest({ sessionId: effectiveSessionId, interface: 'web', action: 'unload_agent', payload: { agentId }, projectDir });
|
|
81
|
-
return { agentId, success: r?.success !== false, error: r?.error || null };
|
|
82
|
-
}
|
|
83
|
-
return { agentId, success: false, error: `Unhandled action: ${action}` };
|
|
84
|
-
} catch (err) {
|
|
85
|
-
return { agentId, success: false, error: err.message || String(err) };
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
const results = [];
|
|
89
|
-
for (const id of agentIds) results.push(await runOne(id));
|
|
90
|
-
const ok = results.filter(r => r.success).length;
|
|
91
|
-
const failed = results.length - ok;
|
|
92
|
-
res.json({ success: failed === 0, summary: { total: results.length, ok, failed }, results });
|
|
93
|
-
} catch (error) {
|
|
94
|
-
logger.error('Bulk action failed', { error: error.message });
|
|
95
|
-
res.status(500).json({ success: false, error: error.message });
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async function startServer(orchestrator) {
|
|
101
|
-
const app = express();
|
|
102
|
-
app.use(express.json());
|
|
103
|
-
app.post('/api/agents/bulk', bulkHandler(orchestrator, SILENT_LOGGER));
|
|
104
|
-
server = createServer(app);
|
|
105
|
-
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
106
|
-
const { port } = server.address();
|
|
107
|
-
baseUrl = `http://127.0.0.1:${port}`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function post(body) {
|
|
111
|
-
const r = await fetch(`${baseUrl}/api/agents/bulk`, {
|
|
112
|
-
method: 'POST',
|
|
113
|
-
headers: { 'Content-Type': 'application/json' },
|
|
114
|
-
body: JSON.stringify(body),
|
|
115
|
-
});
|
|
116
|
-
let parsed = null;
|
|
117
|
-
try { parsed = await r.json(); } catch { /* ignore */ }
|
|
118
|
-
return { status: r.status, body: parsed };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
afterEach(async () => {
|
|
122
|
-
if (server) {
|
|
123
|
-
await new Promise((r) => server.close(r));
|
|
124
|
-
server = null;
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe('POST /api/agents/bulk — validation', () => {
|
|
129
|
-
it('rejects missing action with 400', async () => {
|
|
130
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
131
|
-
const r = await post({ agentIds: ['a'] });
|
|
132
|
-
expect(r.status).toBe(400);
|
|
133
|
-
expect(r.body.error).toMatch(/Invalid action/);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('rejects unknown action with 400', async () => {
|
|
137
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
138
|
-
const r = await post({ action: 'invade-poland', agentIds: ['a'] });
|
|
139
|
-
expect(r.status).toBe(400);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('rejects empty agentIds with 400', async () => {
|
|
143
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
144
|
-
const r = await post({ action: 'delete', agentIds: [] });
|
|
145
|
-
expect(r.status).toBe(400);
|
|
146
|
-
expect(r.body.error).toMatch(/non-empty array/);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('rejects >100 agents with 422', async () => {
|
|
150
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
151
|
-
const ids = Array.from({ length: 101 }, (_, i) => `a-${i}`);
|
|
152
|
-
const r = await post({ action: 'delete', agentIds: ids });
|
|
153
|
-
expect(r.status).toBe(422);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it('set-model requires params.model', async () => {
|
|
157
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
158
|
-
const r = await post({ action: 'set-model', agentIds: ['a'] });
|
|
159
|
-
expect(r.status).toBe(400);
|
|
160
|
-
expect(r.body.error).toMatch(/params\.model/);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('enable-tool requires params.tool', async () => {
|
|
164
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
165
|
-
const r = await post({ action: 'enable-tool', agentIds: ['a'] });
|
|
166
|
-
expect(r.status).toBe(400);
|
|
167
|
-
expect(r.body.error).toMatch(/params\.tool/);
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
describe('POST /api/agents/bulk — happy paths', () => {
|
|
172
|
-
it('delete: returns success when every agent deletes ok', async () => {
|
|
173
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
174
|
-
const r = await post({ action: 'delete', agentIds: ['a', 'b', 'c'] });
|
|
175
|
-
expect(r.status).toBe(200);
|
|
176
|
-
expect(r.body.success).toBe(true);
|
|
177
|
-
expect(r.body.summary).toEqual({ total: 3, ok: 3, failed: 0 });
|
|
178
|
-
expect(orch.processRequest).toHaveBeenCalledTimes(3);
|
|
179
|
-
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
180
|
-
action: 'delete_agent', payload: expect.objectContaining({ agentId: 'a' }),
|
|
181
|
-
}));
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('pause routes to pause_agent', async () => {
|
|
185
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
186
|
-
await post({ action: 'pause', agentIds: ['a'] });
|
|
187
|
-
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({ action: 'pause_agent' }));
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('resume routes to resume_agent', async () => {
|
|
191
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
192
|
-
await post({ action: 'resume', agentIds: ['a'] });
|
|
193
|
-
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({ action: 'resume_agent' }));
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('unload routes to unload_agent', async () => {
|
|
197
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
198
|
-
await post({ action: 'unload', agentIds: ['a'] });
|
|
199
|
-
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({ action: 'unload_agent' }));
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('set-model forwards model in payload', async () => {
|
|
203
|
-
orch = makeOrchestrator(); await startServer(orch);
|
|
204
|
-
await post({ action: 'set-model', agentIds: ['a'], params: { model: 'Kimi-K2.6' } });
|
|
205
|
-
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
206
|
-
action: 'switch_model',
|
|
207
|
-
payload: expect.objectContaining({ agentId: 'a', model: 'Kimi-K2.6' }),
|
|
208
|
-
}));
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it('enable-tool: appends to capabilities[]', async () => {
|
|
212
|
-
orch = makeOrchestrator({ agentPool: { getAgent: jest.fn(async (id) => ({ id, capabilities: ['memory'] })) } });
|
|
213
|
-
await startServer(orch);
|
|
214
|
-
await post({ action: 'enable-tool', agentIds: ['a'], params: { tool: 'terminal' } });
|
|
215
|
-
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
216
|
-
action: 'update_agent',
|
|
217
|
-
payload: expect.objectContaining({
|
|
218
|
-
agentId: 'a',
|
|
219
|
-
updates: expect.objectContaining({ capabilities: ['memory', 'terminal'] }),
|
|
220
|
-
}),
|
|
221
|
-
}));
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it('disable-tool: removes from capabilities[]', async () => {
|
|
225
|
-
orch = makeOrchestrator({ agentPool: { getAgent: jest.fn(async (id) => ({ id, capabilities: ['memory', 'terminal'] })) } });
|
|
226
|
-
await startServer(orch);
|
|
227
|
-
await post({ action: 'disable-tool', agentIds: ['a'], params: { tool: 'terminal' } });
|
|
228
|
-
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
229
|
-
action: 'update_agent',
|
|
230
|
-
payload: expect.objectContaining({
|
|
231
|
-
updates: expect.objectContaining({ capabilities: ['memory'] }),
|
|
232
|
-
}),
|
|
233
|
-
}));
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('archive uses stateManager.archiveAgent when available', async () => {
|
|
237
|
-
const archiveAgent = jest.fn().mockResolvedValue(undefined);
|
|
238
|
-
orch = makeOrchestrator({ stateManager: { archiveAgent } });
|
|
239
|
-
await startServer(orch);
|
|
240
|
-
await post({ action: 'archive', agentIds: ['a', 'b'] });
|
|
241
|
-
expect(archiveAgent).toHaveBeenCalledTimes(2);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('archive falls back to unload when stateManager.archiveAgent missing', async () => {
|
|
245
|
-
orch = makeOrchestrator();
|
|
246
|
-
await startServer(orch);
|
|
247
|
-
await post({ action: 'archive', agentIds: ['a'] });
|
|
248
|
-
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({ action: 'unload_agent' }));
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
describe('POST /api/agents/bulk — partial success', () => {
|
|
253
|
-
it('one failure does NOT abort the batch; per-agent results returned', async () => {
|
|
254
|
-
orch = makeOrchestrator({
|
|
255
|
-
processRequest: jest.fn(async ({ payload }) => payload.agentId === 'b'
|
|
256
|
-
? { success: false, error: 'agent b is borked' }
|
|
257
|
-
: { success: true }),
|
|
258
|
-
});
|
|
259
|
-
await startServer(orch);
|
|
260
|
-
const r = await post({ action: 'delete', agentIds: ['a', 'b', 'c'] });
|
|
261
|
-
expect(r.status).toBe(200);
|
|
262
|
-
expect(r.body.success).toBe(false);
|
|
263
|
-
expect(r.body.summary).toEqual({ total: 3, ok: 2, failed: 1 });
|
|
264
|
-
expect(r.body.results.find(x => x.agentId === 'a').success).toBe(true);
|
|
265
|
-
expect(r.body.results.find(x => x.agentId === 'b').success).toBe(false);
|
|
266
|
-
expect(r.body.results.find(x => x.agentId === 'b').error).toMatch(/borked/);
|
|
267
|
-
expect(r.body.results.find(x => x.agentId === 'c').success).toBe(true);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('thrown error in dispatch is captured as per-agent failure', async () => {
|
|
271
|
-
orch = makeOrchestrator({
|
|
272
|
-
processRequest: jest.fn(async ({ payload }) => {
|
|
273
|
-
if (payload.agentId === 'b') throw new Error('orchestrator on fire');
|
|
274
|
-
return { success: true };
|
|
275
|
-
}),
|
|
276
|
-
});
|
|
277
|
-
await startServer(orch);
|
|
278
|
-
const r = await post({ action: 'unload', agentIds: ['a', 'b'] });
|
|
279
|
-
expect(r.body.success).toBe(false);
|
|
280
|
-
expect(r.body.results.find(x => x.agentId === 'b').error).toMatch(/on fire/);
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it('non-delete action against missing agent: returns "Agent not found"', async () => {
|
|
284
|
-
orch = makeOrchestrator();
|
|
285
|
-
await startServer(orch);
|
|
286
|
-
const r = await post({ action: 'pause', agentIds: ['missing-1', 'a'] });
|
|
287
|
-
expect(r.body.summary).toEqual({ total: 2, ok: 1, failed: 1 });
|
|
288
|
-
expect(r.body.results.find(x => x.agentId === 'missing-1').error).toMatch(/not found/i);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// Regression guard for the silent "Invalid interface: WEB" bug.
|
|
292
|
-
//
|
|
293
|
-
// The bulk handler used to hardcode `interface: 'WEB'` (uppercase),
|
|
294
|
-
// but orchestrator.processRequest validates against INTERFACE_TYPES
|
|
295
|
-
// — which uses LOWERCASE strings ('web', 'cli', etc.). Every bulk
|
|
296
|
-
// action — delete, pause, resume, unload, set-model, enable-tool,
|
|
297
|
-
// disable-tool, archive — failed with `Invalid interface: WEB`
|
|
298
|
-
// before even dispatching. The error was caught in `runOne`'s
|
|
299
|
-
// try/catch and surfaced as per-agent failure ("Failed to delete
|
|
300
|
-
// N agents") with no hint at the actual cause.
|
|
301
|
-
//
|
|
302
|
-
// Lock the lowercase contract for every action that flows through
|
|
303
|
-
// processRequest, so any case-drift trips this test immediately.
|
|
304
|
-
it('forwards lowercase "web" as the interface (not "WEB") for every dispatch', async () => {
|
|
305
|
-
orch = makeOrchestrator();
|
|
306
|
-
await startServer(orch);
|
|
307
|
-
// Hit one representative of each routed action.
|
|
308
|
-
await post({ action: 'delete', agentIds: ['a'] });
|
|
309
|
-
await post({ action: 'pause', agentIds: ['a'] });
|
|
310
|
-
await post({ action: 'unload', agentIds: ['a'] });
|
|
311
|
-
await post({ action: 'set-model', agentIds: ['a'], params: { model: 'k' } });
|
|
312
|
-
|
|
313
|
-
// Every call to processRequest must have used lowercase 'web'.
|
|
314
|
-
for (const call of orch.processRequest.mock.calls) {
|
|
315
|
-
expect(call[0].interface).toBe('web');
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it('falls flat if the orchestrator rejects "WEB" — regression of the original bug', async () => {
|
|
320
|
-
// Simulate the real orchestrator's validation: throw on anything
|
|
321
|
-
// not matching INTERFACE_TYPES.WEB ('web'). With the fix, every
|
|
322
|
-
// call should succeed because the handler now passes lowercase.
|
|
323
|
-
orch = makeOrchestrator({
|
|
324
|
-
processRequest: jest.fn(async ({ interface: iface }) => {
|
|
325
|
-
if (iface !== 'web') throw new Error(`Invalid interface: ${iface}`);
|
|
326
|
-
return { success: true };
|
|
327
|
-
}),
|
|
328
|
-
});
|
|
329
|
-
await startServer(orch);
|
|
330
|
-
const r = await post({ action: 'delete', agentIds: ['a', 'b'] });
|
|
331
|
-
expect(r.body.success).toBe(true);
|
|
332
|
-
expect(r.body.summary).toEqual({ total: 2, ok: 2, failed: 0 });
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
// Regression guard for the My Squadron > group operation (delete) bug.
|
|
336
|
-
// Before the fix, deleting selected agents in the UI failed with
|
|
337
|
-
// "Failed to delete N agents" whenever any of them was unloaded —
|
|
338
|
-
// the bulk handler let `delete` flow through to the orchestrator, but
|
|
339
|
-
// the orchestrator's _handleDeleteAgent threw "Agent not found" for
|
|
340
|
-
// anything not in the live pool. Now agentPool.deleteAgent is
|
|
341
|
-
// idempotent (clears persistent state regardless of pool membership)
|
|
342
|
-
// and the orchestrator surfaces that success up through processRequest.
|
|
343
|
-
// From the bulk route's perspective: every missing-prefixed agent
|
|
344
|
-
// should report success.
|
|
345
|
-
it('delete against missing/unloaded agents succeeds (idempotent cleanup)', async () => {
|
|
346
|
-
orch = makeOrchestrator();
|
|
347
|
-
await startServer(orch);
|
|
348
|
-
const r = await post({ action: 'delete', agentIds: ['missing-1', 'missing-2', 'a'] });
|
|
349
|
-
expect(r.status).toBe(200);
|
|
350
|
-
expect(r.body.success).toBe(true);
|
|
351
|
-
expect(r.body.summary).toEqual({ total: 3, ok: 3, failed: 0 });
|
|
352
|
-
// All three reached processRequest (no short-circuit on missing).
|
|
353
|
-
expect(orch.processRequest).toHaveBeenCalledTimes(3);
|
|
354
|
-
for (const id of ['missing-1', 'missing-2', 'a']) {
|
|
355
|
-
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
356
|
-
action: 'delete_agent',
|
|
357
|
-
payload: expect.objectContaining({ agentId: id }),
|
|
358
|
-
}));
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Tests for POST /api/agents/bulk — multi-agent action fanout.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: mini Express harness via http.createServer + fetch, mirroring
|
|
5
|
+
* archivedAgentDelete.test.js. The route handler under test is inlined
|
|
6
|
+
* from src/interfaces/webServer.js — when the production handler
|
|
7
|
+
* changes, this inline copy MUST be updated in the same commit.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, afterEach, jest } from '@jest/globals';
|
|
10
|
+
import express from 'express';
|
|
11
|
+
import { createServer } from 'http';
|
|
12
|
+
|
|
13
|
+
const SILENT_LOGGER = { info() {}, warn() {}, error() {}, debug() {} };
|
|
14
|
+
|
|
15
|
+
let server, baseUrl, orch;
|
|
16
|
+
|
|
17
|
+
function makeOrchestrator(overrides = {}) {
|
|
18
|
+
const agentPool = {
|
|
19
|
+
getAgent: jest.fn(async (id) => (id.startsWith('missing-') ? null : { id, capabilities: ['memory', 'tools'] })),
|
|
20
|
+
...(overrides.agentPool || {}),
|
|
21
|
+
};
|
|
22
|
+
const processRequest = overrides.processRequest || jest.fn(async ({ action, payload }) => ({ success: true, data: { action, payload } }));
|
|
23
|
+
return {
|
|
24
|
+
config: { project: { directory: '/tmp/test' } },
|
|
25
|
+
agentPool,
|
|
26
|
+
processRequest,
|
|
27
|
+
stateManager: overrides.stateManager || {},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Inlined handler — keep in sync with src/interfaces/webServer.js.
|
|
32
|
+
function bulkHandler(orchestrator, logger) {
|
|
33
|
+
return async (req, res) => {
|
|
34
|
+
const BULK_MAX_AGENTS = 100;
|
|
35
|
+
const VALID_ACTIONS = ['delete', 'unload', 'pause', 'resume', 'set-model', 'enable-tool', 'disable-tool', 'archive'];
|
|
36
|
+
try {
|
|
37
|
+
const { action, agentIds, params = {}, sessionId } = req.body || {};
|
|
38
|
+
if (!action || !VALID_ACTIONS.includes(action)) {
|
|
39
|
+
return res.status(400).json({ success: false, error: `Invalid action: ${action}. Must be one of: ${VALID_ACTIONS.join(', ')}` });
|
|
40
|
+
}
|
|
41
|
+
if (!Array.isArray(agentIds) || agentIds.length === 0) {
|
|
42
|
+
return res.status(400).json({ success: false, error: 'agentIds must be a non-empty array' });
|
|
43
|
+
}
|
|
44
|
+
if (agentIds.length > BULK_MAX_AGENTS) {
|
|
45
|
+
return res.status(422).json({ success: false, error: `Too many agents in one call: ${agentIds.length}. Maximum is ${BULK_MAX_AGENTS}.` });
|
|
46
|
+
}
|
|
47
|
+
if (action === 'set-model' && (!params.model || typeof params.model !== 'string')) {
|
|
48
|
+
return res.status(400).json({ success: false, error: 'set-model requires params.model (string)' });
|
|
49
|
+
}
|
|
50
|
+
if ((action === 'enable-tool' || action === 'disable-tool') && (!params.tool || typeof params.tool !== 'string')) {
|
|
51
|
+
return res.status(400).json({ success: false, error: `${action} requires params.tool (string)` });
|
|
52
|
+
}
|
|
53
|
+
const projectDir = orchestrator.config.project?.directory || process.cwd();
|
|
54
|
+
const effectiveSessionId = sessionId || 'bulk-action';
|
|
55
|
+
const actionMap = { 'delete': 'delete_agent', 'unload': 'unload_agent', 'pause': 'pause_agent', 'resume': 'resume_agent', 'set-model': 'switch_model' };
|
|
56
|
+
const runOne = async (agentId) => {
|
|
57
|
+
try {
|
|
58
|
+
const agent = await orchestrator.agentPool.getAgent(agentId).catch(() => null);
|
|
59
|
+
if (!agent && action !== 'delete') return { agentId, success: false, error: 'Agent not found' };
|
|
60
|
+
if (actionMap[action]) {
|
|
61
|
+
const payload = action === 'set-model' ? { agentId, model: params.model } : { agentId };
|
|
62
|
+
const r = await orchestrator.processRequest({ sessionId: effectiveSessionId, interface: 'web', action: actionMap[action], payload, projectDir });
|
|
63
|
+
return { agentId, success: r?.success !== false, error: r?.error || null };
|
|
64
|
+
}
|
|
65
|
+
if (action === 'enable-tool' || action === 'disable-tool') {
|
|
66
|
+
if (!agent) return { agentId, success: false, error: 'Agent not loaded' };
|
|
67
|
+
const caps = Array.isArray(agent.capabilities) ? [...agent.capabilities] : [];
|
|
68
|
+
const tool = params.tool;
|
|
69
|
+
const i = caps.indexOf(tool);
|
|
70
|
+
if (action === 'enable-tool' && i === -1) caps.push(tool);
|
|
71
|
+
if (action === 'disable-tool' && i !== -1) caps.splice(i, 1);
|
|
72
|
+
const r = await orchestrator.processRequest({ sessionId: effectiveSessionId, interface: 'web', action: 'update_agent', payload: { agentId, updates: { capabilities: caps } }, projectDir });
|
|
73
|
+
return { agentId, success: r?.success !== false, error: r?.error || null };
|
|
74
|
+
}
|
|
75
|
+
if (action === 'archive') {
|
|
76
|
+
if (orchestrator.stateManager?.archiveAgent) {
|
|
77
|
+
await orchestrator.stateManager.archiveAgent(agentId, projectDir);
|
|
78
|
+
return { agentId, success: true };
|
|
79
|
+
}
|
|
80
|
+
const r = await orchestrator.processRequest({ sessionId: effectiveSessionId, interface: 'web', action: 'unload_agent', payload: { agentId }, projectDir });
|
|
81
|
+
return { agentId, success: r?.success !== false, error: r?.error || null };
|
|
82
|
+
}
|
|
83
|
+
return { agentId, success: false, error: `Unhandled action: ${action}` };
|
|
84
|
+
} catch (err) {
|
|
85
|
+
return { agentId, success: false, error: err.message || String(err) };
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const results = [];
|
|
89
|
+
for (const id of agentIds) results.push(await runOne(id));
|
|
90
|
+
const ok = results.filter(r => r.success).length;
|
|
91
|
+
const failed = results.length - ok;
|
|
92
|
+
res.json({ success: failed === 0, summary: { total: results.length, ok, failed }, results });
|
|
93
|
+
} catch (error) {
|
|
94
|
+
logger.error('Bulk action failed', { error: error.message });
|
|
95
|
+
res.status(500).json({ success: false, error: error.message });
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function startServer(orchestrator) {
|
|
101
|
+
const app = express();
|
|
102
|
+
app.use(express.json());
|
|
103
|
+
app.post('/api/agents/bulk', bulkHandler(orchestrator, SILENT_LOGGER));
|
|
104
|
+
server = createServer(app);
|
|
105
|
+
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
106
|
+
const { port } = server.address();
|
|
107
|
+
baseUrl = `http://127.0.0.1:${port}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function post(body) {
|
|
111
|
+
const r = await fetch(`${baseUrl}/api/agents/bulk`, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: { 'Content-Type': 'application/json' },
|
|
114
|
+
body: JSON.stringify(body),
|
|
115
|
+
});
|
|
116
|
+
let parsed = null;
|
|
117
|
+
try { parsed = await r.json(); } catch { /* ignore */ }
|
|
118
|
+
return { status: r.status, body: parsed };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
afterEach(async () => {
|
|
122
|
+
if (server) {
|
|
123
|
+
await new Promise((r) => server.close(r));
|
|
124
|
+
server = null;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('POST /api/agents/bulk — validation', () => {
|
|
129
|
+
it('rejects missing action with 400', async () => {
|
|
130
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
131
|
+
const r = await post({ agentIds: ['a'] });
|
|
132
|
+
expect(r.status).toBe(400);
|
|
133
|
+
expect(r.body.error).toMatch(/Invalid action/);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('rejects unknown action with 400', async () => {
|
|
137
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
138
|
+
const r = await post({ action: 'invade-poland', agentIds: ['a'] });
|
|
139
|
+
expect(r.status).toBe(400);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('rejects empty agentIds with 400', async () => {
|
|
143
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
144
|
+
const r = await post({ action: 'delete', agentIds: [] });
|
|
145
|
+
expect(r.status).toBe(400);
|
|
146
|
+
expect(r.body.error).toMatch(/non-empty array/);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('rejects >100 agents with 422', async () => {
|
|
150
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
151
|
+
const ids = Array.from({ length: 101 }, (_, i) => `a-${i}`);
|
|
152
|
+
const r = await post({ action: 'delete', agentIds: ids });
|
|
153
|
+
expect(r.status).toBe(422);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('set-model requires params.model', async () => {
|
|
157
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
158
|
+
const r = await post({ action: 'set-model', agentIds: ['a'] });
|
|
159
|
+
expect(r.status).toBe(400);
|
|
160
|
+
expect(r.body.error).toMatch(/params\.model/);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('enable-tool requires params.tool', async () => {
|
|
164
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
165
|
+
const r = await post({ action: 'enable-tool', agentIds: ['a'] });
|
|
166
|
+
expect(r.status).toBe(400);
|
|
167
|
+
expect(r.body.error).toMatch(/params\.tool/);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('POST /api/agents/bulk — happy paths', () => {
|
|
172
|
+
it('delete: returns success when every agent deletes ok', async () => {
|
|
173
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
174
|
+
const r = await post({ action: 'delete', agentIds: ['a', 'b', 'c'] });
|
|
175
|
+
expect(r.status).toBe(200);
|
|
176
|
+
expect(r.body.success).toBe(true);
|
|
177
|
+
expect(r.body.summary).toEqual({ total: 3, ok: 3, failed: 0 });
|
|
178
|
+
expect(orch.processRequest).toHaveBeenCalledTimes(3);
|
|
179
|
+
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
180
|
+
action: 'delete_agent', payload: expect.objectContaining({ agentId: 'a' }),
|
|
181
|
+
}));
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('pause routes to pause_agent', async () => {
|
|
185
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
186
|
+
await post({ action: 'pause', agentIds: ['a'] });
|
|
187
|
+
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({ action: 'pause_agent' }));
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('resume routes to resume_agent', async () => {
|
|
191
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
192
|
+
await post({ action: 'resume', agentIds: ['a'] });
|
|
193
|
+
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({ action: 'resume_agent' }));
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('unload routes to unload_agent', async () => {
|
|
197
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
198
|
+
await post({ action: 'unload', agentIds: ['a'] });
|
|
199
|
+
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({ action: 'unload_agent' }));
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('set-model forwards model in payload', async () => {
|
|
203
|
+
orch = makeOrchestrator(); await startServer(orch);
|
|
204
|
+
await post({ action: 'set-model', agentIds: ['a'], params: { model: 'Kimi-K2.6' } });
|
|
205
|
+
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
206
|
+
action: 'switch_model',
|
|
207
|
+
payload: expect.objectContaining({ agentId: 'a', model: 'Kimi-K2.6' }),
|
|
208
|
+
}));
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('enable-tool: appends to capabilities[]', async () => {
|
|
212
|
+
orch = makeOrchestrator({ agentPool: { getAgent: jest.fn(async (id) => ({ id, capabilities: ['memory'] })) } });
|
|
213
|
+
await startServer(orch);
|
|
214
|
+
await post({ action: 'enable-tool', agentIds: ['a'], params: { tool: 'terminal' } });
|
|
215
|
+
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
216
|
+
action: 'update_agent',
|
|
217
|
+
payload: expect.objectContaining({
|
|
218
|
+
agentId: 'a',
|
|
219
|
+
updates: expect.objectContaining({ capabilities: ['memory', 'terminal'] }),
|
|
220
|
+
}),
|
|
221
|
+
}));
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('disable-tool: removes from capabilities[]', async () => {
|
|
225
|
+
orch = makeOrchestrator({ agentPool: { getAgent: jest.fn(async (id) => ({ id, capabilities: ['memory', 'terminal'] })) } });
|
|
226
|
+
await startServer(orch);
|
|
227
|
+
await post({ action: 'disable-tool', agentIds: ['a'], params: { tool: 'terminal' } });
|
|
228
|
+
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
229
|
+
action: 'update_agent',
|
|
230
|
+
payload: expect.objectContaining({
|
|
231
|
+
updates: expect.objectContaining({ capabilities: ['memory'] }),
|
|
232
|
+
}),
|
|
233
|
+
}));
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('archive uses stateManager.archiveAgent when available', async () => {
|
|
237
|
+
const archiveAgent = jest.fn().mockResolvedValue(undefined);
|
|
238
|
+
orch = makeOrchestrator({ stateManager: { archiveAgent } });
|
|
239
|
+
await startServer(orch);
|
|
240
|
+
await post({ action: 'archive', agentIds: ['a', 'b'] });
|
|
241
|
+
expect(archiveAgent).toHaveBeenCalledTimes(2);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('archive falls back to unload when stateManager.archiveAgent missing', async () => {
|
|
245
|
+
orch = makeOrchestrator();
|
|
246
|
+
await startServer(orch);
|
|
247
|
+
await post({ action: 'archive', agentIds: ['a'] });
|
|
248
|
+
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({ action: 'unload_agent' }));
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe('POST /api/agents/bulk — partial success', () => {
|
|
253
|
+
it('one failure does NOT abort the batch; per-agent results returned', async () => {
|
|
254
|
+
orch = makeOrchestrator({
|
|
255
|
+
processRequest: jest.fn(async ({ payload }) => payload.agentId === 'b'
|
|
256
|
+
? { success: false, error: 'agent b is borked' }
|
|
257
|
+
: { success: true }),
|
|
258
|
+
});
|
|
259
|
+
await startServer(orch);
|
|
260
|
+
const r = await post({ action: 'delete', agentIds: ['a', 'b', 'c'] });
|
|
261
|
+
expect(r.status).toBe(200);
|
|
262
|
+
expect(r.body.success).toBe(false);
|
|
263
|
+
expect(r.body.summary).toEqual({ total: 3, ok: 2, failed: 1 });
|
|
264
|
+
expect(r.body.results.find(x => x.agentId === 'a').success).toBe(true);
|
|
265
|
+
expect(r.body.results.find(x => x.agentId === 'b').success).toBe(false);
|
|
266
|
+
expect(r.body.results.find(x => x.agentId === 'b').error).toMatch(/borked/);
|
|
267
|
+
expect(r.body.results.find(x => x.agentId === 'c').success).toBe(true);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('thrown error in dispatch is captured as per-agent failure', async () => {
|
|
271
|
+
orch = makeOrchestrator({
|
|
272
|
+
processRequest: jest.fn(async ({ payload }) => {
|
|
273
|
+
if (payload.agentId === 'b') throw new Error('orchestrator on fire');
|
|
274
|
+
return { success: true };
|
|
275
|
+
}),
|
|
276
|
+
});
|
|
277
|
+
await startServer(orch);
|
|
278
|
+
const r = await post({ action: 'unload', agentIds: ['a', 'b'] });
|
|
279
|
+
expect(r.body.success).toBe(false);
|
|
280
|
+
expect(r.body.results.find(x => x.agentId === 'b').error).toMatch(/on fire/);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('non-delete action against missing agent: returns "Agent not found"', async () => {
|
|
284
|
+
orch = makeOrchestrator();
|
|
285
|
+
await startServer(orch);
|
|
286
|
+
const r = await post({ action: 'pause', agentIds: ['missing-1', 'a'] });
|
|
287
|
+
expect(r.body.summary).toEqual({ total: 2, ok: 1, failed: 1 });
|
|
288
|
+
expect(r.body.results.find(x => x.agentId === 'missing-1').error).toMatch(/not found/i);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Regression guard for the silent "Invalid interface: WEB" bug.
|
|
292
|
+
//
|
|
293
|
+
// The bulk handler used to hardcode `interface: 'WEB'` (uppercase),
|
|
294
|
+
// but orchestrator.processRequest validates against INTERFACE_TYPES
|
|
295
|
+
// — which uses LOWERCASE strings ('web', 'cli', etc.). Every bulk
|
|
296
|
+
// action — delete, pause, resume, unload, set-model, enable-tool,
|
|
297
|
+
// disable-tool, archive — failed with `Invalid interface: WEB`
|
|
298
|
+
// before even dispatching. The error was caught in `runOne`'s
|
|
299
|
+
// try/catch and surfaced as per-agent failure ("Failed to delete
|
|
300
|
+
// N agents") with no hint at the actual cause.
|
|
301
|
+
//
|
|
302
|
+
// Lock the lowercase contract for every action that flows through
|
|
303
|
+
// processRequest, so any case-drift trips this test immediately.
|
|
304
|
+
it('forwards lowercase "web" as the interface (not "WEB") for every dispatch', async () => {
|
|
305
|
+
orch = makeOrchestrator();
|
|
306
|
+
await startServer(orch);
|
|
307
|
+
// Hit one representative of each routed action.
|
|
308
|
+
await post({ action: 'delete', agentIds: ['a'] });
|
|
309
|
+
await post({ action: 'pause', agentIds: ['a'] });
|
|
310
|
+
await post({ action: 'unload', agentIds: ['a'] });
|
|
311
|
+
await post({ action: 'set-model', agentIds: ['a'], params: { model: 'k' } });
|
|
312
|
+
|
|
313
|
+
// Every call to processRequest must have used lowercase 'web'.
|
|
314
|
+
for (const call of orch.processRequest.mock.calls) {
|
|
315
|
+
expect(call[0].interface).toBe('web');
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('falls flat if the orchestrator rejects "WEB" — regression of the original bug', async () => {
|
|
320
|
+
// Simulate the real orchestrator's validation: throw on anything
|
|
321
|
+
// not matching INTERFACE_TYPES.WEB ('web'). With the fix, every
|
|
322
|
+
// call should succeed because the handler now passes lowercase.
|
|
323
|
+
orch = makeOrchestrator({
|
|
324
|
+
processRequest: jest.fn(async ({ interface: iface }) => {
|
|
325
|
+
if (iface !== 'web') throw new Error(`Invalid interface: ${iface}`);
|
|
326
|
+
return { success: true };
|
|
327
|
+
}),
|
|
328
|
+
});
|
|
329
|
+
await startServer(orch);
|
|
330
|
+
const r = await post({ action: 'delete', agentIds: ['a', 'b'] });
|
|
331
|
+
expect(r.body.success).toBe(true);
|
|
332
|
+
expect(r.body.summary).toEqual({ total: 2, ok: 2, failed: 0 });
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Regression guard for the My Squadron > group operation (delete) bug.
|
|
336
|
+
// Before the fix, deleting selected agents in the UI failed with
|
|
337
|
+
// "Failed to delete N agents" whenever any of them was unloaded —
|
|
338
|
+
// the bulk handler let `delete` flow through to the orchestrator, but
|
|
339
|
+
// the orchestrator's _handleDeleteAgent threw "Agent not found" for
|
|
340
|
+
// anything not in the live pool. Now agentPool.deleteAgent is
|
|
341
|
+
// idempotent (clears persistent state regardless of pool membership)
|
|
342
|
+
// and the orchestrator surfaces that success up through processRequest.
|
|
343
|
+
// From the bulk route's perspective: every missing-prefixed agent
|
|
344
|
+
// should report success.
|
|
345
|
+
it('delete against missing/unloaded agents succeeds (idempotent cleanup)', async () => {
|
|
346
|
+
orch = makeOrchestrator();
|
|
347
|
+
await startServer(orch);
|
|
348
|
+
const r = await post({ action: 'delete', agentIds: ['missing-1', 'missing-2', 'a'] });
|
|
349
|
+
expect(r.status).toBe(200);
|
|
350
|
+
expect(r.body.success).toBe(true);
|
|
351
|
+
expect(r.body.summary).toEqual({ total: 3, ok: 3, failed: 0 });
|
|
352
|
+
// All three reached processRequest (no short-circuit on missing).
|
|
353
|
+
expect(orch.processRequest).toHaveBeenCalledTimes(3);
|
|
354
|
+
for (const id of ['missing-1', 'missing-2', 'a']) {
|
|
355
|
+
expect(orch.processRequest).toHaveBeenCalledWith(expect.objectContaining({
|
|
356
|
+
action: 'delete_agent',
|
|
357
|
+
payload: expect.objectContaining({ agentId: id }),
|
|
358
|
+
}));
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
});
|