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
package/src/core/agentPool.js
CHANGED
|
@@ -1,2476 +1,2475 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentPool - Manages the lifecycle, state, and communication of all active agents
|
|
3
|
-
*
|
|
4
|
-
* Purpose:
|
|
5
|
-
* - Agent creation and destruction
|
|
6
|
-
* - Agent notification and routing
|
|
7
|
-
* - Multi-agent conversation coordination
|
|
8
|
-
* - Agent state persistence and recovery
|
|
9
|
-
* - Agent activity management
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
AGENT_TYPES,
|
|
14
|
-
AGENT_STATUS,
|
|
15
|
-
AGENT_MODES,
|
|
16
|
-
MESSAGE_ROLES,
|
|
17
|
-
MESSAGE_TYPES,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*
|
|
67
|
-
* @param {
|
|
68
|
-
* @param {string} config.
|
|
69
|
-
* @param {string} config.
|
|
70
|
-
* @param {string} config.
|
|
71
|
-
* @param {
|
|
72
|
-
* @param {
|
|
73
|
-
* @param {
|
|
74
|
-
* @
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
config.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
await skillsService.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
enhancedSystemPrompt += '
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
directoryAccess
|
|
161
|
-
console.log('AgentPool DEBUG: createAgent -
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
//
|
|
218
|
-
// via the
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
this.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
*
|
|
286
|
-
* @
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
*
|
|
303
|
-
* @param {
|
|
304
|
-
* @
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
||
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
...
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
//
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
newConversation.
|
|
453
|
-
newConversation.
|
|
454
|
-
newConversation.
|
|
455
|
-
newConversation.
|
|
456
|
-
newConversation.
|
|
457
|
-
|
|
458
|
-
//
|
|
459
|
-
//
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
//
|
|
500
|
-
//
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
//
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
//
|
|
533
|
-
//
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
this.
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
*
|
|
550
|
-
* @param {
|
|
551
|
-
* @
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
this.
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
agent.conversations.full.
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
agent.conversations[agent.currentModel].
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
*
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
*
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
*
|
|
663
|
-
* @
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
*
|
|
681
|
-
*
|
|
682
|
-
*
|
|
683
|
-
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
* @
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
//
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
this.
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
//
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
*
|
|
767
|
-
* @param {
|
|
768
|
-
* @param {
|
|
769
|
-
* @
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
agent.
|
|
789
|
-
agent.
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
*
|
|
820
|
-
* @
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
agent.
|
|
837
|
-
agent.
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
agentId
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
*
|
|
859
|
-
*
|
|
860
|
-
* @
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
*
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
*
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
*
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
892
|
-
* @
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
const
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
//
|
|
912
|
-
//
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
this.
|
|
945
|
-
this.
|
|
946
|
-
this.
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
//
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
*
|
|
973
|
-
*
|
|
974
|
-
* @
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
this.
|
|
1006
|
-
this.
|
|
1007
|
-
this.
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
*
|
|
1032
|
-
*
|
|
1033
|
-
* @
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
agent.
|
|
1085
|
-
agent.
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
previousMessageCount
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
*
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
*
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
*
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
*
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
*
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
//
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
m.
|
|
1167
|
-
|
|
1168
|
-
);
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
const
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
*
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
*
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
agent.
|
|
1201
|
-
agent.
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
*
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
*
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
*
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
//
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
*
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
*
|
|
1261
|
-
* @param {
|
|
1262
|
-
* @
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
agent.
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
*
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
*
|
|
1323
|
-
* @
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
*
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
*
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
*
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
//
|
|
1385
|
-
//
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
*
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1394
|
-
*
|
|
1395
|
-
*
|
|
1396
|
-
*
|
|
1397
|
-
*
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1400
|
-
*
|
|
1401
|
-
* @param {
|
|
1402
|
-
*
|
|
1403
|
-
*
|
|
1404
|
-
* @
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
info.
|
|
1414
|
-
this.
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
//
|
|
1419
|
-
//
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
agent.
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
//
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
*
|
|
1439
|
-
*
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
*
|
|
1453
|
-
* @param {
|
|
1454
|
-
* @
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
//
|
|
1464
|
-
//
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
//
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
//
|
|
1484
|
-
//
|
|
1485
|
-
//
|
|
1486
|
-
//
|
|
1487
|
-
//
|
|
1488
|
-
//
|
|
1489
|
-
//
|
|
1490
|
-
//
|
|
1491
|
-
//
|
|
1492
|
-
//
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
//
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
*
|
|
1516
|
-
* @param {
|
|
1517
|
-
* @
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
//
|
|
1527
|
-
//
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
//
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
*
|
|
1571
|
-
* @param {
|
|
1572
|
-
* @
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
//
|
|
1582
|
-
//
|
|
1583
|
-
//
|
|
1584
|
-
//
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
*
|
|
1611
|
-
*
|
|
1612
|
-
*
|
|
1613
|
-
* @param {Object}
|
|
1614
|
-
* @param {
|
|
1615
|
-
* @param {string}
|
|
1616
|
-
* @
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
*
|
|
1621
|
-
*
|
|
1622
|
-
*
|
|
1623
|
-
*
|
|
1624
|
-
*
|
|
1625
|
-
*
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1628
|
-
*
|
|
1629
|
-
*
|
|
1630
|
-
*
|
|
1631
|
-
*
|
|
1632
|
-
* -
|
|
1633
|
-
*
|
|
1634
|
-
* • contains a
|
|
1635
|
-
* •
|
|
1636
|
-
*
|
|
1637
|
-
*
|
|
1638
|
-
*
|
|
1639
|
-
* -
|
|
1640
|
-
* -
|
|
1641
|
-
*
|
|
1642
|
-
*
|
|
1643
|
-
*
|
|
1644
|
-
*
|
|
1645
|
-
*
|
|
1646
|
-
*
|
|
1647
|
-
*
|
|
1648
|
-
* @param {
|
|
1649
|
-
* @
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
if (!content) return;
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
//
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
memoryService
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
//
|
|
1670
|
-
//
|
|
1671
|
-
//
|
|
1672
|
-
//
|
|
1673
|
-
//
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
const
|
|
1687
|
-
|
|
1688
|
-
const
|
|
1689
|
-
const
|
|
1690
|
-
|
|
1691
|
-
//
|
|
1692
|
-
//
|
|
1693
|
-
//
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
//
|
|
1705
|
-
//
|
|
1706
|
-
//
|
|
1707
|
-
|
|
1708
|
-
const
|
|
1709
|
-
|
|
1710
|
-
.
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
//
|
|
1721
|
-
//
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
const
|
|
1730
|
-
|
|
1731
|
-
.
|
|
1732
|
-
.replace(
|
|
1733
|
-
.
|
|
1734
|
-
|
|
1735
|
-
const
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
*
|
|
1754
|
-
*
|
|
1755
|
-
*
|
|
1756
|
-
*
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
.
|
|
1764
|
-
.
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
*
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
if (a.size === 0
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
*
|
|
1783
|
-
*
|
|
1784
|
-
*
|
|
1785
|
-
*
|
|
1786
|
-
*
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
*
|
|
1798
|
-
*
|
|
1799
|
-
*
|
|
1800
|
-
*
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
//
|
|
1811
|
-
//
|
|
1812
|
-
//
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
//
|
|
1817
|
-
//
|
|
1818
|
-
//
|
|
1819
|
-
//
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
//
|
|
1825
|
-
//
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
//
|
|
1831
|
-
//
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
*
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
*
|
|
1861
|
-
*
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
const
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
//
|
|
1872
|
-
//
|
|
1873
|
-
//
|
|
1874
|
-
//
|
|
1875
|
-
//
|
|
1876
|
-
//
|
|
1877
|
-
|
|
1878
|
-
const
|
|
1879
|
-
const
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
*
|
|
1889
|
-
*
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
//
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
const
|
|
1904
|
-
const
|
|
1905
|
-
const
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
const
|
|
1911
|
-
const
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
agent.taskList.
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
*
|
|
1947
|
-
* @
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
agent.messageQueues.
|
|
1957
|
-
agent.messageQueues.
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
*
|
|
1967
|
-
* @
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
*
|
|
1989
|
-
*
|
|
1990
|
-
*
|
|
1991
|
-
* @param {string}
|
|
1992
|
-
* @
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
let
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
const
|
|
2008
|
-
const
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
//
|
|
2064
|
-
//
|
|
2065
|
-
|
|
2066
|
-
const
|
|
2067
|
-
|
|
2068
|
-
//
|
|
2069
|
-
//
|
|
2070
|
-
//
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
//
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
const
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
//
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
*
|
|
2137
|
-
* @param {string}
|
|
2138
|
-
* @param {
|
|
2139
|
-
* @
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
const
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
*
|
|
2219
|
-
*
|
|
2220
|
-
*
|
|
2221
|
-
*
|
|
2222
|
-
*
|
|
2223
|
-
*
|
|
2224
|
-
*
|
|
2225
|
-
*
|
|
2226
|
-
*
|
|
2227
|
-
* @param {string}
|
|
2228
|
-
* @
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
const
|
|
2239
|
-
|
|
2240
|
-
//
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
const
|
|
2246
|
-
|
|
2247
|
-
conversation.
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
*
|
|
2267
|
-
* @param {string}
|
|
2268
|
-
* @param {
|
|
2269
|
-
* @param {
|
|
2270
|
-
*
|
|
2271
|
-
*
|
|
2272
|
-
*
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
//
|
|
2293
|
-
//
|
|
2294
|
-
//
|
|
2295
|
-
//
|
|
2296
|
-
//
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
conversation.
|
|
2302
|
-
conversation.
|
|
2303
|
-
conversation.
|
|
2304
|
-
conversation.
|
|
2305
|
-
conversation.
|
|
2306
|
-
conversation.
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
*
|
|
2324
|
-
*
|
|
2325
|
-
* @param {string}
|
|
2326
|
-
* @
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
conversation.
|
|
2341
|
-
conversation.
|
|
2342
|
-
conversation.
|
|
2343
|
-
conversation.
|
|
2344
|
-
conversation.
|
|
2345
|
-
conversation.
|
|
2346
|
-
conversation.
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
*
|
|
2356
|
-
* @param {string}
|
|
2357
|
-
* @
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
*
|
|
2393
|
-
*
|
|
2394
|
-
* @
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
conversation.
|
|
2413
|
-
conversation.
|
|
2414
|
-
conversation.
|
|
2415
|
-
conversation.
|
|
2416
|
-
conversation.
|
|
2417
|
-
conversation.
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
//
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
//
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
*
|
|
2455
|
-
* @
|
|
2456
|
-
* @
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
1
|
+
/**
|
|
2
|
+
* AgentPool - Manages the lifecycle, state, and communication of all active agents
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* - Agent creation and destruction
|
|
6
|
+
* - Agent notification and routing
|
|
7
|
+
* - Multi-agent conversation coordination
|
|
8
|
+
* - Agent state persistence and recovery
|
|
9
|
+
* - Agent activity management
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
AGENT_TYPES,
|
|
14
|
+
AGENT_STATUS,
|
|
15
|
+
AGENT_MODES,
|
|
16
|
+
MESSAGE_ROLES,
|
|
17
|
+
MESSAGE_TYPES,
|
|
18
|
+
MODEL_FORMAT_VERSIONS,
|
|
19
|
+
SYSTEM_DEFAULTS
|
|
20
|
+
} from '../utilities/constants.js';
|
|
21
|
+
import DirectoryAccessManager from '../utilities/directoryAccessManager.js';
|
|
22
|
+
import { getVisualEditorBridge } from '../services/visualEditorBridge.js';
|
|
23
|
+
|
|
24
|
+
class AgentPool {
|
|
25
|
+
// Stopwords for the _tokenize / _jaccard similarity check used by
|
|
26
|
+
// auto-save-as-plan dedup. Tight list — only words that appear in
|
|
27
|
+
// virtually every English sentence regardless of content, so that
|
|
28
|
+
// their presence in both messages doesn't inflate similarity.
|
|
29
|
+
static _STOPWORDS = new Set([
|
|
30
|
+
'the', 'and', 'for', 'but', 'are', 'was', 'were',
|
|
31
|
+
'has', 'have', 'had', 'this', 'that', 'with', 'will',
|
|
32
|
+
'you', 'your', 'our', 'their', 'them', 'they',
|
|
33
|
+
'can', 'could', 'should', 'would',
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
constructor(config, logger, stateManager, contextManager, toolsRegistry = null) {
|
|
37
|
+
this.config = config;
|
|
38
|
+
this.logger = logger;
|
|
39
|
+
this.stateManager = stateManager;
|
|
40
|
+
this.contextManager = contextManager;
|
|
41
|
+
this.toolsRegistry = toolsRegistry;
|
|
42
|
+
|
|
43
|
+
// Agent registry - maps agent ID to agent object
|
|
44
|
+
this.agents = new Map();
|
|
45
|
+
|
|
46
|
+
// Agent directory for discovery
|
|
47
|
+
this.agentDirectory = new Map();
|
|
48
|
+
|
|
49
|
+
// Paused agents tracking
|
|
50
|
+
this.pausedAgents = new Map();
|
|
51
|
+
|
|
52
|
+
// Agent notification queue
|
|
53
|
+
this.notificationQueue = new Map();
|
|
54
|
+
|
|
55
|
+
this.maxAgentsPerProject = config.system?.maxAgentsPerProject || SYSTEM_DEFAULTS.MAX_AGENTS_PER_PROJECT;
|
|
56
|
+
|
|
57
|
+
// MessageProcessor reference for triggering responses (set via setMessageProcessor)
|
|
58
|
+
this.messageProcessor = null;
|
|
59
|
+
|
|
60
|
+
// Initialize directory access manager
|
|
61
|
+
this.directoryAccessManager = new DirectoryAccessManager(config, logger);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a new agent with specified configuration
|
|
66
|
+
* @param {Object} config - Agent configuration
|
|
67
|
+
* @param {string} config.name - Agent name
|
|
68
|
+
* @param {string} config.type - Agent type ('user-created', 'system-agent', 'agent-engineer')
|
|
69
|
+
* @param {string} config.systemPrompt - Agent's system prompt
|
|
70
|
+
* @param {string} config.preferredModel - Preferred LLM model
|
|
71
|
+
* @param {Array} config.capabilities - Available tools/capabilities
|
|
72
|
+
* @param {Object} config.directoryAccess - Directory access configuration
|
|
73
|
+
* @param {string} config.projectDir - Project directory for default access setup
|
|
74
|
+
* @returns {Promise<Object>} Created agent object
|
|
75
|
+
*/
|
|
76
|
+
async createAgent(config) {
|
|
77
|
+
// Check agent limit
|
|
78
|
+
if (this.agents.size >= this.maxAgentsPerProject) {
|
|
79
|
+
throw new Error(`Maximum agents per project exceeded (${this.maxAgentsPerProject})`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const agentId = this._generateAgentId(config.name);
|
|
83
|
+
const now = new Date().toISOString();
|
|
84
|
+
|
|
85
|
+
// Enhance system prompt with tool descriptions if available
|
|
86
|
+
let enhancedSystemPrompt = config.systemPrompt;
|
|
87
|
+
if (this.toolsRegistry && config.capabilities && config.capabilities.length > 0) {
|
|
88
|
+
try {
|
|
89
|
+
enhancedSystemPrompt = this.toolsRegistry.enhanceSystemPrompt(
|
|
90
|
+
config.systemPrompt,
|
|
91
|
+
config.capabilities,
|
|
92
|
+
{
|
|
93
|
+
compact: config.compactToolDescriptions || false,
|
|
94
|
+
layered: config.layeredToolDescriptions || false,
|
|
95
|
+
includeExamples: config.includeToolExamples !== false,
|
|
96
|
+
includeUsageGuidelines: config.includeUsageGuidelines !== false,
|
|
97
|
+
includeSecurityNotes: config.includeSecurityNotes !== false
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
this.logger?.info(`System prompt enhanced with tool descriptions`, {
|
|
102
|
+
agentId,
|
|
103
|
+
capabilities: config.capabilities,
|
|
104
|
+
originalLength: config.systemPrompt?.length || 0,
|
|
105
|
+
enhancedLength: enhancedSystemPrompt?.length || 0
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
this.logger?.error(`Failed to enhance system prompt with tools`, {
|
|
109
|
+
agentId,
|
|
110
|
+
error: error.message,
|
|
111
|
+
capabilities: config.capabilities
|
|
112
|
+
});
|
|
113
|
+
// Fall back to original prompt
|
|
114
|
+
enhancedSystemPrompt = config.systemPrompt;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Inject assigned skills index into system prompt
|
|
119
|
+
if (config.skills && config.skills.length > 0) {
|
|
120
|
+
try {
|
|
121
|
+
const { getSkillsService } = await import('../services/skillsService.js');
|
|
122
|
+
const skillsService = getSkillsService(this.logger);
|
|
123
|
+
await skillsService.initialize();
|
|
124
|
+
const summaries = await skillsService.getSkillSummaries(config.skills);
|
|
125
|
+
if (summaries.length > 0) {
|
|
126
|
+
enhancedSystemPrompt += '\n\n## ASSIGNED SKILLS\n\n';
|
|
127
|
+
enhancedSystemPrompt += 'Use the skills tool to browse and load skill content. Use "describe" to see sections, "read-section" to load specific parts.\n\n';
|
|
128
|
+
for (const s of summaries) {
|
|
129
|
+
const sections = s.sections?.length ? `\n Sections: ${s.sections.map(h => h.replace(/^#+\s*/, '')).join(', ')}` : '';
|
|
130
|
+
enhancedSystemPrompt += `- **${s.name}** (${s.lineCount} lines): ${s.description}${sections}\n`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
this.logger?.warn('Failed to inject skills index into system prompt', { error: error.message });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Setup directory access configuration
|
|
139
|
+
let directoryAccess;
|
|
140
|
+
|
|
141
|
+
console.log('AgentPool DEBUG: createAgent - config.directoryAccess:', config.directoryAccess ? 'EXISTS' : 'NULL/UNDEFINED');
|
|
142
|
+
if (config.directoryAccess) {
|
|
143
|
+
console.log('AgentPool DEBUG: createAgent - directoryAccess from config:', JSON.stringify(config.directoryAccess, null, 2));
|
|
144
|
+
}
|
|
145
|
+
console.log('AgentPool DEBUG: createAgent - config.projectDir:', config.projectDir);
|
|
146
|
+
|
|
147
|
+
if (config.directoryAccess) {
|
|
148
|
+
// Validate provided directory access configuration
|
|
149
|
+
const validation = this.directoryAccessManager.validateAccessConfiguration(config.directoryAccess);
|
|
150
|
+
console.log('AgentPool DEBUG: createAgent - validation result:', validation);
|
|
151
|
+
if (!validation.valid) {
|
|
152
|
+
throw new Error(`Invalid directory access configuration: ${validation.errors.join(', ')}`);
|
|
153
|
+
}
|
|
154
|
+
directoryAccess = config.directoryAccess;
|
|
155
|
+
console.log('AgentPool DEBUG: createAgent - Using provided directoryAccess');
|
|
156
|
+
} else {
|
|
157
|
+
// Create default directory access based on project directory
|
|
158
|
+
const projectDir = config.projectDir || process.cwd();
|
|
159
|
+
directoryAccess = DirectoryAccessManager.createProjectDefaults(projectDir);
|
|
160
|
+
console.log('AgentPool DEBUG: createAgent - Created default directoryAccess for projectDir:', projectDir);
|
|
161
|
+
console.log('AgentPool DEBUG: createAgent - Default directoryAccess:', JSON.stringify(directoryAccess, null, 2));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const agent = {
|
|
165
|
+
id: agentId,
|
|
166
|
+
type: config.type || AGENT_TYPES.USER_CREATED,
|
|
167
|
+
name: config.name || `Agent-${Date.now()}`,
|
|
168
|
+
systemPrompt: enhancedSystemPrompt,
|
|
169
|
+
originalSystemPrompt: config.systemPrompt, // Store original for reference
|
|
170
|
+
preferredModel: config.preferredModel,
|
|
171
|
+
status: AGENT_STATUS.ACTIVE,
|
|
172
|
+
capabilities: config.capabilities || [],
|
|
173
|
+
directoryAccess: directoryAccess, // Directory access configuration
|
|
174
|
+
// Per-tool configuration — keyed by tool id (e.g., 'terminal',
|
|
175
|
+
// 'filesystem'). When a tool is instantiated for this agent, the
|
|
176
|
+
// object under toolConfig[toolId] is merged into the tool's
|
|
177
|
+
// constructor config. Previously tools were constructed from global
|
|
178
|
+
// defaults only, so there was no way to set per-agent knobs like
|
|
179
|
+
// terminal allowed-commands, filesystem size limits, web stealth
|
|
180
|
+
// level, etc. See ToolManager integration in step 2.
|
|
181
|
+
toolConfig: (config.toolConfig && typeof config.toolConfig === 'object' && !Array.isArray(config.toolConfig))
|
|
182
|
+
? { ...config.toolConfig }
|
|
183
|
+
: {},
|
|
184
|
+
conversations: {
|
|
185
|
+
full: {
|
|
186
|
+
messages: [],
|
|
187
|
+
lastUpdated: now
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
currentModel: config.preferredModel,
|
|
191
|
+
dynamicModelRouting: config.dynamicModelRouting || false,
|
|
192
|
+
routingStrategy: config.routingStrategy || '',
|
|
193
|
+
skills: config.skills || [],
|
|
194
|
+
platformProvided: config.platformProvided !== false, // Default true — all models are platform-provided
|
|
195
|
+
|
|
196
|
+
// Agent Mode Configuration
|
|
197
|
+
mode: config.mode || AGENT_MODES.CHAT,
|
|
198
|
+
currentTask: null, // Current autonomous task being executed
|
|
199
|
+
taskStartTime: null,
|
|
200
|
+
maxIterations: config.maxIterations || 10, // Safety limit for autonomous loops
|
|
201
|
+
iterationCount: 0,
|
|
202
|
+
stopRequested: false,
|
|
203
|
+
delayEndTime: null, // When agent delay expires (for agentDelay tool)
|
|
204
|
+
ttl: null, // Time-to-live: processing cycles remaining (null = no TTL, number = cycles left)
|
|
205
|
+
|
|
206
|
+
// Message Queues for scheduler processing
|
|
207
|
+
messageQueues: {
|
|
208
|
+
toolResults: [], // Tool execution results waiting to be processed
|
|
209
|
+
interAgentMessages: [], // Messages from other agents
|
|
210
|
+
userMessages: [] // Messages from users
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
createdAt: now,
|
|
214
|
+
lastActivity: now,
|
|
215
|
+
pausedUntil: null,
|
|
216
|
+
// Used by platformControlTool for ancestry checks. null = created
|
|
217
|
+
// via the UI / no parent agent. Set to <agentId> when an agent
|
|
218
|
+
// creates another via the platformcontrol tool.
|
|
219
|
+
createdBy: typeof config.createdBy === 'string' ? config.createdBy : null,
|
|
220
|
+
metadata: config.metadata || {},
|
|
221
|
+
|
|
222
|
+
// CRITICAL: Store sessionId for API key resolution
|
|
223
|
+
sessionId: config.sessionId,
|
|
224
|
+
|
|
225
|
+
// Inter-agent conversation tracking to prevent spam
|
|
226
|
+
interAgentTracking: new Map(), // recipientId -> { lastSent, lastReceived, lastType }
|
|
227
|
+
|
|
228
|
+
// Task Management System for agent-mode autonomous operation
|
|
229
|
+
taskList: {
|
|
230
|
+
tasks: [], // Array of task objects
|
|
231
|
+
lastUpdated: now
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
// Incoming messages tracking (for unprocessed messages)
|
|
235
|
+
incomingMessages: []
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
this.logger.info(`Agent created with routing config`, {
|
|
239
|
+
agentId,
|
|
240
|
+
dynamicModelRouting: agent.dynamicModelRouting,
|
|
241
|
+
platformProvided: agent.platformProvided,
|
|
242
|
+
preferredModel: agent.preferredModel
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Initialize model-specific conversation with dual storage structure
|
|
246
|
+
if (config.preferredModel) {
|
|
247
|
+
agent.conversations[config.preferredModel] = {
|
|
248
|
+
// Dual storage for compactization support
|
|
249
|
+
messages: [], // Original messages - never modified
|
|
250
|
+
compactizedMessages: null, // Working copy - null until first compaction
|
|
251
|
+
|
|
252
|
+
// Compactization metadata
|
|
253
|
+
lastCompactization: null, // Timestamp of last compaction
|
|
254
|
+
compactizationCount: 0, // Number of times compacted
|
|
255
|
+
compactizationStrategy: null, // 'summarization', 'truncation', 'aggressive'
|
|
256
|
+
originalTokenCount: 0, // Token count before last compaction
|
|
257
|
+
compactedTokenCount: 0, // Token count after last compaction
|
|
258
|
+
|
|
259
|
+
// Backward compatibility
|
|
260
|
+
tokenCount: 0, // Current effective token count
|
|
261
|
+
lastUpdated: now,
|
|
262
|
+
formatVersion: this._getModelFormatVersion(config.preferredModel)
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Add to registry and directory
|
|
267
|
+
this.agents.set(agentId, agent);
|
|
268
|
+
this._updateAgentDirectory(agent);
|
|
269
|
+
|
|
270
|
+
// Persist agent state (use wrapper that resolves agent object from ID)
|
|
271
|
+
await this.persistAgentState(agentId);
|
|
272
|
+
|
|
273
|
+
this.logger.info(`Agent created: ${agentId}`, {
|
|
274
|
+
agentId,
|
|
275
|
+
name: agent.name,
|
|
276
|
+
type: agent.type,
|
|
277
|
+
model: agent.preferredModel
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return agent;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Retrieve agent instance by ID
|
|
285
|
+
* @param {string} agentId - Agent identifier
|
|
286
|
+
* @returns {Promise<Object|null>} Agent object or null if not found
|
|
287
|
+
*/
|
|
288
|
+
async getAgent(agentId, enrichWithSchedulerStatus = false) {
|
|
289
|
+
const agent = this.agents.get(agentId);
|
|
290
|
+
if (!agent) return null;
|
|
291
|
+
|
|
292
|
+
// Optionally enrich with scheduler status for UI
|
|
293
|
+
if (enrichWithSchedulerStatus && this.scheduler) {
|
|
294
|
+
agent.inScheduler = this.scheduler.isAgentInScheduler(agentId);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return agent;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Update an existing agent's configuration
|
|
302
|
+
* @param {string} agentId - Agent identifier
|
|
303
|
+
* @param {Object} updates - Updates to apply to the agent
|
|
304
|
+
* @returns {Promise<Object>} Updated agent object
|
|
305
|
+
*/
|
|
306
|
+
async updateAgent(agentId, updates) {
|
|
307
|
+
const agent = await this.getAgent(agentId);
|
|
308
|
+
if (!agent) {
|
|
309
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
this.logger.info(`Updating agent: ${agentId}`, {
|
|
313
|
+
updates,
|
|
314
|
+
currentName: agent.name
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Validate directory access configuration if being updated
|
|
318
|
+
if (updates.directoryAccess) {
|
|
319
|
+
const validation = this.directoryAccessManager.validateAccessConfiguration(updates.directoryAccess);
|
|
320
|
+
if (!validation.valid) {
|
|
321
|
+
throw new Error(`Invalid directory access configuration: ${validation.errors.join(', ')}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this.logger.info(`Directory access validation passed for agent: ${agentId}`, {
|
|
325
|
+
workingDirectory: updates.directoryAccess.workingDirectory,
|
|
326
|
+
readOnlyDirs: updates.directoryAccess.readOnlyDirectories?.length || 0,
|
|
327
|
+
writeEnabledDirs: updates.directoryAccess.writeEnabledDirectories?.length || 0
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Validate per-tool config if being updated. Must be a plain object
|
|
332
|
+
// keyed by tool id; each value is an object of config overrides that
|
|
333
|
+
// will be merged into the tool's constructor config at instantiation.
|
|
334
|
+
// We don't validate individual tool schemas here — that happens at
|
|
335
|
+
// tool construction time where the tool knows its own shape.
|
|
336
|
+
if (updates.toolConfig !== undefined) {
|
|
337
|
+
if (updates.toolConfig === null
|
|
338
|
+
|| typeof updates.toolConfig !== 'object'
|
|
339
|
+
|| Array.isArray(updates.toolConfig)) {
|
|
340
|
+
throw new Error('Invalid toolConfig: must be a plain object keyed by tool id');
|
|
341
|
+
}
|
|
342
|
+
for (const [toolId, cfg] of Object.entries(updates.toolConfig)) {
|
|
343
|
+
if (cfg !== null && (typeof cfg !== 'object' || Array.isArray(cfg))) {
|
|
344
|
+
throw new Error(`Invalid toolConfig.${toolId}: must be an object or null`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// If originalSystemPrompt is being updated (user edited the raw prompt), store it
|
|
350
|
+
// and use it as the base for enhancement. Otherwise use the existing originalSystemPrompt.
|
|
351
|
+
if (updates.originalSystemPrompt !== undefined) {
|
|
352
|
+
// User explicitly set a new base prompt — store it
|
|
353
|
+
this.logger.info(`Original system prompt updated by user`, {
|
|
354
|
+
agentId,
|
|
355
|
+
oldLength: (agent.originalSystemPrompt || '').length,
|
|
356
|
+
newLength: updates.originalSystemPrompt.length
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// If capabilities or system prompt are being updated, regenerate the enhanced system prompt
|
|
361
|
+
if ((updates.capabilities || updates.originalSystemPrompt !== undefined) && this.toolsRegistry) {
|
|
362
|
+
try {
|
|
363
|
+
// Priority: new user prompt > existing original prompt > existing system prompt
|
|
364
|
+
const baseSystemPrompt = updates.originalSystemPrompt !== undefined
|
|
365
|
+
? updates.originalSystemPrompt
|
|
366
|
+
: (agent.originalSystemPrompt || agent.systemPrompt || '');
|
|
367
|
+
const capabilities = updates.capabilities || agent.capabilities || [];
|
|
368
|
+
|
|
369
|
+
const enhancedSystemPrompt = this.toolsRegistry.enhanceSystemPrompt(
|
|
370
|
+
baseSystemPrompt,
|
|
371
|
+
capabilities,
|
|
372
|
+
{
|
|
373
|
+
compact: agent.compactToolDescriptions || false,
|
|
374
|
+
includeExamples: agent.includeToolExamples !== false,
|
|
375
|
+
includeUsageGuidelines: agent.includeUsageGuidelines !== false,
|
|
376
|
+
includeSecurityNotes: agent.includeSecurityNotes !== false
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
updates.systemPrompt = enhancedSystemPrompt;
|
|
381
|
+
// Always keep originalSystemPrompt in sync with what the user wrote
|
|
382
|
+
if (updates.originalSystemPrompt === undefined) {
|
|
383
|
+
updates.originalSystemPrompt = baseSystemPrompt;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
this.logger.info(`System prompt regenerated with updated capabilities`, {
|
|
387
|
+
agentId,
|
|
388
|
+
oldCapabilities: agent.capabilities,
|
|
389
|
+
newCapabilities: capabilities,
|
|
390
|
+
originalLength: baseSystemPrompt?.length || 0,
|
|
391
|
+
enhancedLength: enhancedSystemPrompt?.length || 0
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// The scheduler caches per-(agent, model) Responses-API prompts
|
|
395
|
+
// built from this agent's `originalSystemPrompt` + capabilities.
|
|
396
|
+
// Both inputs just changed, so any cached rebuilds are stale.
|
|
397
|
+
// No-op when the scheduler isn't attached (tests / very-early
|
|
398
|
+
// boot) or when it predates this method (old binaries during
|
|
399
|
+
// a rolling upgrade).
|
|
400
|
+
try {
|
|
401
|
+
this.scheduler?._invalidateNativePromptCache?.(agentId);
|
|
402
|
+
} catch (e) {
|
|
403
|
+
this.logger.debug?.('Failed to invalidate native prompt cache', { agentId, error: e.message });
|
|
404
|
+
}
|
|
405
|
+
} catch (error) {
|
|
406
|
+
this.logger.error(`Failed to regenerate system prompt with updated capabilities`, {
|
|
407
|
+
agentId,
|
|
408
|
+
error: error.message,
|
|
409
|
+
capabilities: updates.capabilities
|
|
410
|
+
});
|
|
411
|
+
// Continue with update even if enhancement fails
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Create updated agent object with new values
|
|
416
|
+
const updatedAgent = {
|
|
417
|
+
...agent,
|
|
418
|
+
...updates,
|
|
419
|
+
id: agentId, // Ensure ID cannot be changed
|
|
420
|
+
lastModified: new Date().toISOString(),
|
|
421
|
+
lastActivity: new Date().toISOString()
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// CRITICAL FIX: When preferredModel changes, also update currentModel
|
|
425
|
+
// This ensures the UI immediately reflects the model change
|
|
426
|
+
if (updates.preferredModel && updates.preferredModel !== agent.preferredModel) {
|
|
427
|
+
const oldModel = agent.preferredModel;
|
|
428
|
+
const newModel = updates.preferredModel;
|
|
429
|
+
|
|
430
|
+
updatedAgent.currentModel = newModel;
|
|
431
|
+
|
|
432
|
+
// CRITICAL FIX: Initialize conversation for new model if it doesn't exist
|
|
433
|
+
if (!updatedAgent.conversations[newModel]) {
|
|
434
|
+
updatedAgent.conversations[newModel] = this._createEmptyConversation(newModel);
|
|
435
|
+
this.logger.info(`Created conversation for new model: ${newModel}`, { agentId });
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Copy conversation history from old model to new model
|
|
439
|
+
// This preserves context when switching models
|
|
440
|
+
if (oldModel && updatedAgent.conversations[oldModel]) {
|
|
441
|
+
const oldConversation = updatedAgent.conversations[oldModel];
|
|
442
|
+
const newConversation = updatedAgent.conversations[newModel];
|
|
443
|
+
|
|
444
|
+
// Copy messages if new conversation is empty
|
|
445
|
+
if (newConversation.messages.length === 0 && oldConversation.messages.length > 0) {
|
|
446
|
+
// Copy original messages
|
|
447
|
+
newConversation.messages = [...oldConversation.messages];
|
|
448
|
+
|
|
449
|
+
// Copy compacted messages if they exist
|
|
450
|
+
if (oldConversation.compactizedMessages) {
|
|
451
|
+
newConversation.compactizedMessages = [...oldConversation.compactizedMessages];
|
|
452
|
+
newConversation.lastCompactization = oldConversation.lastCompactization;
|
|
453
|
+
newConversation.compactizationCount = oldConversation.compactizationCount;
|
|
454
|
+
newConversation.compactizationStrategy = oldConversation.compactizationStrategy;
|
|
455
|
+
newConversation.originalTokenCount = oldConversation.originalTokenCount;
|
|
456
|
+
newConversation.compactedTokenCount = oldConversation.compactedTokenCount;
|
|
457
|
+
// CRITICAL: Copy the sync watermark too — without this, getMessagesForAI
|
|
458
|
+
// cannot sync new messages to compactizedMessages after a model switch,
|
|
459
|
+
// causing the AI to only see the compacted summary and repeat itself endlessly.
|
|
460
|
+
newConversation.originalMessageCountAtCompaction = oldConversation.originalMessageCountAtCompaction;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
newConversation.lastUpdated = new Date().toISOString();
|
|
464
|
+
|
|
465
|
+
this.logger.info(`Copied conversation history from ${oldModel} to ${newModel}`, {
|
|
466
|
+
agentId,
|
|
467
|
+
messageCount: newConversation.messages.length,
|
|
468
|
+
hasCompacted: !!newConversation.compactizedMessages
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
this.logger.info(`Model changed via UI - updating both preferredModel and currentModel`, {
|
|
474
|
+
agentId,
|
|
475
|
+
oldModel,
|
|
476
|
+
newModel,
|
|
477
|
+
conversationCopied: oldModel && updatedAgent.conversations[oldModel]?.messages.length > 0
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Update agent in registry
|
|
482
|
+
this.agents.set(agentId, updatedAgent);
|
|
483
|
+
|
|
484
|
+
// Log the actual update for debugging
|
|
485
|
+
this.logger.info(`Agent updated in registry with mode: ${updatedAgent.mode}`, {
|
|
486
|
+
agentId,
|
|
487
|
+
beforeMode: agent.mode,
|
|
488
|
+
afterMode: updatedAgent.mode,
|
|
489
|
+
allUpdates: Object.keys(updates)
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// Update agent directory
|
|
493
|
+
this._updateAgentDirectory(updatedAgent);
|
|
494
|
+
|
|
495
|
+
// Persist the updated agent state
|
|
496
|
+
await this.stateManager.persistAgentState(updatedAgent);
|
|
497
|
+
|
|
498
|
+
// Record the mode transition (both directions) into the scheduler's
|
|
499
|
+
// per-agent history. This is where UI-toggle and programmatic
|
|
500
|
+
// update_agent flips land — internal scheduler flips go through
|
|
501
|
+
// scheduler._transitionMode directly. Both feed the same ring buffer
|
|
502
|
+
// and the same /scheduler visualizer row.
|
|
503
|
+
if (updates.mode !== undefined && agent.mode !== updates.mode && this.scheduler?.recordModeTransition) {
|
|
504
|
+
this.scheduler.recordModeTransition(agentId, agent.mode, updates.mode, 'user-toggle');
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// CRITICAL: If agent was switched to AGENT mode, add it to scheduler
|
|
508
|
+
if (updates.mode === AGENT_MODES.AGENT && this.scheduler) {
|
|
509
|
+
// CRITICAL FIX: Use the session ID from updates first, then agent's sessionId
|
|
510
|
+
// Register session with scheduler for API key resolution
|
|
511
|
+
// NOTE: The scheduler now uses AgentActivityService to determine which agents
|
|
512
|
+
// should be active, so we just register the session here
|
|
513
|
+
const sessionId = updates.sessionId || updatedAgent.sessionId;
|
|
514
|
+
|
|
515
|
+
if (!sessionId) {
|
|
516
|
+
this.logger.warn(`Agent ${agentId} switching to AGENT mode but has no sessionId - this will cause API key resolution issues`);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
this.logger.info(`Registering agent session with scheduler (switched to AGENT mode): ${agentId}`, {
|
|
520
|
+
agentName: updatedAgent.name,
|
|
521
|
+
sessionId: sessionId,
|
|
522
|
+
hasSessionId: !!sessionId
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
await this.scheduler.addAgent(agentId, {
|
|
526
|
+
sessionId: sessionId,
|
|
527
|
+
triggeredBy: 'mode-change-to-agent'
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// If agent was switched from AGENT to CHAT mode, clean up session tracking
|
|
532
|
+
// NOTE: The agent will automatically become inactive in the next scheduler cycle
|
|
533
|
+
// based on AgentActivityService.shouldAgentBeActive() returning false
|
|
534
|
+
if (agent.mode === AGENT_MODES.AGENT && updates.mode === AGENT_MODES.CHAT && this.scheduler) {
|
|
535
|
+
this.logger.info(`Agent mode changed to CHAT - will become inactive: ${agentId}`);
|
|
536
|
+
this.scheduler.removeAgent(agentId, 'mode-change-to-chat');
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
this.logger.info(`Agent updated successfully: ${agentId}`, {
|
|
540
|
+
newName: updatedAgent.name,
|
|
541
|
+
changes: Object.keys(updates)
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
return updatedAgent;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Agent notification from Message Processor for inter-agent communication
|
|
549
|
+
* @param {string} agentId - Target agent ID
|
|
550
|
+
* @param {Object} message - Message object with agent redirect
|
|
551
|
+
* @returns {Promise<boolean>} Success status
|
|
552
|
+
*/
|
|
553
|
+
async notifyAgent(agentId, message) {
|
|
554
|
+
const agent = await this.getAgent(agentId);
|
|
555
|
+
if (!agent) {
|
|
556
|
+
this.logger.warn(`Agent notification failed - agent not found: ${agentId}`);
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Check if agent is paused
|
|
561
|
+
if (this._isAgentPaused(agent)) {
|
|
562
|
+
this.logger.info(`Agent notification queued - agent is paused: ${agentId}`);
|
|
563
|
+
this._queueNotification(agentId, message);
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Add notification to agent's conversation
|
|
568
|
+
const notificationMessage = {
|
|
569
|
+
id: `msg-${Date.now()}`,
|
|
570
|
+
conversationId: message.conversationId,
|
|
571
|
+
agentId: message.from, // sender agent ID
|
|
572
|
+
content: message.content,
|
|
573
|
+
role: MESSAGE_ROLES.SYSTEM,
|
|
574
|
+
timestamp: new Date().toISOString(),
|
|
575
|
+
type: MESSAGE_TYPES.AGENT_NOTIFICATION,
|
|
576
|
+
fromAgent: message.from,
|
|
577
|
+
context: message.context,
|
|
578
|
+
urgent: message.urgent || false,
|
|
579
|
+
requiresResponse: message.requiresResponse || false
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
// Add to full conversation
|
|
583
|
+
agent.conversations.full.messages.push(notificationMessage);
|
|
584
|
+
agent.conversations.full.lastUpdated = new Date().toISOString();
|
|
585
|
+
|
|
586
|
+
// Add to current model conversation
|
|
587
|
+
if (agent.currentModel && agent.conversations[agent.currentModel]) {
|
|
588
|
+
const formattedMessage = this._formatMessageForModel(notificationMessage, agent.currentModel);
|
|
589
|
+
agent.conversations[agent.currentModel].messages.push(formattedMessage);
|
|
590
|
+
agent.conversations[agent.currentModel].lastUpdated = new Date().toISOString();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Update agent activity
|
|
594
|
+
agent.lastActivity = new Date().toISOString();
|
|
595
|
+
await this.persistAgentState(agentId);
|
|
596
|
+
|
|
597
|
+
this.logger.info(`Agent notified: ${agentId}`, {
|
|
598
|
+
fromAgent: message.from,
|
|
599
|
+
urgent: message.urgent,
|
|
600
|
+
requiresResponse: message.requiresResponse
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Get all agents (returns full agent objects)
|
|
608
|
+
* @returns {Promise<Array>} List of all agent objects
|
|
609
|
+
*/
|
|
610
|
+
async getAllAgents() {
|
|
611
|
+
const agents = Array.from(this.agents.values());
|
|
612
|
+
|
|
613
|
+
// Update pause status for all agents
|
|
614
|
+
for (const agent of agents) {
|
|
615
|
+
this._updateAgentPauseStatus(agent);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return agents;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* List all active agents with their current status
|
|
623
|
+
* @returns {Promise<Array>} Array of agent objects
|
|
624
|
+
*/
|
|
625
|
+
async listActiveAgents() {
|
|
626
|
+
const agents = Array.from(this.agents.values());
|
|
627
|
+
|
|
628
|
+
// Update pause status for all agents
|
|
629
|
+
for (const agent of agents) {
|
|
630
|
+
this._updateAgentPauseStatus(agent);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return agents.map(agent => ({
|
|
634
|
+
id: agent.id,
|
|
635
|
+
name: agent.name,
|
|
636
|
+
type: agent.type,
|
|
637
|
+
status: agent.status,
|
|
638
|
+
mode: agent.mode,
|
|
639
|
+
systemPrompt: agent.systemPrompt,
|
|
640
|
+
originalSystemPrompt: agent.originalSystemPrompt,
|
|
641
|
+
preferredModel: agent.preferredModel,
|
|
642
|
+
currentModel: agent.currentModel,
|
|
643
|
+
dynamicModelRouting: agent.dynamicModelRouting,
|
|
644
|
+
routingStrategy: agent.routingStrategy || '',
|
|
645
|
+
skills: agent.skills || [],
|
|
646
|
+
platformProvided: agent.platformProvided,
|
|
647
|
+
capabilities: agent.capabilities,
|
|
648
|
+
directoryAccess: agent.directoryAccess,
|
|
649
|
+
toolConfig: agent.toolConfig || {},
|
|
650
|
+
lastActivity: agent.lastActivity,
|
|
651
|
+
isPaused: this._isAgentPaused(agent),
|
|
652
|
+
pausedUntil: agent.pausedUntil,
|
|
653
|
+
messageCount: agent.conversations.full.messages.length,
|
|
654
|
+
createdAt: agent.createdAt,
|
|
655
|
+
// First user message snippet for card preview (2 lines max)
|
|
656
|
+
firstUserMessage: this._getFirstUserMessageSnippet(agent)
|
|
657
|
+
}));
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Persist agent state to storage
|
|
662
|
+
* @param {string} agentId - Agent identifier
|
|
663
|
+
* @returns {Promise<void>}
|
|
664
|
+
*/
|
|
665
|
+
async persistAgentState(agentId) {
|
|
666
|
+
const agent = await this.getAgent(agentId);
|
|
667
|
+
if (!agent) {
|
|
668
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
await this.stateManager.persistAgentState(agent);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Rehydrate an agent from persisted state on session boot. NOT the
|
|
676
|
+
* pause-toggle (see `resumeAgent(agentId)` below). The two were
|
|
677
|
+
* historically both named `resumeAgent`, which made this one dead
|
|
678
|
+
* code — JS class bodies keep the second definition only, so callers
|
|
679
|
+
* passing an object ended up in the id-based handler and silently got
|
|
680
|
+
* "Agent not found".
|
|
681
|
+
*
|
|
682
|
+
* Callers: agentPool.restoreAgent, which is in turn called from
|
|
683
|
+
* orchestrator's boot-time state restoration path.
|
|
684
|
+
*
|
|
685
|
+
* @param {Object} agentData - Persisted agent data
|
|
686
|
+
* @returns {Promise<Object>} Restored agent object
|
|
687
|
+
*/
|
|
688
|
+
async rehydrateFromState(agentData) {
|
|
689
|
+
const agent = {
|
|
690
|
+
...agentData,
|
|
691
|
+
status: agentData.status === 'paused' && this._isPauseExpired(agentData) ? 'active' : agentData.status
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
// RECOVERY: If agent was paused awaiting user input (e.g., credentials),
|
|
695
|
+
// the promise is now lost due to server/UI restart. Resume the agent.
|
|
696
|
+
if (agent.awaitingUserInput) {
|
|
697
|
+
this.logger.warn(`Agent ${agent.id} was awaiting user input (${agent.awaitingUserInput.type}) - recovering from interrupted state`, {
|
|
698
|
+
inputType: agent.awaitingUserInput.type,
|
|
699
|
+
siteId: agent.awaitingUserInput.siteId,
|
|
700
|
+
startedAt: agent.awaitingUserInput.startedAt
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// Clear the awaiting flag and resume agent
|
|
704
|
+
delete agent.awaitingUserInput;
|
|
705
|
+
agent.status = AGENT_STATUS.ACTIVE;
|
|
706
|
+
|
|
707
|
+
// Add a system message to the agent's queue so it knows what happened
|
|
708
|
+
if (!agent.messageQueues) {
|
|
709
|
+
agent.messageQueues = { toolResults: [], interAgentMessages: [], userMessages: [] };
|
|
710
|
+
}
|
|
711
|
+
agent.messageQueues.toolResults.push({
|
|
712
|
+
id: `recovery-${Date.now()}`,
|
|
713
|
+
toolId: 'system-recovery',
|
|
714
|
+
status: 'info',
|
|
715
|
+
result: {
|
|
716
|
+
message: 'Agent was waiting for user input (credentials) when the session was interrupted. The credential request has been cancelled. Please retry the authentication if needed.',
|
|
717
|
+
recoveredFrom: 'awaitingUserInput',
|
|
718
|
+
timestamp: new Date().toISOString()
|
|
719
|
+
},
|
|
720
|
+
timestamp: new Date().toISOString()
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Validate conversations structure
|
|
725
|
+
if (!agent.conversations || !agent.conversations.full) {
|
|
726
|
+
agent.conversations = {
|
|
727
|
+
full: {
|
|
728
|
+
messages: [],
|
|
729
|
+
lastUpdated: new Date().toISOString()
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// CRITICAL: Restore interAgentTracking as a Map (it comes as plain object from JSON)
|
|
735
|
+
if (!agent.interAgentTracking || !(agent.interAgentTracking instanceof Map)) {
|
|
736
|
+
// Convert plain object to Map, or create empty Map
|
|
737
|
+
if (agent.interAgentTracking && typeof agent.interAgentTracking === 'object') {
|
|
738
|
+
agent.interAgentTracking = new Map(Object.entries(agent.interAgentTracking));
|
|
739
|
+
} else {
|
|
740
|
+
agent.interAgentTracking = new Map();
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Add to registry and directory
|
|
745
|
+
this.agents.set(agent.id, agent);
|
|
746
|
+
this._updateAgentDirectory(agent);
|
|
747
|
+
|
|
748
|
+
// CRITICAL: Migrate conversation structure to ensure new fields exist
|
|
749
|
+
// This handles agents persisted before the originalMessageCountAtCompaction fix
|
|
750
|
+
await this.migrateConversationStructure(agent.id);
|
|
751
|
+
|
|
752
|
+
// Process any queued notifications
|
|
753
|
+
await this._processQueuedNotifications(agent.id);
|
|
754
|
+
|
|
755
|
+
this.logger.info(`Agent resumed: ${agent.id}`, {
|
|
756
|
+
name: agent.name,
|
|
757
|
+
status: agent.status,
|
|
758
|
+
messageCount: agent.conversations.full.messages.length
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
return agent;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Pause agent for specified duration
|
|
766
|
+
* @param {string} agentId - Agent identifier
|
|
767
|
+
* @param {number|Date} duration - Pause duration in seconds or Date object
|
|
768
|
+
* @param {string} reason - Reason for pause
|
|
769
|
+
* @returns {Promise<Object>} Pause confirmation
|
|
770
|
+
*/
|
|
771
|
+
async pauseAgent(agentId, duration, reason = 'Agent pause requested') {
|
|
772
|
+
const agent = await this.getAgent(agentId);
|
|
773
|
+
if (!agent) {
|
|
774
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
let pauseUntil;
|
|
778
|
+
if (duration instanceof Date) {
|
|
779
|
+
pauseUntil = duration;
|
|
780
|
+
} else {
|
|
781
|
+
// Duration in seconds
|
|
782
|
+
const maxPauseDuration = this.config.system?.maxPauseDuration || 300;
|
|
783
|
+
const pauseSeconds = Math.min(duration, maxPauseDuration);
|
|
784
|
+
pauseUntil = new Date(Date.now() + pauseSeconds * 1000);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
agent.status = AGENT_STATUS.PAUSED;
|
|
788
|
+
agent.pausedUntil = pauseUntil.toISOString();
|
|
789
|
+
agent.lastActivity = new Date().toISOString();
|
|
790
|
+
|
|
791
|
+
// Add to paused agents tracking
|
|
792
|
+
this.pausedAgents.set(agentId, {
|
|
793
|
+
agentId,
|
|
794
|
+
pausedAt: new Date().toISOString(),
|
|
795
|
+
pausedUntil: pauseUntil.toISOString(),
|
|
796
|
+
reason,
|
|
797
|
+
originalStatus: AGENT_STATUS.ACTIVE
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
await this.persistAgentState(agentId);
|
|
801
|
+
|
|
802
|
+
this.logger.info(`Agent paused: ${agentId}`, {
|
|
803
|
+
pausedUntil: pauseUntil.toISOString(),
|
|
804
|
+
reason,
|
|
805
|
+
durationSeconds: Math.round((pauseUntil.getTime() - Date.now()) / 1000)
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
return {
|
|
809
|
+
success: true,
|
|
810
|
+
agentId,
|
|
811
|
+
pausedUntil: pauseUntil.toISOString(),
|
|
812
|
+
reason,
|
|
813
|
+
message: `Agent paused until ${pauseUntil.toISOString()}`
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Resume paused agent
|
|
819
|
+
* @param {string} agentId - Agent identifier
|
|
820
|
+
* @returns {Promise<Object>} Resume confirmation
|
|
821
|
+
*/
|
|
822
|
+
async resumeAgent(agentId) {
|
|
823
|
+
const agent = await this.getAgent(agentId);
|
|
824
|
+
if (!agent) {
|
|
825
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
if (agent.status !== AGENT_STATUS.PAUSED) {
|
|
829
|
+
return {
|
|
830
|
+
success: true,
|
|
831
|
+
message: `Agent ${agentId} is not paused`
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
agent.status = AGENT_STATUS.ACTIVE;
|
|
836
|
+
agent.pausedUntil = null;
|
|
837
|
+
agent.lastActivity = new Date().toISOString();
|
|
838
|
+
|
|
839
|
+
// Remove from paused agents tracking
|
|
840
|
+
this.pausedAgents.delete(agentId);
|
|
841
|
+
|
|
842
|
+
// Process any queued notifications
|
|
843
|
+
await this._processQueuedNotifications(agentId);
|
|
844
|
+
|
|
845
|
+
await this.persistAgentState(agentId);
|
|
846
|
+
|
|
847
|
+
this.logger.info(`Agent resumed: ${agentId}`);
|
|
848
|
+
|
|
849
|
+
return {
|
|
850
|
+
success: true,
|
|
851
|
+
agentId,
|
|
852
|
+
message: `Agent ${agentId} resumed successfully`
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Restore agent from saved state. Thin wrapper around rehydrateFromState
|
|
858
|
+
* kept for the public-API name orchestrator + stateManager still use.
|
|
859
|
+
* @param {Object} agentState - Saved agent state
|
|
860
|
+
* @returns {Promise<Object>} Restored agent
|
|
861
|
+
*/
|
|
862
|
+
async restoreAgent(agentState) {
|
|
863
|
+
return await this.rehydrateFromState(agentState);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* Get agent discovery directory
|
|
868
|
+
* @returns {Array} Array of agent info for discovery
|
|
869
|
+
*/
|
|
870
|
+
getAgentDirectory() {
|
|
871
|
+
return Array.from(this.agentDirectory.values());
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* List all active agents
|
|
876
|
+
* @returns {Array} Array of active agents
|
|
877
|
+
*/
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Delete an agent and clean up its resources.
|
|
881
|
+
*
|
|
882
|
+
* Best-effort / idempotent: if the agent isn't currently loaded in
|
|
883
|
+
* memory we skip the live-state cleanup (attachments, visual editor,
|
|
884
|
+
* terminal processes — those only exist for loaded agents anyway)
|
|
885
|
+
* and go straight to clearing persistent state. This is how the
|
|
886
|
+
* bulk-delete UI can remove agents that were unloaded earlier in
|
|
887
|
+
* the session or that survived a process restart with no in-memory
|
|
888
|
+
* footprint. `stateManager.deleteAgentState` is itself ENOENT-safe,
|
|
889
|
+
* so deleting an already-gone agent is a no-op success.
|
|
890
|
+
*
|
|
891
|
+
* @param {string} agentId - Agent identifier
|
|
892
|
+
* @returns {Promise<Object>} Deletion result
|
|
893
|
+
*/
|
|
894
|
+
async deleteAgent(agentId) {
|
|
895
|
+
const agent = await this.getAgent(agentId);
|
|
896
|
+
const wasLoaded = !!agent;
|
|
897
|
+
|
|
898
|
+
if (wasLoaded) {
|
|
899
|
+
// Clean up file attachments with reference counting
|
|
900
|
+
if (this.fileAttachmentService) {
|
|
901
|
+
try {
|
|
902
|
+
await this.fileAttachmentService.deleteAgentAttachments(agentId);
|
|
903
|
+
this.logger.info(`File attachments cleaned up for agent: ${agentId}`);
|
|
904
|
+
} catch (error) {
|
|
905
|
+
this.logger.warn(`Failed to clean up file attachments for agent: ${error.message}`, { agentId });
|
|
906
|
+
// Continue with agent deletion even if attachment cleanup fails
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Clear codebase-knowledge cache for this agent — bytes live in
|
|
911
|
+
// the service's in-memory map; without this the per-agent entry
|
|
912
|
+
// would persist until the next process restart.
|
|
913
|
+
try {
|
|
914
|
+
const { getCodebaseKnowledgeService } = await import('../services/codebaseKnowledgeService.js');
|
|
915
|
+
getCodebaseKnowledgeService(this.logger).clear(agentId);
|
|
916
|
+
} catch (e) {
|
|
917
|
+
this.logger.debug?.('codebase-knowledge clear failed on agent delete', { agentId, error: e.message });
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Clean up visual editor instance
|
|
921
|
+
try {
|
|
922
|
+
const visualEditorBridge = getVisualEditorBridge();
|
|
923
|
+
if (visualEditorBridge.hasInstance(agentId)) {
|
|
924
|
+
await visualEditorBridge.stopInstance(agentId);
|
|
925
|
+
this.logger.info(`Visual editor instance cleaned up for agent: ${agentId}`);
|
|
926
|
+
}
|
|
927
|
+
} catch (error) {
|
|
928
|
+
this.logger.warn(`Failed to clean up visual editor for agent: ${error.message}`, { agentId });
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// Kill any running terminal processes for this agent
|
|
932
|
+
try {
|
|
933
|
+
const terminalTool = this.toolsRegistry?.getTool?.('terminal');
|
|
934
|
+
if (terminalTool && typeof terminalTool.cleanupAgent === 'function') {
|
|
935
|
+
await terminalTool.cleanupAgent(agentId);
|
|
936
|
+
this.logger.info(`Terminal processes cleaned up for agent: ${agentId}`);
|
|
937
|
+
}
|
|
938
|
+
} catch (error) {
|
|
939
|
+
this.logger.warn(`Failed to clean up terminal processes for agent: ${error.message}`, { agentId });
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// Clean up agent resources
|
|
943
|
+
this.agents.delete(agentId);
|
|
944
|
+
this.agentDirectory.delete(agentId);
|
|
945
|
+
this.pausedAgents.delete(agentId);
|
|
946
|
+
this.notificationQueue.delete(agentId);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Clean up persistent state — runs whether or not the agent was
|
|
950
|
+
// loaded. deleteAgentState handles missing files gracefully.
|
|
951
|
+
try {
|
|
952
|
+
await this.stateManager.deleteAgentState(agentId);
|
|
953
|
+
} catch (error) {
|
|
954
|
+
this.logger.warn(`Failed to delete agent persistent state: ${error.message}`, { agentId });
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
this.logger.info(`Agent deleted: ${agentId}`, {
|
|
958
|
+
agentName: agent?.name || '(not loaded)',
|
|
959
|
+
wasLoaded,
|
|
960
|
+
totalAgents: this.agents.size
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
return {
|
|
964
|
+
success: true,
|
|
965
|
+
agentId,
|
|
966
|
+
remainingAgents: this.agents.size
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Unload an agent from server memory without deleting persistent files
|
|
972
|
+
* Agent can be reloaded later using the Load Agent feature
|
|
973
|
+
* @param {string} agentId - Agent identifier
|
|
974
|
+
* @returns {Promise<Object>} Unload result
|
|
975
|
+
*/
|
|
976
|
+
async unloadAgent(agentId) {
|
|
977
|
+
const agent = await this.getAgent(agentId);
|
|
978
|
+
if (!agent) {
|
|
979
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const agentName = agent.name;
|
|
983
|
+
|
|
984
|
+
// Persist current state before unloading (so it can be reloaded later)
|
|
985
|
+
try {
|
|
986
|
+
await this.persistAgentState(agentId);
|
|
987
|
+
this.logger.info(`Agent state persisted before unload: ${agentId}`);
|
|
988
|
+
} catch (error) {
|
|
989
|
+
this.logger.warn(`Failed to persist agent state before unload: ${error.message}`, { agentId });
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// Clean up visual editor instance
|
|
993
|
+
try {
|
|
994
|
+
const visualEditorBridge = getVisualEditorBridge();
|
|
995
|
+
if (visualEditorBridge.hasInstance(agentId)) {
|
|
996
|
+
await visualEditorBridge.stopInstance(agentId);
|
|
997
|
+
this.logger.info(`Visual editor instance cleaned up for unloaded agent: ${agentId}`);
|
|
998
|
+
}
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
this.logger.warn(`Failed to clean up visual editor for unloaded agent: ${error.message}`, { agentId });
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// Remove from memory only (keep persistent files)
|
|
1004
|
+
this.agents.delete(agentId);
|
|
1005
|
+
this.agentDirectory.delete(agentId);
|
|
1006
|
+
this.pausedAgents.delete(agentId);
|
|
1007
|
+
this.notificationQueue.delete(agentId);
|
|
1008
|
+
|
|
1009
|
+
// Remove from scheduler if present
|
|
1010
|
+
if (this.scheduler) {
|
|
1011
|
+
this.scheduler.removeAgent(agentId, 'unloaded');
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
this.logger.info(`Agent unloaded from memory: ${agentId}`, {
|
|
1015
|
+
agentName,
|
|
1016
|
+
totalAgents: this.agents.size,
|
|
1017
|
+
note: 'Persistent files preserved for future reload'
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
return {
|
|
1021
|
+
success: true,
|
|
1022
|
+
agentId,
|
|
1023
|
+
agentName,
|
|
1024
|
+
remainingAgents: this.agents.size,
|
|
1025
|
+
message: `Agent "${agentName}" unloaded. Use Load Agent to reload it.`
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Clear all conversation history for an agent
|
|
1031
|
+
* Resets the agent to a fresh state while keeping configuration
|
|
1032
|
+
* @param {string} agentId - Agent identifier
|
|
1033
|
+
* @returns {Promise<Object>} Clear result
|
|
1034
|
+
*/
|
|
1035
|
+
async clearConversation(agentId) {
|
|
1036
|
+
const agent = await this.getAgent(agentId);
|
|
1037
|
+
if (!agent) {
|
|
1038
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const previousMessageCount = agent.conversations?.full?.messages?.length || 0;
|
|
1042
|
+
|
|
1043
|
+
// Reset full conversation
|
|
1044
|
+
agent.conversations.full = {
|
|
1045
|
+
messages: [],
|
|
1046
|
+
lastUpdated: new Date().toISOString()
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
// Reset model-specific conversations
|
|
1050
|
+
for (const key of Object.keys(agent.conversations)) {
|
|
1051
|
+
if (key !== 'full') {
|
|
1052
|
+
agent.conversations[key] = {
|
|
1053
|
+
messages: [],
|
|
1054
|
+
compactizedMessages: null,
|
|
1055
|
+
lastUpdated: new Date().toISOString(),
|
|
1056
|
+
compactionState: {
|
|
1057
|
+
isCompacted: false,
|
|
1058
|
+
lastCompactionTime: null,
|
|
1059
|
+
originalMessageCount: 0,
|
|
1060
|
+
compactedMessageCount: 0
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Clear message queues
|
|
1067
|
+
if (agent.messageQueues) {
|
|
1068
|
+
agent.messageQueues = {
|
|
1069
|
+
toolResults: [],
|
|
1070
|
+
interAgentMessages: [],
|
|
1071
|
+
userMessages: []
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// Clear task list
|
|
1076
|
+
if (agent.taskList) {
|
|
1077
|
+
agent.taskList = {
|
|
1078
|
+
tasks: [],
|
|
1079
|
+
lastUpdated: new Date().toISOString()
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
agent.currentTask = null;
|
|
1084
|
+
agent.taskStartTime = null;
|
|
1085
|
+
agent.iterationCount = 0;
|
|
1086
|
+
|
|
1087
|
+
// Persist the cleared state
|
|
1088
|
+
await this.persistAgentState(agentId);
|
|
1089
|
+
|
|
1090
|
+
this.logger.info(`Conversation cleared for agent: ${agentId}`, {
|
|
1091
|
+
agentName: agent.name,
|
|
1092
|
+
previousMessageCount
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
return {
|
|
1096
|
+
success: true,
|
|
1097
|
+
agentId,
|
|
1098
|
+
previousMessageCount,
|
|
1099
|
+
message: `Cleared ${previousMessageCount} messages`
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* Generate unique agent ID
|
|
1105
|
+
* @private
|
|
1106
|
+
*/
|
|
1107
|
+
_generateAgentId(name) {
|
|
1108
|
+
const sanitizedName = name.toLowerCase().replace(/[^a-z0-9]/g, '-');
|
|
1109
|
+
const timestamp = Date.now();
|
|
1110
|
+
return `agent-${sanitizedName}-${timestamp}`;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Update agent directory for discovery
|
|
1115
|
+
* @private
|
|
1116
|
+
*/
|
|
1117
|
+
_updateAgentDirectory(agent) {
|
|
1118
|
+
this.agentDirectory.set(agent.id, {
|
|
1119
|
+
id: agent.id,
|
|
1120
|
+
name: agent.name,
|
|
1121
|
+
type: agent.type,
|
|
1122
|
+
capabilities: agent.capabilities,
|
|
1123
|
+
status: agent.status,
|
|
1124
|
+
description: this._generateAgentDescription(agent)
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* Generate agent description for directory
|
|
1130
|
+
* @private
|
|
1131
|
+
*/
|
|
1132
|
+
_generateAgentDescription(agent) {
|
|
1133
|
+
let description = `${agent.name} (${agent.type})`;
|
|
1134
|
+
|
|
1135
|
+
if (agent.capabilities.length > 0) {
|
|
1136
|
+
description += ` - Capabilities: ${agent.capabilities.join(', ')}`;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
return description;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* Check if agent is currently paused
|
|
1144
|
+
* @private
|
|
1145
|
+
*/
|
|
1146
|
+
_isAgentPaused(agent) {
|
|
1147
|
+
if (agent.status !== AGENT_STATUS.PAUSED || !agent.pausedUntil) {
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
return new Date() < new Date(agent.pausedUntil);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/**
|
|
1155
|
+
* Get first user message snippet for card preview
|
|
1156
|
+
* @private
|
|
1157
|
+
*/
|
|
1158
|
+
_getFirstUserMessageSnippet(agent) {
|
|
1159
|
+
const messages = agent.conversations?.full?.messages;
|
|
1160
|
+
if (!messages || messages.length === 0) return null;
|
|
1161
|
+
|
|
1162
|
+
// Find first user message — include consolidated-input since that's how
|
|
1163
|
+
// user messages are stored after queue processing. Skip task-boundary.
|
|
1164
|
+
const firstUser = messages.find(m =>
|
|
1165
|
+
m.role === 'user' && m.content &&
|
|
1166
|
+
m.type !== 'task-boundary'
|
|
1167
|
+
);
|
|
1168
|
+
if (!firstUser) return null;
|
|
1169
|
+
|
|
1170
|
+
// Handle both string and array content formats
|
|
1171
|
+
const text = typeof firstUser.content === 'string'
|
|
1172
|
+
? firstUser.content
|
|
1173
|
+
: Array.isArray(firstUser.content)
|
|
1174
|
+
? firstUser.content.filter(b => b.type === 'text').map(b => b.text).join('\n')
|
|
1175
|
+
: null;
|
|
1176
|
+
if (!text) return null;
|
|
1177
|
+
|
|
1178
|
+
// Take first 2 non-empty lines, cap at 120 chars
|
|
1179
|
+
const lines = text.split('\n').filter(l => l.trim());
|
|
1180
|
+
const snippet = lines.slice(0, 2).join('\n');
|
|
1181
|
+
return snippet.length > 120 ? snippet.slice(0, 117) + '...' : snippet;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* Check if pause duration has expired
|
|
1186
|
+
* @private
|
|
1187
|
+
*/
|
|
1188
|
+
_isPauseExpired(agent) {
|
|
1189
|
+
if (!agent.pausedUntil) return true;
|
|
1190
|
+
return new Date() >= new Date(agent.pausedUntil);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* Update agent pause status
|
|
1195
|
+
* @private
|
|
1196
|
+
*/
|
|
1197
|
+
_updateAgentPauseStatus(agent) {
|
|
1198
|
+
if (agent.status === AGENT_STATUS.PAUSED && this._isPauseExpired(agent)) {
|
|
1199
|
+
agent.status = AGENT_STATUS.ACTIVE;
|
|
1200
|
+
agent.pausedUntil = null;
|
|
1201
|
+
this.pausedAgents.delete(agent.id);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* Queue notification for paused agent
|
|
1207
|
+
* @private
|
|
1208
|
+
*/
|
|
1209
|
+
_queueNotification(agentId, message) {
|
|
1210
|
+
if (!this.notificationQueue.has(agentId)) {
|
|
1211
|
+
this.notificationQueue.set(agentId, []);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
this.notificationQueue.get(agentId).push({
|
|
1215
|
+
...message,
|
|
1216
|
+
queuedAt: new Date().toISOString()
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Process queued notifications for agent
|
|
1222
|
+
* @private
|
|
1223
|
+
*/
|
|
1224
|
+
async _processQueuedNotifications(agentId) {
|
|
1225
|
+
const notifications = this.notificationQueue.get(agentId);
|
|
1226
|
+
if (!notifications || notifications.length === 0) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
this.logger.info(`Processing ${notifications.length} queued notifications for agent: ${agentId}`);
|
|
1231
|
+
|
|
1232
|
+
for (const notification of notifications) {
|
|
1233
|
+
await this.notifyAgent(agentId, notification);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// Clear queue
|
|
1237
|
+
this.notificationQueue.delete(agentId);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Format message for specific model
|
|
1242
|
+
* @private
|
|
1243
|
+
*/
|
|
1244
|
+
_formatMessageForModel(message) {
|
|
1245
|
+
// This would be implemented with model-specific formatting logic
|
|
1246
|
+
// For now, return the message as-is
|
|
1247
|
+
return { ...message };
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/**
|
|
1251
|
+
* Get model format version
|
|
1252
|
+
* @private
|
|
1253
|
+
*/
|
|
1254
|
+
_getModelFormatVersion(model) {
|
|
1255
|
+
return MODEL_FORMAT_VERSIONS[model] || MODEL_FORMAT_VERSIONS.DEFAULT;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* Refresh tool descriptions for an existing agent
|
|
1260
|
+
* @param {string} agentId - Agent identifier
|
|
1261
|
+
* @param {Object} options - Refresh options
|
|
1262
|
+
* @returns {Promise<boolean>} Success status
|
|
1263
|
+
*/
|
|
1264
|
+
async refreshAgentToolDescriptions(agentId, options = {}) {
|
|
1265
|
+
const agent = await this.getAgent(agentId);
|
|
1266
|
+
if (!agent || !this.toolsRegistry) {
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
try {
|
|
1271
|
+
// Use original prompt if available, otherwise current prompt
|
|
1272
|
+
const basePrompt = agent.originalSystemPrompt || agent.systemPrompt;
|
|
1273
|
+
|
|
1274
|
+
// Enhance with current tool capabilities
|
|
1275
|
+
const enhancedPrompt = this.toolsRegistry.enhanceSystemPrompt(
|
|
1276
|
+
basePrompt,
|
|
1277
|
+
agent.capabilities,
|
|
1278
|
+
{
|
|
1279
|
+
compact: options.compact || false,
|
|
1280
|
+
includeExamples: options.includeExamples !== false,
|
|
1281
|
+
includeUsageGuidelines: options.includeUsageGuidelines !== false,
|
|
1282
|
+
includeSecurityNotes: options.includeSecurityNotes !== false
|
|
1283
|
+
}
|
|
1284
|
+
);
|
|
1285
|
+
|
|
1286
|
+
// Update agent's system prompt
|
|
1287
|
+
agent.systemPrompt = enhancedPrompt;
|
|
1288
|
+
agent.lastActivity = new Date().toISOString();
|
|
1289
|
+
|
|
1290
|
+
// Persist changes
|
|
1291
|
+
await this.persistAgentState(agentId);
|
|
1292
|
+
|
|
1293
|
+
this.logger?.info(`Agent tool descriptions refreshed: ${agentId}`, {
|
|
1294
|
+
capabilities: agent.capabilities,
|
|
1295
|
+
promptLength: enhancedPrompt.length
|
|
1296
|
+
});
|
|
1297
|
+
|
|
1298
|
+
return true;
|
|
1299
|
+
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
this.logger?.error(`Failed to refresh tool descriptions for agent: ${agentId}`, {
|
|
1302
|
+
error: error.message
|
|
1303
|
+
});
|
|
1304
|
+
return false;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* Set or update tools registry for the agent pool
|
|
1310
|
+
* @param {ToolsRegistry} toolsRegistry - Tools registry instance
|
|
1311
|
+
*/
|
|
1312
|
+
setToolsRegistry(toolsRegistry) {
|
|
1313
|
+
this.toolsRegistry = toolsRegistry;
|
|
1314
|
+
|
|
1315
|
+
this.logger?.info('Tools registry updated for agent pool', {
|
|
1316
|
+
hasRegistry: !!toolsRegistry
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* Bulk refresh tool descriptions for all agents
|
|
1322
|
+
* @param {Object} options - Refresh options
|
|
1323
|
+
* @returns {Promise<Object>} Results summary
|
|
1324
|
+
*/
|
|
1325
|
+
async bulkRefreshToolDescriptions(options = {}) {
|
|
1326
|
+
const results = {
|
|
1327
|
+
total: this.agents.size,
|
|
1328
|
+
successful: 0,
|
|
1329
|
+
failed: 0,
|
|
1330
|
+
skipped: 0
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
for (const [agentId, agent] of this.agents.entries()) {
|
|
1334
|
+
if (!agent.capabilities || agent.capabilities.length === 0) {
|
|
1335
|
+
results.skipped++;
|
|
1336
|
+
continue;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
const success = await this.refreshAgentToolDescriptions(agentId, options);
|
|
1340
|
+
if (success) {
|
|
1341
|
+
results.successful++;
|
|
1342
|
+
} else {
|
|
1343
|
+
results.failed++;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
this.logger?.info('Bulk tool descriptions refresh completed', results);
|
|
1348
|
+
return results;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* Set MessageProcessor reference for triggering responses
|
|
1353
|
+
* @param {MessageProcessor} messageProcessor - MessageProcessor instance
|
|
1354
|
+
*/
|
|
1355
|
+
setMessageProcessor(messageProcessor) {
|
|
1356
|
+
this.messageProcessor = messageProcessor;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
/**
|
|
1360
|
+
* Set AgentScheduler reference for managing agent modes
|
|
1361
|
+
* @param {AgentScheduler} scheduler - AgentScheduler instance
|
|
1362
|
+
*/
|
|
1363
|
+
setScheduler(scheduler) {
|
|
1364
|
+
this.scheduler = scheduler;
|
|
1365
|
+
|
|
1366
|
+
this.logger?.info('AgentScheduler reference set for agent pool', {
|
|
1367
|
+
hasScheduler: !!scheduler
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* Set FileAttachmentService reference for cleaning up attachments
|
|
1373
|
+
* @param {FileAttachmentService} fileAttachmentService - FileAttachmentService instance
|
|
1374
|
+
*/
|
|
1375
|
+
setFileAttachmentService(fileAttachmentService) {
|
|
1376
|
+
this.fileAttachmentService = fileAttachmentService;
|
|
1377
|
+
|
|
1378
|
+
this.logger?.info('FileAttachmentService reference set for agent pool', {
|
|
1379
|
+
hasService: !!fileAttachmentService
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// OLD INTER-AGENT MESSAGE QUEUE SYSTEM REMOVED
|
|
1384
|
+
// Now using the new messageQueues system with AgentScheduler
|
|
1385
|
+
// Inter-agent messages are queued via addInterAgentMessage() method
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Wake an agent out of any paused/delayed state because a message has
|
|
1389
|
+
* arrived for it. Shared by addUserMessage / addInterAgentMessage /
|
|
1390
|
+
* addToolResult — every inbound path MUST route through this to avoid
|
|
1391
|
+
* the "silent queue into a delayed agent" bug where the recipient never
|
|
1392
|
+
* wakes up and the message sits until the scheduler back-off naturally
|
|
1393
|
+
* expires.
|
|
1394
|
+
*
|
|
1395
|
+
* Rationale: delays/pauses are "leave this agent alone" signals set by
|
|
1396
|
+
* the scheduler on error back-off or by the agentDelayTool for timed
|
|
1397
|
+
* waits. Any caller going to the trouble of actually addressing the
|
|
1398
|
+
* agent is an explicit "act now" signal that overrides the wait.
|
|
1399
|
+
*
|
|
1400
|
+
* @param {Object} agent - Agent object (already fetched)
|
|
1401
|
+
* @param {string} reason - Source label for logs (e.g. 'user-message',
|
|
1402
|
+
* 'inter-agent-message', 'tool-result')
|
|
1403
|
+
* @returns {Promise<{wasPaused:boolean, hadDelay:boolean, hadPausedUntil:boolean}>}
|
|
1404
|
+
* @private
|
|
1405
|
+
*/
|
|
1406
|
+
async _wakeAgentForMessage(agent, reason) {
|
|
1407
|
+
const info = { wasPaused: false, hadDelay: false, hadPausedUntil: false };
|
|
1408
|
+
if (!agent) return info;
|
|
1409
|
+
|
|
1410
|
+
// Auto-resume explicitly paused agent.
|
|
1411
|
+
if (agent.status === AGENT_STATUS.PAUSED) {
|
|
1412
|
+
info.wasPaused = true;
|
|
1413
|
+
this.logger.info(`Auto-resuming paused agent ${agent.id} due to ${reason}`);
|
|
1414
|
+
await this.resumeAgent(agent.id);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// Clear scheduler-enforced delay (rate-limit back-off, api-key delay,
|
|
1418
|
+
// builtin webTool delay, etc.). Only clear if actually in the future;
|
|
1419
|
+
// stale past values don't matter but shouldn't trigger a broadcast.
|
|
1420
|
+
if (agent.delayEndTime && new Date(agent.delayEndTime).getTime() > Date.now()) {
|
|
1421
|
+
info.hadDelay = true;
|
|
1422
|
+
agent.delayEndTime = null;
|
|
1423
|
+
this.logger.info(`Cleared scheduler delay for agent ${agent.id} — ${reason} takes precedence`);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// Defensive: if pausedUntil is set but status isn't PAUSED (shouldn't
|
|
1427
|
+
// happen but protects against state drift), clear it too.
|
|
1428
|
+
if (agent.pausedUntil && new Date(agent.pausedUntil).getTime() > Date.now()) {
|
|
1429
|
+
info.hadPausedUntil = true;
|
|
1430
|
+
agent.pausedUntil = null;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
return info;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
/**
|
|
1437
|
+
* Broadcast a delay-clear to the WS so the UI's delay chip disappears
|
|
1438
|
+
* immediately instead of waiting for the next scheduler tick.
|
|
1439
|
+
* @private
|
|
1440
|
+
*/
|
|
1441
|
+
async _broadcastWake(agentId, reason) {
|
|
1442
|
+
if (!this.scheduler?.broadcastAgentStateUpdate) return;
|
|
1443
|
+
try {
|
|
1444
|
+
await this.scheduler.broadcastAgentStateUpdate(agentId, reason);
|
|
1445
|
+
} catch (err) {
|
|
1446
|
+
this.logger.warn(`Failed to broadcast wake for ${agentId}: ${err.message}`);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/**
|
|
1451
|
+
* Add message to agent's user message queue
|
|
1452
|
+
* @param {string} agentId - Agent ID
|
|
1453
|
+
* @param {Object} message - User message to queue
|
|
1454
|
+
* @returns {Promise<void>}
|
|
1455
|
+
*/
|
|
1456
|
+
async addUserMessage(agentId, message) {
|
|
1457
|
+
const agent = await this.getAgent(agentId);
|
|
1458
|
+
if (!agent) {
|
|
1459
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
// Any inbound message — user, inter-agent, or tool-result — takes
|
|
1463
|
+
// precedence over scheduler back-off and manual pauses. See
|
|
1464
|
+
// _wakeAgentForMessage for the rationale.
|
|
1465
|
+
const wakeInfo = await this._wakeAgentForMessage(agent, 'user-message');
|
|
1466
|
+
|
|
1467
|
+
const queuedMessage = {
|
|
1468
|
+
...message,
|
|
1469
|
+
id: message.id || `user-msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
1470
|
+
queuedAt: new Date().toISOString(),
|
|
1471
|
+
timestamp: message.timestamp || new Date().toISOString()
|
|
1472
|
+
};
|
|
1473
|
+
|
|
1474
|
+
agent.messageQueues.userMessages.push(queuedMessage);
|
|
1475
|
+
|
|
1476
|
+
// Auto-create a task for AGENT mode agents so the scheduler picks them up.
|
|
1477
|
+
// Scheduling condition is purely task-based: has pending tasks AND in agent mode.
|
|
1478
|
+
if (agent.mode === AGENT_MODES.AGENT) {
|
|
1479
|
+
this._autoCreateTaskForMessage(agent, queuedMessage, 'user', 'high');
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
// ── Auto-save substantive user messages as plan/* memories ───────
|
|
1483
|
+
// Observed in production: across 670-message agent sessions the
|
|
1484
|
+
// agent NEVER wrote a memory voluntarily. Compaction then summarized
|
|
1485
|
+
// away the user's literal asks, the agent paraphrased what was left,
|
|
1486
|
+
// and ended up doing work the user never requested. Belt-and-
|
|
1487
|
+
// suspenders alongside the OPERATING POSTURE prompt nudge: when a
|
|
1488
|
+
// user message looks substantive (long, or contains a numbered/
|
|
1489
|
+
// bulleted multi-part ask), the SYSTEM saves it as `plan/<auto>` so
|
|
1490
|
+
// the system-prompt auto-injection makes the user's words visible
|
|
1491
|
+
// every turn — even if the agent itself never thought to save.
|
|
1492
|
+
// Best-effort: never block the message-enqueue path.
|
|
1493
|
+
this._autoSaveUserMessageAsPlan(agentId, queuedMessage).catch(err => {
|
|
1494
|
+
this.logger.debug?.('Auto-save of user message as plan/* failed (continuing)', {
|
|
1495
|
+
agentId, error: err?.message,
|
|
1496
|
+
});
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
await this.persistAgentState(agentId);
|
|
1500
|
+
|
|
1501
|
+
// If we cleared a delay, surface it on the WS so the delay chip in the
|
|
1502
|
+
// chat header disappears without waiting for the next scheduler cycle.
|
|
1503
|
+
if (wakeInfo.hadDelay) {
|
|
1504
|
+
await this._broadcastWake(agentId, 'user-message-clears-delay');
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
this.logger.info(`User message queued for agent: ${agentId}`, {
|
|
1508
|
+
messageId: queuedMessage.id,
|
|
1509
|
+
queueSize: agent.messageQueues.userMessages.length
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Add message to agent's inter-agent message queue
|
|
1515
|
+
* @param {string} agentId - Agent ID
|
|
1516
|
+
* @param {Object} message - Inter-agent message to queue
|
|
1517
|
+
* @returns {Promise<void>}
|
|
1518
|
+
*/
|
|
1519
|
+
async addInterAgentMessage(agentId, message) {
|
|
1520
|
+
const agent = await this.getAgent(agentId);
|
|
1521
|
+
if (!agent) {
|
|
1522
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
// An inter-agent ping from another agent is an explicit "act now"
|
|
1526
|
+
// signal and must override any scheduler back-off or manual pause
|
|
1527
|
+
// on the recipient. See _wakeAgentForMessage.
|
|
1528
|
+
const wakeInfo = await this._wakeAgentForMessage(agent, 'inter-agent-message');
|
|
1529
|
+
|
|
1530
|
+
const queuedMessage = {
|
|
1531
|
+
...message,
|
|
1532
|
+
id: message.id || message.messageId || `inter-agent-msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
1533
|
+
queuedAt: new Date().toISOString(),
|
|
1534
|
+
timestamp: message.timestamp || new Date().toISOString()
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1537
|
+
agent.messageQueues.interAgentMessages.push(queuedMessage);
|
|
1538
|
+
|
|
1539
|
+
// Auto-create a task for AGENT mode agents so the scheduler picks them up.
|
|
1540
|
+
if (agent.mode === AGENT_MODES.AGENT) {
|
|
1541
|
+
const senderName = message.senderName || message.sender || 'Unknown Agent';
|
|
1542
|
+
this._autoCreateTaskForMessage(agent, queuedMessage, `inter-agent from ${senderName}`, 'medium');
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
await this.persistAgentState(agentId);
|
|
1546
|
+
|
|
1547
|
+
// CRITICAL: Register recipient with scheduler so it has a sessionId for API key resolution.
|
|
1548
|
+
// Inter-agent messages carry the sender's sessionId — reuse it for the recipient.
|
|
1549
|
+
if (this.scheduler && message.sessionId) {
|
|
1550
|
+
await this.scheduler.addAgent(agentId, {
|
|
1551
|
+
sessionId: message.sessionId,
|
|
1552
|
+
triggeredBy: 'inter-agent-message'
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
if (wakeInfo.hadDelay) {
|
|
1557
|
+
await this._broadcastWake(agentId, 'inter-agent-message-clears-delay');
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
this.logger.info(`Inter-agent message queued for agent: ${agentId}`, {
|
|
1561
|
+
messageId: queuedMessage.id,
|
|
1562
|
+
sender: message.sender || message.senderName,
|
|
1563
|
+
queueSize: agent.messageQueues.interAgentMessages.length,
|
|
1564
|
+
sessionRegistered: !!(this.scheduler && message.sessionId)
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/**
|
|
1569
|
+
* Add tool result to agent's tool results queue
|
|
1570
|
+
* @param {string} agentId - Agent ID
|
|
1571
|
+
* @param {Object} toolResult - Tool result to queue
|
|
1572
|
+
* @returns {Promise<void>}
|
|
1573
|
+
*/
|
|
1574
|
+
async addToolResult(agentId, toolResult) {
|
|
1575
|
+
const agent = await this.getAgent(agentId);
|
|
1576
|
+
if (!agent) {
|
|
1577
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// Tool results are "external async work finished — continue."
|
|
1581
|
+
// If the agent is delayed or paused when a result arrives (e.g. a
|
|
1582
|
+
// long image/video render completes while an unrelated rate-limit
|
|
1583
|
+
// back-off is still in effect) we wake it so the result can be
|
|
1584
|
+
// consumed immediately instead of sitting until the back-off expires.
|
|
1585
|
+
const wakeInfo = await this._wakeAgentForMessage(agent, 'tool-result');
|
|
1586
|
+
|
|
1587
|
+
const queuedResult = {
|
|
1588
|
+
...toolResult,
|
|
1589
|
+
id: toolResult.id || `tool-result-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
1590
|
+
queuedAt: new Date().toISOString(),
|
|
1591
|
+
timestamp: toolResult.timestamp || new Date().toISOString()
|
|
1592
|
+
};
|
|
1593
|
+
|
|
1594
|
+
agent.messageQueues.toolResults.push(queuedResult);
|
|
1595
|
+
await this.persistAgentState(agentId);
|
|
1596
|
+
|
|
1597
|
+
if (wakeInfo.hadDelay) {
|
|
1598
|
+
await this._broadcastWake(agentId, 'tool-result-clears-delay');
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
this.logger.debug(`Tool result queued for agent: ${agentId}`, {
|
|
1602
|
+
toolId: toolResult.toolId,
|
|
1603
|
+
status: toolResult.status,
|
|
1604
|
+
queueSize: agent.messageQueues.toolResults.length
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
* Auto-create a task from an incoming message for AGENT mode agents.
|
|
1610
|
+
* This ensures the scheduler (which uses pending tasks as the sole activation
|
|
1611
|
+
* condition for AGENT mode) picks up the agent for processing.
|
|
1612
|
+
* @param {Object} agent - Agent object
|
|
1613
|
+
* @param {Object} message - The queued message
|
|
1614
|
+
* @param {string} source - Source label (e.g. 'user', 'inter-agent from AgentX')
|
|
1615
|
+
* @param {string} priority - Task priority ('high', 'medium', 'low')
|
|
1616
|
+
* @private
|
|
1617
|
+
*/
|
|
1618
|
+
/**
|
|
1619
|
+
* Save a substantive user message as a `plan/*` memory automatically.
|
|
1620
|
+
*
|
|
1621
|
+
* Rationale (Talisman case study, May 2026): agents observed in
|
|
1622
|
+
* production never wrote a single memory across hundreds of
|
|
1623
|
+
* messages, even when the OPERATING POSTURE prompt explicitly told
|
|
1624
|
+
* them to. The user's literal ask then got lost in compaction and
|
|
1625
|
+
* the agent went off-course. This system-level safety net puts the
|
|
1626
|
+
* user's message into the durable plan/* store — which the system
|
|
1627
|
+
* prompt auto-injects every turn — without depending on the model
|
|
1628
|
+
* making the call.
|
|
1629
|
+
*
|
|
1630
|
+
* What counts as "substantive":
|
|
1631
|
+
* - Content length ≥ 60 chars (~12 words) — short acks/yes-no don't qualify
|
|
1632
|
+
* - AND any of:
|
|
1633
|
+
* • contains a numbered list ("1.", "2.", "3." …)
|
|
1634
|
+
* • contains a bullet list (-, *, • at line start)
|
|
1635
|
+
* • OR is ≥ 120 chars (longer than a one-line ack)
|
|
1636
|
+
*
|
|
1637
|
+
* What gets saved:
|
|
1638
|
+
* - title: `plan/user-<short-slug>-<timestamp>`
|
|
1639
|
+
* - description: "auto-saved from user message at <iso>"
|
|
1640
|
+
* - content: the verbatim user message
|
|
1641
|
+
*
|
|
1642
|
+
* The agent can rename, consolidate, or delete these later. They
|
|
1643
|
+
* exist as a fail-safe — if the agent does its job and saves its
|
|
1644
|
+
* own better-named plan, these auto-saves can be cleaned up. If
|
|
1645
|
+
* the agent doesn't, at least the user's words survive compaction.
|
|
1646
|
+
*
|
|
1647
|
+
* @param {string} agentId
|
|
1648
|
+
* @param {Object} message - The queued user message
|
|
1649
|
+
* @private
|
|
1650
|
+
*/
|
|
1651
|
+
async _autoSaveUserMessageAsPlan(agentId, message) {
|
|
1652
|
+
const content = typeof message?.content === 'string' ? message.content : '';
|
|
1653
|
+
if (!content) return;
|
|
1654
|
+
if (!this._looksSubstantive(content)) return;
|
|
1655
|
+
|
|
1656
|
+
// Lazy-load to keep agentPool's load order light. The same import
|
|
1657
|
+
// pattern as agentScheduler's plan injection.
|
|
1658
|
+
let memoryService;
|
|
1659
|
+
try {
|
|
1660
|
+
const mod = await import('../services/memoryService.js');
|
|
1661
|
+
memoryService = mod.getMemoryService(this.logger);
|
|
1662
|
+
await memoryService.initialize();
|
|
1663
|
+
} catch (e) {
|
|
1664
|
+
this.logger.debug?.('Auto-save plan: memory service unavailable', { error: e.message });
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// ── Deduplication ────────────────────────────────────────────────
|
|
1669
|
+
// Users repeat themselves ("I repeat my old message", "did you do
|
|
1670
|
+
// it all?" + paste the same thing). Without dedup the auto-saver
|
|
1671
|
+
// would create N copies of essentially the same plan. Load
|
|
1672
|
+
// existing plan/user-* memories and skip when the new content is
|
|
1673
|
+
// ≥70% similar to any of them (Jaccard over normalized word sets).
|
|
1674
|
+
let existingPlans = [];
|
|
1675
|
+
try {
|
|
1676
|
+
const all = await memoryService.loadMemories(agentId);
|
|
1677
|
+
existingPlans = (all || []).filter(m =>
|
|
1678
|
+
typeof m?.title === 'string' && m.title.startsWith('plan/user-')
|
|
1679
|
+
);
|
|
1680
|
+
} catch (e) {
|
|
1681
|
+
// Treat unreadable store as empty — we may still write a fresh entry.
|
|
1682
|
+
this.logger.debug?.('Auto-save plan: existing memories unreadable', { agentId, error: e.message });
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
const newTokens = this._tokenize(content);
|
|
1686
|
+
for (const existing of existingPlans) {
|
|
1687
|
+
const existingTokens = this._tokenize(existing.content || '');
|
|
1688
|
+
const sim = this._jaccard(newTokens, existingTokens);
|
|
1689
|
+
const containment = this._overlapCoefficient(newTokens, existingTokens);
|
|
1690
|
+
// Jaccard catches near-identical reformulations. Containment
|
|
1691
|
+
// catches the "I repeat my old message — <same content>" case
|
|
1692
|
+
// where the user re-pastes the original plus a preamble. Either
|
|
1693
|
+
// signal is enough to suppress the duplicate.
|
|
1694
|
+
if (sim >= 0.7 || containment >= 0.85) {
|
|
1695
|
+
this.logger.info?.('Auto-save plan: skipping near-duplicate of existing plan', {
|
|
1696
|
+
agentId, existingTitle: existing.title,
|
|
1697
|
+
jaccard: sim.toFixed(2), containment: containment.toFixed(2),
|
|
1698
|
+
});
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
// ── Per-agent cap ────────────────────────────────────────────────
|
|
1704
|
+
// Bound the total auto-saved plans so an active session doesn't
|
|
1705
|
+
// bloat the agent's plan/* namespace indefinitely. Keep the K most
|
|
1706
|
+
// recent; delete the oldest auto-saves beyond that.
|
|
1707
|
+
const AUTO_PLAN_CAP = 8;
|
|
1708
|
+
const existingAutoSaves = existingPlans
|
|
1709
|
+
.filter(m => /^plan\/user-/.test(m.title))
|
|
1710
|
+
.sort((a, b) => String(a.createdAt || '').localeCompare(String(b.createdAt || '')));
|
|
1711
|
+
while (existingAutoSaves.length >= AUTO_PLAN_CAP) {
|
|
1712
|
+
const oldest = existingAutoSaves.shift();
|
|
1713
|
+
try {
|
|
1714
|
+
await memoryService.deleteMemory(agentId, oldest.id);
|
|
1715
|
+
this.logger.info?.('Auto-save plan: retired oldest auto-save to keep cap', {
|
|
1716
|
+
agentId, retiredTitle: oldest.title, cap: AUTO_PLAN_CAP,
|
|
1717
|
+
});
|
|
1718
|
+
} catch (e) {
|
|
1719
|
+
// Non-fatal — if we can't delete the oldest, just skip this entry
|
|
1720
|
+
// and proceed with the write. Worst case the plan list grows
|
|
1721
|
+
// by one beyond the cap — still bounded over time.
|
|
1722
|
+
this.logger.debug?.('Auto-save plan: retire-oldest failed', { agentId, error: e.message });
|
|
1723
|
+
break;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// ── Write the new memory ─────────────────────────────────────────
|
|
1728
|
+
const firstLine = (content.match(/[^\n]+/) || [''])[0].trim();
|
|
1729
|
+
const slug = firstLine
|
|
1730
|
+
.toLowerCase()
|
|
1731
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
1732
|
+
.replace(/^-+|-+$/g, '')
|
|
1733
|
+
.slice(0, 40) || 'request';
|
|
1734
|
+
const ts = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-');
|
|
1735
|
+
const title = `plan/user-${slug}-${ts}`;
|
|
1736
|
+
|
|
1737
|
+
try {
|
|
1738
|
+
await memoryService.addMemory(agentId, {
|
|
1739
|
+
title,
|
|
1740
|
+
description: `Auto-saved from user message at ${message.timestamp || new Date().toISOString()}`,
|
|
1741
|
+
content,
|
|
1742
|
+
});
|
|
1743
|
+
this.logger.info?.('Auto-saved user message as plan/* memory', {
|
|
1744
|
+
agentId, title, contentLength: content.length,
|
|
1745
|
+
});
|
|
1746
|
+
} catch (e) {
|
|
1747
|
+
this.logger.debug?.('Auto-save plan: write failed', { agentId, title, error: e.message });
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* Tokenize a string into a lowercased word set for similarity checks.
|
|
1753
|
+
* Strips punctuation, drops short words (<3 chars), and drops a
|
|
1754
|
+
* small stopword set so that common words like "the" / "and" don't
|
|
1755
|
+
* inflate similarity scores between otherwise different messages.
|
|
1756
|
+
* @private
|
|
1757
|
+
*/
|
|
1758
|
+
_tokenize(s) {
|
|
1759
|
+
if (typeof s !== 'string') return new Set();
|
|
1760
|
+
return new Set(
|
|
1761
|
+
s.toLowerCase()
|
|
1762
|
+
.replace(/[^a-z0-9\s]+/g, ' ')
|
|
1763
|
+
.split(/\s+/)
|
|
1764
|
+
.filter(w => w.length >= 3 && !AgentPool._STOPWORDS.has(w))
|
|
1765
|
+
);
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/**
|
|
1769
|
+
* Jaccard similarity over two word sets.
|
|
1770
|
+
* @private
|
|
1771
|
+
*/
|
|
1772
|
+
_jaccard(a, b) {
|
|
1773
|
+
if (a.size === 0 && b.size === 0) return 1;
|
|
1774
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
1775
|
+
let intersection = 0;
|
|
1776
|
+
for (const w of a) if (b.has(w)) intersection += 1;
|
|
1777
|
+
return intersection / (a.size + b.size - intersection);
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
/**
|
|
1781
|
+
* Overlap coefficient — intersection / size-of-smaller-set.
|
|
1782
|
+
* Returns 1.0 when one set is fully contained in the other,
|
|
1783
|
+
* regardless of how much the other set adds. Catches the "user
|
|
1784
|
+
* re-pastes their request with a preamble" duplicate case where
|
|
1785
|
+
* Jaccard would mark the messages as merely similar.
|
|
1786
|
+
* @private
|
|
1787
|
+
*/
|
|
1788
|
+
_overlapCoefficient(a, b) {
|
|
1789
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
1790
|
+
let intersection = 0;
|
|
1791
|
+
for (const w of a) if (b.has(w)) intersection += 1;
|
|
1792
|
+
return intersection / Math.min(a.size, b.size);
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
/**
|
|
1796
|
+
* Heuristic — does this user message look like a real request worth
|
|
1797
|
+
* preserving as a plan/*? Errs on the side of saving more (recall
|
|
1798
|
+
* over precision) — a stray auto-save is cheap; a lost user request
|
|
1799
|
+
* is catastrophic.
|
|
1800
|
+
* @private
|
|
1801
|
+
*/
|
|
1802
|
+
_looksSubstantive(text) {
|
|
1803
|
+
if (typeof text !== 'string') return false;
|
|
1804
|
+
const t = text.trim();
|
|
1805
|
+
if (t.length < 30) return false;
|
|
1806
|
+
// Tool-result wrappers and previous-task boundaries are not user voice.
|
|
1807
|
+
if (t.startsWith('[Tool Results') || t.startsWith('[Previous Task')) return false;
|
|
1808
|
+
|
|
1809
|
+
// ── Pollution filter 1: dominated by questions ────────────────────
|
|
1810
|
+
// A message that's mostly questions wants an ANSWER, not a plan.
|
|
1811
|
+
// If the majority of non-empty lines end in '?' (or are
|
|
1812
|
+
// question-shaped), this is a query, not a request.
|
|
1813
|
+
if (this._dominatedByQuestions(t)) return false;
|
|
1814
|
+
|
|
1815
|
+
// ── Pollution filter 2: list items are just refs (paths, urls) ───
|
|
1816
|
+
// A list of file paths / URLs / commit hashes is the user pointing
|
|
1817
|
+
// the agent at things, not a multi-part plan. Save it only if the
|
|
1818
|
+
// surrounding prose carries imperative intent — and even then the
|
|
1819
|
+
// length gate handles that path.
|
|
1820
|
+
const hasList = /^\s*(?:\d+[.)]|[-*•])\s/m.test(t);
|
|
1821
|
+
if (hasList && this._listItemsAreJustReferences(t)) return false;
|
|
1822
|
+
|
|
1823
|
+
// ── Now apply the structural triggers ────────────────────────────
|
|
1824
|
+
// Numbered list — "1." / "1)" at a line start. Multi-part intent.
|
|
1825
|
+
// Require a minimum total length to avoid "1. yes 2. no" nonsense.
|
|
1826
|
+
if (/^\s*\d+[.)]\s/m.test(t) && t.length >= 60) return true;
|
|
1827
|
+
// Bullet list at line start. Same — strong intent signal + length.
|
|
1828
|
+
if (/^\s*[-*•]\s/m.test(t) && t.length >= 60) return true;
|
|
1829
|
+
// Free-form prose with no list markers must be substantial AND
|
|
1830
|
+
// contain an imperative-like signal (a verb you'd give as an
|
|
1831
|
+
// order). Raised from 120 → 150 to skip more pleasantries.
|
|
1832
|
+
if (t.length >= 150 && this._hasImperativeSignal(t)) return true;
|
|
1833
|
+
return false;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/**
|
|
1837
|
+
* Heuristic: is this message mostly questions?
|
|
1838
|
+
* @private
|
|
1839
|
+
*/
|
|
1840
|
+
_dominatedByQuestions(t) {
|
|
1841
|
+
// Split into non-empty lines.
|
|
1842
|
+
const lines = t.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
|
|
1843
|
+
if (lines.length === 0) return false;
|
|
1844
|
+
// Strip leading list markers so we can look at the line's intent.
|
|
1845
|
+
const stripMarker = (l) => l.replace(/^(?:\d+[.)]|[-*•])\s+/, '');
|
|
1846
|
+
let questionLines = 0;
|
|
1847
|
+
for (const raw of lines) {
|
|
1848
|
+
const line = stripMarker(raw);
|
|
1849
|
+
// Ends in '?', OR starts with a question word at the line head.
|
|
1850
|
+
if (/\?\s*$/.test(line) || /^(?:what|why|how|when|where|who|which|is\b|are\b|do\b|does\b|can\b|could\b|should\b|would\b)\b/i.test(line)) {
|
|
1851
|
+
questionLines += 1;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
// Strict-majority rule: more than half of lines are questions.
|
|
1855
|
+
return questionLines * 2 > lines.length;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
/**
|
|
1859
|
+
* Heuristic: are the list items in this message just references
|
|
1860
|
+
* (file paths, URLs, commit hashes) with no imperative verb of their own?
|
|
1861
|
+
* @private
|
|
1862
|
+
*/
|
|
1863
|
+
_listItemsAreJustReferences(t) {
|
|
1864
|
+
const lines = t.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
|
|
1865
|
+
const listItems = lines.filter(l => /^(?:\d+[.)]|[-*•])\s/.test(l));
|
|
1866
|
+
if (listItems.length === 0) return false;
|
|
1867
|
+
let refLikeCount = 0;
|
|
1868
|
+
for (const li of listItems) {
|
|
1869
|
+
const body = li.replace(/^(?:\d+[.)]|[-*•])\s+/, '').trim();
|
|
1870
|
+
// Only treat as a "reference" if the line IS the reference —
|
|
1871
|
+
// i.e. a path/URL/hash with no surrounding English. A short bug
|
|
1872
|
+
// description like "login button does nothing on Safari" still
|
|
1873
|
+
// counts as content, not a reference.
|
|
1874
|
+
// Path: contains '/' or '\' OR starts with '.' AND has NO spaces
|
|
1875
|
+
// URL: starts with http(s)://
|
|
1876
|
+
// Hash: 7-40 hex chars only, no spaces
|
|
1877
|
+
const isPath = (/[/\\]/.test(body) || /^\./.test(body)) && !/\s/.test(body);
|
|
1878
|
+
const isUrl = /^https?:\/\//.test(body) && !/\s/.test(body);
|
|
1879
|
+
const isHash = /^[0-9a-f]{7,40}$/i.test(body);
|
|
1880
|
+
if (isPath || isUrl || isHash) refLikeCount += 1;
|
|
1881
|
+
}
|
|
1882
|
+
// Strict-majority of list items are reference-like → ignore.
|
|
1883
|
+
return refLikeCount * 2 > listItems.length;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
/**
|
|
1887
|
+
* Heuristic: does the message contain a verb that signals "do this"?
|
|
1888
|
+
* Conservative — favors recall over precision.
|
|
1889
|
+
* @private
|
|
1890
|
+
*/
|
|
1891
|
+
_hasImperativeSignal(t) {
|
|
1892
|
+
// Word-boundary match against a set of common imperative verbs.
|
|
1893
|
+
// Order matters only for readability — we check membership.
|
|
1894
|
+
return /\b(?:fix|add|build|implement|create|change|remove|delete|update|refactor|rewrite|migrate|integrate|configure|setup|set\s+up|design|generate|make|write|test|verify|ensure|review|optimize|improve|replace|move|rename|extract|split|merge|deploy|publish|ship|release|debug|investigate|analyze|reproduce|escalate|prioritize|schedule)\b/i.test(t);
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
_autoCreateTaskForMessage(agent, message, source, priority) {
|
|
1898
|
+
if (!agent.taskList) {
|
|
1899
|
+
agent.taskList = { tasks: [], lastUpdated: new Date().toISOString() };
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
const content = message.content || '';
|
|
1903
|
+
const titleContent = content.trim().replace(/\n+/g, ' ').replace(/\s+/g, ' ');
|
|
1904
|
+
const firstSentence = titleContent.split(/[.!?]/)[0].trim();
|
|
1905
|
+
const title = firstSentence.length > 50
|
|
1906
|
+
? firstSentence.substring(0, 47) + '...'
|
|
1907
|
+
: firstSentence || 'Process message';
|
|
1908
|
+
|
|
1909
|
+
const isInterAgent = source.startsWith('inter-agent');
|
|
1910
|
+
const requiresReply = isInterAgent && message.requiresReply === true;
|
|
1911
|
+
const taskTitle = isInterAgent
|
|
1912
|
+
? (requiresReply
|
|
1913
|
+
? `Handle and reply to ${source}: ${title}`
|
|
1914
|
+
: `Handle ${source}: ${title}`)
|
|
1915
|
+
: `Process ${source} request: ${title}`;
|
|
1916
|
+
const taskDescription = isInterAgent
|
|
1917
|
+
? (requiresReply
|
|
1918
|
+
? `Handle ${source} message and reply using the agentcommunication tool with action="reply-to-message": "${content.length > 200 ? content.substring(0, 197) + '...' : content}"`
|
|
1919
|
+
: `Handle ${source} message: "${content.length > 200 ? content.substring(0, 197) + '...' : content}"`)
|
|
1920
|
+
: `Handle ${source} message: "${content.length > 200 ? content.substring(0, 197) + '...' : content}"`;
|
|
1921
|
+
|
|
1922
|
+
const task = {
|
|
1923
|
+
id: `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
1924
|
+
title: taskTitle,
|
|
1925
|
+
description: taskDescription,
|
|
1926
|
+
status: 'pending',
|
|
1927
|
+
priority,
|
|
1928
|
+
createdAt: new Date().toISOString(),
|
|
1929
|
+
updatedAt: new Date().toISOString(),
|
|
1930
|
+
source: 'auto-created',
|
|
1931
|
+
messageId: message.id
|
|
1932
|
+
};
|
|
1933
|
+
|
|
1934
|
+
agent.taskList.tasks.push(task);
|
|
1935
|
+
agent.taskList.lastUpdated = new Date().toISOString();
|
|
1936
|
+
|
|
1937
|
+
this.logger.info(`Auto-created task for ${source} message`, {
|
|
1938
|
+
agentId: agent.id,
|
|
1939
|
+
taskId: task.id,
|
|
1940
|
+
title: task.title
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
/**
|
|
1945
|
+
* Clear all message queues for an agent
|
|
1946
|
+
* @param {string} agentId - Agent ID
|
|
1947
|
+
* @returns {Promise<void>}
|
|
1948
|
+
*/
|
|
1949
|
+
async clearAgentQueues(agentId) {
|
|
1950
|
+
const agent = await this.getAgent(agentId);
|
|
1951
|
+
if (!agent) {
|
|
1952
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
agent.messageQueues.toolResults = [];
|
|
1956
|
+
agent.messageQueues.interAgentMessages = [];
|
|
1957
|
+
agent.messageQueues.userMessages = [];
|
|
1958
|
+
|
|
1959
|
+
await this.persistAgentState(agentId);
|
|
1960
|
+
|
|
1961
|
+
this.logger.info(`Message queues cleared for agent: ${agentId}`);
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
/**
|
|
1965
|
+
* Get total queued messages count for an agent
|
|
1966
|
+
* @param {string} agentId - Agent ID
|
|
1967
|
+
* @returns {Promise<Object>} Queue counts by type
|
|
1968
|
+
*/
|
|
1969
|
+
async getQueueCounts(agentId) {
|
|
1970
|
+
const agent = await this.getAgent(agentId);
|
|
1971
|
+
if (!agent) {
|
|
1972
|
+
return { toolResults: 0, interAgentMessages: 0, userMessages: 0, total: 0 };
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
const counts = {
|
|
1976
|
+
toolResults: agent.messageQueues.toolResults.length,
|
|
1977
|
+
interAgentMessages: agent.messageQueues.interAgentMessages.length,
|
|
1978
|
+
userMessages: agent.messageQueues.userMessages.length
|
|
1979
|
+
};
|
|
1980
|
+
|
|
1981
|
+
counts.total = counts.toolResults + counts.interAgentMessages + counts.userMessages;
|
|
1982
|
+
|
|
1983
|
+
return counts;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
/**
|
|
1987
|
+
* Get messages for AI request - returns compacted if available, otherwise original
|
|
1988
|
+
* CRITICAL FIX: Ensures compacted messages stay in sync with new messages after compaction
|
|
1989
|
+
* This is the primary method that should be used when preparing messages for AI service
|
|
1990
|
+
* @param {string} agentId - Agent ID
|
|
1991
|
+
* @param {string} modelId - Model ID
|
|
1992
|
+
* @returns {Promise<Array>} Messages array to send to AI
|
|
1993
|
+
*/
|
|
1994
|
+
async getMessagesForAI(agentId, modelId) {
|
|
1995
|
+
const ENABLE_COMPACT_DEBUG = process.env.COMPACT_DEBUG === 'true';
|
|
1996
|
+
|
|
1997
|
+
// Helper: Remove trailing empty messages from array (cleans up malformed conversations)
|
|
1998
|
+
const cleanTrailingEmptyMessages = (messages) => {
|
|
1999
|
+
if (!messages || messages.length === 0) return messages;
|
|
2000
|
+
|
|
2001
|
+
let cleaned = [...messages];
|
|
2002
|
+
let removedCount = 0;
|
|
2003
|
+
|
|
2004
|
+
// Remove trailing empty messages
|
|
2005
|
+
while (cleaned.length > 0) {
|
|
2006
|
+
const lastMsg = cleaned[cleaned.length - 1];
|
|
2007
|
+
const content = lastMsg?.content;
|
|
2008
|
+
const isEmpty = !content || (typeof content === 'string' && !content.trim());
|
|
2009
|
+
|
|
2010
|
+
if (isEmpty) {
|
|
2011
|
+
cleaned.pop();
|
|
2012
|
+
removedCount++;
|
|
2013
|
+
} else {
|
|
2014
|
+
break;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
if (removedCount > 0) {
|
|
2019
|
+
this.logger?.warn(`Removed ${removedCount} trailing empty message(s) from conversation`, {
|
|
2020
|
+
agentId,
|
|
2021
|
+
modelId,
|
|
2022
|
+
originalLength: messages.length,
|
|
2023
|
+
cleanedLength: cleaned.length
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
return cleaned;
|
|
2028
|
+
};
|
|
2029
|
+
|
|
2030
|
+
const agent = await this.getAgent(agentId);
|
|
2031
|
+
if (!agent) {
|
|
2032
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
const conversation = agent.conversations[modelId];
|
|
2036
|
+
if (!conversation) {
|
|
2037
|
+
this.logger.warn(`No conversation found for model: ${modelId}`, { agentId });
|
|
2038
|
+
return [];
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
// If no compacted messages exist, return original (cleaned)
|
|
2042
|
+
if (!conversation.compactizedMessages) {
|
|
2043
|
+
this.logger.debug('Retrieved messages for AI (no compaction)', {
|
|
2044
|
+
agentId,
|
|
2045
|
+
modelId,
|
|
2046
|
+
messageCount: conversation.messages.length
|
|
2047
|
+
});
|
|
2048
|
+
|
|
2049
|
+
if (ENABLE_COMPACT_DEBUG) {
|
|
2050
|
+
console.log('[GET-MESSAGES-FOR-AI]', {
|
|
2051
|
+
agentId,
|
|
2052
|
+
modelId,
|
|
2053
|
+
returnedArray: 'originalMessages',
|
|
2054
|
+
messageCount: conversation.messages.length,
|
|
2055
|
+
reason: 'No compacted messages exist'
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
return cleanTrailingEmptyMessages(conversation.messages);
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
// CRITICAL FIX: Only sync messages added AFTER compaction
|
|
2063
|
+
// We track originalMessageCountAtCompaction to know which messages are truly new
|
|
2064
|
+
// vs which ones were already included in the compaction (sandwich strategy)
|
|
2065
|
+
const compactedLength = conversation.compactizedMessages.length;
|
|
2066
|
+
const originalLength = conversation.messages.length;
|
|
2067
|
+
// SAFETY: If watermark is null/undefined (bug, migration, or cleared state),
|
|
2068
|
+
// fall back to compactedLength — NOT originalLength. Using originalLength silently
|
|
2069
|
+
// drops all unsynced messages because (originalLength > originalLength) is always false.
|
|
2070
|
+
// Using compactedLength ensures any messages beyond what's in the compacted array get synced.
|
|
2071
|
+
const originalCountAtCompaction = conversation.originalMessageCountAtCompaction ?? compactedLength;
|
|
2072
|
+
|
|
2073
|
+
// Only sync if NEW messages were added after compaction
|
|
2074
|
+
// (i.e., current original length > original length when compaction happened)
|
|
2075
|
+
if (originalLength > originalCountAtCompaction) {
|
|
2076
|
+
// New messages exist that weren't present during compaction
|
|
2077
|
+
const newMessageCount = originalLength - originalCountAtCompaction;
|
|
2078
|
+
const newMessages = conversation.messages.slice(-newMessageCount);
|
|
2079
|
+
|
|
2080
|
+
this.logger.info('Syncing truly new messages after compaction', {
|
|
2081
|
+
agentId,
|
|
2082
|
+
modelId,
|
|
2083
|
+
compactedLength,
|
|
2084
|
+
originalLength,
|
|
2085
|
+
originalCountAtCompaction,
|
|
2086
|
+
newMessageCount,
|
|
2087
|
+
newMessageRoles: newMessages.map(m => m.role)
|
|
2088
|
+
});
|
|
2089
|
+
|
|
2090
|
+
// Append only the truly new messages to compacted array
|
|
2091
|
+
conversation.compactizedMessages.push(...newMessages);
|
|
2092
|
+
|
|
2093
|
+
// Update the tracking to include newly synced messages
|
|
2094
|
+
conversation.originalMessageCountAtCompaction = originalLength;
|
|
2095
|
+
|
|
2096
|
+
// Persist the update
|
|
2097
|
+
await this.persistAgentState(agentId);
|
|
2098
|
+
} else if (originalLength > compactedLength) {
|
|
2099
|
+
// Length mismatch but no new messages - this is expected with sandwich compaction
|
|
2100
|
+
// The compacted version has fewer messages due to summarization, not missing messages
|
|
2101
|
+
this.logger.debug('Compacted messages shorter than original (expected with sandwich compaction)', {
|
|
2102
|
+
agentId,
|
|
2103
|
+
modelId,
|
|
2104
|
+
compactedLength,
|
|
2105
|
+
originalLength,
|
|
2106
|
+
originalCountAtCompaction,
|
|
2107
|
+
note: 'No sync needed - compaction reduces message count'
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
this.logger.debug('Retrieved messages for AI (compacted + synced)', {
|
|
2112
|
+
agentId,
|
|
2113
|
+
modelId,
|
|
2114
|
+
messageCount: conversation.compactizedMessages.length,
|
|
2115
|
+
wasResynced: originalLength > compactedLength
|
|
2116
|
+
});
|
|
2117
|
+
|
|
2118
|
+
if (ENABLE_COMPACT_DEBUG) {
|
|
2119
|
+
console.log('[GET-MESSAGES-FOR-AI]', {
|
|
2120
|
+
agentId,
|
|
2121
|
+
modelId,
|
|
2122
|
+
returnedArray: 'compactizedMessages',
|
|
2123
|
+
messageCount: conversation.compactizedMessages.length,
|
|
2124
|
+
originalMessageCount: conversation.messages.length,
|
|
2125
|
+
wasSynced: originalLength > compactedLength,
|
|
2126
|
+
syncedMessageCount: originalLength > compactedLength ? originalLength - compactedLength : 0,
|
|
2127
|
+
reason: 'Compacted messages exist, returning compacted version'
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
return cleanTrailingEmptyMessages(conversation.compactizedMessages);
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
/**
|
|
2135
|
+
* Add message to conversation (stores in original messages array)
|
|
2136
|
+
* @param {string} agentId - Agent ID
|
|
2137
|
+
* @param {string} modelId - Model ID
|
|
2138
|
+
* @param {Object} message - Message object to add
|
|
2139
|
+
* @returns {Promise<void>}
|
|
2140
|
+
*/
|
|
2141
|
+
async addMessageToConversation(agentId, modelId, message) {
|
|
2142
|
+
const ENABLE_COMPACT_DEBUG = process.env.COMPACT_DEBUG === 'true';
|
|
2143
|
+
|
|
2144
|
+
const agent = await this.getAgent(agentId);
|
|
2145
|
+
if (!agent) {
|
|
2146
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// Ensure model conversation exists
|
|
2150
|
+
if (!agent.conversations[modelId]) {
|
|
2151
|
+
agent.conversations[modelId] = this._createEmptyConversation(modelId);
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
const conversation = agent.conversations[modelId];
|
|
2155
|
+
|
|
2156
|
+
// GUARD: Skip empty messages - they should never be added to history
|
|
2157
|
+
const messageContent = message.content;
|
|
2158
|
+
if (!messageContent || (typeof messageContent === 'string' && !messageContent.trim())) {
|
|
2159
|
+
this.logger?.warn(`Skipping empty message for agent ${agentId}`, {
|
|
2160
|
+
role: message.role,
|
|
2161
|
+
modelId,
|
|
2162
|
+
hasContent: !!messageContent
|
|
2163
|
+
});
|
|
2164
|
+
return; // Don't add empty messages
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
const originalLengthBefore = conversation.messages.length;
|
|
2168
|
+
const compactedLengthBefore = conversation.compactizedMessages?.length || 0;
|
|
2169
|
+
|
|
2170
|
+
// Always add to original messages (never modify original)
|
|
2171
|
+
conversation.messages.push({
|
|
2172
|
+
...message,
|
|
2173
|
+
timestamp: message.timestamp || new Date().toISOString()
|
|
2174
|
+
});
|
|
2175
|
+
|
|
2176
|
+
// If compacted version exists, also add to it (append new messages after compaction)
|
|
2177
|
+
if (conversation.compactizedMessages) {
|
|
2178
|
+
conversation.compactizedMessages.push({
|
|
2179
|
+
...message,
|
|
2180
|
+
timestamp: message.timestamp || new Date().toISOString()
|
|
2181
|
+
});
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
conversation.lastUpdated = new Date().toISOString();
|
|
2185
|
+
|
|
2186
|
+
if (ENABLE_COMPACT_DEBUG) {
|
|
2187
|
+
console.log('[ADD-MESSAGE-TO-CONVERSATION]', {
|
|
2188
|
+
agentId,
|
|
2189
|
+
modelId,
|
|
2190
|
+
role: message.role,
|
|
2191
|
+
hasCompactedVersion: !!conversation.compactizedMessages,
|
|
2192
|
+
originalMessages: {
|
|
2193
|
+
before: originalLengthBefore,
|
|
2194
|
+
after: conversation.messages.length,
|
|
2195
|
+
added: 1
|
|
2196
|
+
},
|
|
2197
|
+
compactizedMessages: conversation.compactizedMessages ? {
|
|
2198
|
+
before: compactedLengthBefore,
|
|
2199
|
+
after: conversation.compactizedMessages.length,
|
|
2200
|
+
added: 1
|
|
2201
|
+
} : null,
|
|
2202
|
+
behavior: conversation.compactizedMessages ? 'Added to BOTH arrays' : 'Added to original only'
|
|
2203
|
+
});
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
await this.persistAgentState(agentId);
|
|
2207
|
+
|
|
2208
|
+
this.logger.debug('Message added to conversation', {
|
|
2209
|
+
agentId,
|
|
2210
|
+
modelId,
|
|
2211
|
+
role: message.role,
|
|
2212
|
+
hasCompacted: !!conversation.compactizedMessages
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
/**
|
|
2217
|
+
* Sync pending messages from conversation.messages to compactizedMessages.
|
|
2218
|
+
* The scheduler's addMessageToConversation only pushes to conversation.messages,
|
|
2219
|
+
* NOT to compactizedMessages. This method syncs any pending messages that haven't
|
|
2220
|
+
* been pushed to compactizedMessages yet.
|
|
2221
|
+
*
|
|
2222
|
+
* MUST be called before compaction reads compactizedMessages, otherwise compaction
|
|
2223
|
+
* will process a stale snapshot and the watermark will mark unsynced messages as
|
|
2224
|
+
* "already compacted", permanently losing them.
|
|
2225
|
+
*
|
|
2226
|
+
* @param {string} agentId - Agent ID
|
|
2227
|
+
* @param {string} modelId - Model ID
|
|
2228
|
+
* @returns {Promise<{synced: number}>} Number of messages synced
|
|
2229
|
+
*/
|
|
2230
|
+
async syncPendingMessages(agentId, modelId) {
|
|
2231
|
+
const agent = await this.getAgent(agentId);
|
|
2232
|
+
if (!agent) return { synced: 0 };
|
|
2233
|
+
|
|
2234
|
+
const conversation = agent.conversations[modelId];
|
|
2235
|
+
if (!conversation || !conversation.compactizedMessages) return { synced: 0 };
|
|
2236
|
+
|
|
2237
|
+
const originalLength = conversation.messages.length;
|
|
2238
|
+
const compactedLength = conversation.compactizedMessages.length;
|
|
2239
|
+
// SAFETY: Use ?? compactedLength instead of || originalLength to prevent silent message loss
|
|
2240
|
+
// when watermark is null (see getMessagesForAI for detailed explanation)
|
|
2241
|
+
const originalCountAtCompaction = conversation.originalMessageCountAtCompaction ?? compactedLength;
|
|
2242
|
+
|
|
2243
|
+
if (originalLength > originalCountAtCompaction) {
|
|
2244
|
+
const newCount = originalLength - originalCountAtCompaction;
|
|
2245
|
+
const newMessages = conversation.messages.slice(-newCount);
|
|
2246
|
+
conversation.compactizedMessages.push(...newMessages);
|
|
2247
|
+
conversation.originalMessageCountAtCompaction = originalLength;
|
|
2248
|
+
|
|
2249
|
+
this.logger.info('Pre-compaction sync: pushed pending messages to compactizedMessages', {
|
|
2250
|
+
agentId,
|
|
2251
|
+
modelId,
|
|
2252
|
+
synced: newCount,
|
|
2253
|
+
newMessageRoles: newMessages.map(m => m.role),
|
|
2254
|
+
compactizedMessagesLength: conversation.compactizedMessages.length,
|
|
2255
|
+
watermarkWasNull: conversation.originalMessageCountAtCompaction === null
|
|
2256
|
+
});
|
|
2257
|
+
|
|
2258
|
+
return { synced: newCount };
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
return { synced: 0 };
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
/**
|
|
2265
|
+
* Update compacted messages after compactization
|
|
2266
|
+
* @param {string} agentId - Agent ID
|
|
2267
|
+
* @param {string} modelId - Model ID
|
|
2268
|
+
* @param {Object} compactionResult - Compaction result with messages and metadata
|
|
2269
|
+
* @param {number} [preCompactionMessageCount] - Message count recorded BEFORE compaction started.
|
|
2270
|
+
* If provided, used as the watermark instead of current messages.length. This prevents
|
|
2271
|
+
* messages added DURING compaction from being silently lost.
|
|
2272
|
+
* @returns {Promise<void>}
|
|
2273
|
+
*/
|
|
2274
|
+
async updateCompactedMessages(agentId, modelId, compactionResult, preCompactionMessageCount) {
|
|
2275
|
+
const agent = await this.getAgent(agentId);
|
|
2276
|
+
if (!agent) {
|
|
2277
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
// Ensure model conversation exists (important for model switching scenarios)
|
|
2281
|
+
if (!agent.conversations[modelId]) {
|
|
2282
|
+
agent.conversations[modelId] = this._createEmptyConversation(modelId);
|
|
2283
|
+
this.logger.debug(`Created conversation for model switching: ${modelId}`);
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
const conversation = agent.conversations[modelId];
|
|
2287
|
+
|
|
2288
|
+
// Update compacted messages
|
|
2289
|
+
conversation.compactizedMessages = compactionResult.compactedMessages;
|
|
2290
|
+
|
|
2291
|
+
// CRITICAL: Use the pre-compaction watermark if provided, NOT current messages.length.
|
|
2292
|
+
// If we use current messages.length, any messages added DURING compaction (e.g., user
|
|
2293
|
+
// messages arriving via WebSocket while the summarization API call is in flight) would
|
|
2294
|
+
// be marked as "already compacted" even though they weren't in the compaction input.
|
|
2295
|
+
// Using the pre-compaction count ensures those messages are detected as "new" by
|
|
2296
|
+
// getMessagesForAI's sync logic and properly appended to the compacted array.
|
|
2297
|
+
conversation.originalMessageCountAtCompaction = preCompactionMessageCount || conversation.messages.length;
|
|
2298
|
+
|
|
2299
|
+
// Update metadata
|
|
2300
|
+
conversation.lastCompactization = new Date().toISOString();
|
|
2301
|
+
conversation.compactizationCount += 1;
|
|
2302
|
+
conversation.compactizationStrategy = compactionResult.strategy;
|
|
2303
|
+
conversation.originalTokenCount = compactionResult.originalTokenCount;
|
|
2304
|
+
conversation.compactedTokenCount = compactionResult.compactedTokenCount;
|
|
2305
|
+
conversation.tokenCount = compactionResult.compactedTokenCount;
|
|
2306
|
+
conversation.lastUpdated = new Date().toISOString();
|
|
2307
|
+
|
|
2308
|
+
await this.persistAgentState(agentId);
|
|
2309
|
+
|
|
2310
|
+
this.logger.info('Compacted messages updated', {
|
|
2311
|
+
agentId,
|
|
2312
|
+
modelId,
|
|
2313
|
+
strategy: compactionResult.strategy,
|
|
2314
|
+
originalTokens: compactionResult.originalTokenCount,
|
|
2315
|
+
compactedTokens: compactionResult.compactedTokenCount,
|
|
2316
|
+
reductionPercent: compactionResult.reductionPercent,
|
|
2317
|
+
compactizationCount: conversation.compactizationCount
|
|
2318
|
+
});
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
/**
|
|
2322
|
+
* Clear compacted messages and revert to original
|
|
2323
|
+
* Useful for debugging or if compaction needs to be redone
|
|
2324
|
+
* @param {string} agentId - Agent ID
|
|
2325
|
+
* @param {string} modelId - Model ID
|
|
2326
|
+
* @returns {Promise<void>}
|
|
2327
|
+
*/
|
|
2328
|
+
async clearCompactedMessages(agentId, modelId) {
|
|
2329
|
+
const agent = await this.getAgent(agentId);
|
|
2330
|
+
if (!agent) {
|
|
2331
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
const conversation = agent.conversations[modelId];
|
|
2335
|
+
if (!conversation) {
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
conversation.compactizedMessages = null;
|
|
2340
|
+
conversation.lastCompactization = null;
|
|
2341
|
+
conversation.compactizationCount = 0;
|
|
2342
|
+
conversation.compactizationStrategy = null;
|
|
2343
|
+
conversation.originalTokenCount = 0;
|
|
2344
|
+
conversation.compactedTokenCount = 0;
|
|
2345
|
+
conversation.tokenCount = 0;
|
|
2346
|
+
conversation.originalMessageCountAtCompaction = null;
|
|
2347
|
+
|
|
2348
|
+
await this.persistAgentState(agentId);
|
|
2349
|
+
|
|
2350
|
+
this.logger.info('Compacted messages cleared', { agentId, modelId });
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
/**
|
|
2354
|
+
* Get compaction metadata for a conversation
|
|
2355
|
+
* @param {string} agentId - Agent ID
|
|
2356
|
+
* @param {string} modelId - Model ID
|
|
2357
|
+
* @returns {Promise<Object|null>} Compaction metadata or null if no compaction
|
|
2358
|
+
*/
|
|
2359
|
+
async getCompactionMetadata(agentId, modelId) {
|
|
2360
|
+
const agent = await this.getAgent(agentId);
|
|
2361
|
+
if (!agent) {
|
|
2362
|
+
return null;
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
const conversation = agent.conversations[modelId];
|
|
2366
|
+
if (!conversation) {
|
|
2367
|
+
return null;
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
// Return metadata whether compacted or not
|
|
2371
|
+
const isCompacted = !!conversation.compactizedMessages;
|
|
2372
|
+
|
|
2373
|
+
return {
|
|
2374
|
+
isCompacted,
|
|
2375
|
+
lastCompactization: conversation.lastCompactization || null,
|
|
2376
|
+
compactizationCount: conversation.compactizationCount || 0,
|
|
2377
|
+
strategy: conversation.compactizationStrategy || null,
|
|
2378
|
+
originalTokenCount: conversation.originalTokenCount || 0,
|
|
2379
|
+
compactedTokenCount: conversation.compactedTokenCount || 0,
|
|
2380
|
+
reductionPercent: conversation.originalTokenCount > 0
|
|
2381
|
+
? ((conversation.originalTokenCount - conversation.compactedTokenCount) / conversation.originalTokenCount) * 100
|
|
2382
|
+
: 0,
|
|
2383
|
+
originalMessages: conversation.messages || [],
|
|
2384
|
+
compactedMessages: conversation.compactizedMessages || null,
|
|
2385
|
+
originalMessageCount: conversation.messages?.length || 0,
|
|
2386
|
+
compactedMessageCount: conversation.compactizedMessages?.length || 0
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
/**
|
|
2391
|
+
* Migrate existing agent conversations to dual storage structure
|
|
2392
|
+
* Ensures backward compatibility with existing agents
|
|
2393
|
+
* @param {string} agentId - Agent ID
|
|
2394
|
+
* @returns {Promise<boolean>} True if migration was needed and performed
|
|
2395
|
+
*/
|
|
2396
|
+
async migrateConversationStructure(agentId) {
|
|
2397
|
+
const agent = await this.getAgent(agentId);
|
|
2398
|
+
if (!agent) {
|
|
2399
|
+
return false;
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
let migrated = false;
|
|
2403
|
+
|
|
2404
|
+
// Check each conversation for migration needs
|
|
2405
|
+
for (const [modelId, conversation] of Object.entries(agent.conversations)) {
|
|
2406
|
+
if (modelId === 'full') continue; // Skip full conversation
|
|
2407
|
+
|
|
2408
|
+
// Check if conversation needs migration (missing new fields)
|
|
2409
|
+
if (conversation.compactizedMessages === undefined) {
|
|
2410
|
+
// Add new fields
|
|
2411
|
+
conversation.compactizedMessages = null;
|
|
2412
|
+
conversation.lastCompactization = null;
|
|
2413
|
+
conversation.compactizationCount = 0;
|
|
2414
|
+
conversation.compactizationStrategy = null;
|
|
2415
|
+
conversation.originalTokenCount = 0;
|
|
2416
|
+
conversation.compactedTokenCount = 0;
|
|
2417
|
+
conversation.originalMessageCountAtCompaction = null;
|
|
2418
|
+
|
|
2419
|
+
migrated = true;
|
|
2420
|
+
|
|
2421
|
+
this.logger.info('Migrated conversation structure', {
|
|
2422
|
+
agentId,
|
|
2423
|
+
modelId,
|
|
2424
|
+
messageCount: conversation.messages?.length || 0
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
// CRITICAL: Migrate existing compacted conversations that don't have the new tracking field
|
|
2429
|
+
// This prevents the sync bug from re-adding messages already included in compaction
|
|
2430
|
+
if (conversation.compactizedMessages && conversation.originalMessageCountAtCompaction === undefined) {
|
|
2431
|
+
// Set to current original length to prevent any sync from running
|
|
2432
|
+
// This is safe because any truly new messages would have been added to both arrays
|
|
2433
|
+
conversation.originalMessageCountAtCompaction = conversation.messages?.length || 0;
|
|
2434
|
+
migrated = true;
|
|
2435
|
+
|
|
2436
|
+
this.logger.info('Migrated compaction tracking field', {
|
|
2437
|
+
agentId,
|
|
2438
|
+
modelId,
|
|
2439
|
+
originalMessageCountAtCompaction: conversation.originalMessageCountAtCompaction,
|
|
2440
|
+
compactedMessageCount: conversation.compactizedMessages.length
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
if (migrated) {
|
|
2446
|
+
await this.persistAgentState(agentId);
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
return migrated;
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
/**
|
|
2453
|
+
* Create empty conversation structure with all required fields
|
|
2454
|
+
* @private
|
|
2455
|
+
* @param {string} modelId - Model ID
|
|
2456
|
+
* @returns {Object} Empty conversation structure
|
|
2457
|
+
*/
|
|
2458
|
+
_createEmptyConversation(modelId) {
|
|
2459
|
+
return {
|
|
2460
|
+
messages: [],
|
|
2461
|
+
compactizedMessages: null,
|
|
2462
|
+
lastCompactization: null,
|
|
2463
|
+
compactizationCount: 0,
|
|
2464
|
+
compactizationStrategy: null,
|
|
2465
|
+
originalTokenCount: 0,
|
|
2466
|
+
compactedTokenCount: 0,
|
|
2467
|
+
tokenCount: 0,
|
|
2468
|
+
originalMessageCountAtCompaction: null, // Tracks original length at compaction time
|
|
2469
|
+
lastUpdated: new Date().toISOString(),
|
|
2470
|
+
formatVersion: this._getModelFormatVersion(modelId)
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2476
2475
|
export default AgentPool;
|