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,417 +1,417 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EmbeddingService orchestrator tests.
|
|
3
|
-
*
|
|
4
|
-
* Pins:
|
|
5
|
-
* - 'disabled' is a first-class state (consumers can detect via isEnabled).
|
|
6
|
-
* - Provider is lazy-instantiated based on config; hot-swap via updateConfig.
|
|
7
|
-
* - VectorStores are per-scope, get-or-created, file-backed under stateDir.
|
|
8
|
-
* - Telemetry tracks calls + tokens, per-day buckets, and budget enforcement.
|
|
9
|
-
* - Budget exhaustion throws RATE_LIMITED; local provider is exempt
|
|
10
|
-
* from the cloud budget cap.
|
|
11
|
-
* - Health surface reflects provider readiness + budget state.
|
|
12
|
-
* - Per-scope store path layout matches the documented contract
|
|
13
|
-
* (agents/<id>/<surface>.vec.json vs. global/<surface>.vec.json).
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { describe, test, expect, jest, beforeEach, afterEach } from '@jest/globals';
|
|
17
|
-
import os from 'os';
|
|
18
|
-
import fsp from 'fs/promises';
|
|
19
|
-
import path from 'path';
|
|
20
|
-
import { EmbeddingService } from '../embeddingService.js';
|
|
21
|
-
import {
|
|
22
|
-
EmbeddingError,
|
|
23
|
-
EMBEDDING_ERROR_CODES,
|
|
24
|
-
EmbeddingProvider,
|
|
25
|
-
} from '../providerInterface.js';
|
|
26
|
-
|
|
27
|
-
class StubProvider extends EmbeddingProvider {
|
|
28
|
-
constructor({ dimensions = 4, name = 'azure', fingerprint = 'azure:stub-v1' } = {}) {
|
|
29
|
-
super();
|
|
30
|
-
this._info = {
|
|
31
|
-
name,
|
|
32
|
-
modelId: 'stub-model',
|
|
33
|
-
dimensions,
|
|
34
|
-
maxBatchSize: 16,
|
|
35
|
-
maxTokensPerText: 100,
|
|
36
|
-
distinguishesQueryAndDocument: false,
|
|
37
|
-
isLocal: name === 'local',
|
|
38
|
-
};
|
|
39
|
-
this._fingerprint = fingerprint;
|
|
40
|
-
this.calls = 0;
|
|
41
|
-
}
|
|
42
|
-
getInfo() { return this._info; }
|
|
43
|
-
getModelFingerprint() { return this._fingerprint; }
|
|
44
|
-
async _embed(texts) {
|
|
45
|
-
this.calls += texts.length;
|
|
46
|
-
return texts.map((_, i) =>
|
|
47
|
-
Array.from({ length: this._info.dimensions }, (_, j) => (i + 1) * (j + 1) * 0.1));
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Build an EmbeddingService with stub providers + fake apiKeyManager. */
|
|
52
|
-
function makeService({ tmpDir, providerName = 'azure', dimensions = 4, configExtra = {} }) {
|
|
53
|
-
const apiKeyManager = {
|
|
54
|
-
getEmbeddingApiKey: jest.fn(() => 'stub-key'),
|
|
55
|
-
};
|
|
56
|
-
const stubProvider = new StubProvider({ name: providerName, dimensions });
|
|
57
|
-
const svc = new EmbeddingService({
|
|
58
|
-
config: { provider: providerName, ...configExtra },
|
|
59
|
-
apiKeyManager,
|
|
60
|
-
baseUrl: 'https://stub-backend.example',
|
|
61
|
-
stateDir: tmpDir,
|
|
62
|
-
providerOverrides: { [providerName]: () => stubProvider },
|
|
63
|
-
});
|
|
64
|
-
return { svc, stubProvider, apiKeyManager };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
describe('EmbeddingService — disabled by default', () => {
|
|
68
|
-
let tmpDir;
|
|
69
|
-
beforeEach(async () => {
|
|
70
|
-
tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-'));
|
|
71
|
-
});
|
|
72
|
-
afterEach(async () => {
|
|
73
|
-
await fsp.rm(tmpDir, { recursive: true, force: true });
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('no config → disabled', () => {
|
|
77
|
-
const svc = new EmbeddingService({
|
|
78
|
-
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
79
|
-
baseUrl: 'https://x',
|
|
80
|
-
stateDir: tmpDir,
|
|
81
|
-
});
|
|
82
|
-
expect(svc.isEnabled).toBe(false);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test('disabled state: embedQuery throws NOT_INITIALIZED', async () => {
|
|
86
|
-
const svc = new EmbeddingService({
|
|
87
|
-
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
88
|
-
baseUrl: 'https://x',
|
|
89
|
-
stateDir: tmpDir,
|
|
90
|
-
});
|
|
91
|
-
await expect(svc.embedQuery('hi')).rejects.toMatchObject({
|
|
92
|
-
code: EMBEDDING_ERROR_CODES.NOT_INITIALIZED,
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('disabled state: health() returns ok:false with reason', async () => {
|
|
97
|
-
const svc = new EmbeddingService({
|
|
98
|
-
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
99
|
-
baseUrl: 'https://x',
|
|
100
|
-
stateDir: tmpDir,
|
|
101
|
-
});
|
|
102
|
-
const h = await svc.health();
|
|
103
|
-
expect(h.ok).toBe(false);
|
|
104
|
-
expect(h.reason).toMatch(/disabled/i);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test('disabled state: getStore throws', async () => {
|
|
108
|
-
const svc = new EmbeddingService({
|
|
109
|
-
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
110
|
-
baseUrl: 'https://x',
|
|
111
|
-
stateDir: tmpDir,
|
|
112
|
-
});
|
|
113
|
-
await expect(svc.getStore({ agentId: 'a1', surface: 'memory' })).rejects.toThrow(/disabled/i);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('EmbeddingService — provider routing', () => {
|
|
118
|
-
let tmpDir;
|
|
119
|
-
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
120
|
-
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
121
|
-
|
|
122
|
-
test('provider=azure: uses azure factory', () => {
|
|
123
|
-
const { svc, stubProvider } = makeService({ tmpDir, providerName: 'azure' });
|
|
124
|
-
expect(svc.getProvider()).toBe(stubProvider);
|
|
125
|
-
expect(svc.getProvider().getInfo().name).toBe('azure');
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('provider lookup is lazy (no work until first request)', () => {
|
|
129
|
-
const apiKeyManager = { getEmbeddingApiKey: jest.fn(() => 'k') };
|
|
130
|
-
const factory = jest.fn(() => new StubProvider());
|
|
131
|
-
const svc = new EmbeddingService({
|
|
132
|
-
config: { provider: 'azure' },
|
|
133
|
-
apiKeyManager,
|
|
134
|
-
baseUrl: 'https://x',
|
|
135
|
-
stateDir: tmpDir,
|
|
136
|
-
providerOverrides: { azure: factory },
|
|
137
|
-
});
|
|
138
|
-
expect(factory).not.toHaveBeenCalled();
|
|
139
|
-
svc.getProvider();
|
|
140
|
-
expect(factory).toHaveBeenCalledTimes(1);
|
|
141
|
-
svc.getProvider();
|
|
142
|
-
expect(factory).toHaveBeenCalledTimes(1); // cached
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test('unknown provider name surfaces NOT_INITIALIZED', () => {
|
|
146
|
-
const svc = new EmbeddingService({
|
|
147
|
-
config: { provider: 'cohere' },
|
|
148
|
-
apiKeyManager: { getEmbeddingApiKey: () => 'k' },
|
|
149
|
-
baseUrl: 'https://x',
|
|
150
|
-
stateDir: tmpDir,
|
|
151
|
-
});
|
|
152
|
-
expect(() => svc.getProvider()).toThrow(/cohere/);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test('updateConfig hot-swaps the provider', async () => {
|
|
156
|
-
const { svc, stubProvider } = makeService({ tmpDir, providerName: 'azure' });
|
|
157
|
-
expect(svc.getProvider()).toBe(stubProvider);
|
|
158
|
-
const local = new StubProvider({ name: 'local', dimensions: 8, fingerprint: 'local:foo' });
|
|
159
|
-
await svc.updateConfig({ provider: 'local' });
|
|
160
|
-
// After updateConfig we need a new override for the new provider name.
|
|
161
|
-
svc._providerOverrides.local = () => local;
|
|
162
|
-
expect(svc.getProvider()).toBe(local);
|
|
163
|
-
expect(svc.getProvider()).not.toBe(stubProvider);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
describe('EmbeddingService — embed surfaces + telemetry', () => {
|
|
168
|
-
let tmpDir;
|
|
169
|
-
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
170
|
-
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
171
|
-
|
|
172
|
-
test('embedQuery increments telemetry', async () => {
|
|
173
|
-
const { svc } = makeService({ tmpDir });
|
|
174
|
-
expect(svc.getTelemetry().callsTotal).toBe(0);
|
|
175
|
-
await svc.embedQuery('hello world');
|
|
176
|
-
const t = svc.getTelemetry();
|
|
177
|
-
expect(t.callsTotal).toBe(1);
|
|
178
|
-
expect(t.tokensTotal).toBeGreaterThan(0);
|
|
179
|
-
expect(t.today.calls).toBe(1);
|
|
180
|
-
expect(t.lastCallAt).not.toBeNull();
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test('embedDocuments counts batch size in call total', async () => {
|
|
184
|
-
const { svc } = makeService({ tmpDir });
|
|
185
|
-
await svc.embedDocuments(['a', 'b', 'c']);
|
|
186
|
-
expect(svc.getTelemetry().callsTotal).toBe(3);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test('lastError is captured on failure', async () => {
|
|
190
|
-
const apiKeyManager = { getEmbeddingApiKey: () => 'k' };
|
|
191
|
-
class FailingProvider extends StubProvider {
|
|
192
|
-
async _embed() {
|
|
193
|
-
throw new EmbeddingError(EMBEDDING_ERROR_CODES.RATE_LIMITED, 'too fast', { provider: 'azure', transient: true });
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
const svc = new EmbeddingService({
|
|
197
|
-
config: { provider: 'azure' },
|
|
198
|
-
apiKeyManager,
|
|
199
|
-
baseUrl: 'https://x',
|
|
200
|
-
stateDir: tmpDir,
|
|
201
|
-
providerOverrides: { azure: () => new FailingProvider() },
|
|
202
|
-
});
|
|
203
|
-
await expect(svc.embedQuery('hi')).rejects.toMatchObject({ code: EMBEDDING_ERROR_CODES.RATE_LIMITED });
|
|
204
|
-
const t = svc.getTelemetry();
|
|
205
|
-
expect(t.lastError).toMatchObject({ code: 'RATE_LIMITED' });
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
describe('EmbeddingService — budget enforcement', () => {
|
|
210
|
-
let tmpDir;
|
|
211
|
-
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
212
|
-
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
213
|
-
|
|
214
|
-
test('exceeding daily token limit (cloud) throws RATE_LIMITED', async () => {
|
|
215
|
-
const { svc } = makeService({
|
|
216
|
-
tmpDir,
|
|
217
|
-
providerName: 'azure',
|
|
218
|
-
configExtra: { budget: { dailyTokenLimit: 4 } }, // ~16 chars total
|
|
219
|
-
});
|
|
220
|
-
// First call uses ~estimateTokens('hello world')=3 tokens. Allowed.
|
|
221
|
-
await svc.embedQuery('hello world');
|
|
222
|
-
// Now should still allow the next small call but block once we cross 4.
|
|
223
|
-
await expect(svc.embedQuery('hello world')).rejects.toMatchObject({
|
|
224
|
-
code: EMBEDDING_ERROR_CODES.RATE_LIMITED,
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test('local provider is exempt from the cloud token cap', async () => {
|
|
229
|
-
const { svc } = makeService({
|
|
230
|
-
tmpDir,
|
|
231
|
-
providerName: 'local',
|
|
232
|
-
configExtra: { budget: { dailyTokenLimit: 1 } }, // trivially exceeded
|
|
233
|
-
});
|
|
234
|
-
// Many calls; none should hit the budget.
|
|
235
|
-
for (let i = 0; i < 5; i++) {
|
|
236
|
-
|
|
237
|
-
await svc.embedQuery('a longer string that would exceed 1 token easily');
|
|
238
|
-
}
|
|
239
|
-
expect(svc.getTelemetry().callsTotal).toBe(5);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test('dailyTokenLimit=0 disables the cap entirely', async () => {
|
|
243
|
-
const { svc } = makeService({
|
|
244
|
-
tmpDir,
|
|
245
|
-
providerName: 'azure',
|
|
246
|
-
configExtra: { budget: { dailyTokenLimit: 0 } },
|
|
247
|
-
});
|
|
248
|
-
for (let i = 0; i < 3; i++) {
|
|
249
|
-
|
|
250
|
-
await svc.embedQuery('hello world');
|
|
251
|
-
}
|
|
252
|
-
expect(svc.getTelemetry().callsTotal).toBe(3);
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
describe('EmbeddingService — VectorStore per-scope', () => {
|
|
257
|
-
let tmpDir;
|
|
258
|
-
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
259
|
-
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
260
|
-
|
|
261
|
-
test('agent-scoped store path = embeddings/agents/<id>/<surface>.vec.json', async () => {
|
|
262
|
-
const { svc } = makeService({ tmpDir });
|
|
263
|
-
const store = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
264
|
-
expect(store._filePath).toBe(path.join(tmpDir, 'embeddings', 'agents', 'a1', 'memory.vec.json'));
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test('global-scoped store path = embeddings/global/<surface>.vec.json', async () => {
|
|
268
|
-
const { svc } = makeService({ tmpDir });
|
|
269
|
-
const store = await svc.getStore({ surface: 'composio' });
|
|
270
|
-
expect(store._filePath).toBe(path.join(tmpDir, 'embeddings', 'global', 'composio.vec.json'));
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test('getStore is get-or-create: second call returns same instance', async () => {
|
|
274
|
-
const { svc } = makeService({ tmpDir });
|
|
275
|
-
const a = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
276
|
-
const b = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
277
|
-
expect(a).toBe(b);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test('different scopes return different stores', async () => {
|
|
281
|
-
const { svc } = makeService({ tmpDir });
|
|
282
|
-
const a = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
283
|
-
const b = await svc.getStore({ agentId: 'a2', surface: 'memory' });
|
|
284
|
-
const c = await svc.getStore({ surface: 'composio' });
|
|
285
|
-
expect(a).not.toBe(b);
|
|
286
|
-
expect(a).not.toBe(c);
|
|
287
|
-
expect(b).not.toBe(c);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
test('store carries provider fingerprint + dimensions', async () => {
|
|
291
|
-
const { svc, stubProvider } = makeService({ tmpDir, dimensions: 8 });
|
|
292
|
-
const store = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
293
|
-
const s = await store.stats();
|
|
294
|
-
expect(s.dimensions).toBe(8);
|
|
295
|
-
expect(s.modelFingerprint).toBe(stubProvider.getModelFingerprint());
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test('updateConfig flushes the store map (new provider = re-load)', async () => {
|
|
299
|
-
const { svc } = makeService({ tmpDir, providerName: 'azure' });
|
|
300
|
-
const before = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
301
|
-
await svc.updateConfig({ provider: 'local' });
|
|
302
|
-
svc._providerOverrides.local = () => new StubProvider({ name: 'local', dimensions: 8, fingerprint: 'local:swap' });
|
|
303
|
-
const after = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
304
|
-
expect(after).not.toBe(before);
|
|
305
|
-
expect((await after.stats()).modelFingerprint).toBe('local:swap');
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
test('scope missing surface throws', async () => {
|
|
309
|
-
const { svc } = makeService({ tmpDir });
|
|
310
|
-
await expect(svc.getStore({ agentId: 'a1' })).rejects.toThrow(/surface/);
|
|
311
|
-
await expect(svc.getStore({})).rejects.toThrow(/surface/);
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
describe('EmbeddingService — warmup', () => {
|
|
316
|
-
let tmpDir;
|
|
317
|
-
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
318
|
-
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
319
|
-
|
|
320
|
-
test('warmup calls provider.warmup() when available', async () => {
|
|
321
|
-
class LocalLike extends StubProvider {
|
|
322
|
-
constructor() { super({ name: 'local' }); this.warmupCalled = 0; }
|
|
323
|
-
async warmup() { this.warmupCalled++; }
|
|
324
|
-
}
|
|
325
|
-
const provider = new LocalLike();
|
|
326
|
-
const svc = new EmbeddingService({
|
|
327
|
-
config: { provider: 'local' },
|
|
328
|
-
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
329
|
-
baseUrl: 'https://x',
|
|
330
|
-
stateDir: tmpDir,
|
|
331
|
-
providerOverrides: { local: () => provider },
|
|
332
|
-
});
|
|
333
|
-
await svc.warmup();
|
|
334
|
-
expect(provider.warmupCalled).toBe(1);
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
test('warmup is a no-op when disabled', async () => {
|
|
338
|
-
const svc = new EmbeddingService({
|
|
339
|
-
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
340
|
-
baseUrl: 'https://x',
|
|
341
|
-
stateDir: tmpDir,
|
|
342
|
-
});
|
|
343
|
-
await expect(svc.warmup()).resolves.toBeUndefined();
|
|
344
|
-
});
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
describe('EmbeddingService — config persistence (Settings UI path)', () => {
|
|
348
|
-
let tmpDir;
|
|
349
|
-
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-cfg-')); });
|
|
350
|
-
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
351
|
-
|
|
352
|
-
function makeSvc(extra = {}) {
|
|
353
|
-
return new EmbeddingService({
|
|
354
|
-
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
355
|
-
baseUrl: 'https://x',
|
|
356
|
-
stateDir: tmpDir,
|
|
357
|
-
...extra,
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
test('updateConfig({persist:true}) writes to disk; loadPersistedConfig reads it back', async () => {
|
|
362
|
-
const first = makeSvc();
|
|
363
|
-
await first.updateConfig({ provider: 'azure', budget: { dailyTokenLimit: 500 } }, { persist: true });
|
|
364
|
-
expect(first.config.provider).toBe('azure');
|
|
365
|
-
|
|
366
|
-
const second = makeSvc();
|
|
367
|
-
expect(second.config.provider).toBe('disabled'); // default before load
|
|
368
|
-
const loaded = await second.loadPersistedConfig();
|
|
369
|
-
expect(loaded).toBe(true);
|
|
370
|
-
expect(second.config.provider).toBe('azure');
|
|
371
|
-
expect(second.config.budget.dailyTokenLimit).toBe(500);
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
test('updateConfig({persist:false}) does NOT touch the file', async () => {
|
|
375
|
-
const svc = makeSvc();
|
|
376
|
-
await svc.updateConfig({ provider: 'local' }); // default persist=false
|
|
377
|
-
|
|
378
|
-
const second = makeSvc();
|
|
379
|
-
const loaded = await second.loadPersistedConfig();
|
|
380
|
-
expect(loaded).toBe(false); // nothing on disk
|
|
381
|
-
expect(second.config.provider).toBe('disabled');
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
test('loadPersistedConfig is graceful when the file is missing', async () => {
|
|
385
|
-
const svc = makeSvc();
|
|
386
|
-
const loaded = await svc.loadPersistedConfig();
|
|
387
|
-
expect(loaded).toBe(false);
|
|
388
|
-
expect(svc.config.provider).toBe('disabled');
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
test('loadPersistedConfig is graceful with corrupt JSON', async () => {
|
|
392
|
-
const file = path.join(tmpDir, 'embeddings', 'embeddings-config.json');
|
|
393
|
-
await fsp.mkdir(path.dirname(file), { recursive: true });
|
|
394
|
-
await fsp.writeFile(file, 'not json{{');
|
|
395
|
-
const svc = makeSvc();
|
|
396
|
-
const loaded = await svc.loadPersistedConfig();
|
|
397
|
-
expect(loaded).toBe(false); // ignored
|
|
398
|
-
expect(svc.config.provider).toBe('disabled'); // safe default
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
test('persist uses atomic .tmp + rename (no partial file on disk after success)', async () => {
|
|
402
|
-
const svc = makeSvc();
|
|
403
|
-
await svc.updateConfig({ provider: 'azure' }, { persist: true });
|
|
404
|
-
const dir = path.join(tmpDir, 'embeddings');
|
|
405
|
-
const entries = await fsp.readdir(dir);
|
|
406
|
-
expect(entries).toContain('embeddings-config.json');
|
|
407
|
-
expect(entries.filter(e => e.endsWith('.tmp'))).toEqual([]);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
test('invalid config does NOT persist (validation fails first)', async () => {
|
|
411
|
-
const svc = makeSvc();
|
|
412
|
-
const result = await svc.updateConfig({ provider: 'cohere' }, { persist: true });
|
|
413
|
-
expect(result.ok).toBe(false);
|
|
414
|
-
const file = path.join(tmpDir, 'embeddings', 'embeddings-config.json');
|
|
415
|
-
await expect(fsp.access(file)).rejects.toMatchObject({ code: 'ENOENT' });
|
|
416
|
-
});
|
|
417
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* EmbeddingService orchestrator tests.
|
|
3
|
+
*
|
|
4
|
+
* Pins:
|
|
5
|
+
* - 'disabled' is a first-class state (consumers can detect via isEnabled).
|
|
6
|
+
* - Provider is lazy-instantiated based on config; hot-swap via updateConfig.
|
|
7
|
+
* - VectorStores are per-scope, get-or-created, file-backed under stateDir.
|
|
8
|
+
* - Telemetry tracks calls + tokens, per-day buckets, and budget enforcement.
|
|
9
|
+
* - Budget exhaustion throws RATE_LIMITED; local provider is exempt
|
|
10
|
+
* from the cloud budget cap.
|
|
11
|
+
* - Health surface reflects provider readiness + budget state.
|
|
12
|
+
* - Per-scope store path layout matches the documented contract
|
|
13
|
+
* (agents/<id>/<surface>.vec.json vs. global/<surface>.vec.json).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { describe, test, expect, jest, beforeEach, afterEach } from '@jest/globals';
|
|
17
|
+
import os from 'os';
|
|
18
|
+
import fsp from 'fs/promises';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import { EmbeddingService } from '../embeddingService.js';
|
|
21
|
+
import {
|
|
22
|
+
EmbeddingError,
|
|
23
|
+
EMBEDDING_ERROR_CODES,
|
|
24
|
+
EmbeddingProvider,
|
|
25
|
+
} from '../providerInterface.js';
|
|
26
|
+
|
|
27
|
+
class StubProvider extends EmbeddingProvider {
|
|
28
|
+
constructor({ dimensions = 4, name = 'azure', fingerprint = 'azure:stub-v1' } = {}) {
|
|
29
|
+
super();
|
|
30
|
+
this._info = {
|
|
31
|
+
name,
|
|
32
|
+
modelId: 'stub-model',
|
|
33
|
+
dimensions,
|
|
34
|
+
maxBatchSize: 16,
|
|
35
|
+
maxTokensPerText: 100,
|
|
36
|
+
distinguishesQueryAndDocument: false,
|
|
37
|
+
isLocal: name === 'local',
|
|
38
|
+
};
|
|
39
|
+
this._fingerprint = fingerprint;
|
|
40
|
+
this.calls = 0;
|
|
41
|
+
}
|
|
42
|
+
getInfo() { return this._info; }
|
|
43
|
+
getModelFingerprint() { return this._fingerprint; }
|
|
44
|
+
async _embed(texts) {
|
|
45
|
+
this.calls += texts.length;
|
|
46
|
+
return texts.map((_, i) =>
|
|
47
|
+
Array.from({ length: this._info.dimensions }, (_, j) => (i + 1) * (j + 1) * 0.1));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Build an EmbeddingService with stub providers + fake apiKeyManager. */
|
|
52
|
+
function makeService({ tmpDir, providerName = 'azure', dimensions = 4, configExtra = {} }) {
|
|
53
|
+
const apiKeyManager = {
|
|
54
|
+
getEmbeddingApiKey: jest.fn(() => 'stub-key'),
|
|
55
|
+
};
|
|
56
|
+
const stubProvider = new StubProvider({ name: providerName, dimensions });
|
|
57
|
+
const svc = new EmbeddingService({
|
|
58
|
+
config: { provider: providerName, ...configExtra },
|
|
59
|
+
apiKeyManager,
|
|
60
|
+
baseUrl: 'https://stub-backend.example',
|
|
61
|
+
stateDir: tmpDir,
|
|
62
|
+
providerOverrides: { [providerName]: () => stubProvider },
|
|
63
|
+
});
|
|
64
|
+
return { svc, stubProvider, apiKeyManager };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
describe('EmbeddingService — disabled by default', () => {
|
|
68
|
+
let tmpDir;
|
|
69
|
+
beforeEach(async () => {
|
|
70
|
+
tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-'));
|
|
71
|
+
});
|
|
72
|
+
afterEach(async () => {
|
|
73
|
+
await fsp.rm(tmpDir, { recursive: true, force: true });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('no config → disabled', () => {
|
|
77
|
+
const svc = new EmbeddingService({
|
|
78
|
+
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
79
|
+
baseUrl: 'https://x',
|
|
80
|
+
stateDir: tmpDir,
|
|
81
|
+
});
|
|
82
|
+
expect(svc.isEnabled).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('disabled state: embedQuery throws NOT_INITIALIZED', async () => {
|
|
86
|
+
const svc = new EmbeddingService({
|
|
87
|
+
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
88
|
+
baseUrl: 'https://x',
|
|
89
|
+
stateDir: tmpDir,
|
|
90
|
+
});
|
|
91
|
+
await expect(svc.embedQuery('hi')).rejects.toMatchObject({
|
|
92
|
+
code: EMBEDDING_ERROR_CODES.NOT_INITIALIZED,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('disabled state: health() returns ok:false with reason', async () => {
|
|
97
|
+
const svc = new EmbeddingService({
|
|
98
|
+
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
99
|
+
baseUrl: 'https://x',
|
|
100
|
+
stateDir: tmpDir,
|
|
101
|
+
});
|
|
102
|
+
const h = await svc.health();
|
|
103
|
+
expect(h.ok).toBe(false);
|
|
104
|
+
expect(h.reason).toMatch(/disabled/i);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('disabled state: getStore throws', async () => {
|
|
108
|
+
const svc = new EmbeddingService({
|
|
109
|
+
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
110
|
+
baseUrl: 'https://x',
|
|
111
|
+
stateDir: tmpDir,
|
|
112
|
+
});
|
|
113
|
+
await expect(svc.getStore({ agentId: 'a1', surface: 'memory' })).rejects.toThrow(/disabled/i);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('EmbeddingService — provider routing', () => {
|
|
118
|
+
let tmpDir;
|
|
119
|
+
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
120
|
+
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
121
|
+
|
|
122
|
+
test('provider=azure: uses azure factory', () => {
|
|
123
|
+
const { svc, stubProvider } = makeService({ tmpDir, providerName: 'azure' });
|
|
124
|
+
expect(svc.getProvider()).toBe(stubProvider);
|
|
125
|
+
expect(svc.getProvider().getInfo().name).toBe('azure');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('provider lookup is lazy (no work until first request)', () => {
|
|
129
|
+
const apiKeyManager = { getEmbeddingApiKey: jest.fn(() => 'k') };
|
|
130
|
+
const factory = jest.fn(() => new StubProvider());
|
|
131
|
+
const svc = new EmbeddingService({
|
|
132
|
+
config: { provider: 'azure' },
|
|
133
|
+
apiKeyManager,
|
|
134
|
+
baseUrl: 'https://x',
|
|
135
|
+
stateDir: tmpDir,
|
|
136
|
+
providerOverrides: { azure: factory },
|
|
137
|
+
});
|
|
138
|
+
expect(factory).not.toHaveBeenCalled();
|
|
139
|
+
svc.getProvider();
|
|
140
|
+
expect(factory).toHaveBeenCalledTimes(1);
|
|
141
|
+
svc.getProvider();
|
|
142
|
+
expect(factory).toHaveBeenCalledTimes(1); // cached
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('unknown provider name surfaces NOT_INITIALIZED', () => {
|
|
146
|
+
const svc = new EmbeddingService({
|
|
147
|
+
config: { provider: 'cohere' },
|
|
148
|
+
apiKeyManager: { getEmbeddingApiKey: () => 'k' },
|
|
149
|
+
baseUrl: 'https://x',
|
|
150
|
+
stateDir: tmpDir,
|
|
151
|
+
});
|
|
152
|
+
expect(() => svc.getProvider()).toThrow(/cohere/);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('updateConfig hot-swaps the provider', async () => {
|
|
156
|
+
const { svc, stubProvider } = makeService({ tmpDir, providerName: 'azure' });
|
|
157
|
+
expect(svc.getProvider()).toBe(stubProvider);
|
|
158
|
+
const local = new StubProvider({ name: 'local', dimensions: 8, fingerprint: 'local:foo' });
|
|
159
|
+
await svc.updateConfig({ provider: 'local' });
|
|
160
|
+
// After updateConfig we need a new override for the new provider name.
|
|
161
|
+
svc._providerOverrides.local = () => local;
|
|
162
|
+
expect(svc.getProvider()).toBe(local);
|
|
163
|
+
expect(svc.getProvider()).not.toBe(stubProvider);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('EmbeddingService — embed surfaces + telemetry', () => {
|
|
168
|
+
let tmpDir;
|
|
169
|
+
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
170
|
+
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
171
|
+
|
|
172
|
+
test('embedQuery increments telemetry', async () => {
|
|
173
|
+
const { svc } = makeService({ tmpDir });
|
|
174
|
+
expect(svc.getTelemetry().callsTotal).toBe(0);
|
|
175
|
+
await svc.embedQuery('hello world');
|
|
176
|
+
const t = svc.getTelemetry();
|
|
177
|
+
expect(t.callsTotal).toBe(1);
|
|
178
|
+
expect(t.tokensTotal).toBeGreaterThan(0);
|
|
179
|
+
expect(t.today.calls).toBe(1);
|
|
180
|
+
expect(t.lastCallAt).not.toBeNull();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('embedDocuments counts batch size in call total', async () => {
|
|
184
|
+
const { svc } = makeService({ tmpDir });
|
|
185
|
+
await svc.embedDocuments(['a', 'b', 'c']);
|
|
186
|
+
expect(svc.getTelemetry().callsTotal).toBe(3);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('lastError is captured on failure', async () => {
|
|
190
|
+
const apiKeyManager = { getEmbeddingApiKey: () => 'k' };
|
|
191
|
+
class FailingProvider extends StubProvider {
|
|
192
|
+
async _embed() {
|
|
193
|
+
throw new EmbeddingError(EMBEDDING_ERROR_CODES.RATE_LIMITED, 'too fast', { provider: 'azure', transient: true });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const svc = new EmbeddingService({
|
|
197
|
+
config: { provider: 'azure' },
|
|
198
|
+
apiKeyManager,
|
|
199
|
+
baseUrl: 'https://x',
|
|
200
|
+
stateDir: tmpDir,
|
|
201
|
+
providerOverrides: { azure: () => new FailingProvider() },
|
|
202
|
+
});
|
|
203
|
+
await expect(svc.embedQuery('hi')).rejects.toMatchObject({ code: EMBEDDING_ERROR_CODES.RATE_LIMITED });
|
|
204
|
+
const t = svc.getTelemetry();
|
|
205
|
+
expect(t.lastError).toMatchObject({ code: 'RATE_LIMITED' });
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('EmbeddingService — budget enforcement', () => {
|
|
210
|
+
let tmpDir;
|
|
211
|
+
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
212
|
+
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
213
|
+
|
|
214
|
+
test('exceeding daily token limit (cloud) throws RATE_LIMITED', async () => {
|
|
215
|
+
const { svc } = makeService({
|
|
216
|
+
tmpDir,
|
|
217
|
+
providerName: 'azure',
|
|
218
|
+
configExtra: { budget: { dailyTokenLimit: 4 } }, // ~16 chars total
|
|
219
|
+
});
|
|
220
|
+
// First call uses ~estimateTokens('hello world')=3 tokens. Allowed.
|
|
221
|
+
await svc.embedQuery('hello world');
|
|
222
|
+
// Now should still allow the next small call but block once we cross 4.
|
|
223
|
+
await expect(svc.embedQuery('hello world')).rejects.toMatchObject({
|
|
224
|
+
code: EMBEDDING_ERROR_CODES.RATE_LIMITED,
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('local provider is exempt from the cloud token cap', async () => {
|
|
229
|
+
const { svc } = makeService({
|
|
230
|
+
tmpDir,
|
|
231
|
+
providerName: 'local',
|
|
232
|
+
configExtra: { budget: { dailyTokenLimit: 1 } }, // trivially exceeded
|
|
233
|
+
});
|
|
234
|
+
// Many calls; none should hit the budget.
|
|
235
|
+
for (let i = 0; i < 5; i++) {
|
|
236
|
+
|
|
237
|
+
await svc.embedQuery('a longer string that would exceed 1 token easily');
|
|
238
|
+
}
|
|
239
|
+
expect(svc.getTelemetry().callsTotal).toBe(5);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('dailyTokenLimit=0 disables the cap entirely', async () => {
|
|
243
|
+
const { svc } = makeService({
|
|
244
|
+
tmpDir,
|
|
245
|
+
providerName: 'azure',
|
|
246
|
+
configExtra: { budget: { dailyTokenLimit: 0 } },
|
|
247
|
+
});
|
|
248
|
+
for (let i = 0; i < 3; i++) {
|
|
249
|
+
|
|
250
|
+
await svc.embedQuery('hello world');
|
|
251
|
+
}
|
|
252
|
+
expect(svc.getTelemetry().callsTotal).toBe(3);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe('EmbeddingService — VectorStore per-scope', () => {
|
|
257
|
+
let tmpDir;
|
|
258
|
+
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
259
|
+
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
260
|
+
|
|
261
|
+
test('agent-scoped store path = embeddings/agents/<id>/<surface>.vec.json', async () => {
|
|
262
|
+
const { svc } = makeService({ tmpDir });
|
|
263
|
+
const store = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
264
|
+
expect(store._filePath).toBe(path.join(tmpDir, 'embeddings', 'agents', 'a1', 'memory.vec.json'));
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('global-scoped store path = embeddings/global/<surface>.vec.json', async () => {
|
|
268
|
+
const { svc } = makeService({ tmpDir });
|
|
269
|
+
const store = await svc.getStore({ surface: 'composio' });
|
|
270
|
+
expect(store._filePath).toBe(path.join(tmpDir, 'embeddings', 'global', 'composio.vec.json'));
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test('getStore is get-or-create: second call returns same instance', async () => {
|
|
274
|
+
const { svc } = makeService({ tmpDir });
|
|
275
|
+
const a = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
276
|
+
const b = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
277
|
+
expect(a).toBe(b);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test('different scopes return different stores', async () => {
|
|
281
|
+
const { svc } = makeService({ tmpDir });
|
|
282
|
+
const a = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
283
|
+
const b = await svc.getStore({ agentId: 'a2', surface: 'memory' });
|
|
284
|
+
const c = await svc.getStore({ surface: 'composio' });
|
|
285
|
+
expect(a).not.toBe(b);
|
|
286
|
+
expect(a).not.toBe(c);
|
|
287
|
+
expect(b).not.toBe(c);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test('store carries provider fingerprint + dimensions', async () => {
|
|
291
|
+
const { svc, stubProvider } = makeService({ tmpDir, dimensions: 8 });
|
|
292
|
+
const store = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
293
|
+
const s = await store.stats();
|
|
294
|
+
expect(s.dimensions).toBe(8);
|
|
295
|
+
expect(s.modelFingerprint).toBe(stubProvider.getModelFingerprint());
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test('updateConfig flushes the store map (new provider = re-load)', async () => {
|
|
299
|
+
const { svc } = makeService({ tmpDir, providerName: 'azure' });
|
|
300
|
+
const before = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
301
|
+
await svc.updateConfig({ provider: 'local' });
|
|
302
|
+
svc._providerOverrides.local = () => new StubProvider({ name: 'local', dimensions: 8, fingerprint: 'local:swap' });
|
|
303
|
+
const after = await svc.getStore({ agentId: 'a1', surface: 'memory' });
|
|
304
|
+
expect(after).not.toBe(before);
|
|
305
|
+
expect((await after.stats()).modelFingerprint).toBe('local:swap');
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('scope missing surface throws', async () => {
|
|
309
|
+
const { svc } = makeService({ tmpDir });
|
|
310
|
+
await expect(svc.getStore({ agentId: 'a1' })).rejects.toThrow(/surface/);
|
|
311
|
+
await expect(svc.getStore({})).rejects.toThrow(/surface/);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe('EmbeddingService — warmup', () => {
|
|
316
|
+
let tmpDir;
|
|
317
|
+
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-svc-')); });
|
|
318
|
+
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
319
|
+
|
|
320
|
+
test('warmup calls provider.warmup() when available', async () => {
|
|
321
|
+
class LocalLike extends StubProvider {
|
|
322
|
+
constructor() { super({ name: 'local' }); this.warmupCalled = 0; }
|
|
323
|
+
async warmup() { this.warmupCalled++; }
|
|
324
|
+
}
|
|
325
|
+
const provider = new LocalLike();
|
|
326
|
+
const svc = new EmbeddingService({
|
|
327
|
+
config: { provider: 'local' },
|
|
328
|
+
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
329
|
+
baseUrl: 'https://x',
|
|
330
|
+
stateDir: tmpDir,
|
|
331
|
+
providerOverrides: { local: () => provider },
|
|
332
|
+
});
|
|
333
|
+
await svc.warmup();
|
|
334
|
+
expect(provider.warmupCalled).toBe(1);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test('warmup is a no-op when disabled', async () => {
|
|
338
|
+
const svc = new EmbeddingService({
|
|
339
|
+
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
340
|
+
baseUrl: 'https://x',
|
|
341
|
+
stateDir: tmpDir,
|
|
342
|
+
});
|
|
343
|
+
await expect(svc.warmup()).resolves.toBeUndefined();
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
describe('EmbeddingService — config persistence (Settings UI path)', () => {
|
|
348
|
+
let tmpDir;
|
|
349
|
+
beforeEach(async () => { tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'loxia-emb-cfg-')); });
|
|
350
|
+
afterEach(async () => { await fsp.rm(tmpDir, { recursive: true, force: true }); });
|
|
351
|
+
|
|
352
|
+
function makeSvc(extra = {}) {
|
|
353
|
+
return new EmbeddingService({
|
|
354
|
+
apiKeyManager: { getEmbeddingApiKey: () => null },
|
|
355
|
+
baseUrl: 'https://x',
|
|
356
|
+
stateDir: tmpDir,
|
|
357
|
+
...extra,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
test('updateConfig({persist:true}) writes to disk; loadPersistedConfig reads it back', async () => {
|
|
362
|
+
const first = makeSvc();
|
|
363
|
+
await first.updateConfig({ provider: 'azure', budget: { dailyTokenLimit: 500 } }, { persist: true });
|
|
364
|
+
expect(first.config.provider).toBe('azure');
|
|
365
|
+
|
|
366
|
+
const second = makeSvc();
|
|
367
|
+
expect(second.config.provider).toBe('disabled'); // default before load
|
|
368
|
+
const loaded = await second.loadPersistedConfig();
|
|
369
|
+
expect(loaded).toBe(true);
|
|
370
|
+
expect(second.config.provider).toBe('azure');
|
|
371
|
+
expect(second.config.budget.dailyTokenLimit).toBe(500);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('updateConfig({persist:false}) does NOT touch the file', async () => {
|
|
375
|
+
const svc = makeSvc();
|
|
376
|
+
await svc.updateConfig({ provider: 'local' }); // default persist=false
|
|
377
|
+
|
|
378
|
+
const second = makeSvc();
|
|
379
|
+
const loaded = await second.loadPersistedConfig();
|
|
380
|
+
expect(loaded).toBe(false); // nothing on disk
|
|
381
|
+
expect(second.config.provider).toBe('disabled');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('loadPersistedConfig is graceful when the file is missing', async () => {
|
|
385
|
+
const svc = makeSvc();
|
|
386
|
+
const loaded = await svc.loadPersistedConfig();
|
|
387
|
+
expect(loaded).toBe(false);
|
|
388
|
+
expect(svc.config.provider).toBe('disabled');
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('loadPersistedConfig is graceful with corrupt JSON', async () => {
|
|
392
|
+
const file = path.join(tmpDir, 'embeddings', 'embeddings-config.json');
|
|
393
|
+
await fsp.mkdir(path.dirname(file), { recursive: true });
|
|
394
|
+
await fsp.writeFile(file, 'not json{{');
|
|
395
|
+
const svc = makeSvc();
|
|
396
|
+
const loaded = await svc.loadPersistedConfig();
|
|
397
|
+
expect(loaded).toBe(false); // ignored
|
|
398
|
+
expect(svc.config.provider).toBe('disabled'); // safe default
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test('persist uses atomic .tmp + rename (no partial file on disk after success)', async () => {
|
|
402
|
+
const svc = makeSvc();
|
|
403
|
+
await svc.updateConfig({ provider: 'azure' }, { persist: true });
|
|
404
|
+
const dir = path.join(tmpDir, 'embeddings');
|
|
405
|
+
const entries = await fsp.readdir(dir);
|
|
406
|
+
expect(entries).toContain('embeddings-config.json');
|
|
407
|
+
expect(entries.filter(e => e.endsWith('.tmp'))).toEqual([]);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test('invalid config does NOT persist (validation fails first)', async () => {
|
|
411
|
+
const svc = makeSvc();
|
|
412
|
+
const result = await svc.updateConfig({ provider: 'cohere' }, { persist: true });
|
|
413
|
+
expect(result.ok).toBe(false);
|
|
414
|
+
const file = path.join(tmpDir, 'embeddings', 'embeddings-config.json');
|
|
415
|
+
await expect(fsp.access(file)).rejects.toMatchObject({ code: 'ENOENT' });
|
|
416
|
+
});
|
|
417
|
+
});
|