@tyvm/knowhow 0.0.159 → 0.0.160
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/package.json +1 -1
- package/ts_build/package.json +1 -1
- package/ts_build/src/chat/CliChatService.d.ts +2 -1
- package/ts_build/src/chat/CliChatService.js +29 -3
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.js +21 -17
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/renderer/ConsoleRenderer.js +32 -25
- package/ts_build/src/chat/renderer/ConsoleRenderer.js.map +1 -1
- package/ts_build/src/logger.d.ts +1 -0
- package/ts_build/src/logger.js +13 -1
- package/ts_build/src/logger.js.map +1 -1
- package/ts_build/src/utils/InputQueueManager.d.ts +8 -1
- package/ts_build/src/utils/InputQueueManager.js +72 -2
- package/ts_build/src/utils/InputQueueManager.js.map +1 -1
- package/ts_build/src/utils/index.d.ts +3 -1
- package/ts_build/src/utils/index.js +11 -3
- package/ts_build/src/utils/index.js.map +1 -1
- package/CONFIG.md +0 -429
- package/PROCESSING.md +0 -450
- package/WORKER.md +0 -260
- package/autodoc/README.md +0 -324
- package/autodoc/chat-guide.md +0 -444
- package/autodoc/cli-reference.md +0 -720
- package/autodoc/config-reference.md +0 -642
- package/autodoc/embeddings-guide.md +0 -467
- package/autodoc/generate-guide.md +0 -437
- package/autodoc/language-plugin-guide.md +0 -417
- package/autodoc/modules-guide.md +0 -379
- package/autodoc/plugins-guide.md +0 -721
- package/autodoc/quickstart-guide.md +0 -126
- package/autodoc/skills-guide.md +0 -584
- package/autodoc/worker-guide.md +0 -519
- package/docs/input-queue-manager.md +0 -142
- package/docs/multi-worker-management.md +0 -142
- package/docs/shell-commands.md +0 -174
- package/jest.config.js +0 -19
- package/jest.manual.config.js +0 -19
- package/scripts/README.md +0 -119
- package/scripts/build-for-node.sh +0 -148
- package/scripts/cache-miss-debug.ts +0 -365
- package/scripts/check-isolated-deps.sh +0 -69
- package/scripts/check-model-pricing.ts +0 -509
- package/scripts/compare-openrouter-coverage.ts +0 -576
- package/scripts/migrate-usage-json.js +0 -147
- package/scripts/publish.sh +0 -86
- package/scripts/restore_keys.sh +0 -59
- package/scripts/test-repetition-hint.ts +0 -302
- package/scripts/unset_keys.sh +0 -60
- package/src/agents/base/base.ts +0 -1963
- package/src/agents/base/prompt.ts +0 -55
- package/src/agents/configurable/ConfigAgent.ts +0 -24
- package/src/agents/developer/developer.ts +0 -76
- package/src/agents/historyRollback.ts +0 -36
- package/src/agents/index.ts +0 -52
- package/src/agents/interface.ts +0 -11
- package/src/agents/patcher/codebase.md +0 -27
- package/src/agents/patcher/patcher.ts +0 -131
- package/src/agents/researcher/researcher.ts +0 -115
- package/src/agents/setup/setup.ts +0 -63
- package/src/agents/tools/addInternalTools.ts +0 -21
- package/src/agents/tools/agentCall.ts +0 -34
- package/src/agents/tools/aiClient.ts +0 -178
- package/src/agents/tools/askHuman.ts +0 -10
- package/src/agents/tools/callPlugin.ts +0 -10
- package/src/agents/tools/connectAgent.ts +0 -355
- package/src/agents/tools/embeddingSearch.ts +0 -6
- package/src/agents/tools/execCommand.ts +0 -162
- package/src/agents/tools/fileSearch.ts +0 -35
- package/src/agents/tools/finalAnswer.ts +0 -4
- package/src/agents/tools/googleSearch.ts +0 -243
- package/src/agents/tools/index.ts +0 -33
- package/src/agents/tools/language/definitions.ts +0 -105
- package/src/agents/tools/language/index.ts +0 -20
- package/src/agents/tools/lazy/definitions.ts +0 -72
- package/src/agents/tools/lazy/disableTools.ts +0 -16
- package/src/agents/tools/lazy/enableTools.ts +0 -16
- package/src/agents/tools/lazy/index.ts +0 -3
- package/src/agents/tools/lazy/listAvailableTools.ts +0 -17
- package/src/agents/tools/lintFile.ts +0 -19
- package/src/agents/tools/list.ts +0 -739
- package/src/agents/tools/loadImageAsBase64.ts +0 -83
- package/src/agents/tools/loadWebpage.ts +0 -19
- package/src/agents/tools/mcp/connectMcpServer.ts +0 -40
- package/src/agents/tools/mcp/definitions.ts +0 -67
- package/src/agents/tools/mcp/disconnectMcpServer.ts +0 -40
- package/src/agents/tools/mcp/index.ts +0 -3
- package/src/agents/tools/mcp/listAvailableMcpServers.ts +0 -28
- package/src/agents/tools/minimal/callTool.ts +0 -45
- package/src/agents/tools/minimal/definitions.ts +0 -50
- package/src/agents/tools/minimal/index.ts +0 -3
- package/src/agents/tools/minimal/inspectTools.ts +0 -27
- package/src/agents/tools/modifyFile.ts +0 -53
- package/src/agents/tools/observe.ts +0 -247
- package/src/agents/tools/patch.ts +0 -1287
- package/src/agents/tools/readBlocks.ts +0 -83
- package/src/agents/tools/readFile.ts +0 -105
- package/src/agents/tools/replyToParent.ts +0 -123
- package/src/agents/tools/runGenerate.ts +0 -210
- package/src/agents/tools/scanFile.ts +0 -16
- package/src/agents/tools/sendAgentMessage.ts +0 -139
- package/src/agents/tools/skills.ts +0 -79
- package/src/agents/tools/startAgentTask.ts +0 -393
- package/src/agents/tools/stringReplace.ts +0 -105
- package/src/agents/tools/textSearch.ts +0 -128
- package/src/agents/tools/types/fileblock.ts +0 -5
- package/src/agents/tools/visionTool.ts +0 -35
- package/src/agents/tools/waitForAgentCompleted.ts +0 -270
- package/src/agents/tools/writeFile.ts +0 -106
- package/src/agents/tools/ycmd/client.ts +0 -608
- package/src/agents/tools/ycmd/definitions.ts +0 -294
- package/src/agents/tools/ycmd/detection.ts +0 -211
- package/src/agents/tools/ycmd/index.ts +0 -11
- package/src/agents/tools/ycmd/installer.ts +0 -251
- package/src/agents/tools/ycmd/server.ts +0 -545
- package/src/agents/tools/ycmd/serverManager.ts +0 -316
- package/src/agents/tools/ycmd/tools/completion.ts +0 -113
- package/src/agents/tools/ycmd/tools/diagnostics.ts +0 -166
- package/src/agents/tools/ycmd/tools/getLocations.ts +0 -173
- package/src/agents/tools/ycmd/tools/goto.ts +0 -169
- package/src/agents/tools/ycmd/tools/refactor.ts +0 -204
- package/src/agents/tools/ycmd/tools/signature.ts +0 -174
- package/src/agents/tools/ycmd/tools/start.ts +0 -95
- package/src/agents/tools/ycmd/utils/pathUtils.ts +0 -59
- package/src/ai.ts +0 -306
- package/src/auth/browserLogin.ts +0 -406
- package/src/auth/environmentAuth.ts +0 -51
- package/src/auth/errors.ts +0 -6
- package/src/auth/jwtStore.ts +0 -70
- package/src/auth/keyAuth.ts +0 -142
- package/src/auth/keyManager.ts +0 -331
- package/src/auth/registerIdentity.ts +0 -67
- package/src/auth/spinner.ts +0 -23
- package/src/chat/CliChatService.ts +0 -438
- package/src/chat/modules/AgentModule.ts +0 -1664
- package/src/chat/modules/AskModule.ts +0 -98
- package/src/chat/modules/BaseChatModule.ts +0 -65
- package/src/chat/modules/ClipboardImageModule.ts +0 -136
- package/src/chat/modules/CustomCommandsModule.ts +0 -80
- package/src/chat/modules/InternalChatModule.ts +0 -216
- package/src/chat/modules/RemoteSyncModule.ts +0 -482
- package/src/chat/modules/RendererModule.ts +0 -139
- package/src/chat/modules/SearchModule.ts +0 -168
- package/src/chat/modules/SessionsModule.ts +0 -969
- package/src/chat/modules/SetupModule.ts +0 -185
- package/src/chat/modules/ShellCommandModule.ts +0 -98
- package/src/chat/modules/SystemModule.ts +0 -161
- package/src/chat/modules/ToolCommandModule.ts +0 -102
- package/src/chat/modules/VoiceModule.ts +0 -70
- package/src/chat/modules/index.ts +0 -8
- package/src/chat/renderer/CompactRenderer.ts +0 -229
- package/src/chat/renderer/ConsoleRenderer.ts +0 -160
- package/src/chat/renderer/FancyRenderer.ts +0 -440
- package/src/chat/renderer/PlainRenderer.ts +0 -218
- package/src/chat/renderer/index.ts +0 -6
- package/src/chat/renderer/loadRenderer.ts +0 -316
- package/src/chat/renderer/messagesToRenderEvents.ts +0 -96
- package/src/chat/renderer/types.ts +0 -99
- package/src/chat/types.ts +0 -146
- package/src/chat.ts +0 -140
- package/src/cli.ts +0 -180
- package/src/clients/anthropic.ts +0 -772
- package/src/clients/attribution.ts +0 -53
- package/src/clients/cerebras.ts +0 -10
- package/src/clients/contextLimits.ts +0 -263
- package/src/clients/copilot.ts +0 -23
- package/src/clients/deepseek.ts +0 -16
- package/src/clients/fireworks.ts +0 -71
- package/src/clients/gemini.ts +0 -1169
- package/src/clients/github.ts +0 -16
- package/src/clients/groq.ts +0 -15
- package/src/clients/http.ts +0 -719
- package/src/clients/index.ts +0 -1153
- package/src/clients/knowhow.ts +0 -130
- package/src/clients/knowhowMcp.ts +0 -118
- package/src/clients/llama.ts +0 -16
- package/src/clients/meta.ts +0 -27
- package/src/clients/mistral.ts +0 -16
- package/src/clients/nvidia.ts +0 -16
- package/src/clients/openai.ts +0 -1186
- package/src/clients/openrouter.ts +0 -24
- package/src/clients/pricing/anthropic.ts +0 -149
- package/src/clients/pricing/cerebras.ts +0 -11
- package/src/clients/pricing/copilot.ts +0 -60
- package/src/clients/pricing/deepseek.ts +0 -15
- package/src/clients/pricing/fireworks.ts +0 -182
- package/src/clients/pricing/github.ts +0 -69
- package/src/clients/pricing/google.ts +0 -385
- package/src/clients/pricing/groq.ts +0 -56
- package/src/clients/pricing/index.ts +0 -44
- package/src/clients/pricing/llama.ts +0 -18
- package/src/clients/pricing/meta.ts +0 -12
- package/src/clients/pricing/mistral.ts +0 -34
- package/src/clients/pricing/models.ts +0 -25
- package/src/clients/pricing/nvidia.ts +0 -102
- package/src/clients/pricing/openai.ts +0 -409
- package/src/clients/pricing/openrouter.ts +0 -36
- package/src/clients/pricing/qwen.ts +0 -80
- package/src/clients/pricing/types.ts +0 -125
- package/src/clients/pricing/xai.ts +0 -137
- package/src/clients/qwen.ts +0 -16
- package/src/clients/responseMetadata.ts +0 -221
- package/src/clients/types.ts +0 -531
- package/src/clients/withRetry.ts +0 -89
- package/src/clients/xai.ts +0 -693
- package/src/cloudWorker.ts +0 -394
- package/src/commands/agent.ts +0 -342
- package/src/commands/agents.ts +0 -1037
- package/src/commands/behaviors.ts +0 -47
- package/src/commands/convert.ts +0 -291
- package/src/commands/hash.ts +0 -179
- package/src/commands/mcp.ts +0 -743
- package/src/commands/misc.ts +0 -373
- package/src/commands/modules.ts +0 -704
- package/src/commands/processes.ts +0 -248
- package/src/commands/remote.ts +0 -77
- package/src/commands/replay.ts +0 -503
- package/src/commands/sandbox.ts +0 -566
- package/src/commands/services.ts +0 -168
- package/src/commands/skills.ts +0 -107
- package/src/commands/workers.ts +0 -168
- package/src/config.ts +0 -468
- package/src/conversion.ts +0 -69
- package/src/dataset/diffs/README.md +0 -12
- package/src/dataset/diffs/debug-errors.ts +0 -29
- package/src/dataset/diffs/debug.ts +0 -64
- package/src/dataset/diffs/generate.ts +0 -71
- package/src/dataset/diffs/jsonl.ts +0 -53
- package/src/dataset/diffs/test.ts +0 -194
- package/src/embeddings.ts +0 -494
- package/src/fileSync.ts +0 -369
- package/src/generate.ts +0 -529
- package/src/hashes.ts +0 -180
- package/src/index.ts +0 -261
- package/src/logger.ts +0 -197
- package/src/login.ts +0 -209
- package/src/microphone.ts +0 -141
- package/src/migrations.ts +0 -151
- package/src/plugins/AgentsMdPlugin.ts +0 -118
- package/src/plugins/GitPlugin.ts +0 -598
- package/src/plugins/LinterPlugin.ts +0 -214
- package/src/plugins/PluginBase.ts +0 -70
- package/src/plugins/SkillsPlugin.ts +0 -37
- package/src/plugins/embedding.ts +0 -82
- package/src/plugins/exec.ts +0 -70
- package/src/plugins/language.ts +0 -290
- package/src/plugins/plugins.ts +0 -175
- package/src/plugins/tmux.ts +0 -132
- package/src/plugins/types.ts +0 -36
- package/src/plugins/url.ts +0 -101
- package/src/plugins/vim.ts +0 -89
- package/src/processes/ProcessManager.ts +0 -96
- package/src/processes/managedProcessShim.ts +0 -138
- package/src/processors/Base64ImageDetector.ts +0 -502
- package/src/processors/CustomVariables.ts +0 -693
- package/src/processors/GemmaToolProcessor.ts +0 -163
- package/src/processors/HarmonyToolProcessor.ts +0 -264
- package/src/processors/JsonCompressor.ts +0 -510
- package/src/processors/MinimalToolsMessageProcessor.ts +0 -113
- package/src/processors/TokenCompressor.ts +0 -620
- package/src/processors/ToolResponseCache.ts +0 -322
- package/src/processors/XmlToolCallProcessor.ts +0 -533
- package/src/processors/index.ts +0 -14
- package/src/processors/tools/boundedToolResponse.ts +0 -49
- package/src/processors/tools/grepToolResponse.ts +0 -230
- package/src/processors/tools/index.ts +0 -23
- package/src/processors/tools/jqToolResponse.ts +0 -159
- package/src/processors/tools/listStoredToolResponses.ts +0 -124
- package/src/processors/tools/tailToolResponse.ts +0 -148
- package/src/prompts/BasicCodeDocumenter.ts +0 -1
- package/src/prompts/BasicProjectDocumenter.ts +0 -1
- package/src/prompts/EmbeddingSeachHelper.ts +0 -1
- package/src/prompts/KnowhowConfigExamples.ts +0 -376
- package/src/prompts/index.ts +0 -9
- package/src/remotes.ts +0 -110
- package/src/server/index.ts +0 -16
- package/src/services/AgentService.ts +0 -134
- package/src/services/AgentSyncFs.ts +0 -775
- package/src/services/AgentSyncKnowhowWeb.ts +0 -389
- package/src/services/BehaviorsService.ts +0 -466
- package/src/services/DockerService.ts +0 -511
- package/src/services/EmbeddingService.ts +0 -427
- package/src/services/EmbeddingsService.ts +0 -73
- package/src/services/EventService.ts +0 -279
- package/src/services/ExtensionsService.ts +0 -25
- package/src/services/KnowhowClient.ts +0 -928
- package/src/services/LazyToolsService.ts +0 -210
- package/src/services/Mcp.ts +0 -727
- package/src/services/McpServer.ts +0 -155
- package/src/services/McpWebsocketTransport.ts +0 -87
- package/src/services/MediaProcessorService.ts +0 -669
- package/src/services/MessageProcessor.ts +0 -114
- package/src/services/MinimalToolsService.ts +0 -177
- package/src/services/RuntimeReloadService.ts +0 -31
- package/src/services/S3.ts +0 -61
- package/src/services/SessionManager.ts +0 -358
- package/src/services/SkillsService.ts +0 -822
- package/src/services/SyncedAgentWatcher.ts +0 -124
- package/src/services/SyncerService.ts +0 -191
- package/src/services/TaskRegistry.ts +0 -110
- package/src/services/Tools.ts +0 -580
- package/src/services/TracingService.ts +0 -116
- package/src/services/conversion/ConversionService.ts +0 -757
- package/src/services/conversion/index.ts +0 -2
- package/src/services/conversion/types.ts +0 -87
- package/src/services/flags.ts +0 -51
- package/src/services/index.ts +0 -117
- package/src/services/modules/computerUse.ts +0 -218
- package/src/services/modules/index.ts +0 -314
- package/src/services/modules/types.ts +0 -109
- package/src/services/types.ts +0 -60
- package/src/services/watchers/FsSyncer.ts +0 -199
- package/src/services/watchers/RemoteSyncer.ts +0 -196
- package/src/services/watchers/index.ts +0 -2
- package/src/tunnel.ts +0 -243
- package/src/types.ts +0 -282
- package/src/util/Trace.ts +0 -236
- package/src/utils/InputQueueManager.ts +0 -493
- package/src/utils/errors.ts +0 -0
- package/src/utils/http.ts +0 -150
- package/src/utils/index.ts +0 -111
- package/src/worker.ts +0 -696
- package/src/workerRegistry.ts +0 -152
- package/src/workers/auth/PasskeySetup.ts +0 -181
- package/src/workers/auth/WorkerPasskeyAuth.ts +0 -190
- package/src/workers/auth/WsMiddleware.ts +0 -99
- package/src/workers/auth/authMiddleware.ts +0 -104
- package/src/workers/auth/types.ts +0 -70
- package/src/workers/tools/getChallenge.ts +0 -33
- package/src/workers/tools/index.ts +0 -20
- package/src/workers/tools/listAllowedPorts.ts +0 -30
- package/src/workers/tools/lock.ts +0 -31
- package/src/workers/tools/reloadConfig.ts +0 -84
- package/src/workers/tools/unlock.ts +0 -116
- package/tests/XmlToolCallProcessor.test.ts +0 -468
- package/tests/agents/interrupt.test.ts +0 -200
- package/tests/agents/observe-base.test.ts +0 -183
- package/tests/agents/tools/connectAgent.test.ts +0 -138
- package/tests/agents/tools/observe.test.ts +0 -179
- package/tests/agents/tools/readFile.test.ts +0 -88
- package/tests/agents/tools/replyToParent.test.ts +0 -125
- package/tests/agents/tools/sendAgentMessage.test.ts +0 -99
- package/tests/agents/tools/waitForAgentCompleted.test.ts +0 -147
- package/tests/chat/detach-attach-rendering.test.ts +0 -315
- package/tests/clients/AIClient.test.ts +0 -668
- package/tests/clients/anthropic.test.ts +0 -202
- package/tests/clients/contextLimits.test.ts +0 -97
- package/tests/clients/fireworks.test.ts +0 -65
- package/tests/clients/openai.images.test.ts +0 -105
- package/tests/clients/pricing.test.ts +0 -206
- package/tests/clients/reasoningEffort.test.ts +0 -140
- package/tests/compressor/bigstring.test.ts +0 -450
- package/tests/compressor/bigstring.txt +0 -1
- package/tests/compressor/githubjson.txt +0 -1
- package/tests/compressor/toolResponseCache.test.ts +0 -304
- package/tests/compressor/usageBase64.json +0 -159
- package/tests/compressor/usageBase64.test.ts +0 -175
- package/tests/fixtures/fake-secret.txt +0 -1
- package/tests/manual/agent-events/run-test.ts +0 -203
- package/tests/manual/browser-login/README.md +0 -189
- package/tests/manual/browser-login/test_browser_login_basic.ts +0 -115
- package/tests/manual/browser-login/test_cli_integration.ts +0 -169
- package/tests/manual/browser-login/test_cross_platform_browser.ts +0 -186
- package/tests/manual/browser-login/test_error_scenarios.ts +0 -223
- package/tests/manual/cli/no-env.sh +0 -274
- package/tests/manual/clients/completions.json +0 -1151
- package/tests/manual/clients/completions.test.ts +0 -198
- package/tests/manual/file-edits/figma.test.ts +0 -3
- package/tests/manual/file-edits/fileblocks/readwrite.test.ts +0 -100
- package/tests/manual/file-edits/patching/input.txt +0 -145
- package/tests/manual/file-edits/patching/output.txt +0 -145
- package/tests/manual/file-edits/patching/patch.txt +0 -15
- package/tests/manual/file-edits/patching/unseen.txt +0 -80
- package/tests/manual/file-edits/patching.test.ts +0 -139
- package/tests/manual/modalities/README.md +0 -157
- package/tests/manual/modalities/google.modalities.test.ts +0 -335
- package/tests/manual/modalities/openai.modalities.test.ts +0 -329
- package/tests/manual/modalities/streaming.test.ts +0 -260
- package/tests/manual/modalities/xai.modalities.test.ts +0 -294
- package/tests/manual/test-concurrent-ask.ts +0 -43
- package/tests/manual/ycmd/debug_diagnostics_test.ts +0 -127
- package/tests/manual/ycmd/fixtures/debug_diagnostics.ts +0 -26
- package/tests/manual/ycmd/fixtures/file_change_test.ts +0 -17
- package/tests/manual/ycmd/minimal_advanced_test.ts +0 -108
- package/tests/manual/ycmd/simple_diagnostics_test.ts +0 -61
- package/tests/manual/ycmd/simple_test.ts +0 -74
- package/tests/manual/ycmd/test-typescript-sample.ts +0 -34
- package/tests/manual/ycmd/test_advanced_features.ts +0 -407
- package/tests/manual/ycmd/test_advanced_with_tools.ts +0 -320
- package/tests/manual/ycmd/test_comprehensive_typescript.ts +0 -179
- package/tests/manual/ycmd/test_diagnostics_file_changes.ts +0 -249
- package/tests/manual/ycmd/test_diagnostics_fix.ts +0 -99
- package/tests/manual/ycmd/test_diagnostics_simple.ts +0 -100
- package/tests/manual/ycmd/test_diagnostics_timing.ts +0 -120
- package/tests/manual/ycmd/test_discover_commands.ts +0 -310
- package/tests/manual/ycmd/test_endpoints.ts +0 -115
- package/tests/manual/ycmd/test_final_comprehensive.ts +0 -218
- package/tests/manual/ycmd/test_final_validation.ts +0 -150
- package/tests/manual/ycmd/test_implementation.js +0 -42
- package/tests/manual/ycmd/test_individual_ycmd_tool.ts +0 -39
- package/tests/manual/ycmd/test_server_manager.ts +0 -52
- package/tests/manual/ycmd/test_simple_debug.ts +0 -86
- package/tests/manual/ycmd/test_tsserver_workflow.js +0 -83
- package/tests/manual/ycmd/test_tsserver_workflow.ts +0 -122
- package/tests/manual/ycmd/test_typescript_simple.ts +0 -48
- package/tests/manual/ycmd/test_typescript_ycmd.ts +0 -105
- package/tests/manual/ycmd/test_workspace_config.ts +0 -90
- package/tests/manual/ycmd/test_ycmd_auto_start.ts +0 -137
- package/tests/manual/ycmd/test_ycmd_comprehensive.ts +0 -73
- package/tests/manual/ycmd/test_ycmd_connection.py +0 -10
- package/tests/manual/ycmd/test_ycmd_direct.ts +0 -142
- package/tests/manual/ycmd/test_ycmd_experiment.ts +0 -48
- package/tests/manual/ycmd/test_ycmd_final.ts +0 -200
- package/tests/manual/ycmd/test_ycmd_fixed.py +0 -18
- package/tests/manual/ycmd/test_ycmd_integration.ts +0 -112
- package/tests/manual/ycmd/test_ycmd_simple.ts +0 -45
- package/tests/manual/ycmd/test_ycmd_usage.py +0 -27
- package/tests/manual/ycmd/working_simple_test.ts +0 -134
- package/tests/patching/corrupted.test.ts +0 -78
- package/tests/patching/fixtures/transcript-mangles/agent-sync-fs-nested-catch.json +0 -9
- package/tests/patching/fixtures/transcript-mangles/automation-functions-crossed.json +0 -9
- package/tests/patching/fixtures/transcript-mangles/fruit-ninja-confirmed-retirement.json +0 -8
- package/tests/patching/imports.patch.txt +0 -11
- package/tests/patching/imports.test.ts +0 -43
- package/tests/patching/imports.txt +0 -11
- package/tests/patching/interface.patch.txt +0 -15
- package/tests/patching/interface.txt +0 -7
- package/tests/patching/patchFileOutput.test.ts +0 -328
- package/tests/patching/regression-2026.test.ts +0 -752
- package/tests/patching/transcriptMangleRegression.test.ts +0 -125
- package/tests/plugins/language/languagePlugin-content-triggers.test.ts +0 -346
- package/tests/plugins/language/languagePlugin-integration.test.ts +0 -471
- package/tests/plugins/language/languagePlugin.test.ts +0 -385
- package/tests/processes/managedProcessShim.test.ts +0 -59
- package/tests/processors/Base64ImageDetector.test.ts +0 -979
- package/tests/processors/CustomVariables.test.ts +0 -981
- package/tests/processors/CustomVariablesBenchmark.test.ts +0 -237
- package/tests/processors/HarmonyToolProcessor.test.ts +0 -490
- package/tests/processors/TokenCompressor.test.ts +0 -674
- package/tests/processors/ToolResponseCache.test.ts +0 -864
- package/tests/processors/tools/boundedRetrievalTools.test.ts +0 -68
- package/tests/processors/tools/grepToolResponse.test.ts +0 -159
- package/tests/services/ConversionService.test.ts +0 -154
- package/tests/services/DockerService.test.ts +0 -24
- package/tests/services/Tools.test.ts +0 -1738
- package/tests/services/ToolsService.setFunction.test.ts +0 -68
- package/tests/services/WorkerReloadConfig.test.ts +0 -141
- package/tests/test.spec.ts +0 -163
- package/tests/unit/InputQueueManager.test.ts +0 -174
- package/tests/unit/McpWebsocketTransport.test.ts +0 -56
- package/tests/unit/agents/historyRollback.test.ts +0 -53
- package/tests/unit/agents/tools/loadImageAsBase64.test.ts +0 -44
- package/tests/unit/auth/jwtStore.test.ts +0 -55
- package/tests/unit/auth/keyAuth.test.ts +0 -152
- package/tests/unit/auth/keyManager.test.ts +0 -130
- package/tests/unit/auth/registerIdentity.test.ts +0 -99
- package/tests/unit/chat/ToolCommandModule.test.ts +0 -71
- package/tests/unit/chat/historyCommandArgs.test.ts +0 -37
- package/tests/unit/clients/AIClient.test.ts +0 -454
- package/tests/unit/clients/anthropic.responseMetadata.test.ts +0 -81
- package/tests/unit/clients/attribution.test.ts +0 -185
- package/tests/unit/clients/http.responseMetadata.test.ts +0 -86
- package/tests/unit/clients/openai.responseMetadata.test.ts +0 -90
- package/tests/unit/clients/responseMetadata.test.ts +0 -123
- package/tests/unit/clients/withRetry.test.ts +0 -319
- package/tests/unit/commands/agents.test.ts +0 -111
- package/tests/unit/commands/github-credentials.test.ts +0 -210
- package/tests/unit/commands/misc.test.ts +0 -85
- package/tests/unit/commands/remote.test.ts +0 -79
- package/tests/unit/commands/sandbox.test.ts +0 -350
- package/tests/unit/hashes.test.ts +0 -23
- package/tests/unit/input-queue.test.ts +0 -80
- package/tests/unit/login.test.ts +0 -218
- package/tests/unit/modules/moduleLoading.test.ts +0 -305
- package/tests/unit/plugins/pluginLoading.test.ts +0 -97
- package/tests/unit/services/AgentSyncKnowhowWeb.test.ts +0 -38
- package/tests/unit/services/KnowhowClient.test.ts +0 -84
- package/tests/unit/services/LazyToolsService.test.ts +0 -53
- package/tests/unit/services/Mcp.test.ts +0 -56
- package/tests/unit/services/MinimalToolsService.test.ts +0 -424
- package/tests/unit/services/RuntimeReloadService.test.ts +0 -29
- package/tests/unit/services/SessionManager.test.ts +0 -43
- package/tests/unit/services/SyncedAgentWatcher.test.ts +0 -89
- package/tests/unit/services/SyncerService.test.ts +0 -45
- package/tests/unit/tunnel.test.ts +0 -51
- package/tests/unit/util/Trace.test.ts +0 -119
- package/tests/unit/utils/http.test.ts +0 -43
- package/ts_build/tests/XmlToolCallProcessor.test.d.ts +0 -1
- package/ts_build/tests/XmlToolCallProcessor.test.js +0 -376
- package/ts_build/tests/XmlToolCallProcessor.test.js.map +0 -1
- package/ts_build/tests/agents/interrupt.test.d.ts +0 -14
- package/ts_build/tests/agents/interrupt.test.js +0 -136
- package/ts_build/tests/agents/interrupt.test.js.map +0 -1
- package/ts_build/tests/agents/observe-base.test.d.ts +0 -1
- package/ts_build/tests/agents/observe-base.test.js +0 -149
- package/ts_build/tests/agents/observe-base.test.js.map +0 -1
- package/ts_build/tests/agents/tools/connectAgent.test.d.ts +0 -1
- package/ts_build/tests/agents/tools/connectAgent.test.js +0 -142
- package/ts_build/tests/agents/tools/connectAgent.test.js.map +0 -1
- package/ts_build/tests/agents/tools/observe.test.d.ts +0 -1
- package/ts_build/tests/agents/tools/observe.test.js +0 -148
- package/ts_build/tests/agents/tools/observe.test.js.map +0 -1
- package/ts_build/tests/agents/tools/readFile.test.d.ts +0 -1
- package/ts_build/tests/agents/tools/readFile.test.js +0 -90
- package/ts_build/tests/agents/tools/readFile.test.js.map +0 -1
- package/ts_build/tests/agents/tools/replyToParent.test.d.ts +0 -1
- package/ts_build/tests/agents/tools/replyToParent.test.js +0 -116
- package/ts_build/tests/agents/tools/replyToParent.test.js.map +0 -1
- package/ts_build/tests/agents/tools/sendAgentMessage.test.d.ts +0 -1
- package/ts_build/tests/agents/tools/sendAgentMessage.test.js +0 -112
- package/ts_build/tests/agents/tools/sendAgentMessage.test.js.map +0 -1
- package/ts_build/tests/agents/tools/waitForAgentCompleted.test.d.ts +0 -1
- package/ts_build/tests/agents/tools/waitForAgentCompleted.test.js +0 -147
- package/ts_build/tests/agents/tools/waitForAgentCompleted.test.js.map +0 -1
- package/ts_build/tests/chat/detach-attach-rendering.test.d.ts +0 -1
- package/ts_build/tests/chat/detach-attach-rendering.test.js +0 -168
- package/ts_build/tests/chat/detach-attach-rendering.test.js.map +0 -1
- package/ts_build/tests/clients/AIClient.test.d.ts +0 -1
- package/ts_build/tests/clients/AIClient.test.js +0 -483
- package/ts_build/tests/clients/AIClient.test.js.map +0 -1
- package/ts_build/tests/clients/anthropic.test.d.ts +0 -1
- package/ts_build/tests/clients/anthropic.test.js +0 -159
- package/ts_build/tests/clients/anthropic.test.js.map +0 -1
- package/ts_build/tests/clients/contextLimits.test.d.ts +0 -1
- package/ts_build/tests/clients/contextLimits.test.js +0 -69
- package/ts_build/tests/clients/contextLimits.test.js.map +0 -1
- package/ts_build/tests/clients/fireworks.test.d.ts +0 -1
- package/ts_build/tests/clients/fireworks.test.js +0 -65
- package/ts_build/tests/clients/fireworks.test.js.map +0 -1
- package/ts_build/tests/clients/openai.images.test.d.ts +0 -1
- package/ts_build/tests/clients/openai.images.test.js +0 -94
- package/ts_build/tests/clients/openai.images.test.js.map +0 -1
- package/ts_build/tests/clients/pricing.test.d.ts +0 -1
- package/ts_build/tests/clients/pricing.test.js +0 -124
- package/ts_build/tests/clients/pricing.test.js.map +0 -1
- package/ts_build/tests/clients/reasoningEffort.test.d.ts +0 -1
- package/ts_build/tests/clients/reasoningEffort.test.js +0 -116
- package/ts_build/tests/clients/reasoningEffort.test.js.map +0 -1
- package/ts_build/tests/compressor/bigstring.test.d.ts +0 -1
- package/ts_build/tests/compressor/bigstring.test.js +0 -275
- package/ts_build/tests/compressor/bigstring.test.js.map +0 -1
- package/ts_build/tests/compressor/toolResponseCache.test.d.ts +0 -1
- package/ts_build/tests/compressor/toolResponseCache.test.js +0 -250
- package/ts_build/tests/compressor/toolResponseCache.test.js.map +0 -1
- package/ts_build/tests/compressor/usageBase64.test.d.ts +0 -1
- package/ts_build/tests/compressor/usageBase64.test.js +0 -109
- package/ts_build/tests/compressor/usageBase64.test.js.map +0 -1
- package/ts_build/tests/manual/agent-events/run-test.d.ts +0 -2
- package/ts_build/tests/manual/agent-events/run-test.js +0 -148
- package/ts_build/tests/manual/agent-events/run-test.js.map +0 -1
- package/ts_build/tests/manual/browser-login/test_browser_login_basic.d.ts +0 -2
- package/ts_build/tests/manual/browser-login/test_browser_login_basic.js +0 -118
- package/ts_build/tests/manual/browser-login/test_browser_login_basic.js.map +0 -1
- package/ts_build/tests/manual/browser-login/test_cli_integration.d.ts +0 -2
- package/ts_build/tests/manual/browser-login/test_cli_integration.js +0 -163
- package/ts_build/tests/manual/browser-login/test_cli_integration.js.map +0 -1
- package/ts_build/tests/manual/browser-login/test_cross_platform_browser.d.ts +0 -2
- package/ts_build/tests/manual/browser-login/test_cross_platform_browser.js +0 -169
- package/ts_build/tests/manual/browser-login/test_cross_platform_browser.js.map +0 -1
- package/ts_build/tests/manual/browser-login/test_error_scenarios.d.ts +0 -2
- package/ts_build/tests/manual/browser-login/test_error_scenarios.js +0 -207
- package/ts_build/tests/manual/browser-login/test_error_scenarios.js.map +0 -1
- package/ts_build/tests/manual/clients/completions.test.d.ts +0 -1
- package/ts_build/tests/manual/clients/completions.test.js +0 -151
- package/ts_build/tests/manual/clients/completions.test.js.map +0 -1
- package/ts_build/tests/manual/file-edits/figma.test.d.ts +0 -0
- package/ts_build/tests/manual/file-edits/figma.test.js +0 -2
- package/ts_build/tests/manual/file-edits/figma.test.js.map +0 -1
- package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.d.ts +0 -1
- package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.js +0 -110
- package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.js.map +0 -1
- package/ts_build/tests/manual/file-edits/patching.test.d.ts +0 -1
- package/ts_build/tests/manual/file-edits/patching.test.js +0 -129
- package/ts_build/tests/manual/file-edits/patching.test.js.map +0 -1
- package/ts_build/tests/manual/modalities/google.modalities.test.d.ts +0 -1
- package/ts_build/tests/manual/modalities/google.modalities.test.js +0 -262
- package/ts_build/tests/manual/modalities/google.modalities.test.js.map +0 -1
- package/ts_build/tests/manual/modalities/openai.modalities.test.d.ts +0 -1
- package/ts_build/tests/manual/modalities/openai.modalities.test.js +0 -262
- package/ts_build/tests/manual/modalities/openai.modalities.test.js.map +0 -1
- package/ts_build/tests/manual/modalities/streaming.test.d.ts +0 -1
- package/ts_build/tests/manual/modalities/streaming.test.js +0 -216
- package/ts_build/tests/manual/modalities/streaming.test.js.map +0 -1
- package/ts_build/tests/manual/modalities/xai.modalities.test.d.ts +0 -1
- package/ts_build/tests/manual/modalities/xai.modalities.test.js +0 -219
- package/ts_build/tests/manual/modalities/xai.modalities.test.js.map +0 -1
- package/ts_build/tests/manual/test-concurrent-ask.d.ts +0 -1
- package/ts_build/tests/manual/test-concurrent-ask.js +0 -22
- package/ts_build/tests/manual/test-concurrent-ask.js.map +0 -1
- package/ts_build/tests/manual/ycmd/debug_diagnostics_test.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/debug_diagnostics_test.js +0 -124
- package/ts_build/tests/manual/ycmd/debug_diagnostics_test.js.map +0 -1
- package/ts_build/tests/manual/ycmd/minimal_advanced_test.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/minimal_advanced_test.js +0 -114
- package/ts_build/tests/manual/ycmd/minimal_advanced_test.js.map +0 -1
- package/ts_build/tests/manual/ycmd/simple_diagnostics_test.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/simple_diagnostics_test.js +0 -84
- package/ts_build/tests/manual/ycmd/simple_diagnostics_test.js.map +0 -1
- package/ts_build/tests/manual/ycmd/simple_test.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/simple_test.js +0 -92
- package/ts_build/tests/manual/ycmd/simple_test.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test-typescript-sample.d.ts +0 -14
- package/ts_build/tests/manual/ycmd/test-typescript-sample.js +0 -20
- package/ts_build/tests/manual/ycmd/test-typescript-sample.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_advanced_features.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_advanced_features.js +0 -306
- package/ts_build/tests/manual/ycmd/test_advanced_features.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_advanced_with_tools.d.ts +0 -3
- package/ts_build/tests/manual/ycmd/test_advanced_with_tools.js +0 -271
- package/ts_build/tests/manual/ycmd/test_advanced_with_tools.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_comprehensive_typescript.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_comprehensive_typescript.js +0 -196
- package/ts_build/tests/manual/ycmd/test_comprehensive_typescript.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_diagnostics_file_changes.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/test_diagnostics_file_changes.js +0 -184
- package/ts_build/tests/manual/ycmd/test_diagnostics_file_changes.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_diagnostics_fix.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_diagnostics_fix.js +0 -116
- package/ts_build/tests/manual/ycmd/test_diagnostics_fix.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_diagnostics_simple.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/test_diagnostics_simple.js +0 -114
- package/ts_build/tests/manual/ycmd/test_diagnostics_simple.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_diagnostics_timing.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/test_diagnostics_timing.js +0 -129
- package/ts_build/tests/manual/ycmd/test_diagnostics_timing.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_discover_commands.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_discover_commands.js +0 -252
- package/ts_build/tests/manual/ycmd/test_discover_commands.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_endpoints.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_endpoints.js +0 -130
- package/ts_build/tests/manual/ycmd/test_endpoints.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_final_comprehensive.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_final_comprehensive.js +0 -231
- package/ts_build/tests/manual/ycmd/test_final_comprehensive.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_final_validation.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_final_validation.js +0 -170
- package/ts_build/tests/manual/ycmd/test_final_validation.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_individual_ycmd_tool.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_individual_ycmd_tool.js +0 -37
- package/ts_build/tests/manual/ycmd/test_individual_ycmd_tool.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_server_manager.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/test_server_manager.js +0 -38
- package/ts_build/tests/manual/ycmd/test_server_manager.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_simple_debug.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_simple_debug.js +0 -109
- package/ts_build/tests/manual/ycmd/test_simple_debug.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_tsserver_workflow.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/test_tsserver_workflow.js +0 -138
- package/ts_build/tests/manual/ycmd/test_tsserver_workflow.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_typescript_simple.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/test_typescript_simple.js +0 -76
- package/ts_build/tests/manual/ycmd/test_typescript_simple.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_typescript_ycmd.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/test_typescript_ycmd.js +0 -115
- package/ts_build/tests/manual/ycmd/test_typescript_ycmd.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_workspace_config.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/test_workspace_config.js +0 -99
- package/ts_build/tests/manual/ycmd/test_workspace_config.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_ycmd_auto_start.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_ycmd_auto_start.js +0 -140
- package/ts_build/tests/manual/ycmd/test_ycmd_auto_start.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_ycmd_comprehensive.d.ts +0 -1
- package/ts_build/tests/manual/ycmd/test_ycmd_comprehensive.js +0 -93
- package/ts_build/tests/manual/ycmd/test_ycmd_comprehensive.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_ycmd_direct.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_ycmd_direct.js +0 -159
- package/ts_build/tests/manual/ycmd/test_ycmd_direct.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_ycmd_experiment.d.ts +0 -15
- package/ts_build/tests/manual/ycmd/test_ycmd_experiment.js +0 -68
- package/ts_build/tests/manual/ycmd/test_ycmd_experiment.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_ycmd_final.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_ycmd_final.js +0 -205
- package/ts_build/tests/manual/ycmd/test_ycmd_final.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_ycmd_integration.d.ts +0 -3
- package/ts_build/tests/manual/ycmd/test_ycmd_integration.js +0 -119
- package/ts_build/tests/manual/ycmd/test_ycmd_integration.js.map +0 -1
- package/ts_build/tests/manual/ycmd/test_ycmd_simple.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/test_ycmd_simple.js +0 -36
- package/ts_build/tests/manual/ycmd/test_ycmd_simple.js.map +0 -1
- package/ts_build/tests/manual/ycmd/working_simple_test.d.ts +0 -2
- package/ts_build/tests/manual/ycmd/working_simple_test.js +0 -144
- package/ts_build/tests/manual/ycmd/working_simple_test.js.map +0 -1
- package/ts_build/tests/patching/corrupted.test.d.ts +0 -1
- package/ts_build/tests/patching/corrupted.test.js +0 -53
- package/ts_build/tests/patching/corrupted.test.js.map +0 -1
- package/ts_build/tests/patching/imports.test.d.ts +0 -1
- package/ts_build/tests/patching/imports.test.js +0 -71
- package/ts_build/tests/patching/imports.test.js.map +0 -1
- package/ts_build/tests/patching/patchFileOutput.test.d.ts +0 -1
- package/ts_build/tests/patching/patchFileOutput.test.js +0 -269
- package/ts_build/tests/patching/patchFileOutput.test.js.map +0 -1
- package/ts_build/tests/patching/regression-2026.test.d.ts +0 -1
- package/ts_build/tests/patching/regression-2026.test.js +0 -528
- package/ts_build/tests/patching/regression-2026.test.js.map +0 -1
- package/ts_build/tests/patching/transcriptMangleRegression.test.d.ts +0 -1
- package/ts_build/tests/patching/transcriptMangleRegression.test.js +0 -104
- package/ts_build/tests/patching/transcriptMangleRegression.test.js.map +0 -1
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.d.ts +0 -1
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +0 -294
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +0 -1
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.d.ts +0 -1
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +0 -343
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +0 -1
- package/ts_build/tests/plugins/language/languagePlugin.test.d.ts +0 -1
- package/ts_build/tests/plugins/language/languagePlugin.test.js +0 -315
- package/ts_build/tests/plugins/language/languagePlugin.test.js.map +0 -1
- package/ts_build/tests/processes/managedProcessShim.test.d.ts +0 -1
- package/ts_build/tests/processes/managedProcessShim.test.js +0 -92
- package/ts_build/tests/processes/managedProcessShim.test.js.map +0 -1
- package/ts_build/tests/processors/Base64ImageDetector.test.d.ts +0 -1
- package/ts_build/tests/processors/Base64ImageDetector.test.js +0 -792
- package/ts_build/tests/processors/Base64ImageDetector.test.js.map +0 -1
- package/ts_build/tests/processors/CustomVariables.test.d.ts +0 -1
- package/ts_build/tests/processors/CustomVariables.test.js +0 -764
- package/ts_build/tests/processors/CustomVariables.test.js.map +0 -1
- package/ts_build/tests/processors/CustomVariablesBenchmark.test.d.ts +0 -1
- package/ts_build/tests/processors/CustomVariablesBenchmark.test.js +0 -179
- package/ts_build/tests/processors/CustomVariablesBenchmark.test.js.map +0 -1
- package/ts_build/tests/processors/HarmonyToolProcessor.test.d.ts +0 -1
- package/ts_build/tests/processors/HarmonyToolProcessor.test.js +0 -382
- package/ts_build/tests/processors/HarmonyToolProcessor.test.js.map +0 -1
- package/ts_build/tests/processors/TokenCompressor.test.d.ts +0 -1
- package/ts_build/tests/processors/TokenCompressor.test.js +0 -473
- package/ts_build/tests/processors/TokenCompressor.test.js.map +0 -1
- package/ts_build/tests/processors/ToolResponseCache.test.d.ts +0 -1
- package/ts_build/tests/processors/ToolResponseCache.test.js +0 -657
- package/ts_build/tests/processors/ToolResponseCache.test.js.map +0 -1
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.d.ts +0 -1
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js +0 -57
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js.map +0 -1
- package/ts_build/tests/processors/tools/grepToolResponse.test.d.ts +0 -1
- package/ts_build/tests/processors/tools/grepToolResponse.test.js +0 -77
- package/ts_build/tests/processors/tools/grepToolResponse.test.js.map +0 -1
- package/ts_build/tests/services/ConversionService.test.d.ts +0 -1
- package/ts_build/tests/services/ConversionService.test.js +0 -154
- package/ts_build/tests/services/ConversionService.test.js.map +0 -1
- package/ts_build/tests/services/DockerService.test.d.ts +0 -1
- package/ts_build/tests/services/DockerService.test.js +0 -22
- package/ts_build/tests/services/DockerService.test.js.map +0 -1
- package/ts_build/tests/services/Tools.test.d.ts +0 -1
- package/ts_build/tests/services/Tools.test.js +0 -1365
- package/ts_build/tests/services/Tools.test.js.map +0 -1
- package/ts_build/tests/services/ToolsService.setFunction.test.d.ts +0 -1
- package/ts_build/tests/services/ToolsService.setFunction.test.js +0 -51
- package/ts_build/tests/services/ToolsService.setFunction.test.js.map +0 -1
- package/ts_build/tests/services/WorkerReloadConfig.test.d.ts +0 -1
- package/ts_build/tests/services/WorkerReloadConfig.test.js +0 -86
- package/ts_build/tests/services/WorkerReloadConfig.test.js.map +0 -1
- package/ts_build/tests/test.spec.d.ts +0 -1
- package/ts_build/tests/test.spec.js +0 -139
- package/ts_build/tests/test.spec.js.map +0 -1
- package/ts_build/tests/unit/InputQueueManager.test.d.ts +0 -1
- package/ts_build/tests/unit/InputQueueManager.test.js +0 -104
- package/ts_build/tests/unit/InputQueueManager.test.js.map +0 -1
- package/ts_build/tests/unit/McpWebsocketTransport.test.d.ts +0 -1
- package/ts_build/tests/unit/McpWebsocketTransport.test.js +0 -47
- package/ts_build/tests/unit/McpWebsocketTransport.test.js.map +0 -1
- package/ts_build/tests/unit/agents/historyRollback.test.d.ts +0 -1
- package/ts_build/tests/unit/agents/historyRollback.test.js +0 -42
- package/ts_build/tests/unit/agents/historyRollback.test.js.map +0 -1
- package/ts_build/tests/unit/agents/tools/loadImageAsBase64.test.d.ts +0 -1
- package/ts_build/tests/unit/agents/tools/loadImageAsBase64.test.js +0 -67
- package/ts_build/tests/unit/agents/tools/loadImageAsBase64.test.js.map +0 -1
- package/ts_build/tests/unit/auth/jwtStore.test.d.ts +0 -1
- package/ts_build/tests/unit/auth/jwtStore.test.js +0 -46
- package/ts_build/tests/unit/auth/jwtStore.test.js.map +0 -1
- package/ts_build/tests/unit/auth/keyAuth.test.d.ts +0 -1
- package/ts_build/tests/unit/auth/keyAuth.test.js +0 -118
- package/ts_build/tests/unit/auth/keyAuth.test.js.map +0 -1
- package/ts_build/tests/unit/auth/keyManager.test.d.ts +0 -1
- package/ts_build/tests/unit/auth/keyManager.test.js +0 -96
- package/ts_build/tests/unit/auth/keyManager.test.js.map +0 -1
- package/ts_build/tests/unit/auth/registerIdentity.test.d.ts +0 -1
- package/ts_build/tests/unit/auth/registerIdentity.test.js +0 -92
- package/ts_build/tests/unit/auth/registerIdentity.test.js.map +0 -1
- package/ts_build/tests/unit/chat/ToolCommandModule.test.d.ts +0 -1
- package/ts_build/tests/unit/chat/ToolCommandModule.test.js +0 -61
- package/ts_build/tests/unit/chat/ToolCommandModule.test.js.map +0 -1
- package/ts_build/tests/unit/chat/historyCommandArgs.test.d.ts +0 -1
- package/ts_build/tests/unit/chat/historyCommandArgs.test.js +0 -29
- package/ts_build/tests/unit/chat/historyCommandArgs.test.js.map +0 -1
- package/ts_build/tests/unit/clients/AIClient.test.d.ts +0 -1
- package/ts_build/tests/unit/clients/AIClient.test.js +0 -342
- package/ts_build/tests/unit/clients/AIClient.test.js.map +0 -1
- package/ts_build/tests/unit/clients/anthropic.responseMetadata.test.d.ts +0 -1
- package/ts_build/tests/unit/clients/anthropic.responseMetadata.test.js +0 -77
- package/ts_build/tests/unit/clients/anthropic.responseMetadata.test.js.map +0 -1
- package/ts_build/tests/unit/clients/attribution.test.d.ts +0 -1
- package/ts_build/tests/unit/clients/attribution.test.js +0 -153
- package/ts_build/tests/unit/clients/attribution.test.js.map +0 -1
- package/ts_build/tests/unit/clients/http.responseMetadata.test.d.ts +0 -1
- package/ts_build/tests/unit/clients/http.responseMetadata.test.js +0 -77
- package/ts_build/tests/unit/clients/http.responseMetadata.test.js.map +0 -1
- package/ts_build/tests/unit/clients/openai.responseMetadata.test.d.ts +0 -1
- package/ts_build/tests/unit/clients/openai.responseMetadata.test.js +0 -86
- package/ts_build/tests/unit/clients/openai.responseMetadata.test.js.map +0 -1
- package/ts_build/tests/unit/clients/responseMetadata.test.d.ts +0 -1
- package/ts_build/tests/unit/clients/responseMetadata.test.js +0 -96
- package/ts_build/tests/unit/clients/responseMetadata.test.js.map +0 -1
- package/ts_build/tests/unit/clients/withRetry.test.d.ts +0 -1
- package/ts_build/tests/unit/clients/withRetry.test.js +0 -225
- package/ts_build/tests/unit/clients/withRetry.test.js.map +0 -1
- package/ts_build/tests/unit/commands/agents.test.d.ts +0 -1
- package/ts_build/tests/unit/commands/agents.test.js +0 -119
- package/ts_build/tests/unit/commands/agents.test.js.map +0 -1
- package/ts_build/tests/unit/commands/github-credentials.test.d.ts +0 -1
- package/ts_build/tests/unit/commands/github-credentials.test.js +0 -145
- package/ts_build/tests/unit/commands/github-credentials.test.js.map +0 -1
- package/ts_build/tests/unit/commands/misc.test.d.ts +0 -1
- package/ts_build/tests/unit/commands/misc.test.js +0 -70
- package/ts_build/tests/unit/commands/misc.test.js.map +0 -1
- package/ts_build/tests/unit/commands/remote.test.d.ts +0 -1
- package/ts_build/tests/unit/commands/remote.test.js +0 -70
- package/ts_build/tests/unit/commands/remote.test.js.map +0 -1
- package/ts_build/tests/unit/commands/sandbox.test.d.ts +0 -1
- package/ts_build/tests/unit/commands/sandbox.test.js +0 -258
- package/ts_build/tests/unit/commands/sandbox.test.js.map +0 -1
- package/ts_build/tests/unit/hashes.test.d.ts +0 -1
- package/ts_build/tests/unit/hashes.test.js +0 -25
- package/ts_build/tests/unit/hashes.test.js.map +0 -1
- package/ts_build/tests/unit/input-queue.test.d.ts +0 -1
- package/ts_build/tests/unit/input-queue.test.js +0 -32
- package/ts_build/tests/unit/input-queue.test.js.map +0 -1
- package/ts_build/tests/unit/login.test.d.ts +0 -1
- package/ts_build/tests/unit/login.test.js +0 -166
- package/ts_build/tests/unit/login.test.js.map +0 -1
- package/ts_build/tests/unit/modules/moduleLoading.test.d.ts +0 -1
- package/ts_build/tests/unit/modules/moduleLoading.test.js +0 -272
- package/ts_build/tests/unit/modules/moduleLoading.test.js.map +0 -1
- package/ts_build/tests/unit/plugins/pluginLoading.test.d.ts +0 -1
- package/ts_build/tests/unit/plugins/pluginLoading.test.js +0 -117
- package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +0 -1
- package/ts_build/tests/unit/services/AgentSyncKnowhowWeb.test.d.ts +0 -1
- package/ts_build/tests/unit/services/AgentSyncKnowhowWeb.test.js +0 -32
- package/ts_build/tests/unit/services/AgentSyncKnowhowWeb.test.js.map +0 -1
- package/ts_build/tests/unit/services/KnowhowClient.test.d.ts +0 -1
- package/ts_build/tests/unit/services/KnowhowClient.test.js +0 -69
- package/ts_build/tests/unit/services/KnowhowClient.test.js.map +0 -1
- package/ts_build/tests/unit/services/LazyToolsService.test.d.ts +0 -1
- package/ts_build/tests/unit/services/LazyToolsService.test.js +0 -47
- package/ts_build/tests/unit/services/LazyToolsService.test.js.map +0 -1
- package/ts_build/tests/unit/services/Mcp.test.d.ts +0 -1
- package/ts_build/tests/unit/services/Mcp.test.js +0 -45
- package/ts_build/tests/unit/services/Mcp.test.js.map +0 -1
- package/ts_build/tests/unit/services/MinimalToolsService.test.d.ts +0 -1
- package/ts_build/tests/unit/services/MinimalToolsService.test.js +0 -320
- package/ts_build/tests/unit/services/MinimalToolsService.test.js.map +0 -1
- package/ts_build/tests/unit/services/RuntimeReloadService.test.d.ts +0 -1
- package/ts_build/tests/unit/services/RuntimeReloadService.test.js +0 -26
- package/ts_build/tests/unit/services/RuntimeReloadService.test.js.map +0 -1
- package/ts_build/tests/unit/services/SessionManager.test.d.ts +0 -1
- package/ts_build/tests/unit/services/SessionManager.test.js +0 -72
- package/ts_build/tests/unit/services/SessionManager.test.js.map +0 -1
- package/ts_build/tests/unit/services/SyncedAgentWatcher.test.d.ts +0 -1
- package/ts_build/tests/unit/services/SyncedAgentWatcher.test.js +0 -78
- package/ts_build/tests/unit/services/SyncedAgentWatcher.test.js.map +0 -1
- package/ts_build/tests/unit/services/SyncerService.test.d.ts +0 -1
- package/ts_build/tests/unit/services/SyncerService.test.js +0 -40
- package/ts_build/tests/unit/services/SyncerService.test.js.map +0 -1
- package/ts_build/tests/unit/tunnel.test.d.ts +0 -1
- package/ts_build/tests/unit/tunnel.test.js +0 -47
- package/ts_build/tests/unit/tunnel.test.js.map +0 -1
- package/ts_build/tests/unit/util/Trace.test.d.ts +0 -1
- package/ts_build/tests/unit/util/Trace.test.js +0 -107
- package/ts_build/tests/unit/util/Trace.test.js.map +0 -1
- package/ts_build/tests/unit/utils/http.test.d.ts +0 -1
- package/ts_build/tests/unit/utils/http.test.js +0 -69
- package/ts_build/tests/unit/utils/http.test.js.map +0 -1
- package/tsconfig.json +0 -34
- package/tslint.json +0 -11
package/src/agents/base/base.ts
DELETED
|
@@ -1,1963 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "events"; // kept for reference; agentEvents now uses EventService
|
|
2
|
-
import {
|
|
3
|
-
CompletionResponse,
|
|
4
|
-
CompletionOptions,
|
|
5
|
-
GenericClient,
|
|
6
|
-
Message,
|
|
7
|
-
MessageContent,
|
|
8
|
-
OutputMessage,
|
|
9
|
-
Tool,
|
|
10
|
-
ToolCall,
|
|
11
|
-
} from "../../clients/types";
|
|
12
|
-
import { IAgent } from "../interface";
|
|
13
|
-
import { ToolsService } from "../../services/Tools";
|
|
14
|
-
import {
|
|
15
|
-
mcpToolName,
|
|
16
|
-
replaceEscapedNewLines,
|
|
17
|
-
restoreEscapedNewLines,
|
|
18
|
-
} from "../../utils";
|
|
19
|
-
import { EventService } from "../../services/EventService";
|
|
20
|
-
import { AIClient, Clients } from "../../clients";
|
|
21
|
-
import { Models } from "../../ai";
|
|
22
|
-
import { MessageProcessor } from "../../services/MessageProcessor";
|
|
23
|
-
import { DEFAULT_CONTEXT_LIMIT } from "../../clients/contextLimits";
|
|
24
|
-
import { Marked } from "../../utils";
|
|
25
|
-
import { TraceAll } from "../../util/Trace";
|
|
26
|
-
|
|
27
|
-
export { Message, Tool, ToolCall };
|
|
28
|
-
export interface ModelPreference {
|
|
29
|
-
model: string;
|
|
30
|
-
provider: keyof typeof Clients.clients;
|
|
31
|
-
reasoning_effort?: CompletionOptions["reasoning_effort"];
|
|
32
|
-
reasoning_summary?: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface AgentContext {
|
|
36
|
-
Tools?: ToolsService;
|
|
37
|
-
Events?: EventService;
|
|
38
|
-
messageProcessor?: MessageProcessor;
|
|
39
|
-
Clients?: AIClient;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface ToolCallEvent {
|
|
43
|
-
toolCall: ToolCall;
|
|
44
|
-
functionResp: any;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
type ChatCompletion = Parameters<GenericClient["createChatCompletion"]>[0];
|
|
48
|
-
type CachedChatCompletion = { messages: ChatCompletion["messages"] } & Partial<
|
|
49
|
-
Omit<ChatCompletion, "messages">
|
|
50
|
-
>;
|
|
51
|
-
|
|
52
|
-
interface CompletedCompaction {
|
|
53
|
-
compressionEnd: number;
|
|
54
|
-
originalPrefix: Message[];
|
|
55
|
-
replacementPrefix: Message[];
|
|
56
|
-
summary: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* A source is anything that can (a) start producing data, (b) hand each datum to
|
|
61
|
-
* a callback, and (c) be torn down. This is the unified contract behind
|
|
62
|
-
* `agent.observe()` — tool polls, events, streams, promises, peer agent threads,
|
|
63
|
-
* file tails, etc. all become adapters that satisfy this shape.
|
|
64
|
-
*/
|
|
65
|
-
export interface ObservationSource {
|
|
66
|
-
/** Human-friendly label for logs / delivered messages. */
|
|
67
|
-
label: string;
|
|
68
|
-
/**
|
|
69
|
-
* Begin producing. Call `emit(datum)` for each piece of data. Return a
|
|
70
|
-
* teardown function (or void). May be sync or async.
|
|
71
|
-
*/
|
|
72
|
-
start(
|
|
73
|
-
emit: (datum: unknown) => void
|
|
74
|
-
): void | (() => void) | Promise<void | (() => void)>;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface ObserveOpts {
|
|
78
|
-
/** Only deliver when the datum changed since the last emit. Default true. */
|
|
79
|
-
onlyOnChange?: boolean;
|
|
80
|
-
/** Max number of updates before auto-stopping. Default 20. */
|
|
81
|
-
maxUpdates?: number;
|
|
82
|
-
/** Max total duration in ms before auto-stopping. Default 10 min. */
|
|
83
|
-
maxDurationMs?: number;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
interface ActiveObservation {
|
|
87
|
-
id: string;
|
|
88
|
-
label: string;
|
|
89
|
-
updates: number;
|
|
90
|
-
teardownPromise: Promise<void | (() => void)>;
|
|
91
|
-
expiry?: NodeJS.Timeout;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
@TraceAll()
|
|
95
|
-
export abstract class BaseAgent implements IAgent {
|
|
96
|
-
abstract name: string;
|
|
97
|
-
abstract description: string;
|
|
98
|
-
|
|
99
|
-
private status = "not_started";
|
|
100
|
-
private lastHealthCheckTime: number = 0;
|
|
101
|
-
protected provider = "openai";
|
|
102
|
-
protected modelName: string = Models.openai.GPT_4o;
|
|
103
|
-
protected client: null | GenericClient = null;
|
|
104
|
-
protected modelPreferences: ModelPreference[] = [];
|
|
105
|
-
protected currentModelPreferenceIndex = 0;
|
|
106
|
-
protected easyFinalAnswer = false;
|
|
107
|
-
protected requiredToolNames = ["finalAnswer"];
|
|
108
|
-
protected maxTurns: number | null = null;
|
|
109
|
-
protected maxSpend: number | null = null;
|
|
110
|
-
protected maxRunTimeMs: number | null = null;
|
|
111
|
-
protected startTimeMs: number | null = null;
|
|
112
|
-
protected turnCount = 0;
|
|
113
|
-
protected totalCostUsd = 0;
|
|
114
|
-
protected currentThread = 0;
|
|
115
|
-
protected reasoningEffort: CompletionOptions["reasoning_effort"] | undefined =
|
|
116
|
-
undefined;
|
|
117
|
-
protected summarizeReasoning: boolean | undefined = undefined;
|
|
118
|
-
protected totalInputTokens = 0;
|
|
119
|
-
protected totalOutputTokens = 0;
|
|
120
|
-
protected totalCacheReadTokens = 0;
|
|
121
|
-
protected totalCacheWriteTokens = 0;
|
|
122
|
-
|
|
123
|
-
// The real prompt/input token count reported by the most recent completion
|
|
124
|
-
// response (input + cache read + cache write). This reflects exactly how many
|
|
125
|
-
// tokens the model consumed for the last request and is far more accurate than
|
|
126
|
-
// the whitespace-based estimate in getMessagesLength(). Used to decide when to
|
|
127
|
-
// compact. 0 means "no real usage seen yet" (fall back to the estimate).
|
|
128
|
-
protected lastPromptTokens = 0;
|
|
129
|
-
|
|
130
|
-
protected compressThreshold = 30000;
|
|
131
|
-
protected compressMinMessages = 30;
|
|
132
|
-
|
|
133
|
-
// Set when the user manually requests compaction (e.g. via /compact while
|
|
134
|
-
// attached). Forces the next loop iteration to compress the conversation
|
|
135
|
-
// regardless of whether the token threshold has been reached.
|
|
136
|
-
protected _forceCompact = false;
|
|
137
|
-
|
|
138
|
-
// Compaction is intentionally detached from the main agent loop. A failed
|
|
139
|
-
// attempt is cleared so a later turn can retry without ending the task.
|
|
140
|
-
private _compactionPromise: Promise<void> | null = null;
|
|
141
|
-
private _completedCompaction: CompletedCompaction | null = null;
|
|
142
|
-
private _compactionGeneration = 0;
|
|
143
|
-
|
|
144
|
-
// Interrupt support: resolves the currently awaited tool call or completion
|
|
145
|
-
private _interruptResolve: (() => void) | null = null;
|
|
146
|
-
// A monotonically-increasing token identifying the active interruptible window.
|
|
147
|
-
// Used to ensure a stale (slow) operation completing in the background cannot
|
|
148
|
-
// clobber a newer interruptible window's resolver.
|
|
149
|
-
private _interruptToken = 0;
|
|
150
|
-
|
|
151
|
-
protected threads = [] as Message[][];
|
|
152
|
-
|
|
153
|
-
// Message from users
|
|
154
|
-
protected pendingUserMessages = [] as Message[];
|
|
155
|
-
|
|
156
|
-
// Internal messages
|
|
157
|
-
protected pendingMessages = [] as Message[];
|
|
158
|
-
|
|
159
|
-
// Active observations (subscriptions) owned by this agent instance. Kept
|
|
160
|
-
// per-agent (not module scope) so stopObserving() on one agent can't clobber
|
|
161
|
-
// another agent's observers, and so newTask() can tear them all down.
|
|
162
|
-
protected _observations = new Map<string, ActiveObservation>();
|
|
163
|
-
private _observationSeq = 0;
|
|
164
|
-
|
|
165
|
-
protected taskBreakdown = "";
|
|
166
|
-
protected summaries = [] as string[];
|
|
167
|
-
protected currentTaskId: string | null = null;
|
|
168
|
-
/**
|
|
169
|
-
* The taskId of the parent agent that spawned this task, if any.
|
|
170
|
-
* Used by self-referential tools (e.g. replyToParent) to know who to
|
|
171
|
-
* report back to without relying solely on reading metadata.json off disk.
|
|
172
|
-
*/
|
|
173
|
-
public parentTaskId: string | null = null;
|
|
174
|
-
|
|
175
|
-
public agentEvents = new EventService();
|
|
176
|
-
public eventTypes = {
|
|
177
|
-
newThread: "new_thread",
|
|
178
|
-
threadUpdate: "thread_update",
|
|
179
|
-
costUpdate: "cost_update",
|
|
180
|
-
agentLog: "agent:log",
|
|
181
|
-
toolCall: "tool:pre_call",
|
|
182
|
-
toolUsed: "tool:post_call",
|
|
183
|
-
agentStatus: "agent:status",
|
|
184
|
-
notStarted: "not_started",
|
|
185
|
-
inProgress: "in_progress",
|
|
186
|
-
done: "done",
|
|
187
|
-
pause: "pause",
|
|
188
|
-
kill: "kill",
|
|
189
|
-
unpause: "unpause",
|
|
190
|
-
agentMsg: "agent:msg",
|
|
191
|
-
userSay: "user:say",
|
|
192
|
-
agentSay: "agent:say",
|
|
193
|
-
agentNewTask: "agent:newTask",
|
|
194
|
-
agentTaskComplete: "agent:taskComplete",
|
|
195
|
-
tokenUsage: "agent:tokenUsage",
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
public tools: ToolsService;
|
|
199
|
-
public events: EventService;
|
|
200
|
-
public messageProcessor: MessageProcessor;
|
|
201
|
-
public clientService: AIClient;
|
|
202
|
-
|
|
203
|
-
disabledTools = [];
|
|
204
|
-
|
|
205
|
-
constructor(context: AgentContext) {
|
|
206
|
-
this.tools = context.Tools;
|
|
207
|
-
this.events = context.Events;
|
|
208
|
-
this.messageProcessor = context.messageProcessor || new MessageProcessor();
|
|
209
|
-
this.clientService = context.Clients || Clients;
|
|
210
|
-
|
|
211
|
-
if (!this.tools) {
|
|
212
|
-
throw new Error("ToolsService is required for BaseAgent");
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (!this.events) {
|
|
216
|
-
throw new Error("EventService is required for BaseAgent");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Subscribe to "agent:msg" events for dynamic context loading
|
|
220
|
-
// Use setListener with a key so re-creating the agent doesn't double-subscribe
|
|
221
|
-
this.events.setListener(
|
|
222
|
-
{
|
|
223
|
-
key: `agent:msg:${this.constructor.name}`,
|
|
224
|
-
event: this.eventTypes.agentMsg,
|
|
225
|
-
},
|
|
226
|
-
(eventData: any) => {
|
|
227
|
-
if (
|
|
228
|
-
this.status === this.eventTypes.inProgress ||
|
|
229
|
-
this.status === this.eventTypes.pause
|
|
230
|
-
) {
|
|
231
|
-
const message = {
|
|
232
|
-
role: "user",
|
|
233
|
-
content: JSON.stringify(eventData),
|
|
234
|
-
} as Message;
|
|
235
|
-
this.addPendingMessage(message);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
protected log(
|
|
242
|
-
message: string,
|
|
243
|
-
level: "info" | "warn" | "error" = "info"
|
|
244
|
-
): void {
|
|
245
|
-
this.agentEvents.emit(this.eventTypes.agentLog, {
|
|
246
|
-
agentName: this.name,
|
|
247
|
-
message,
|
|
248
|
-
level,
|
|
249
|
-
timestamp: Date.now(),
|
|
250
|
-
taskId: this.currentTaskId,
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
setCompressThreshold(threshold: number) {
|
|
255
|
-
this.compressThreshold = threshold;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Request that the conversation be compacted at the next loop iteration,
|
|
260
|
-
* regardless of whether the token threshold has been reached. Combined with
|
|
261
|
-
* an interrupt(), this lets a user force compaction on an attached agent
|
|
262
|
-
* mid-task (e.g. via the `/compact` chat command) to drop back to a much
|
|
263
|
-
* cheaper per-interaction context size.
|
|
264
|
-
*/
|
|
265
|
-
requestCompact() {
|
|
266
|
-
this._forceCompact = true;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Returns the effective compress threshold for the current model.
|
|
271
|
-
* If the user has manually set a custom threshold (different from the default 30k),
|
|
272
|
-
* that value is used as-is. Otherwise, the threshold is dynamically computed as
|
|
273
|
-
* 70% of the model's context window limit (or tiered-pricing threshold), falling
|
|
274
|
-
* back to DEFAULT_CONTEXT_LIMIT. The 70% factor leaves headroom so token-count
|
|
275
|
-
* lag doesn't push a request past the model/tier limit before compression fires.
|
|
276
|
-
*/
|
|
277
|
-
getCompressThreshold(): number {
|
|
278
|
-
if (this.compressThreshold !== DEFAULT_CONTEXT_LIMIT) {
|
|
279
|
-
return this.compressThreshold;
|
|
280
|
-
}
|
|
281
|
-
const result = this.clientService.getContextLimit(
|
|
282
|
-
this.getProvider() as string,
|
|
283
|
-
this.getModel()
|
|
284
|
-
);
|
|
285
|
-
const contextLimit = result?.contextLimit ?? DEFAULT_CONTEXT_LIMIT;
|
|
286
|
-
const threshold = result?.threshold ?? contextLimit;
|
|
287
|
-
return Math.floor(threshold * 0.7);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
setMaxTurns(maxTurns: number | null) {
|
|
291
|
-
this.maxTurns = maxTurns;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
setMaxSpend(maxSpend: number | null) {
|
|
295
|
-
this.maxSpend = maxSpend;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
setMaxRunTime(maxRunTimeMs: number | null) {
|
|
299
|
-
this.maxRunTimeMs = maxRunTimeMs;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
newTask(taskId?: string) {
|
|
303
|
-
// Invalidate any detached compaction still finishing for the previous task.
|
|
304
|
-
this._compactionGeneration++;
|
|
305
|
-
this._compactionPromise = null;
|
|
306
|
-
this._completedCompaction = null;
|
|
307
|
-
this.currentThread = 0;
|
|
308
|
-
this.threads = [];
|
|
309
|
-
this.taskBreakdown = "";
|
|
310
|
-
this.summaries = [];
|
|
311
|
-
this.totalCostUsd = 0;
|
|
312
|
-
this.totalInputTokens = 0;
|
|
313
|
-
this.totalOutputTokens = 0;
|
|
314
|
-
this.totalCacheReadTokens = 0;
|
|
315
|
-
this.totalCacheWriteTokens = 0;
|
|
316
|
-
this.lastPromptTokens = 0;
|
|
317
|
-
this.status = this.eventTypes.inProgress;
|
|
318
|
-
this.turnCount = 0;
|
|
319
|
-
this.startTimeMs = Date.now();
|
|
320
|
-
this.currentTaskId = taskId || this.startTimeMs.toString();
|
|
321
|
-
|
|
322
|
-
// Reset interrupt state so a queued/stale interrupt from a prior task
|
|
323
|
-
// can't fire against the new task.
|
|
324
|
-
this._interruptResolve = null;
|
|
325
|
-
this._forceCompact = false;
|
|
326
|
-
|
|
327
|
-
// Tear down any observers from a prior task so they can't leak or inject
|
|
328
|
-
// into the new task.
|
|
329
|
-
this.stopObserving(undefined, "new task started");
|
|
330
|
-
|
|
331
|
-
// Emit event for plugin integration
|
|
332
|
-
const id = taskId || this.startTimeMs.toString();
|
|
333
|
-
this.events.emit(this.eventTypes.agentNewTask, {
|
|
334
|
-
taskId: id,
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
register() {
|
|
339
|
-
this.events.registerAgent(this);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
setModelPreferences(value: ModelPreference[]) {
|
|
343
|
-
this.modelPreferences = value;
|
|
344
|
-
if (value.length) {
|
|
345
|
-
this.updatePreferences(value[0]);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
updatePreferences(value: ModelPreference) {
|
|
350
|
-
this.setModel(value.model);
|
|
351
|
-
this.setProvider(value.provider);
|
|
352
|
-
if (value.reasoning_effort !== undefined) {
|
|
353
|
-
this.reasoningEffort = value.reasoning_effort;
|
|
354
|
-
}
|
|
355
|
-
if (value.reasoning_summary !== undefined) {
|
|
356
|
-
this.summarizeReasoning = value.reasoning_summary;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
nextModel() {
|
|
361
|
-
this.currentModelPreferenceIndex++;
|
|
362
|
-
if (this.currentModelPreferenceIndex >= this.modelPreferences.length) {
|
|
363
|
-
throw new Error("We have exhausted all model preferences.");
|
|
364
|
-
}
|
|
365
|
-
const nextModel = this.modelPreferences[this.currentModelPreferenceIndex];
|
|
366
|
-
this.updatePreferences(nextModel);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
getModel(): string {
|
|
370
|
-
return this.modelName;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
setModel(value: string) {
|
|
374
|
-
this.modelName = value;
|
|
375
|
-
this.client = null; // Reset client to force re-fetch
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
getProvider() {
|
|
379
|
-
return this.provider;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
getParentTaskId(): string | null {
|
|
383
|
-
return this.parentTaskId;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
setParentTaskId(value: string | null) {
|
|
387
|
-
this.parentTaskId = value ?? null;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
setProvider(value: keyof typeof Clients.clients) {
|
|
391
|
-
this.provider = value;
|
|
392
|
-
this.client = null; // Reset client to force re-fetch
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
setReasoningEffort(effort: CompletionOptions["reasoning_effort"]) {
|
|
396
|
-
this.reasoningEffort = effort;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
getReasoningEffort(): CompletionOptions["reasoning_effort"] {
|
|
400
|
-
return this.reasoningEffort;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
setSummarizeReasoning(value: boolean) {
|
|
404
|
-
this.summarizeReasoning = value;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
getSummarizeReasoning(): boolean | undefined {
|
|
408
|
-
return this.summarizeReasoning;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
getClient() {
|
|
412
|
-
if (!this.client) {
|
|
413
|
-
if (this.provider) {
|
|
414
|
-
this.log(`Getting client for provider ${this.provider}`);
|
|
415
|
-
const clientInfo = this.clientService.getClient(this.getProvider());
|
|
416
|
-
if (clientInfo) {
|
|
417
|
-
this.client = clientInfo.client;
|
|
418
|
-
// don't set provider or model yet
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (!this.client) {
|
|
423
|
-
this.log(`Getting client for model ${this.modelName}`);
|
|
424
|
-
const clientInfo = this.clientService.getClient(
|
|
425
|
-
undefined,
|
|
426
|
-
this.getModel()
|
|
427
|
-
);
|
|
428
|
-
|
|
429
|
-
if (clientInfo) {
|
|
430
|
-
this.client = clientInfo.client;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
return this.client;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
setClient(client: GenericClient) {
|
|
438
|
-
this.client = client;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
setEasyFinalAnswer(value: boolean) {
|
|
442
|
-
this.easyFinalAnswer = value;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* Detect if the model's response is a termination signal (e.g. "Done", "Complete", "Finished", "finalAnswer")
|
|
447
|
-
* This handles the case where an agent refuses to call finalAnswer and just says a short termination word.
|
|
448
|
-
*/
|
|
449
|
-
protected isTerminationResponse(content: string): boolean {
|
|
450
|
-
const trimmed = content.trim();
|
|
451
|
-
// Short response (≤ 3 words) that matches a termination word/phrase exactly
|
|
452
|
-
const wordCount = trimmed.split(/\s+/).filter(Boolean).length;
|
|
453
|
-
if (wordCount <= 3) {
|
|
454
|
-
const terminationPattern =
|
|
455
|
-
/^(done|complete|completed|finished|final\s*answer|task\s*complete|all\s*done|that'?s\s*(all|it)|ok(ay)?|yes)[.!]*$/i;
|
|
456
|
-
if (terminationPattern.test(trimmed)) return true;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Check if the first 1-3 words indicate task completion (for longer responses)
|
|
460
|
-
// e.g. "Task complete: ...", "All done.", "No further changes needed.", "Confirmed complete."
|
|
461
|
-
const firstWords = trimmed.split(/\s+/).slice(0, 3).join(" ");
|
|
462
|
-
const firstWordPattern =
|
|
463
|
-
/^(task\s*(complete|completed|done|finished)|all\s*done|no\s*(further|more|additional|changes|action)|confirmed?\s*(complete|done|finished|one\s*last)|nothing\s*(more|further|else)|standing\s*by|everything\s*is|still\s*confirmed|acknowledged|done\s*and|complete\s*(and|\.)|completed\s*successfully|no\s*additional|verified\s*and)/i;
|
|
464
|
-
if (firstWordPattern.test(firstWords)) return true;
|
|
465
|
-
|
|
466
|
-
// If easyFinalAnswer mode is on, also match response starting with "✅" or numbered confirmation lists
|
|
467
|
-
if (this.easyFinalAnswer) {
|
|
468
|
-
if (trimmed.startsWith("✅") || /^[\d\.\-\*]/.test(trimmed)) return true;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// Detect JSON-wrapped finalAnswer output, e.g. {"answer":"..."} or {"finalAnswer":"..."}
|
|
472
|
-
try {
|
|
473
|
-
const parsed = JSON.parse(trimmed);
|
|
474
|
-
if (parsed && typeof parsed === "object") {
|
|
475
|
-
if (typeof parsed.answer === "string") return true;
|
|
476
|
-
if (typeof parsed.finalAnswer === "string") return true;
|
|
477
|
-
}
|
|
478
|
-
} catch (_) {}
|
|
479
|
-
|
|
480
|
-
return false;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
getEnabledTools() {
|
|
484
|
-
return this.tools
|
|
485
|
-
.getTools()
|
|
486
|
-
.filter((t) => !this.disabledTools.includes(t.function.name));
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
getEnabledToolNames() {
|
|
490
|
-
return this.getEnabledTools().map((t) => t.function.name);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/** Restore the exact request-visible tool list persisted for a prior run. */
|
|
494
|
-
restoreEnabledTools(names: string[]) {
|
|
495
|
-
const uniqueNames = [...new Set(names)];
|
|
496
|
-
const lazyTools = this.tools as ToolsService & {
|
|
497
|
-
enableTools?: (patterns: string[]) => unknown;
|
|
498
|
-
restoreEnabledTools?: (names: string[]) => void;
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
// LazyToolsService only exposes explicitly enabled tools. Materialize the
|
|
502
|
-
// saved concrete names before applying the agent-level exact-set filter.
|
|
503
|
-
if (lazyTools.restoreEnabledTools) {
|
|
504
|
-
lazyTools.restoreEnabledTools(uniqueNames);
|
|
505
|
-
} else {
|
|
506
|
-
lazyTools.enableTools?.(uniqueNames);
|
|
507
|
-
}
|
|
508
|
-
const savedNames = new Set(uniqueNames);
|
|
509
|
-
this.disabledTools = this.tools
|
|
510
|
-
.getTools()
|
|
511
|
-
.map((tool) => tool.function.name)
|
|
512
|
-
.filter((name) => !savedNames.has(name));
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
disableTool(toolName: string) {
|
|
516
|
-
this.disabledTools.push(toolName);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
isToolEnabled(toolName: string) {
|
|
520
|
-
return !!this.getEnabledTools().find((t) => t.function.name === toolName);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
enableTool(toolName: string) {
|
|
524
|
-
if (!this.isToolEnabled(toolName)) {
|
|
525
|
-
this.disabledTools = this.disabledTools.filter((t) => t !== toolName);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
private checkLimits(): boolean {
|
|
530
|
-
// Check turn limit
|
|
531
|
-
if (this.maxTurns !== null && this.turnCount >= this.maxTurns) {
|
|
532
|
-
this.log(
|
|
533
|
-
`Turn limit reached: ${this.turnCount}/${this.maxTurns}`,
|
|
534
|
-
"warn"
|
|
535
|
-
);
|
|
536
|
-
return true;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// Check spend limit
|
|
540
|
-
if (this.maxSpend !== null && this.totalCostUsd >= this.maxSpend) {
|
|
541
|
-
this.log(
|
|
542
|
-
`Spend limit reached: $${this.totalCostUsd.toFixed(
|
|
543
|
-
4
|
|
544
|
-
)}/$${this.maxSpend.toFixed(4)}`,
|
|
545
|
-
"warn"
|
|
546
|
-
);
|
|
547
|
-
return true;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// Check runtime limit
|
|
551
|
-
if (this.maxRunTimeMs !== null && this.startTimeMs !== null) {
|
|
552
|
-
const currentRunTimeMs = this.runTime();
|
|
553
|
-
if (currentRunTimeMs >= this.maxRunTimeMs) {
|
|
554
|
-
this.log(
|
|
555
|
-
`Runtime limit reached: ${currentRunTimeMs}ms/${this.maxRunTimeMs}ms`,
|
|
556
|
-
"warn"
|
|
557
|
-
);
|
|
558
|
-
return true;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
return false;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
public runTime() {
|
|
566
|
-
if (this.startTimeMs) {
|
|
567
|
-
return Date.now() - this.startTimeMs;
|
|
568
|
-
}
|
|
569
|
-
return 0;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
private shouldTerminateFromLimits(): boolean {
|
|
573
|
-
return this.checkLimits();
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
getTurnCount(): number {
|
|
577
|
-
return this.turnCount;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
adjustTotalCostUsd(cost: number) {
|
|
581
|
-
if (cost) {
|
|
582
|
-
this.totalCostUsd += cost;
|
|
583
|
-
this.agentEvents.emit(this.eventTypes.costUpdate, this.totalCostUsd);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
getTotalCostUsd() {
|
|
588
|
-
return this.totalCostUsd;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
adjustTokenUsage(
|
|
592
|
-
usage: any,
|
|
593
|
-
messages?: Message[],
|
|
594
|
-
updateLastPromptTokens = true
|
|
595
|
-
) {
|
|
596
|
-
if (!usage) return;
|
|
597
|
-
// Support both OpenAI-style (prompt_tokens/completion_tokens) and Anthropic-style (input_tokens/output_tokens)
|
|
598
|
-
const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? 0;
|
|
599
|
-
const outputTokens = usage.output_tokens ?? usage.completion_tokens ?? 0;
|
|
600
|
-
|
|
601
|
-
const cacheReadTokens =
|
|
602
|
-
usage.cache_read_input_tokens ??
|
|
603
|
-
usage.cache_read_tokens ??
|
|
604
|
-
usage.prompt_tokens_details?.cached_tokens ??
|
|
605
|
-
0;
|
|
606
|
-
const cacheWriteTokens =
|
|
607
|
-
usage.cache_creation_input_tokens ?? usage.cache_write_tokens ?? 0;
|
|
608
|
-
|
|
609
|
-
// Record the real prompt token count for this single completion (not the
|
|
610
|
-
// running total). This represents the full context the model processed for
|
|
611
|
-
// the request: fresh input + cache-read + cache-write tokens. It is used by
|
|
612
|
-
// the compaction check as an accurate replacement for the whitespace-based
|
|
613
|
-
// getMessagesLength() estimate. Only track it for "prompt-shaped" usage
|
|
614
|
-
// (i.e. when there are input tokens) so a usage-less interrupt stub doesn't
|
|
615
|
-
// reset it to 0.
|
|
616
|
-
const promptTokens = inputTokens + cacheReadTokens + cacheWriteTokens;
|
|
617
|
-
if (updateLastPromptTokens && promptTokens > 0) {
|
|
618
|
-
this.lastPromptTokens = promptTokens;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
this.totalInputTokens += inputTokens;
|
|
622
|
-
this.totalOutputTokens += outputTokens;
|
|
623
|
-
this.totalCacheReadTokens += cacheReadTokens;
|
|
624
|
-
this.totalCacheWriteTokens += cacheWriteTokens;
|
|
625
|
-
|
|
626
|
-
this.agentEvents.emit(this.eventTypes.tokenUsage, {
|
|
627
|
-
timestamp: new Date().toISOString(),
|
|
628
|
-
inputTokens,
|
|
629
|
-
outputTokens,
|
|
630
|
-
cacheReadTokens,
|
|
631
|
-
cacheWriteTokens,
|
|
632
|
-
totalInputTokens: this.totalInputTokens,
|
|
633
|
-
totalOutputTokens: this.totalOutputTokens,
|
|
634
|
-
totalCacheReadTokens: this.totalCacheReadTokens,
|
|
635
|
-
totalCacheWriteTokens: this.totalCacheWriteTokens,
|
|
636
|
-
messages,
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
getTokenUsage() {
|
|
641
|
-
return {
|
|
642
|
-
totalInputTokens: this.totalInputTokens,
|
|
643
|
-
totalOutputTokens: this.totalOutputTokens,
|
|
644
|
-
totalCacheReadTokens: this.totalCacheReadTokens,
|
|
645
|
-
totalCacheWriteTokens: this.totalCacheWriteTokens,
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
/**
|
|
650
|
-
* Centralized helper for making an AI completion call while ensuring
|
|
651
|
-
* cost (totalCostUsd) and token usage (input/output/cache) are always
|
|
652
|
-
* updated consistently, regardless of which part of the agent triggers
|
|
653
|
-
* the completion (main call loop, task breakdown, compression, etc).
|
|
654
|
-
*
|
|
655
|
-
* Optionally supports being interrupted via makeInterruptible by passing
|
|
656
|
-
* an `interruptValue` in options - if the operation is interrupted, the
|
|
657
|
-
* interrupt value is returned and used for cost/usage tracking (which,
|
|
658
|
-
* for a typical interrupt stub with no usage/cost, is effectively a
|
|
659
|
-
* no-op).
|
|
660
|
-
*/
|
|
661
|
-
protected async createAgentCompletion(
|
|
662
|
-
params: CachedChatCompletion,
|
|
663
|
-
options: {
|
|
664
|
-
interruptValue?: CompletionResponse;
|
|
665
|
-
updateLastPromptTokens?: boolean;
|
|
666
|
-
} = {}
|
|
667
|
-
): Promise<CompletionResponse> {
|
|
668
|
-
const { interruptValue, updateLastPromptTokens = true } = options;
|
|
669
|
-
|
|
670
|
-
// If you change the tools, or any of this, you bust cache
|
|
671
|
-
// only safe to change messages
|
|
672
|
-
const defaultedParams: ChatCompletion = {
|
|
673
|
-
model: this.getModel(),
|
|
674
|
-
tools: this.getEnabledTools(),
|
|
675
|
-
tool_choice: "auto",
|
|
676
|
-
long_ttl_cache: this.runTime() > 300_000,
|
|
677
|
-
...(this.reasoningEffort !== undefined && {
|
|
678
|
-
reasoning_effort: this.reasoningEffort,
|
|
679
|
-
}),
|
|
680
|
-
...(this.summarizeReasoning !== undefined && {
|
|
681
|
-
reasoning_summary: this.summarizeReasoning,
|
|
682
|
-
}),
|
|
683
|
-
|
|
684
|
-
...params,
|
|
685
|
-
};
|
|
686
|
-
|
|
687
|
-
const callPromise = this.getClient().createChatCompletion(defaultedParams);
|
|
688
|
-
const response = interruptValue
|
|
689
|
-
? await this.makeInterruptible(callPromise, interruptValue)
|
|
690
|
-
: await callPromise;
|
|
691
|
-
|
|
692
|
-
this.adjustTotalCostUsd(response?.usd_cost);
|
|
693
|
-
this.adjustTokenUsage(
|
|
694
|
-
response?.usage,
|
|
695
|
-
params.messages,
|
|
696
|
-
updateLastPromptTokens
|
|
697
|
-
);
|
|
698
|
-
|
|
699
|
-
return response;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
startNewThread(messages: Message[]) {
|
|
703
|
-
this.currentThread++;
|
|
704
|
-
this.agentEvents.emit(this.eventTypes.newThread, messages);
|
|
705
|
-
this.updateCurrentThread(messages);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
updateCurrentThread(messages: Message[]) {
|
|
709
|
-
this.threads[this.currentThread] = messages;
|
|
710
|
-
this.agentEvents.emit(this.eventTypes.threadUpdate, messages);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
getThreads() {
|
|
714
|
-
return this.threads;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
getSummaries() {
|
|
718
|
-
return this.summaries;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
abstract getInitialMessages(
|
|
722
|
-
userInput: string | MessageContent[]
|
|
723
|
-
): Promise<Message[]>;
|
|
724
|
-
|
|
725
|
-
async processToolMessages(toolCall: ToolCall) {
|
|
726
|
-
this.agentEvents.emit(this.eventTypes.toolCall, { toolCall });
|
|
727
|
-
|
|
728
|
-
const interruptMsg = `User interrupted this tool call (${toolCall.function?.name})`;
|
|
729
|
-
const interruptResult: Awaited<ReturnType<typeof this.tools.callTool>> = {
|
|
730
|
-
functionResp: interruptMsg,
|
|
731
|
-
toolCallId: toolCall.id,
|
|
732
|
-
functionName: toolCall.function?.name,
|
|
733
|
-
functionArgs: {},
|
|
734
|
-
toolMessages: [
|
|
735
|
-
{
|
|
736
|
-
role: "tool",
|
|
737
|
-
tool_call_id: toolCall.id,
|
|
738
|
-
name: toolCall.function?.name,
|
|
739
|
-
content: interruptMsg,
|
|
740
|
-
},
|
|
741
|
-
],
|
|
742
|
-
};
|
|
743
|
-
|
|
744
|
-
const { functionResp, toolMessages } = await this.makeInterruptible(
|
|
745
|
-
// Pass this agent in as the per-call context (`_ctx`) so tools can be
|
|
746
|
-
// self-referential: they can read `_ctx.caller` (this agent) and
|
|
747
|
-
// `_ctx.taskId` to, e.g., look up the current task, send pending
|
|
748
|
-
// messages, or subscribe to the caller's events.
|
|
749
|
-
this.tools.callTool(toolCall, this.getEnabledToolNames(), {
|
|
750
|
-
caller: this,
|
|
751
|
-
taskId: this.currentTaskId,
|
|
752
|
-
parentTaskId: this.parentTaskId ?? undefined,
|
|
753
|
-
}),
|
|
754
|
-
interruptResult
|
|
755
|
-
);
|
|
756
|
-
|
|
757
|
-
this.agentEvents.emit(this.eventTypes.toolUsed, {
|
|
758
|
-
toolCall,
|
|
759
|
-
functionResp,
|
|
760
|
-
});
|
|
761
|
-
|
|
762
|
-
return toolMessages;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
logMessages(messages: Message[]) {
|
|
766
|
-
for (const message of messages) {
|
|
767
|
-
// Surface the model's reasoning summary (what it's "thinking") when the
|
|
768
|
-
// provider exposes one, so it appears alongside the assistant's output.
|
|
769
|
-
const reasoningSummary = (message as any).reasoning_summary;
|
|
770
|
-
if (message.role === "assistant" && reasoningSummary) {
|
|
771
|
-
this.agentEvents.emit(this.eventTypes.agentSay, {
|
|
772
|
-
message: `💭 ${reasoningSummary}`,
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
|
-
if (message.role === "assistant" && message.content) {
|
|
776
|
-
this.agentEvents.emit(this.eventTypes.agentSay, {
|
|
777
|
-
message: message.content,
|
|
778
|
-
});
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
formatInputContent(userInput: string) {
|
|
784
|
-
return replaceEscapedNewLines(userInput);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
formatAiResponse(response: string) {
|
|
788
|
-
return restoreEscapedNewLines(response);
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
formatInputMessages(messages: Message[]) {
|
|
792
|
-
return messages.map((m) => ({
|
|
793
|
-
...m,
|
|
794
|
-
content:
|
|
795
|
-
typeof m.content === "string"
|
|
796
|
-
? this.formatInputContent(m.content)
|
|
797
|
-
: m.content,
|
|
798
|
-
})) as Message[];
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
formatOutputMessages(messages: Message[]) {
|
|
802
|
-
return messages.map((m) => ({
|
|
803
|
-
...m,
|
|
804
|
-
content:
|
|
805
|
-
typeof m.content === "string"
|
|
806
|
-
? this.formatAiResponse(m.content)
|
|
807
|
-
: m.content,
|
|
808
|
-
})) as Message[];
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
async healthCheck() {
|
|
812
|
-
try {
|
|
813
|
-
const canCallProvider = await this.getClient().createChatCompletion({
|
|
814
|
-
messages: [{ role: "user", content: "Hello!" }],
|
|
815
|
-
model: this.getModel(),
|
|
816
|
-
max_tokens: 2,
|
|
817
|
-
});
|
|
818
|
-
return true;
|
|
819
|
-
} catch (e) {
|
|
820
|
-
this.log(String(e), "error");
|
|
821
|
-
return false;
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
async selectHealthyModel() {
|
|
826
|
-
const currentTime = Date.now();
|
|
827
|
-
if (currentTime - this.lastHealthCheckTime < 60 * 1000) {
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
let healthy = await this.healthCheck();
|
|
832
|
-
this.lastHealthCheckTime = Date.now();
|
|
833
|
-
while (!healthy) {
|
|
834
|
-
this.nextModel();
|
|
835
|
-
healthy = await this.healthCheck();
|
|
836
|
-
}
|
|
837
|
-
await this.healthCheck();
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
isRequiredToolMissing() {
|
|
841
|
-
const requiredToolAvailable = this.getEnabledToolNames().some(
|
|
842
|
-
(enabled) =>
|
|
843
|
-
this.requiredToolNames.includes(enabled) ||
|
|
844
|
-
this.requiredToolNames.includes(mcpToolName(enabled)) ||
|
|
845
|
-
this.requiredToolNames.some((required) => enabled.endsWith(required))
|
|
846
|
-
);
|
|
847
|
-
|
|
848
|
-
if (requiredToolAvailable) {
|
|
849
|
-
return false;
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
this.log(
|
|
853
|
-
`Required tool: [${
|
|
854
|
-
this.requiredToolNames
|
|
855
|
-
}] not available, checking for finalAnswer. Enabled: ${this.getEnabledToolNames().join(
|
|
856
|
-
", "
|
|
857
|
-
)}`
|
|
858
|
-
);
|
|
859
|
-
|
|
860
|
-
// Otherwise we're missing the required tool, lets use finalAnswer if we have it
|
|
861
|
-
const finalAnswer = "finalAnswer";
|
|
862
|
-
const requiredFinalAnswer = this.requiredToolNames.includes(finalAnswer);
|
|
863
|
-
const hasFinalAnswer = this.getEnabledToolNames().includes(finalAnswer);
|
|
864
|
-
|
|
865
|
-
// We have the final answer tool, but it wasn't required
|
|
866
|
-
if (hasFinalAnswer && !requiredFinalAnswer) {
|
|
867
|
-
this.log(
|
|
868
|
-
"Required tool not available, setting finalAnswer as required tool",
|
|
869
|
-
"warn"
|
|
870
|
-
);
|
|
871
|
-
this.requiredToolNames.push("finalAnswer");
|
|
872
|
-
return false;
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
return true;
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
setNotHealthy() {
|
|
879
|
-
this.lastHealthCheckTime = 0;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
pause() {
|
|
883
|
-
this.log("Pausing agent");
|
|
884
|
-
this.agentEvents.emit(this.eventTypes.pause, this);
|
|
885
|
-
this.status = this.eventTypes.pause;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
unpause() {
|
|
889
|
-
this.log("Unpausing agent");
|
|
890
|
-
this.agentEvents.emit(this.eventTypes.unpause, this);
|
|
891
|
-
this.status = this.eventTypes.inProgress;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
async unpaused() {
|
|
895
|
-
return new Promise((resolve) => {
|
|
896
|
-
this.log("Waiting for agent to unpause");
|
|
897
|
-
this.agentEvents.once(this.eventTypes.unpause, () => {
|
|
898
|
-
this.log("Agent resumed");
|
|
899
|
-
resolve(true);
|
|
900
|
-
});
|
|
901
|
-
this.agentEvents.once(this.eventTypes.done, () => {
|
|
902
|
-
resolve(true);
|
|
903
|
-
});
|
|
904
|
-
});
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
async kill() {
|
|
908
|
-
this.log("Killing agent");
|
|
909
|
-
if (
|
|
910
|
-
this.status === this.eventTypes.kill ||
|
|
911
|
-
this.status === this.eventTypes.done
|
|
912
|
-
) {
|
|
913
|
-
this.log(
|
|
914
|
-
"Agent is already being killed or done, ignoring duplicate kill()",
|
|
915
|
-
"warn"
|
|
916
|
-
);
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
this.agentEvents.emit(this.eventTypes.kill, this);
|
|
920
|
-
this.status = this.eventTypes.kill;
|
|
921
|
-
|
|
922
|
-
this.addPendingMessage({
|
|
923
|
-
role: "user",
|
|
924
|
-
content: `<Workflow>The user has requested the task to end, please call ${this.requiredToolNames} with a report of your ending state</Workflow>`,
|
|
925
|
-
} as Message);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
/**
|
|
929
|
-
* Wrap a promise so it can be interrupted via interrupt().
|
|
930
|
-
* If interrupt() is called while waiting, the promise resolves with the
|
|
931
|
-
* interrupt message instead of waiting for the original operation to complete.
|
|
932
|
-
*/
|
|
933
|
-
protected makeInterruptible<T>(
|
|
934
|
-
promise: Promise<T>,
|
|
935
|
-
interruptValue: T
|
|
936
|
-
): Promise<T> {
|
|
937
|
-
return new Promise<T>((resolve, reject) => {
|
|
938
|
-
// Each interruptible window gets a unique token. `settled` guards this
|
|
939
|
-
// specific window so it can only ever resolve/reject once, and the token
|
|
940
|
-
// ensures a stale (slow) background operation that finishes AFTER this
|
|
941
|
-
// window was interrupted cannot clobber a NEWER window's resolver.
|
|
942
|
-
const myToken = ++this._interruptToken;
|
|
943
|
-
let settled = false;
|
|
944
|
-
|
|
945
|
-
const finish = (fn: () => void) => {
|
|
946
|
-
if (settled) return;
|
|
947
|
-
settled = true;
|
|
948
|
-
// Only clear the shared resolver if it still belongs to this window.
|
|
949
|
-
if (this._interruptToken === myToken) {
|
|
950
|
-
this._interruptResolve = null;
|
|
951
|
-
}
|
|
952
|
-
fn();
|
|
953
|
-
};
|
|
954
|
-
|
|
955
|
-
this._interruptResolve = () => finish(() => resolve(interruptValue));
|
|
956
|
-
|
|
957
|
-
promise
|
|
958
|
-
.then((result) => {
|
|
959
|
-
// If this window was already interrupted/settled, drop the stale
|
|
960
|
-
// result — do NOT touch _interruptResolve (it may belong to a newer
|
|
961
|
-
// window now).
|
|
962
|
-
finish(() => resolve(result));
|
|
963
|
-
})
|
|
964
|
-
.catch((err) => {
|
|
965
|
-
// If the window was already interrupted, swallow the late error.
|
|
966
|
-
if (settled) return;
|
|
967
|
-
finish(() => reject(err));
|
|
968
|
-
});
|
|
969
|
-
});
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
/**
|
|
973
|
-
* Interrupt the currently awaited tool call or AI completion.
|
|
974
|
-
* The waiting promise will resolve immediately with an interrupt message,
|
|
975
|
-
* allowing the agent to continue its loop with the interruption as context.
|
|
976
|
-
*/
|
|
977
|
-
interrupt(message = "User interrupted this action you were waiting on") {
|
|
978
|
-
this.log(`Interrupting current operation: ${message}`);
|
|
979
|
-
|
|
980
|
-
// If the user supplied a real message with the poke (not the default
|
|
981
|
-
// interrupt reason), queue it as a pending user message so the agent
|
|
982
|
-
// actually sees what they said on its next step.
|
|
983
|
-
if (
|
|
984
|
-
message &&
|
|
985
|
-
message !== "User interrupted this action you were waiting on"
|
|
986
|
-
) {
|
|
987
|
-
this.addPendingUserMessage({ role: "user", content: message });
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
if (this._interruptResolve) {
|
|
991
|
-
this._interruptResolve();
|
|
992
|
-
} else {
|
|
993
|
-
// No active interruptible window right now (we're between the AI
|
|
994
|
-
// completion and the tool call, or in message processing). There is
|
|
995
|
-
// nothing to interrupt, so we do NOT queue it — a queued interrupt
|
|
996
|
-
// would pre-empt the next tool call and drop its result. Any message
|
|
997
|
-
// supplied with the poke was already added as a pending user message
|
|
998
|
-
// above, so the agent will still see it on its next step.
|
|
999
|
-
this.log("No active interruptible operation to interrupt", "warn");
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
async call(
|
|
1004
|
-
userInput: string | MessageContent[],
|
|
1005
|
-
_messages?: Message[],
|
|
1006
|
-
retryCount = 0
|
|
1007
|
-
) {
|
|
1008
|
-
if (this.status === this.eventTypes.notStarted) {
|
|
1009
|
-
this.status = this.eventTypes.inProgress;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
if (this.status === this.eventTypes.pause) {
|
|
1013
|
-
await this.unpaused();
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
await this.selectHealthyModel();
|
|
1017
|
-
|
|
1018
|
-
// Increment turn count and check limits (only for new calls, not recursive ones)
|
|
1019
|
-
this.turnCount++;
|
|
1020
|
-
if (this.shouldTerminateFromLimits()) {
|
|
1021
|
-
const currentRunTimeMs = this.startTimeMs
|
|
1022
|
-
? Date.now() - this.startTimeMs
|
|
1023
|
-
: 0;
|
|
1024
|
-
const limitMsg = `Task terminated due to limits reached. Turn: ${
|
|
1025
|
-
this.turnCount
|
|
1026
|
-
}/${this.maxTurns || "unlimited"}, Cost: $${this.totalCostUsd.toFixed(
|
|
1027
|
-
4
|
|
1028
|
-
)}/${
|
|
1029
|
-
this.maxSpend ? "$" + this.maxSpend.toFixed(4) : "unlimited"
|
|
1030
|
-
}, Runtime: ${currentRunTimeMs}ms/${
|
|
1031
|
-
this.maxRunTimeMs ? this.maxRunTimeMs + "ms" : "unlimited"
|
|
1032
|
-
}`;
|
|
1033
|
-
this.status = this.eventTypes.done;
|
|
1034
|
-
this.agentEvents.emit(this.eventTypes.done, limitMsg);
|
|
1035
|
-
return limitMsg;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
try {
|
|
1039
|
-
const model = this.getModel();
|
|
1040
|
-
let messages = _messages || (await this.getInitialMessages(userInput));
|
|
1041
|
-
|
|
1042
|
-
// Process initial messages if this is the first call
|
|
1043
|
-
if (!_messages) {
|
|
1044
|
-
messages = await this.messageProcessor.processMessages(
|
|
1045
|
-
messages,
|
|
1046
|
-
"initial_call"
|
|
1047
|
-
);
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
if (this.pendingUserMessages.length) {
|
|
1051
|
-
messages.push(...this.pendingUserMessages);
|
|
1052
|
-
this.pendingUserMessages = [];
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
if (this.pendingMessages.length) {
|
|
1056
|
-
messages.push(...this.pendingMessages);
|
|
1057
|
-
this.pendingMessages = [];
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
messages = this.formatInputMessages(messages);
|
|
1061
|
-
this.updateCurrentThread(messages);
|
|
1062
|
-
const isMissingTool = this.isRequiredToolMissing();
|
|
1063
|
-
|
|
1064
|
-
// Process messages before each AI call
|
|
1065
|
-
messages = await this.messageProcessor.processMessages(
|
|
1066
|
-
messages,
|
|
1067
|
-
"pre_call"
|
|
1068
|
-
);
|
|
1069
|
-
|
|
1070
|
-
const interruptResponse: CompletionResponse = {
|
|
1071
|
-
choices: [
|
|
1072
|
-
{
|
|
1073
|
-
message: {
|
|
1074
|
-
role: "assistant",
|
|
1075
|
-
content: "User interrupted this AI completion. Please continue.",
|
|
1076
|
-
},
|
|
1077
|
-
},
|
|
1078
|
-
],
|
|
1079
|
-
model,
|
|
1080
|
-
usage: undefined,
|
|
1081
|
-
usd_cost: 0,
|
|
1082
|
-
};
|
|
1083
|
-
|
|
1084
|
-
const response = await this.createAgentCompletion(
|
|
1085
|
-
{
|
|
1086
|
-
model,
|
|
1087
|
-
messages,
|
|
1088
|
-
tools: this.getEnabledTools(),
|
|
1089
|
-
tool_choice: "auto",
|
|
1090
|
-
long_ttl_cache: this.runTime() > 300_000,
|
|
1091
|
-
...(this.reasoningEffort !== undefined && {
|
|
1092
|
-
reasoning_effort: this.reasoningEffort,
|
|
1093
|
-
}),
|
|
1094
|
-
...(this.summarizeReasoning !== undefined && {
|
|
1095
|
-
reasoning_summary: this.summarizeReasoning,
|
|
1096
|
-
}),
|
|
1097
|
-
},
|
|
1098
|
-
{ interruptValue: interruptResponse }
|
|
1099
|
-
);
|
|
1100
|
-
|
|
1101
|
-
// If the agent was paused while the completion was in-flight, wait here
|
|
1102
|
-
// before processing tool calls. This allows the user to send messages
|
|
1103
|
-
// (via addPendingUserMessage) and prevents the agent from proceeding to
|
|
1104
|
-
// tool calls (e.g. finalAnswer) without seeing those interactions.
|
|
1105
|
-
if (this.status === this.eventTypes.pause) {
|
|
1106
|
-
this.log(
|
|
1107
|
-
"Agent was paused after completion, waiting before processing tool calls"
|
|
1108
|
-
);
|
|
1109
|
-
await this.unpaused();
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
if (response?.usd_cost === undefined) {
|
|
1113
|
-
this.log(
|
|
1114
|
-
`Response cost is undefined: ${JSON.stringify(response, null, 2)}`,
|
|
1115
|
-
"warn"
|
|
1116
|
-
);
|
|
1117
|
-
const error = response as any;
|
|
1118
|
-
if (error != null && "response" in error && "data" in error.response) {
|
|
1119
|
-
this.log(
|
|
1120
|
-
`Response data: ${JSON.stringify(error.response.data, null, 2)}`,
|
|
1121
|
-
"warn"
|
|
1122
|
-
);
|
|
1123
|
-
}
|
|
1124
|
-
if (!response?.choices) {
|
|
1125
|
-
const errMsg =
|
|
1126
|
-
(error?.error?.message ?? error?.message) ||
|
|
1127
|
-
JSON.stringify(response);
|
|
1128
|
-
throw new Error(`AI response error: ${errMsg}`);
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
this.log("agent response cost: " + response?.usd_cost);
|
|
1133
|
-
|
|
1134
|
-
// Typically, there's only one choice in the array, but you could have many
|
|
1135
|
-
// If you set `n` to more than 1, you will get multiple choices
|
|
1136
|
-
// Collect all tool calls across all choices up front.
|
|
1137
|
-
// This is used by detectTruncatedToolCalls so that the size heuristic
|
|
1138
|
-
// compares the full completion_tokens against ALL argument content,
|
|
1139
|
-
// rather than just the current choice's single tool call (Anthropic maps
|
|
1140
|
-
// each parallel tool_use block to a separate choice).
|
|
1141
|
-
const allResponseToolCalls = response.choices.flatMap(
|
|
1142
|
-
(c) => c.message.tool_calls ?? []
|
|
1143
|
-
);
|
|
1144
|
-
for (const choice of response.choices) {
|
|
1145
|
-
messages.push(choice.message);
|
|
1146
|
-
|
|
1147
|
-
messages = await this.messageProcessor.processMessages(
|
|
1148
|
-
messages,
|
|
1149
|
-
"post_call"
|
|
1150
|
-
);
|
|
1151
|
-
|
|
1152
|
-
const lastMessage = messages[messages.length - 1];
|
|
1153
|
-
|
|
1154
|
-
this.logMessages([lastMessage]);
|
|
1155
|
-
|
|
1156
|
-
if (lastMessage.tool_calls) {
|
|
1157
|
-
// About to call a tool, process the messages
|
|
1158
|
-
// We could add all the tool calls, and do this once
|
|
1159
|
-
messages = await this.messageProcessor.processMessages(
|
|
1160
|
-
messages,
|
|
1161
|
-
"pre_tools"
|
|
1162
|
-
);
|
|
1163
|
-
|
|
1164
|
-
// Re-derive toolCalls from the freshly processed messages, since
|
|
1165
|
-
// pre_tools processors (e.g. MinimalToolsMessageProcessor) may have
|
|
1166
|
-
// rewritten the tool_calls on a deep-copied message array.
|
|
1167
|
-
const toolCalls = messages[messages.length - 1].tool_calls;
|
|
1168
|
-
|
|
1169
|
-
this.updateCurrentThread(messages);
|
|
1170
|
-
|
|
1171
|
-
const truncationWarning = this.detectTruncatedToolCalls(
|
|
1172
|
-
toolCalls,
|
|
1173
|
-
response,
|
|
1174
|
-
allResponseToolCalls
|
|
1175
|
-
);
|
|
1176
|
-
if (truncationWarning) {
|
|
1177
|
-
messages.push(truncationWarning as Message);
|
|
1178
|
-
this.updateCurrentThread(messages);
|
|
1179
|
-
return this.call(userInput, messages);
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
for (const toolCall of toolCalls) {
|
|
1183
|
-
if (this.status === this.eventTypes.pause) {
|
|
1184
|
-
this.log(
|
|
1185
|
-
"Agent was paused before tool call, waiting before processing tool calls"
|
|
1186
|
-
);
|
|
1187
|
-
await this.unpaused();
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
const toolMessages = await this.processToolMessages(toolCall);
|
|
1191
|
-
// Add the tool responses to the thread
|
|
1192
|
-
messages.push(...(toolMessages as Message[]));
|
|
1193
|
-
|
|
1194
|
-
const finalMessage = toolMessages.find(
|
|
1195
|
-
(called) =>
|
|
1196
|
-
this.requiredToolNames.includes(called.name) ||
|
|
1197
|
-
this.requiredToolNames.includes(mcpToolName(called.name)) ||
|
|
1198
|
-
this.requiredToolNames.some((required) =>
|
|
1199
|
-
called.name.endsWith(required)
|
|
1200
|
-
)
|
|
1201
|
-
);
|
|
1202
|
-
|
|
1203
|
-
if (finalMessage) {
|
|
1204
|
-
// If user added pending messages after finalAnswer was called,
|
|
1205
|
-
// continue running to respond to that feedback instead of returning
|
|
1206
|
-
if (this.pendingUserMessages.length > 0) {
|
|
1207
|
-
this.log(
|
|
1208
|
-
"finalAnswer called but pending user messages exist, continuing to respond to feedback"
|
|
1209
|
-
);
|
|
1210
|
-
messages.push(...this.pendingUserMessages);
|
|
1211
|
-
this.pendingUserMessages = [];
|
|
1212
|
-
this.updateCurrentThread(messages);
|
|
1213
|
-
return this.call(userInput, messages);
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
// Emit task completion event for plugins (like GitPlugin)
|
|
1217
|
-
this.events.emit(this.eventTypes.agentTaskComplete, {
|
|
1218
|
-
taskId:
|
|
1219
|
-
this.currentTaskId ||
|
|
1220
|
-
this.startTimeMs?.toString() ||
|
|
1221
|
-
Date.now().toString(),
|
|
1222
|
-
result: finalMessage.content || "Done",
|
|
1223
|
-
});
|
|
1224
|
-
const doneMsg = finalMessage.content || "Done";
|
|
1225
|
-
|
|
1226
|
-
// Ensure the final thread state (including the finalAnswer result) is
|
|
1227
|
-
// captured before emitting done, so syncers see the complete thread.
|
|
1228
|
-
this.updateCurrentThread(messages);
|
|
1229
|
-
this.agentEvents.emit(this.eventTypes.done, doneMsg);
|
|
1230
|
-
this.status = this.eventTypes.done;
|
|
1231
|
-
return doneMsg;
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
const newToolCalls = response.choices.flatMap(
|
|
1238
|
-
(c) => c.message.tool_calls
|
|
1239
|
-
);
|
|
1240
|
-
// Process messages after tool execution
|
|
1241
|
-
if (newToolCalls && newToolCalls.length > 0) {
|
|
1242
|
-
messages = await this.messageProcessor.processMessages(
|
|
1243
|
-
messages,
|
|
1244
|
-
"post_tools"
|
|
1245
|
-
);
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
// Early exit: not required to call tool
|
|
1249
|
-
const firstMessage = response.choices[0].message;
|
|
1250
|
-
// Auto-detect termination words: if the model is just saying "Done", "Complete", etc.
|
|
1251
|
-
if (
|
|
1252
|
-
response.choices.length === 1 &&
|
|
1253
|
-
firstMessage.content &&
|
|
1254
|
-
this.isTerminationResponse(firstMessage.content)
|
|
1255
|
-
) {
|
|
1256
|
-
this.log(
|
|
1257
|
-
`Termination word detected: "${firstMessage.content.trim()}", treating as finalAnswer`
|
|
1258
|
-
);
|
|
1259
|
-
this.status = this.eventTypes.done;
|
|
1260
|
-
this.agentEvents.emit(this.eventTypes.done, firstMessage.content);
|
|
1261
|
-
return firstMessage.content;
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
if (
|
|
1265
|
-
response.choices.length === 1 &&
|
|
1266
|
-
firstMessage.content &&
|
|
1267
|
-
this.easyFinalAnswer
|
|
1268
|
-
) {
|
|
1269
|
-
this.status = this.eventTypes.done;
|
|
1270
|
-
this.agentEvents.emit(this.eventTypes.done, firstMessage.content);
|
|
1271
|
-
return firstMessage.content;
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
// infinite loop if we cannot exit
|
|
1275
|
-
if (isMissingTool) {
|
|
1276
|
-
const error = `Required tool: ${JSON.stringify(
|
|
1277
|
-
this.requiredToolNames
|
|
1278
|
-
)} not available, options are ${this.getEnabledToolNames().join(", ")}`;
|
|
1279
|
-
this.log(error, "error");
|
|
1280
|
-
this.status = this.eventTypes.done;
|
|
1281
|
-
this.agentEvents.emit(this.eventTypes.done, error);
|
|
1282
|
-
return error;
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
// Early exit: killed, agent was requested to wrap up
|
|
1286
|
-
if (
|
|
1287
|
-
this.pendingUserMessages.length === 0 &&
|
|
1288
|
-
this.status === this.eventTypes.kill
|
|
1289
|
-
) {
|
|
1290
|
-
this.log("Agent killed, stopping execution");
|
|
1291
|
-
this.status = this.eventTypes.done;
|
|
1292
|
-
this.agentEvents.emit(this.eventTypes.done, firstMessage.content);
|
|
1293
|
-
return firstMessage.content;
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
// Compress when either the token threshold is exceeded OR the user has
|
|
1297
|
-
// manually requested compaction (e.g. via /compact on an attached agent).
|
|
1298
|
-
// A manual request bypasses the token threshold but still respects the
|
|
1299
|
-
// minimum-message guard so we never try to compress a tiny conversation.
|
|
1300
|
-
//
|
|
1301
|
-
// The size check uses getContextTokenCount(), which prefers the REAL
|
|
1302
|
-
// prompt token count from the last completion response over the
|
|
1303
|
-
// whitespace-based estimate — so it compares real tokens against the
|
|
1304
|
-
// real (token-based) compress threshold.
|
|
1305
|
-
const completedCompaction = this._completedCompaction;
|
|
1306
|
-
this._completedCompaction = null;
|
|
1307
|
-
let appliedCompaction = false;
|
|
1308
|
-
|
|
1309
|
-
if (completedCompaction) {
|
|
1310
|
-
const currentPrefix = messages.slice(
|
|
1311
|
-
0,
|
|
1312
|
-
completedCompaction.compressionEnd
|
|
1313
|
-
);
|
|
1314
|
-
const prefixIsUnchanged = currentPrefix.every(
|
|
1315
|
-
(message, index) =>
|
|
1316
|
-
message === completedCompaction.originalPrefix[index] ||
|
|
1317
|
-
JSON.stringify(message) ===
|
|
1318
|
-
JSON.stringify(completedCompaction.originalPrefix[index])
|
|
1319
|
-
);
|
|
1320
|
-
|
|
1321
|
-
if (
|
|
1322
|
-
currentPrefix.length === completedCompaction.compressionEnd &&
|
|
1323
|
-
prefixIsUnchanged
|
|
1324
|
-
) {
|
|
1325
|
-
messages = [
|
|
1326
|
-
...completedCompaction.replacementPrefix,
|
|
1327
|
-
...messages.slice(completedCompaction.compressionEnd),
|
|
1328
|
-
];
|
|
1329
|
-
this.summaries.push(completedCompaction.summary);
|
|
1330
|
-
this.startNewThread(messages);
|
|
1331
|
-
this.lastPromptTokens = 0;
|
|
1332
|
-
appliedCompaction = true;
|
|
1333
|
-
} else {
|
|
1334
|
-
this.log(
|
|
1335
|
-
"Discarding completed compaction because its source prefix changed; a later turn will retry",
|
|
1336
|
-
"warn"
|
|
1337
|
-
);
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
const contextTokens = this.getContextTokenCount(messages);
|
|
1342
|
-
const overThreshold =
|
|
1343
|
-
contextTokens > this.getCompressThreshold() &&
|
|
1344
|
-
messages.length > this.compressMinMessages;
|
|
1345
|
-
|
|
1346
|
-
if (
|
|
1347
|
-
!appliedCompaction &&
|
|
1348
|
-
!this._compactionPromise &&
|
|
1349
|
-
(overThreshold || this._forceCompact)
|
|
1350
|
-
) {
|
|
1351
|
-
this.log(
|
|
1352
|
-
this._forceCompact
|
|
1353
|
-
? `Starting background compaction (manual /compact request): ${contextTokens} tokens`
|
|
1354
|
-
: `Starting background compaction: ${contextTokens} tokens exceeds ${this.getCompressThreshold()}`
|
|
1355
|
-
);
|
|
1356
|
-
this._forceCompact = false;
|
|
1357
|
-
this.startBackgroundCompaction(messages);
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
if (["assistant", "tool"].includes(messages[messages.length - 1].role)) {
|
|
1361
|
-
// sometimes the agent just says a message and doesn't call a tool, or compression ends on a tool message
|
|
1362
|
-
|
|
1363
|
-
const statusMessage = this.getStatusMessage();
|
|
1364
|
-
this.logStatus();
|
|
1365
|
-
|
|
1366
|
-
const continuation = `<Workflow>
|
|
1367
|
-
This is an automated runtime instruction, not a new user message.
|
|
1368
|
-
The original user request remains active. Do not say that the user's
|
|
1369
|
-
message was empty, and do not ask the user to repeat the request.
|
|
1370
|
-
|
|
1371
|
-
Your previous response did not terminate the task because it did not
|
|
1372
|
-
call one of the required tools: ${JSON.stringify(
|
|
1373
|
-
this.requiredToolNames
|
|
1374
|
-
)}.
|
|
1375
|
-
If your previous response was intended as the final response, call the
|
|
1376
|
-
appropriate required tool now with that response. Otherwise, continue
|
|
1377
|
-
working on the original request and call an appropriate required tool
|
|
1378
|
-
when the task is complete.
|
|
1379
|
-
|
|
1380
|
-
<TaskStatus>${statusMessage}</TaskStatus>
|
|
1381
|
-
</Workflow>`;
|
|
1382
|
-
|
|
1383
|
-
messages.push({
|
|
1384
|
-
role: "user",
|
|
1385
|
-
content: continuation,
|
|
1386
|
-
});
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
this.updateCurrentThread(messages);
|
|
1390
|
-
return this.call(userInput, messages);
|
|
1391
|
-
} catch (e) {
|
|
1392
|
-
if (e.toString().includes("429")) {
|
|
1393
|
-
this.setNotHealthy();
|
|
1394
|
-
return this.call(userInput, _messages, retryCount);
|
|
1395
|
-
}
|
|
1396
|
-
const errorStr = e.toString();
|
|
1397
|
-
const isNonRetriable =
|
|
1398
|
-
errorStr.includes("401") ||
|
|
1399
|
-
errorStr.includes("403") ||
|
|
1400
|
-
errorStr.includes("404");
|
|
1401
|
-
|
|
1402
|
-
const isRetriable =
|
|
1403
|
-
!isNonRetriable &&
|
|
1404
|
-
(errorStr.match(/5\d\d/) ||
|
|
1405
|
-
errorStr.includes("Failed to get models") ||
|
|
1406
|
-
errorStr.includes("timeout") ||
|
|
1407
|
-
errorStr.includes("ECONNRESET") ||
|
|
1408
|
-
errorStr.includes("ETIMEDOUT") ||
|
|
1409
|
-
errorStr.includes("Invalid response format from MCP"));
|
|
1410
|
-
|
|
1411
|
-
if (isRetriable && retryCount < 3) {
|
|
1412
|
-
const delay = 1000 * Math.pow(2, retryCount);
|
|
1413
|
-
this.log(
|
|
1414
|
-
`Agent request failed (attempt ${
|
|
1415
|
-
retryCount + 1
|
|
1416
|
-
}/3), retrying in ${delay}ms: ${e.message}`,
|
|
1417
|
-
"warn"
|
|
1418
|
-
);
|
|
1419
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1420
|
-
return this.call(userInput, _messages, retryCount + 1);
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
this.log(`Agent failed: ${e}`, "error");
|
|
1424
|
-
|
|
1425
|
-
if (
|
|
1426
|
-
e != null &&
|
|
1427
|
-
typeof e === "object" &&
|
|
1428
|
-
"response" in e &&
|
|
1429
|
-
"data" in (e as any).response
|
|
1430
|
-
) {
|
|
1431
|
-
this.log(
|
|
1432
|
-
`Error response data: ${JSON.stringify(e.response.data, null, 2)}`,
|
|
1433
|
-
"error"
|
|
1434
|
-
);
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
this.agentEvents.emit(this.eventTypes.done, e.message);
|
|
1438
|
-
return e.message;
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
getStatusMessage() {
|
|
1443
|
-
const baseMessage = `Task spend: $${this.getTotalCostUsd().toPrecision(
|
|
1444
|
-
3
|
|
1445
|
-
)}\nElapsed: ${Math.floor(this.runTime() / 1000)}s\n`;
|
|
1446
|
-
|
|
1447
|
-
const remainingTime =
|
|
1448
|
-
this.maxRunTimeMs && this.startTimeMs
|
|
1449
|
-
? this.maxRunTimeMs - (Date.now() - this.startTimeMs)
|
|
1450
|
-
: null;
|
|
1451
|
-
|
|
1452
|
-
const remainingTurns = this.maxTurns
|
|
1453
|
-
? this.maxTurns - this.turnCount
|
|
1454
|
-
: null;
|
|
1455
|
-
|
|
1456
|
-
const timeRemainingMsg = remainingTime
|
|
1457
|
-
? `You have approximately ${Math.floor(
|
|
1458
|
-
remainingTime / 1000
|
|
1459
|
-
)} seconds remaining for this task. `
|
|
1460
|
-
: "";
|
|
1461
|
-
|
|
1462
|
-
const turnsRemainingMsg = remainingTurns
|
|
1463
|
-
? `You have ${remainingTurns} turns remaining. `
|
|
1464
|
-
: "";
|
|
1465
|
-
|
|
1466
|
-
const remainingBudget = this.maxSpend
|
|
1467
|
-
? this.maxSpend - this.totalCostUsd
|
|
1468
|
-
: null;
|
|
1469
|
-
const budgetRemainingMsg = remainingBudget
|
|
1470
|
-
? `You have $${remainingBudget.toFixed(4)} remaining in your budget.`
|
|
1471
|
-
: "";
|
|
1472
|
-
|
|
1473
|
-
const statusMessage = `${baseMessage}\n${timeRemainingMsg}\n${turnsRemainingMsg}\n${budgetRemainingMsg}`;
|
|
1474
|
-
return statusMessage;
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
logStatus() {
|
|
1478
|
-
const statusMessage = this.getStatusMessage();
|
|
1479
|
-
this.agentEvents.emit(this.eventTypes.agentStatus, {
|
|
1480
|
-
agentName: this.name,
|
|
1481
|
-
taskId: this.currentTaskId,
|
|
1482
|
-
statusMessage,
|
|
1483
|
-
details: {
|
|
1484
|
-
totalCostUsd: this.getTotalCostUsd(),
|
|
1485
|
-
elapsedMs: this.runTime(),
|
|
1486
|
-
remainingTimeMs:
|
|
1487
|
-
this.maxRunTimeMs && this.startTimeMs
|
|
1488
|
-
? this.maxRunTimeMs - (Date.now() - this.startTimeMs)
|
|
1489
|
-
: undefined,
|
|
1490
|
-
remainingTurns: this.maxTurns
|
|
1491
|
-
? this.maxTurns - this.turnCount
|
|
1492
|
-
: undefined,
|
|
1493
|
-
remainingBudget: this.maxSpend
|
|
1494
|
-
? this.maxSpend - this.totalCostUsd
|
|
1495
|
-
: undefined,
|
|
1496
|
-
},
|
|
1497
|
-
timestamp: Date.now(),
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
// A new message from system, non blocking
|
|
1502
|
-
addPendingMessage(message: Message) {
|
|
1503
|
-
if (this.status === this.eventTypes.done) {
|
|
1504
|
-
this.log("Agent is done, cannot take more messages", "warn");
|
|
1505
|
-
} else {
|
|
1506
|
-
const pendingMessages = this.pendingMessages.map((m) => m.content);
|
|
1507
|
-
if (pendingMessages.includes(message.content)) {
|
|
1508
|
-
// Ignore messages we already have queue'd up
|
|
1509
|
-
return;
|
|
1510
|
-
}
|
|
1511
|
-
this.pendingMessages.push(message);
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
// A new message from users, blocks completion
|
|
1516
|
-
addPendingUserMessage(message: Message) {
|
|
1517
|
-
if (this.status === this.eventTypes.done) {
|
|
1518
|
-
this.log("Agent is done, cannot take more messages", "warn");
|
|
1519
|
-
} else {
|
|
1520
|
-
const pendingMessages = this.pendingUserMessages.map((m) => m.content);
|
|
1521
|
-
if (pendingMessages.includes(message.content)) {
|
|
1522
|
-
// Ignore messages we already have queue'd up
|
|
1523
|
-
return;
|
|
1524
|
-
}
|
|
1525
|
-
this.pendingUserMessages.push(message);
|
|
1526
|
-
}
|
|
1527
|
-
this.events.emit(this.eventTypes.userSay, message.content);
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
/**
|
|
1531
|
-
* Subscribe to any data-yielding source and stream its data into this agent's
|
|
1532
|
-
* loop as non-blocking pending messages. This is the unified subscription
|
|
1533
|
-
* primitive behind the `observe` tool — a tool poll, an event, a stream, a
|
|
1534
|
-
* peer agent's thread, a file tail, etc. all become `ObservationSource`
|
|
1535
|
-
* adapters feeding the same sink (`addPendingMessage`).
|
|
1536
|
-
*
|
|
1537
|
-
* Returns an observation id that can be passed to `stopObserving`. Auto-expires
|
|
1538
|
-
* after `maxUpdates` or `maxDurationMs` so a forgotten observer never leaks.
|
|
1539
|
-
*/
|
|
1540
|
-
observe(source: ObservationSource, opts: ObserveOpts = {}): string {
|
|
1541
|
-
const {
|
|
1542
|
-
onlyOnChange = true,
|
|
1543
|
-
maxUpdates = 20,
|
|
1544
|
-
maxDurationMs = 10 * 60 * 1000,
|
|
1545
|
-
} = opts;
|
|
1546
|
-
|
|
1547
|
-
this._observationSeq += 1;
|
|
1548
|
-
// Keep the id free of characters that would break id-extraction regexes
|
|
1549
|
-
// (labels can contain parens, spaces, JSON, etc).
|
|
1550
|
-
const safeLabel = source.label.replace(/[^A-Za-z0-9]+/g, "_").slice(0, 24);
|
|
1551
|
-
const id = `obs_${safeLabel}_${Date.now()}_${this._observationSeq}`;
|
|
1552
|
-
let last: string | undefined;
|
|
1553
|
-
|
|
1554
|
-
const emit = (datum: unknown) => {
|
|
1555
|
-
// status gate: don't inject once the agent is done
|
|
1556
|
-
if (this.status === this.eventTypes.done) {
|
|
1557
|
-
this.stopObserving(id);
|
|
1558
|
-
return;
|
|
1559
|
-
}
|
|
1560
|
-
const str = typeof datum === "string" ? datum : JSON.stringify(datum);
|
|
1561
|
-
if (onlyOnChange && str === last) return; // dedupe noise
|
|
1562
|
-
last = str;
|
|
1563
|
-
const obs = this._observations.get(id);
|
|
1564
|
-
if (!obs) return;
|
|
1565
|
-
obs.updates += 1;
|
|
1566
|
-
this.addPendingMessage({
|
|
1567
|
-
role: "user",
|
|
1568
|
-
content: `[observe:${id}] ${source.label} (update ${obs.updates}/${maxUpdates}):\n${str}`,
|
|
1569
|
-
});
|
|
1570
|
-
if (obs.updates >= maxUpdates) {
|
|
1571
|
-
this.stopObserving(id, "max updates reached");
|
|
1572
|
-
}
|
|
1573
|
-
};
|
|
1574
|
-
|
|
1575
|
-
const teardownPromise = Promise.resolve(source.start(emit)).catch((e) => {
|
|
1576
|
-
this.log(
|
|
1577
|
-
`observe(${source.label}) start failed: ${e?.message ?? e}`,
|
|
1578
|
-
"warn"
|
|
1579
|
-
);
|
|
1580
|
-
return undefined;
|
|
1581
|
-
});
|
|
1582
|
-
|
|
1583
|
-
const expiry = maxDurationMs
|
|
1584
|
-
? setTimeout(
|
|
1585
|
-
() => this.stopObserving(id, "max duration reached"),
|
|
1586
|
-
maxDurationMs
|
|
1587
|
-
)
|
|
1588
|
-
: undefined;
|
|
1589
|
-
if (expiry && typeof (expiry as any).unref === "function") {
|
|
1590
|
-
(expiry as any).unref();
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
this._observations.set(id, {
|
|
1594
|
-
id,
|
|
1595
|
-
label: source.label,
|
|
1596
|
-
updates: 0,
|
|
1597
|
-
teardownPromise,
|
|
1598
|
-
expiry,
|
|
1599
|
-
});
|
|
1600
|
-
return id;
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
/**
|
|
1604
|
-
* Cancel one observation (by id) or all of them (no id). Companion to
|
|
1605
|
-
* `observe`. Runs each source's teardown function if it returned one.
|
|
1606
|
-
*/
|
|
1607
|
-
stopObserving(id?: string, reason = "stopped"): string {
|
|
1608
|
-
const ids = id ? [id] : [...this._observations.keys()];
|
|
1609
|
-
let stopped = 0;
|
|
1610
|
-
for (const oid of ids) {
|
|
1611
|
-
const obs = this._observations.get(oid);
|
|
1612
|
-
if (!obs) continue;
|
|
1613
|
-
if (obs.expiry) clearTimeout(obs.expiry);
|
|
1614
|
-
Promise.resolve(obs.teardownPromise)
|
|
1615
|
-
.then((fn) => {
|
|
1616
|
-
if (typeof fn === "function") fn();
|
|
1617
|
-
})
|
|
1618
|
-
.catch(() => {});
|
|
1619
|
-
this._observations.delete(oid);
|
|
1620
|
-
stopped += 1;
|
|
1621
|
-
}
|
|
1622
|
-
return id
|
|
1623
|
-
? stopped
|
|
1624
|
-
? `Stopped observer ${id} (${reason}).`
|
|
1625
|
-
: `No active observer found with id: ${id}.`
|
|
1626
|
-
: `Stopped ${stopped} active observer(s) (${reason}).`;
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
getMessagesLength(messages: Message[]) {
|
|
1630
|
-
return JSON.stringify(messages).split(" ").length;
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1633
|
-
/**
|
|
1634
|
-
* Returns the best available estimate of how many tokens the current
|
|
1635
|
-
* conversation occupies in the model's context window, for the purpose of
|
|
1636
|
-
* deciding when to compact.
|
|
1637
|
-
*
|
|
1638
|
-
* Prefers the REAL prompt token count reported by the most recent completion
|
|
1639
|
-
* response (lastPromptTokens) — this is exactly how many tokens the model
|
|
1640
|
-
* consumed for the last request (fresh input + cache read + cache write), so
|
|
1641
|
-
* it is far more accurate than the whitespace-based getMessagesLength()
|
|
1642
|
-
* heuristic (which counts space-separated chunks of the JSON-stringified
|
|
1643
|
-
* messages and can be off by a large factor).
|
|
1644
|
-
*
|
|
1645
|
-
* Falls back to the whitespace estimate when no real usage has been observed
|
|
1646
|
-
* yet (e.g. before the first completion, or when a provider omits usage).
|
|
1647
|
-
*
|
|
1648
|
-
* Note: lastPromptTokens reflects the context size at the START of the last
|
|
1649
|
-
* request, so it lags by whatever the model generated + tool results appended
|
|
1650
|
-
* since then. That lag is intentionally covered by the 70% headroom baked
|
|
1651
|
-
* into getCompressThreshold().
|
|
1652
|
-
*/
|
|
1653
|
-
getContextTokenCount(messages: Message[]): number {
|
|
1654
|
-
if (this.lastPromptTokens > 0) {
|
|
1655
|
-
return this.lastPromptTokens;
|
|
1656
|
-
}
|
|
1657
|
-
return this.getMessagesLength(messages);
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
/**
|
|
1661
|
-
* Detects whether tool call arguments appear truncated due to hitting the output token limit.
|
|
1662
|
-
* Two signals are checked:
|
|
1663
|
-
* 1. Any tool call argument is empty or invalid JSON (hard truncation).
|
|
1664
|
-
* 2. The model reported many output tokens but the total argument content received is tiny
|
|
1665
|
-
* relative to what those tokens should represent (soft/silent truncation).
|
|
1666
|
-
*
|
|
1667
|
-
* @param toolCalls - The tool calls from the CURRENT choice being checked.
|
|
1668
|
-
* @param allToolCalls - ALL tool calls across ALL choices in the response (used for the size heuristic).
|
|
1669
|
-
* Returns a warning system message if truncation is detected, or null otherwise.
|
|
1670
|
-
*/
|
|
1671
|
-
detectTruncatedToolCalls(
|
|
1672
|
-
toolCalls: ToolCall[],
|
|
1673
|
-
response: CompletionResponse,
|
|
1674
|
-
allToolCalls?: ToolCall[]
|
|
1675
|
-
): { role: string; content: string } | null {
|
|
1676
|
-
// Subtract thinking/reasoning tokens — they're billed as output tokens but
|
|
1677
|
-
// don't produce visible argument content, so including them inflates the heuristic.
|
|
1678
|
-
const rawOutputTokens: number = response?.usage?.completion_tokens || 0;
|
|
1679
|
-
const thinkingTokens: number =
|
|
1680
|
-
(response?.usage?.output_tokens_details?.thinking_tokens ?? 0) +
|
|
1681
|
-
(response?.usage?.output_tokens_details?.reasoning_tokens ?? 0);
|
|
1682
|
-
const outputTokens = Math.max(0, rawOutputTokens - thinkingTokens);
|
|
1683
|
-
// Use allToolCalls for the size heuristic so that parallel tool calls from
|
|
1684
|
-
// Anthropic (each mapped to a separate choice) are counted together. Without
|
|
1685
|
-
// this, a response with N parallel tool calls would compare the full
|
|
1686
|
-
// completion_tokens count against only 1/N of the actual argument content,
|
|
1687
|
-
// causing false-positive truncation warnings.
|
|
1688
|
-
const callsForSizeCheck = allToolCalls ?? toolCalls;
|
|
1689
|
-
const totalArgLength = callsForSizeCheck.reduce(
|
|
1690
|
-
(sum, tc) => sum + (tc.function?.arguments?.length || 0),
|
|
1691
|
-
0
|
|
1692
|
-
);
|
|
1693
|
-
|
|
1694
|
-
// Percentage-based heuristic: if actual arg chars are less than ~10% of the
|
|
1695
|
-
// expected chars (outputTokens * 4 chars/token), the output was likely truncated.
|
|
1696
|
-
// Only apply when outputTokens > 1000 to avoid false positives on small responses.
|
|
1697
|
-
const expectedArgChars = outputTokens * 4;
|
|
1698
|
-
const suspiciouslySmallArgs =
|
|
1699
|
-
outputTokens > 1000 && totalArgLength < expectedArgChars * 0.1;
|
|
1700
|
-
|
|
1701
|
-
for (const toolCall of toolCalls) {
|
|
1702
|
-
const args = toolCall.function?.arguments || "";
|
|
1703
|
-
let isInvalidJson = false;
|
|
1704
|
-
try {
|
|
1705
|
-
JSON.parse(args);
|
|
1706
|
-
} catch {
|
|
1707
|
-
isInvalidJson = true;
|
|
1708
|
-
}
|
|
1709
|
-
if (isInvalidJson || args.trim() === "" || suspiciouslySmallArgs) {
|
|
1710
|
-
this.log(
|
|
1711
|
-
`Tool call '${toolCall.function?.name}' has malformed/truncated arguments — likely hit output token limit (outputTokens=${outputTokens}, argLength=${args.length})`,
|
|
1712
|
-
"warn"
|
|
1713
|
-
);
|
|
1714
|
-
return {
|
|
1715
|
-
role: "user",
|
|
1716
|
-
content:
|
|
1717
|
-
"⚠️ Output limit warning: Your last tool call had incomplete or missing arguments, which usually means you exceeded the output token limit mid-response. The model reported " +
|
|
1718
|
-
outputTokens +
|
|
1719
|
-
" output tokens but only " +
|
|
1720
|
-
totalArgLength +
|
|
1721
|
-
" characters of tool call arguments were received. Please write smaller, more concise content in your tool calls. Aim for no more than 4000 tokens of output per response. Break large responses into smaller pieces if needed.",
|
|
1722
|
-
};
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
return null;
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
extractContentFromMessages(messages: Message[]): string {
|
|
1730
|
-
const content = messages
|
|
1731
|
-
.filter((m) => typeof m.content === "string" && m.content.trim())
|
|
1732
|
-
.map((m) => m.content);
|
|
1733
|
-
|
|
1734
|
-
const finalAnswer = messages
|
|
1735
|
-
.filter((m) => m.tool_calls?.length)
|
|
1736
|
-
.flatMap((t) => t.tool_calls)
|
|
1737
|
-
.filter((t) => t.function.name === "finalAnswer")
|
|
1738
|
-
.map((t) => t.function.arguments);
|
|
1739
|
-
|
|
1740
|
-
return [...content, ...finalAnswer].join("\n\n");
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
private startBackgroundCompaction(messages: Message[]): void {
|
|
1744
|
-
const snapshot = messages.slice();
|
|
1745
|
-
let resumeIndex = snapshot.length - 1;
|
|
1746
|
-
while (resumeIndex >= 0 && snapshot[resumeIndex].role !== "assistant") {
|
|
1747
|
-
resumeIndex--;
|
|
1748
|
-
}
|
|
1749
|
-
const compressionEnd =
|
|
1750
|
-
resumeIndex === -1 ? snapshot.length : resumeIndex;
|
|
1751
|
-
const generation = this._compactionGeneration;
|
|
1752
|
-
|
|
1753
|
-
const promise = (async () => {
|
|
1754
|
-
const taskBreakdown = await this.getTaskBreakdown(snapshot, false);
|
|
1755
|
-
if (generation !== this._compactionGeneration) return;
|
|
1756
|
-
this.taskBreakdown = taskBreakdown;
|
|
1757
|
-
let summary = "";
|
|
1758
|
-
const compactedSnapshot = await this.compressMessages(
|
|
1759
|
-
snapshot,
|
|
1760
|
-
(generatedSummary) => {
|
|
1761
|
-
summary = generatedSummary;
|
|
1762
|
-
},
|
|
1763
|
-
taskBreakdown
|
|
1764
|
-
);
|
|
1765
|
-
if (generation !== this._compactionGeneration) return;
|
|
1766
|
-
|
|
1767
|
-
const preservedSnapshotLength = snapshot.length - compressionEnd;
|
|
1768
|
-
this._completedCompaction = {
|
|
1769
|
-
compressionEnd,
|
|
1770
|
-
originalPrefix: snapshot.slice(0, compressionEnd),
|
|
1771
|
-
replacementPrefix: compactedSnapshot.slice(
|
|
1772
|
-
0,
|
|
1773
|
-
compactedSnapshot.length - preservedSnapshotLength
|
|
1774
|
-
),
|
|
1775
|
-
summary,
|
|
1776
|
-
};
|
|
1777
|
-
this.log("Background compaction completed; it will be applied next turn");
|
|
1778
|
-
})();
|
|
1779
|
-
|
|
1780
|
-
this._compactionPromise = promise;
|
|
1781
|
-
promise
|
|
1782
|
-
.catch((error) => {
|
|
1783
|
-
if (generation === this._compactionGeneration) {
|
|
1784
|
-
this.log(
|
|
1785
|
-
`Background compaction failed; a later turn will retry: ${error}`,
|
|
1786
|
-
"warn"
|
|
1787
|
-
);
|
|
1788
|
-
}
|
|
1789
|
-
})
|
|
1790
|
-
.finally(() => {
|
|
1791
|
-
if (this._compactionPromise === promise) {
|
|
1792
|
-
this._compactionPromise = null;
|
|
1793
|
-
}
|
|
1794
|
-
});
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
async getTaskBreakdown(messages: Message[], cacheResult = true) {
|
|
1798
|
-
if (this.taskBreakdown) {
|
|
1799
|
-
return this.taskBreakdown;
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
const taskPrompt = `
|
|
1803
|
-
TASK INTERRUPT. CONTEXT COMPACTION NEEDED. Analyze all previous messages.
|
|
1804
|
-
|
|
1805
|
-
Generate a detailed task breakdown for this conversation, include a section for the following:
|
|
1806
|
-
1. Task List
|
|
1807
|
-
2. Completion Criteria - when the agent should stop
|
|
1808
|
-
|
|
1809
|
-
Your output will be used to guide the work of the agent, and determine when we've accomplished the goal
|
|
1810
|
-
`;
|
|
1811
|
-
|
|
1812
|
-
const model = this.getModel();
|
|
1813
|
-
|
|
1814
|
-
const taskBreakdownMessages = [
|
|
1815
|
-
...messages,
|
|
1816
|
-
{
|
|
1817
|
-
role: "user",
|
|
1818
|
-
content: taskPrompt,
|
|
1819
|
-
},
|
|
1820
|
-
] as Message[];
|
|
1821
|
-
|
|
1822
|
-
const response = await this.createAgentCompletion(
|
|
1823
|
-
{
|
|
1824
|
-
messages: taskBreakdownMessages,
|
|
1825
|
-
tool_choice: "none",
|
|
1826
|
-
},
|
|
1827
|
-
{ updateLastPromptTokens: false }
|
|
1828
|
-
);
|
|
1829
|
-
|
|
1830
|
-
const breakdownContent = this.extractContentFromMessages(
|
|
1831
|
-
response.choices?.map((c) => c.message)
|
|
1832
|
-
);
|
|
1833
|
-
// The model may return null content (e.g. when it responds with tool calls
|
|
1834
|
-
// instead of text). Guard against storing a literal null so that template
|
|
1835
|
-
// strings don't render "null".
|
|
1836
|
-
if (!breakdownContent) {
|
|
1837
|
-
console.log(JSON.stringify(response.choices, null, 2));
|
|
1838
|
-
throw new Error(
|
|
1839
|
-
"Compaction task-breakdown request returned no text content"
|
|
1840
|
-
);
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
if (cacheResult) {
|
|
1844
|
-
this.taskBreakdown = breakdownContent;
|
|
1845
|
-
}
|
|
1846
|
-
this.log(`task breakdown cost: ${response.usd_cost}`);
|
|
1847
|
-
return breakdownContent;
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
async compressMessages(
|
|
1851
|
-
messages: Message[],
|
|
1852
|
-
onSummary?: (summary: string) => void,
|
|
1853
|
-
taskBreakdown = this.taskBreakdown
|
|
1854
|
-
) {
|
|
1855
|
-
// Preserve the latest agent interaction exactly. Starting the resumed thread
|
|
1856
|
-
// at the final assistant message keeps its tool calls, every tool response,
|
|
1857
|
-
// and any subsequent user messages together as one protocol-valid unit.
|
|
1858
|
-
let resumeIndex = messages.length - 1;
|
|
1859
|
-
while (resumeIndex >= 0 && messages[resumeIndex].role !== "assistant") {
|
|
1860
|
-
resumeIndex--;
|
|
1861
|
-
}
|
|
1862
|
-
const compressionEnd = resumeIndex === -1 ? messages.length : resumeIndex;
|
|
1863
|
-
const toCompress = messages.slice(0, compressionEnd);
|
|
1864
|
-
const resumeMessages = messages.slice(compressionEnd);
|
|
1865
|
-
|
|
1866
|
-
this.log(
|
|
1867
|
-
`Compressing messages from 0 to ${compressionEnd}, resuming from ${resumeIndex}, total messages: ${messages.length}`
|
|
1868
|
-
);
|
|
1869
|
-
const toCompressPrompt = `TASK INTERRUPT. CONTEXT COMPACTION NEEDED.
|
|
1870
|
-
We are compressing our conversation to save memory.
|
|
1871
|
-
Please summarize the conversation so far, so that we may continue the original task with a smaller context
|
|
1872
|
-
|
|
1873
|
-
Include the following sections:
|
|
1874
|
-
1. Initial Request - what this agent was originally tasked with.
|
|
1875
|
-
2. Progress - what has been tried so far,
|
|
1876
|
-
3. Next Steps - what we're about to do next to continue the user's original request.
|
|
1877
|
-
4. Tasks remaining - what tasks are left from the initial task breakdown.
|
|
1878
|
-
|
|
1879
|
-
${
|
|
1880
|
-
taskBreakdown
|
|
1881
|
-
? `Our initial task breakdown: ${taskBreakdown}`
|
|
1882
|
-
: ""
|
|
1883
|
-
}
|
|
1884
|
-
|
|
1885
|
-
This summary will replace the older history. The latest agent interaction will remain verbatim:
|
|
1886
|
-
|
|
1887
|
-
`;
|
|
1888
|
-
|
|
1889
|
-
const compressMessagesPayload = [
|
|
1890
|
-
...toCompress,
|
|
1891
|
-
{
|
|
1892
|
-
role: "user",
|
|
1893
|
-
content: toCompressPrompt,
|
|
1894
|
-
},
|
|
1895
|
-
] as Message[];
|
|
1896
|
-
|
|
1897
|
-
const response = await this.createAgentCompletion(
|
|
1898
|
-
{
|
|
1899
|
-
messages: compressMessagesPayload,
|
|
1900
|
-
tool_choice: "none",
|
|
1901
|
-
},
|
|
1902
|
-
{ updateLastPromptTokens: false }
|
|
1903
|
-
);
|
|
1904
|
-
|
|
1905
|
-
const summary = this.extractContentFromMessages(
|
|
1906
|
-
response.choices?.map((c) => c.message)
|
|
1907
|
-
);
|
|
1908
|
-
|
|
1909
|
-
if (summary.length === 0) {
|
|
1910
|
-
console.log(JSON.stringify(response.choices, null, 2));
|
|
1911
|
-
throw new Error("Compaction summary request returned no text content");
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
if (onSummary) {
|
|
1915
|
-
onSummary(summary);
|
|
1916
|
-
} else {
|
|
1917
|
-
this.summaries.push(summary);
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
const startMessages = [
|
|
1921
|
-
{
|
|
1922
|
-
role: "user",
|
|
1923
|
-
content: `
|
|
1924
|
-
${
|
|
1925
|
-
taskBreakdown
|
|
1926
|
-
? `Initial task breakdown:\n ${taskBreakdown}`
|
|
1927
|
-
: "(No task breakdown available — summarize what you know from context above)"
|
|
1928
|
-
}
|
|
1929
|
-
|
|
1930
|
-
We have just compressed the conversation to save memory:
|
|
1931
|
-
${
|
|
1932
|
-
summary.length > 0
|
|
1933
|
-
? JSON.stringify(summary)
|
|
1934
|
-
: "(summary unavailable — please continue from the task breakdown above)"
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
Please continue the task from where we left off
|
|
1938
|
-
`,
|
|
1939
|
-
},
|
|
1940
|
-
] as Message[];
|
|
1941
|
-
const systemMessages = toCompress.filter((m) => m.role === "system");
|
|
1942
|
-
|
|
1943
|
-
const newMessages = [
|
|
1944
|
-
...systemMessages,
|
|
1945
|
-
...startMessages,
|
|
1946
|
-
...resumeMessages,
|
|
1947
|
-
];
|
|
1948
|
-
|
|
1949
|
-
const oldLength = this.getMessagesLength(messages);
|
|
1950
|
-
const newLength = this.getMessagesLength(newMessages);
|
|
1951
|
-
const compressionRatio = (
|
|
1952
|
-
((oldLength - newLength) / oldLength) *
|
|
1953
|
-
100
|
|
1954
|
-
).toFixed(2);
|
|
1955
|
-
|
|
1956
|
-
this.log(`compression cost: ${response.usd_cost}`);
|
|
1957
|
-
this.log(
|
|
1958
|
-
`Compressed messages from ${oldLength} to ${newLength}, ${compressionRatio}% reduction in size`
|
|
1959
|
-
);
|
|
1960
|
-
|
|
1961
|
-
return newMessages;
|
|
1962
|
-
}
|
|
1963
|
-
}
|