@within-7/minto 0.4.0 → 0.4.2
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/dist/Tool.js +7 -0
- package/dist/Tool.js.map +2 -2
- package/dist/commands/agents/AgentsCommand.js +1 -1
- package/dist/commands/agents/AgentsCommand.js.map +2 -2
- package/dist/commands/agents/constants.js +2 -2
- package/dist/commands/agents/constants.js.map +2 -2
- package/dist/commands/clear.js +4 -3
- package/dist/commands/clear.js.map +2 -2
- package/dist/commands/compact.js +2 -2
- package/dist/commands/compact.js.map +1 -1
- package/dist/commands/context.js +3 -1
- package/dist/commands/context.js.map +2 -2
- package/dist/commands/login.js +128 -0
- package/dist/commands/login.js.map +7 -0
- package/dist/commands/memory.js +33 -82
- package/dist/commands/memory.js.map +2 -2
- package/dist/commands/quit.js +3 -1
- package/dist/commands/quit.js.map +2 -2
- package/dist/commands/resume.js +39 -239
- package/dist/commands/resume.js.map +2 -2
- package/dist/commands/tasks.js +1 -1
- package/dist/commands/tasks.js.map +2 -2
- package/dist/commands/terminalSetup.js +6 -2
- package/dist/commands/terminalSetup.js.map +2 -2
- package/dist/commands.js +2 -0
- package/dist/commands.js.map +2 -2
- package/dist/components/AgentDetailView.js +126 -0
- package/dist/components/AgentDetailView.js.map +7 -0
- package/dist/components/AgentThinkingBlock.js +1 -1
- package/dist/components/AgentThinkingBlock.js.map +2 -2
- package/dist/components/AgentViewBanner.js +22 -0
- package/dist/components/AgentViewBanner.js.map +7 -0
- package/dist/components/HeaderBar.js +1 -1
- package/dist/components/HeaderBar.js.map +2 -2
- package/dist/components/Help.js +8 -1
- package/dist/components/Help.js.map +2 -2
- package/dist/components/HotkeyHelpPanel.js +26 -8
- package/dist/components/HotkeyHelpPanel.js.map +2 -2
- package/dist/components/IdleNotificationBar.js +10 -0
- package/dist/components/IdleNotificationBar.js.map +7 -0
- package/dist/components/ModelSelector/ModelSelector.js +55 -20
- package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
- package/dist/components/PromptInput.js +186 -115
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/RewindPanel.js +272 -0
- package/dist/components/RewindPanel.js.map +7 -0
- package/dist/components/Spinner.js +10 -21
- package/dist/components/Spinner.js.map +2 -2
- package/dist/components/StreamingTextPreview.js +29 -0
- package/dist/components/StreamingTextPreview.js.map +7 -0
- package/dist/components/SubagentBlock.js +3 -2
- package/dist/components/SubagentBlock.js.map +2 -2
- package/dist/components/SubagentProgress.js +4 -4
- package/dist/components/SubagentProgress.js.map +2 -2
- package/dist/components/TabbedListView/SearchInput.js +1 -1
- package/dist/components/TabbedListView/SearchInput.js.map +2 -2
- package/dist/components/TabbedListView/TabbedListView.js +87 -41
- package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
- package/dist/components/TaskCard.js +4 -4
- package/dist/components/TaskCard.js.map +2 -2
- package/dist/components/TeamMemberPanel.js +107 -0
- package/dist/components/TeamMemberPanel.js.map +7 -0
- package/dist/components/ThinkingSelector.js +84 -0
- package/dist/components/ThinkingSelector.js.map +7 -0
- package/dist/components/TitledDivider.js +26 -0
- package/dist/components/TitledDivider.js.map +7 -0
- package/dist/components/TodoPanel.js +31 -30
- package/dist/components/TodoPanel.js.map +2 -2
- package/dist/components/TokenWarning.js +28 -7
- package/dist/components/TokenWarning.js.map +2 -2
- package/dist/components/messages/AssistantTextMessage.js +5 -2
- package/dist/components/messages/AssistantTextMessage.js.map +2 -2
- package/dist/components/messages/AssistantToolUseMessage.js +9 -1
- package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
- package/dist/components/messages/DefaultToolResultFallback.js +11 -0
- package/dist/components/messages/DefaultToolResultFallback.js.map +7 -0
- package/dist/components/messages/ParallelTasksGroupView.js +14 -6
- package/dist/components/messages/ParallelTasksGroupView.js.map +2 -2
- package/dist/components/messages/TaskInModuleView.js +27 -27
- package/dist/components/messages/TaskInModuleView.js.map +2 -2
- package/dist/components/messages/UserGuidanceMessage.js +26 -0
- package/dist/components/messages/UserGuidanceMessage.js.map +7 -0
- package/dist/components/messages/UserPromptMessage.js +2 -1
- package/dist/components/messages/UserPromptMessage.js.map +2 -2
- package/dist/components/messages/UserTeamNotificationMessage.js +91 -0
- package/dist/components/messages/UserTeamNotificationMessage.js.map +7 -0
- package/dist/components/messages/UserTextMessage.js +8 -0
- package/dist/components/messages/UserTextMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolRejectMessage.js +4 -2
- package/dist/components/messages/UserToolResultMessage/UserToolRejectMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolResultMessage.js +18 -1
- package/dist/components/messages/UserToolResultMessage/UserToolResultMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js +12 -1
- package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js.map +2 -2
- package/dist/components/permissions/PermissionRequest.js +4 -0
- package/dist/components/permissions/PermissionRequest.js.map +2 -2
- package/dist/components/permissions/PlanApprovalRequest.js +164 -0
- package/dist/components/permissions/PlanApprovalRequest.js.map +7 -0
- package/dist/constants/agentTeams.js +17 -0
- package/dist/constants/agentTeams.js.map +7 -0
- package/dist/constants/macros.js +2 -1
- package/dist/constants/macros.js.map +2 -2
- package/dist/constants/prompts/agentPrompt.js +1 -0
- package/dist/constants/prompts/agentPrompt.js.map +2 -2
- package/dist/constants/prompts/autoMemory.js +39 -0
- package/dist/constants/prompts/autoMemory.js.map +7 -0
- package/dist/constants/prompts/codeConventions.js +1 -13
- package/dist/constants/prompts/codeConventions.js.map +2 -2
- package/dist/constants/prompts/doingTasks.js +21 -2
- package/dist/constants/prompts/doingTasks.js.map +2 -2
- package/dist/constants/prompts/envInfo.js +6 -7
- package/dist/constants/prompts/envInfo.js.map +2 -2
- package/dist/constants/prompts/index.js +27 -5
- package/dist/constants/prompts/index.js.map +2 -2
- package/dist/constants/prompts/taskManagement.js +2 -43
- package/dist/constants/prompts/taskManagement.js.map +2 -2
- package/dist/constants/prompts/teamOverlays.js +50 -0
- package/dist/constants/prompts/teamOverlays.js.map +7 -0
- package/dist/constants/prompts/toneAndStyle.js +4 -29
- package/dist/constants/prompts/toneAndStyle.js.map +2 -2
- package/dist/constants/prompts/toolUsagePolicy.js +7 -22
- package/dist/constants/prompts/toolUsagePolicy.js.map +2 -2
- package/dist/constants/toolInputExamples.js +2 -2
- package/dist/constants/toolInputExamples.js.map +2 -2
- package/dist/context.js +39 -6
- package/dist/context.js.map +2 -2
- package/dist/core/backupManager.js +1 -1
- package/dist/core/backupManager.js.map +2 -2
- package/dist/core/permissions/rules/planModeRule.js +1 -1
- package/dist/core/permissions/rules/planModeRule.js.map +1 -1
- package/dist/core/permissions/rules/safeModeRule.js +1 -1
- package/dist/core/permissions/rules/safeModeRule.js.map +1 -1
- package/dist/engine/AgentEngine.js +902 -0
- package/dist/engine/AgentEngine.js.map +7 -0
- package/dist/engine/EngineRegistry.js +89 -0
- package/dist/engine/EngineRegistry.js.map +7 -0
- package/dist/engine/foregroundAdapter.js +191 -0
- package/dist/engine/foregroundAdapter.js.map +7 -0
- package/dist/engine/index.js +15 -0
- package/dist/engine/index.js.map +7 -0
- package/dist/engine/types.js +1 -0
- package/dist/engine/types.js.map +7 -0
- package/dist/entrypoints/cli.js +410 -79
- package/dist/entrypoints/cli.js.map +3 -3
- package/dist/hooks/useAgentEngine.js +129 -0
- package/dist/hooks/useAgentEngine.js.map +7 -0
- package/dist/hooks/useAgentTokenStats.js +0 -16
- package/dist/hooks/useAgentTokenStats.js.map +2 -2
- package/dist/hooks/useCanUseTool.js +47 -2
- package/dist/hooks/useCanUseTool.js.map +2 -2
- package/dist/hooks/useDeferredLoading.js +4 -1
- package/dist/hooks/useDeferredLoading.js.map +2 -2
- package/dist/hooks/useIdleNotifications.js +66 -0
- package/dist/hooks/useIdleNotifications.js.map +7 -0
- package/dist/hooks/useSessionTracking.js +9 -7
- package/dist/hooks/useSessionTracking.js.map +2 -2
- package/dist/hooks/useTeamMembers.js +51 -0
- package/dist/hooks/useTeamMembers.js.map +7 -0
- package/dist/i18n/locales/en.js +77 -12
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +77 -12
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/messages.js.map +2 -2
- package/dist/permissions.js +113 -7
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +135 -37
- package/dist/query.js.map +2 -2
- package/dist/screens/REPL.js +504 -361
- package/dist/screens/REPL.js.map +3 -3
- package/dist/screens/ResumeConversation.js +199 -14
- package/dist/screens/ResumeConversation.js.map +2 -2
- package/dist/services/adapters/base.js.map +1 -1
- package/dist/services/agentTeams/backends/headless.js +108 -0
- package/dist/services/agentTeams/backends/headless.js.map +7 -0
- package/dist/services/agentTeams/backends/inProcess.js +102 -0
- package/dist/services/agentTeams/backends/inProcess.js.map +7 -0
- package/dist/services/agentTeams/backends/resolver.js +18 -0
- package/dist/services/agentTeams/backends/resolver.js.map +7 -0
- package/dist/services/agentTeams/backends/tmux.js +168 -0
- package/dist/services/agentTeams/backends/tmux.js.map +7 -0
- package/dist/services/agentTeams/backends/types.js +1 -0
- package/dist/services/agentTeams/backends/types.js.map +7 -0
- package/dist/services/agentTeams/heartbeat.js +88 -0
- package/dist/services/agentTeams/heartbeat.js.map +7 -0
- package/dist/services/agentTeams/index.js +42 -2
- package/dist/services/agentTeams/index.js.map +2 -2
- package/dist/services/agentTeams/injectionChannel.js +105 -0
- package/dist/services/agentTeams/injectionChannel.js.map +7 -0
- package/dist/services/agentTeams/mailbox.js +410 -30
- package/dist/services/agentTeams/mailbox.js.map +2 -2
- package/dist/services/agentTeams/messageFormatter.js +80 -0
- package/dist/services/agentTeams/messageFormatter.js.map +7 -0
- package/dist/services/agentTeams/permissionDelegation.js +71 -0
- package/dist/services/agentTeams/permissionDelegation.js.map +7 -0
- package/dist/services/agentTeams/teamEvents.js +45 -0
- package/dist/services/agentTeams/teamEvents.js.map +7 -0
- package/dist/services/agentTeams/teamManager.js +251 -34
- package/dist/services/agentTeams/teamManager.js.map +2 -2
- package/dist/services/agentTeams/teamTaskStore.js +290 -61
- package/dist/services/agentTeams/teamTaskStore.js.map +2 -2
- package/dist/services/agentTeams/teammateSpawner.js +99 -18
- package/dist/services/agentTeams/teammateSpawner.js.map +2 -2
- package/dist/services/hookExecutor.js +51 -8
- package/dist/services/hookExecutor.js.map +2 -2
- package/dist/services/llm/anthropicProvider.js +56 -59
- package/dist/services/llm/anthropicProvider.js.map +2 -2
- package/dist/services/llm/dispatch.js +24 -5
- package/dist/services/llm/dispatch.js.map +2 -2
- package/dist/services/llm/openaiProvider.js +115 -136
- package/dist/services/llm/openaiProvider.js.map +3 -3
- package/dist/services/llm/types.js +89 -15
- package/dist/services/llm/types.js.map +2 -2
- package/dist/services/mcpClient.js +80 -4
- package/dist/services/mcpClient.js.map +2 -2
- package/dist/services/mintoAuth.js +299 -0
- package/dist/services/mintoAuth.js.map +7 -0
- package/dist/services/oauth.js +3 -3
- package/dist/services/oauth.js.map +2 -2
- package/dist/services/openai.js +91 -20
- package/dist/services/openai.js.map +2 -2
- package/dist/services/plugins/pluginRuntime.js +11 -5
- package/dist/services/plugins/pluginRuntime.js.map +2 -2
- package/dist/services/plugins/pluginValidation.js +4 -2
- package/dist/services/plugins/pluginValidation.js.map +2 -2
- package/dist/services/sandbox/sandboxController.js +11 -3
- package/dist/services/sandbox/sandboxController.js.map +2 -2
- package/dist/services/sessionMemoryInjector.js +77 -0
- package/dist/services/sessionMemoryInjector.js.map +7 -0
- package/dist/services/systemReminder.js +130 -8
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/services/taskStore.js +199 -8
- package/dist/services/taskStore.js.map +3 -3
- package/dist/services/topicDetector.js +169 -0
- package/dist/services/topicDetector.js.map +7 -0
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +0 -13
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
- package/dist/tools/BashTool/BashTool.js +51 -28
- package/dist/tools/BashTool/BashTool.js.map +2 -2
- package/dist/tools/BashTool/prompt.js +95 -118
- package/dist/tools/BashTool/prompt.js.map +2 -2
- package/dist/tools/BashTool/utils.js +39 -1
- package/dist/tools/BashTool/utils.js.map +2 -2
- package/dist/tools/EnterWorktreeTool/EnterWorktreeTool.js +121 -0
- package/dist/tools/EnterWorktreeTool/EnterWorktreeTool.js.map +7 -0
- package/dist/tools/EnterWorktreeTool/prompt.js +22 -0
- package/dist/tools/EnterWorktreeTool/prompt.js.map +7 -0
- package/dist/tools/FileEditTool/FileEditTool.js +9 -4
- package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
- package/dist/tools/FileEditTool/prompt.js +3 -7
- package/dist/tools/FileEditTool/prompt.js.map +2 -2
- package/dist/tools/FileReadTool/FileReadTool.js +125 -3
- package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
- package/dist/tools/FileReadTool/prompt.js +1 -2
- package/dist/tools/FileReadTool/prompt.js.map +2 -2
- package/dist/tools/FileWriteTool/prompt.js +3 -5
- package/dist/tools/FileWriteTool/prompt.js.map +2 -2
- package/dist/tools/GlobTool/GlobTool.js +3 -2
- package/dist/tools/GlobTool/GlobTool.js.map +2 -2
- package/dist/tools/GrepTool/GrepTool.js +16 -5
- package/dist/tools/GrepTool/GrepTool.js.map +2 -2
- package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +2 -2
- package/dist/tools/MCPSearchTool/MCPSearchTool.js +172 -0
- package/dist/tools/MCPSearchTool/MCPSearchTool.js.map +7 -0
- package/dist/tools/MCPSearchTool/prompt.js +77 -0
- package/dist/tools/MCPSearchTool/prompt.js.map +7 -0
- package/dist/tools/MultiEditTool/prompt.js +4 -7
- package/dist/tools/MultiEditTool/prompt.js.map +2 -2
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js +12 -8
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js +54 -1
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
- package/dist/tools/PlanModeTool/prompt.js +23 -74
- package/dist/tools/PlanModeTool/prompt.js.map +2 -2
- package/dist/tools/SendMessageTool/SendMessageTool.js +341 -0
- package/dist/tools/SendMessageTool/SendMessageTool.js.map +7 -0
- package/dist/tools/SendMessageTool/prompt.js +44 -0
- package/dist/tools/SendMessageTool/prompt.js.map +7 -0
- package/dist/tools/TaskCreateTool/prompt.js +15 -4
- package/dist/tools/TaskCreateTool/prompt.js.map +2 -2
- package/dist/tools/TaskListTool/prompt.js +18 -3
- package/dist/tools/TaskListTool/prompt.js.map +2 -2
- package/dist/tools/TaskOutputTool/prompt.js +4 -3
- package/dist/tools/TaskOutputTool/prompt.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +762 -98
- package/dist/tools/TaskTool/TaskTool.js.map +3 -3
- package/dist/tools/TaskTool/constants.js +8 -2
- package/dist/tools/TaskTool/constants.js.map +2 -2
- package/dist/tools/TaskTool/prompt.js +74 -70
- package/dist/tools/TaskTool/prompt.js.map +2 -2
- package/dist/tools/TaskUpdateTool/TaskUpdateTool.js +15 -1
- package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +2 -2
- package/dist/tools/TeamCreateTool/TeamCreateTool.js +129 -0
- package/dist/tools/TeamCreateTool/TeamCreateTool.js.map +7 -0
- package/dist/tools/TeamCreateTool/prompt.js +58 -0
- package/dist/tools/TeamCreateTool/prompt.js.map +7 -0
- package/dist/tools/TeamDeleteTool/TeamDeleteTool.js +151 -0
- package/dist/tools/TeamDeleteTool/TeamDeleteTool.js.map +7 -0
- package/dist/tools/TeamDeleteTool/prompt.js +16 -0
- package/dist/tools/TeamDeleteTool/prompt.js.map +7 -0
- package/dist/tools/URLFetcherTool/URLFetcherTool.js +106 -15
- package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
- package/dist/tools/URLFetcherTool/prompt.js +3 -2
- package/dist/tools/URLFetcherTool/prompt.js.map +2 -2
- package/dist/tools/WebSearchTool/WebSearchTool.js +2 -1
- package/dist/tools/WebSearchTool/WebSearchTool.js.map +2 -2
- package/dist/tools/WebSearchTool/prompt.js +5 -4
- package/dist/tools/WebSearchTool/prompt.js.map +2 -2
- package/dist/tools.js +100 -20
- package/dist/tools.js.map +2 -2
- package/dist/types/PermissionMode.js +35 -6
- package/dist/types/PermissionMode.js.map +2 -2
- package/dist/types/hooks.js +2 -0
- package/dist/types/hooks.js.map +2 -2
- package/dist/types/plugin.js +2 -0
- package/dist/types/plugin.js.map +3 -3
- package/dist/utils/CircuitBreaker.js +15 -9
- package/dist/utils/CircuitBreaker.js.map +2 -2
- package/dist/utils/agentLoader.js +249 -112
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/animationManager.js +40 -3
- package/dist/utils/animationManager.js.map +2 -2
- package/dist/utils/ask.js +7 -6
- package/dist/utils/ask.js.map +2 -2
- package/dist/utils/atomicWrite.js +23 -0
- package/dist/utils/atomicWrite.js.map +7 -0
- package/dist/utils/autoCompactCore.js +73 -56
- package/dist/utils/autoCompactCore.js.map +2 -2
- package/dist/utils/autoMemoryPaths.js +89 -0
- package/dist/utils/autoMemoryPaths.js.map +7 -0
- package/dist/utils/config.js +63 -38
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/configSchema.js +13 -8
- package/dist/utils/configSchema.js.map +2 -2
- package/dist/utils/credentials/index.js +14 -0
- package/dist/utils/credentials/index.js.map +2 -2
- package/dist/utils/dualPath.js +24 -0
- package/dist/utils/dualPath.js.map +7 -0
- package/dist/utils/exit.js +66 -7
- package/dist/utils/exit.js.map +2 -2
- package/dist/utils/externalEditor.js +155 -0
- package/dist/utils/externalEditor.js.map +7 -0
- package/dist/utils/fileLock.js +67 -0
- package/dist/utils/fileLock.js.map +7 -0
- package/dist/utils/format.js +24 -14
- package/dist/utils/format.js.map +2 -2
- package/dist/utils/globalErrorHandler.js +5 -96
- package/dist/utils/globalErrorHandler.js.map +3 -3
- package/dist/utils/groupHandlers/parallelTasksHandler.js +5 -3
- package/dist/utils/groupHandlers/parallelTasksHandler.js.map +2 -2
- package/dist/utils/groupHandlers/taskHandler.js +2 -2
- package/dist/utils/groupHandlers/taskHandler.js.map +2 -2
- package/dist/utils/hookManager.js +64 -6
- package/dist/utils/hookManager.js.map +2 -2
- package/dist/utils/log.js +6 -2
- package/dist/utils/log.js.map +2 -2
- package/dist/utils/markdown.js +237 -19
- package/dist/utils/markdown.js.map +2 -2
- package/dist/utils/messageContextManager.js +18 -5
- package/dist/utils/messageContextManager.js.map +2 -2
- package/dist/utils/messageGroupManager.js +1 -1
- package/dist/utils/messageGroupManager.js.map +2 -2
- package/dist/utils/messages.js +104 -46
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/model.js +2 -2
- package/dist/utils/model.js.map +2 -2
- package/dist/utils/pasteCache.js +8 -4
- package/dist/utils/pasteCache.js.map +2 -2
- package/dist/utils/pluginLoader.js +18 -0
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/secureKeyStorage.js +36 -7
- package/dist/utils/secureKeyStorage.js.map +2 -2
- package/dist/utils/simpleMode.js +7 -0
- package/dist/utils/simpleMode.js.map +7 -0
- package/dist/utils/streamingState.js +11 -1
- package/dist/utils/streamingState.js.map +2 -2
- package/dist/utils/taskDisplayUtils.js +2 -1
- package/dist/utils/taskDisplayUtils.js.map +2 -2
- package/dist/utils/teamConfig.js +2 -2
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/thinking.js +6 -2
- package/dist/utils/thinking.js.map +3 -3
- package/dist/utils/tokenProgress.js +55 -0
- package/dist/utils/tokenProgress.js.map +7 -0
- package/dist/utils/toolRiskClassification.js +26 -17
- package/dist/utils/toolRiskClassification.js.map +2 -2
- package/dist/utils/tooling/toolError.js +12 -0
- package/dist/utils/tooling/toolError.js.map +7 -0
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +10 -8
|
@@ -0,0 +1,902 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MessageInjectionChannel
|
|
3
|
+
} from "../services/agentTeams/injectionChannel.js";
|
|
4
|
+
import { AgentHeartbeat } from "../services/agentTeams/heartbeat.js";
|
|
5
|
+
import { query } from "../query.js";
|
|
6
|
+
import { createUserMessage, getLastAssistantMessageId } from "../utils/messages.js";
|
|
7
|
+
import { hasPermissionsToUseTool } from "../permissions.js";
|
|
8
|
+
import { logError } from "../utils/log.js";
|
|
9
|
+
import { debug } from "../utils/debugLogger.js";
|
|
10
|
+
const MAX_ENGINE_MESSAGES = 2e3;
|
|
11
|
+
class AgentEngine {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this._messages = config.initialMessages ? [...config.initialMessages] : [];
|
|
15
|
+
this._ownMessagesStartIndex = this._messages.length;
|
|
16
|
+
this.channel = new MessageInjectionChannel();
|
|
17
|
+
this._canUseTool = config.canUseTool ?? hasPermissionsToUseTool;
|
|
18
|
+
this._askUser = config.askUser ?? (() => Promise.resolve([]));
|
|
19
|
+
this._setToolJSX = config.setToolJSX ?? (() => {
|
|
20
|
+
});
|
|
21
|
+
this._getBinaryFeedbackResponse = config.getBinaryFeedbackResponse;
|
|
22
|
+
}
|
|
23
|
+
// ── State ─────────────────────────────────────────────────
|
|
24
|
+
_state = "created";
|
|
25
|
+
_messages = [];
|
|
26
|
+
listeners = /* @__PURE__ */ new Set();
|
|
27
|
+
reentryCount = 0;
|
|
28
|
+
// ── Infrastructure ────────────────────────────────────────
|
|
29
|
+
channel;
|
|
30
|
+
heartbeat = null;
|
|
31
|
+
abortController = null;
|
|
32
|
+
idleTimer = null;
|
|
33
|
+
idleResolve = null;
|
|
34
|
+
// ── Mailbox ───────────────────────────────────────────────
|
|
35
|
+
pendingFromSubscription = [];
|
|
36
|
+
unsubMailbox = null;
|
|
37
|
+
unsubWatcher = null;
|
|
38
|
+
channelUnsub = null;
|
|
39
|
+
// ── Dependency injection callbacks ────────────────────────
|
|
40
|
+
_canUseTool;
|
|
41
|
+
_askUser;
|
|
42
|
+
_setToolJSX;
|
|
43
|
+
_getBinaryFeedbackResponse;
|
|
44
|
+
// ── Turn timing ───────────────────────────────────────────
|
|
45
|
+
turnStartTime = null;
|
|
46
|
+
// ── Idle pause (focus-gated) ────────────────────────────
|
|
47
|
+
_idlePaused = false;
|
|
48
|
+
// ── Own messages boundary ───────────────────────────────
|
|
49
|
+
// For teammate engines initialized with parent messages, this marks
|
|
50
|
+
// where the engine's OWN messages begin (after the inherited prefix).
|
|
51
|
+
// Used by the REPL to skip parent messages when rendering a teammate view.
|
|
52
|
+
_ownMessagesStartIndex = 0;
|
|
53
|
+
// ── Public API: Read-only state ───────────────────────────
|
|
54
|
+
get state() {
|
|
55
|
+
return this._state;
|
|
56
|
+
}
|
|
57
|
+
get messages() {
|
|
58
|
+
return this._messages;
|
|
59
|
+
}
|
|
60
|
+
get isRunning() {
|
|
61
|
+
return this._state === "idle" || this._state === "querying";
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Index in messages[] where this engine's OWN messages begin.
|
|
65
|
+
* For the default engine this is 0 (all messages are its own).
|
|
66
|
+
* For teammate engines initialized with parent messages, this is
|
|
67
|
+
* the length of the inherited prefix. Used by the REPL to render
|
|
68
|
+
* only the agent's own activity when focused on a teammate.
|
|
69
|
+
*/
|
|
70
|
+
get ownMessagesStartIndex() {
|
|
71
|
+
return this._ownMessagesStartIndex;
|
|
72
|
+
}
|
|
73
|
+
get agentId() {
|
|
74
|
+
return this.config.agentId;
|
|
75
|
+
}
|
|
76
|
+
get injectionChannel() {
|
|
77
|
+
return this.channel;
|
|
78
|
+
}
|
|
79
|
+
// ── Public API: Lifecycle ─────────────────────────────────
|
|
80
|
+
/**
|
|
81
|
+
* Initialize the engine: set up heartbeat, mailbox subscription,
|
|
82
|
+
* and injection channel listener. Transitions to 'idle'.
|
|
83
|
+
*/
|
|
84
|
+
async start() {
|
|
85
|
+
if (this._state !== "created") return;
|
|
86
|
+
this.transition("idle");
|
|
87
|
+
if (this.config.teamName && this.config.agentId) {
|
|
88
|
+
this.setupHeartbeat();
|
|
89
|
+
this.setupMailboxSubscription();
|
|
90
|
+
}
|
|
91
|
+
this.channelUnsub = this.channel.onMessage(() => {
|
|
92
|
+
debug.trace("ENGINE_CHANNEL_ON_MESSAGE", {
|
|
93
|
+
state: this._state,
|
|
94
|
+
hasMessages: this.channel.hasMessages()
|
|
95
|
+
});
|
|
96
|
+
if (this._state === "idle" && this.channel.hasMessages()) {
|
|
97
|
+
const pending = this.channel.drain();
|
|
98
|
+
if (pending.length > 0) {
|
|
99
|
+
const injectionText = pending.map((m) => m.text).join("\n\n");
|
|
100
|
+
const injectedMsg = createUserMessage(injectionText);
|
|
101
|
+
this.emit({ type: "injected_messages", count: pending.length });
|
|
102
|
+
this.runQueryWithMessages([injectedMsg]).catch((err) => {
|
|
103
|
+
logError(err);
|
|
104
|
+
if (this._state === "querying") {
|
|
105
|
+
this.transition("idle");
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Submit user input to the engine. Creates a user message and runs the query loop.
|
|
114
|
+
* If a string is passed, it's wrapped in a createUserMessage.
|
|
115
|
+
* If Message[] is passed, they're appended directly.
|
|
116
|
+
*/
|
|
117
|
+
async submitPrompt(input) {
|
|
118
|
+
if (this._state !== "idle" && this._state !== "created") {
|
|
119
|
+
if (this._state === "querying" && typeof input === "string") {
|
|
120
|
+
this.injectMessage(input, "user");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
this.cancelIdleTimeout();
|
|
126
|
+
const newMessages = typeof input === "string" ? [createUserMessage(input)] : input;
|
|
127
|
+
await this.runQueryWithMessages(newMessages);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Inject a message into the channel for between-turns or post-query delivery.
|
|
131
|
+
*/
|
|
132
|
+
injectMessage(text, source) {
|
|
133
|
+
this.channel.push(text, source);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Interrupt the current query.
|
|
137
|
+
*/
|
|
138
|
+
abort() {
|
|
139
|
+
if (this.abortController) {
|
|
140
|
+
this.abortController.abort();
|
|
141
|
+
}
|
|
142
|
+
this.cancelIdleTimeout();
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Returns a Promise that resolves when the engine reaches 'idle' or 'stopped'.
|
|
146
|
+
* Used by foreground adapter to block until the current query completes.
|
|
147
|
+
*/
|
|
148
|
+
waitForIdle() {
|
|
149
|
+
if (this._state === "idle" || this._state === "stopped") {
|
|
150
|
+
return Promise.resolve();
|
|
151
|
+
}
|
|
152
|
+
return new Promise((resolve) => {
|
|
153
|
+
const unsub = this.on((event) => {
|
|
154
|
+
if (event.type === "state_change" && (event.to === "idle" || event.to === "stopped")) {
|
|
155
|
+
unsub();
|
|
156
|
+
resolve();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Graceful shutdown: abort any running query, stop heartbeat, clean up.
|
|
163
|
+
*/
|
|
164
|
+
async stop() {
|
|
165
|
+
if (this._state === "stopped" || this._state === "stopping") return;
|
|
166
|
+
this.transition("stopping");
|
|
167
|
+
this.abort();
|
|
168
|
+
this.heartbeat?.stop();
|
|
169
|
+
this.unsubMailbox?.();
|
|
170
|
+
this.unsubWatcher?.();
|
|
171
|
+
this.channelUnsub?.();
|
|
172
|
+
this.channel.dispose();
|
|
173
|
+
this.heartbeat = null;
|
|
174
|
+
this.unsubMailbox = null;
|
|
175
|
+
this.unsubWatcher = null;
|
|
176
|
+
this.channelUnsub = null;
|
|
177
|
+
this.transition("stopped");
|
|
178
|
+
}
|
|
179
|
+
// ── Public API: Event subscription ────────────────────────
|
|
180
|
+
/**
|
|
181
|
+
* Subscribe to engine events. Returns an unsubscribe function.
|
|
182
|
+
*/
|
|
183
|
+
on(listener) {
|
|
184
|
+
this.listeners.add(listener);
|
|
185
|
+
return () => {
|
|
186
|
+
this.listeners.delete(listener);
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// ── Public API: Dependency injection ──────────────────────
|
|
190
|
+
setCanUseTool(fn) {
|
|
191
|
+
this._canUseTool = fn;
|
|
192
|
+
}
|
|
193
|
+
setAskUser(fn) {
|
|
194
|
+
this._askUser = fn;
|
|
195
|
+
}
|
|
196
|
+
setToolJSX(fn) {
|
|
197
|
+
this._setToolJSX = fn;
|
|
198
|
+
}
|
|
199
|
+
setBinaryFeedbackResponse(fn) {
|
|
200
|
+
this._getBinaryFeedbackResponse = fn;
|
|
201
|
+
}
|
|
202
|
+
// ── Public API: Runtime config updates ──────────────────
|
|
203
|
+
/**
|
|
204
|
+
* Promote a default/standalone engine to team-lead.
|
|
205
|
+
* Adds heartbeat + mailbox subscription if the engine is already running.
|
|
206
|
+
*/
|
|
207
|
+
promoteToTeamLead(teamName) {
|
|
208
|
+
this.config.role = "team-lead";
|
|
209
|
+
this.config.teamName = teamName;
|
|
210
|
+
this.config.agentId = `team-lead@${teamName}`;
|
|
211
|
+
if (this._state !== "created") {
|
|
212
|
+
this.setupHeartbeat();
|
|
213
|
+
this.setupMailboxSubscription();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Demote from team-lead back to default/standalone. Stops heartbeat,
|
|
218
|
+
* unsubscribes mailbox, clears team config. Does NOT stop the engine
|
|
219
|
+
* — it continues running as a standalone agent.
|
|
220
|
+
*
|
|
221
|
+
* Must be called when the team is disbanded to prevent stale team
|
|
222
|
+
* infrastructure (heartbeat, mailbox, channel messages) from triggering
|
|
223
|
+
* unexpected re-queries after the team is gone.
|
|
224
|
+
*/
|
|
225
|
+
demoteFromTeamLead() {
|
|
226
|
+
this.heartbeat?.stop();
|
|
227
|
+
this.heartbeat = null;
|
|
228
|
+
this.unsubMailbox?.();
|
|
229
|
+
this.unsubMailbox = null;
|
|
230
|
+
this.unsubWatcher?.();
|
|
231
|
+
this.unsubWatcher = null;
|
|
232
|
+
this.cancelIdleTimeout();
|
|
233
|
+
if (this.config.teamName) {
|
|
234
|
+
try {
|
|
235
|
+
const { getTeam } = require("../services/agentTeams/teamManager");
|
|
236
|
+
const entry = getTeam(this.config.teamName);
|
|
237
|
+
entry?.mailbox.stopAllWatchers();
|
|
238
|
+
} catch {
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
debug.trace("ENGINE_DEMOTE_DRAIN", {
|
|
242
|
+
channelSize: this.channel.size,
|
|
243
|
+
state: this._state
|
|
244
|
+
});
|
|
245
|
+
this.channel.drain();
|
|
246
|
+
this.config.role = "default";
|
|
247
|
+
this.config.teamName = void 0;
|
|
248
|
+
this.config.agentId = "default";
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Update runtime config fields (tools, commands, verbose, etc.).
|
|
252
|
+
* Does NOT restart the engine — changes take effect on the next query.
|
|
253
|
+
*/
|
|
254
|
+
/** Fields that can be updated at runtime via updateConfig(). */
|
|
255
|
+
static MUTABLE_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
256
|
+
"tools",
|
|
257
|
+
"commands",
|
|
258
|
+
"verbose",
|
|
259
|
+
"safeMode",
|
|
260
|
+
"safetyMode",
|
|
261
|
+
"maxThinkingTokens",
|
|
262
|
+
"model",
|
|
263
|
+
"permissionMode",
|
|
264
|
+
"readFileTimestamps",
|
|
265
|
+
"canUseTool",
|
|
266
|
+
"askUser",
|
|
267
|
+
"setToolJSX",
|
|
268
|
+
"getBinaryFeedbackResponse",
|
|
269
|
+
"onMessage",
|
|
270
|
+
"onMessagesSet",
|
|
271
|
+
"isKodingRequest"
|
|
272
|
+
]);
|
|
273
|
+
updateConfig(partial) {
|
|
274
|
+
for (const key of Object.keys(partial)) {
|
|
275
|
+
if (AgentEngine.MUTABLE_CONFIG_KEYS.has(key)) {
|
|
276
|
+
;
|
|
277
|
+
this.config[key] = partial[key];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Pause/resume idle timeout. When paused, the engine won't time out
|
|
283
|
+
* even if idleTimeoutMs is set. Used when the engine is focused by the user.
|
|
284
|
+
*/
|
|
285
|
+
setIdlePaused(paused) {
|
|
286
|
+
const wasPaused = this._idlePaused;
|
|
287
|
+
this._idlePaused = paused;
|
|
288
|
+
if (paused) {
|
|
289
|
+
this.cancelIdleTimeout();
|
|
290
|
+
if (this.idleResolve) {
|
|
291
|
+
const r = this.idleResolve;
|
|
292
|
+
this.idleResolve = null;
|
|
293
|
+
r(true);
|
|
294
|
+
}
|
|
295
|
+
} else if (wasPaused && this._state === "idle" && this.config.idleTimeoutMs && this.config.idleTimeoutMs > 0) {
|
|
296
|
+
this.startIdleTimeout().then((wokenUp) => {
|
|
297
|
+
if (this._state === "stopped" || this._state === "stopping") return;
|
|
298
|
+
if (wokenUp) {
|
|
299
|
+
this.drainAndReQuery();
|
|
300
|
+
} else {
|
|
301
|
+
this.emit({ type: "idle_timeout" });
|
|
302
|
+
this.transition("stopping");
|
|
303
|
+
this.transition("stopped");
|
|
304
|
+
}
|
|
305
|
+
}).catch((err) => debug.trace("IDLE_DRAIN_REQUERY", { error: err }));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/** Read-only access to the engine config. */
|
|
309
|
+
get engineConfig() {
|
|
310
|
+
return this.config;
|
|
311
|
+
}
|
|
312
|
+
// ── Public API: Message manipulation ──────────────────────
|
|
313
|
+
/**
|
|
314
|
+
* Replace the messages array (e.g., for rollback).
|
|
315
|
+
*/
|
|
316
|
+
setMessages(messages) {
|
|
317
|
+
this._messages = [...messages];
|
|
318
|
+
this.emit({ type: "messages_set", messages: this._messages });
|
|
319
|
+
this.config.onMessagesSet?.(this._messages);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Append messages without triggering a query.
|
|
323
|
+
*/
|
|
324
|
+
appendMessages(messages) {
|
|
325
|
+
for (const msg of messages) {
|
|
326
|
+
this._messages.push(msg);
|
|
327
|
+
this.emit({ type: "message", message: msg });
|
|
328
|
+
this.config.onMessage?.(msg);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Trim messages to MAX_ENGINE_MESSAGES if the array has grown too large.
|
|
333
|
+
* Keeps the most recent messages to prevent unbounded memory growth.
|
|
334
|
+
*/
|
|
335
|
+
trimMessages() {
|
|
336
|
+
if (this._messages.length <= MAX_ENGINE_MESSAGES) return;
|
|
337
|
+
const originalLength = this._messages.length;
|
|
338
|
+
let truncated = this._messages.slice(-MAX_ENGINE_MESSAGES);
|
|
339
|
+
while (truncated.length > 0) {
|
|
340
|
+
const first = truncated[0];
|
|
341
|
+
if (!first) break;
|
|
342
|
+
if (first.type === "user" && Array.isArray(first.message.content) && first.message.content.length > 0 && first.message.content.some(
|
|
343
|
+
(block) => block.type === "tool_result"
|
|
344
|
+
)) {
|
|
345
|
+
truncated.shift();
|
|
346
|
+
} else if (first.type === "progress") {
|
|
347
|
+
truncated.shift();
|
|
348
|
+
} else {
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
this._messages = truncated;
|
|
353
|
+
debug.warn("ENGINE_MESSAGES_TRIMMED", {
|
|
354
|
+
original: originalLength,
|
|
355
|
+
kept: this._messages.length
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
// ── Internal: State machine ───────────────────────────────
|
|
359
|
+
static VALID_TRANSITIONS = {
|
|
360
|
+
created: ["idle"],
|
|
361
|
+
idle: ["querying", "stopping", "stopped"],
|
|
362
|
+
querying: ["stopping", "idle"],
|
|
363
|
+
stopping: ["stopped", "idle"],
|
|
364
|
+
stopped: []
|
|
365
|
+
};
|
|
366
|
+
transition(to) {
|
|
367
|
+
const from = this._state;
|
|
368
|
+
if (from === to) return;
|
|
369
|
+
const allowed = AgentEngine.VALID_TRANSITIONS[from];
|
|
370
|
+
if (allowed && !allowed.includes(to)) {
|
|
371
|
+
const msg = `Invalid state transition: ${from} \u2192 ${to}`;
|
|
372
|
+
if (process.env.NODE_ENV === "test") {
|
|
373
|
+
throw new Error(msg);
|
|
374
|
+
}
|
|
375
|
+
debug.warn("INVALID_STATE_TRANSITION", { from, to });
|
|
376
|
+
}
|
|
377
|
+
debug.flow("ENGINE_TRANSITION", { from, to });
|
|
378
|
+
this._state = to;
|
|
379
|
+
this.emit({ type: "state_change", from, to });
|
|
380
|
+
}
|
|
381
|
+
emit(event) {
|
|
382
|
+
for (const listener of this.listeners) {
|
|
383
|
+
try {
|
|
384
|
+
listener(event);
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
// ── Internal: Setup ───────────────────────────────────────
|
|
390
|
+
setupHeartbeat() {
|
|
391
|
+
if (!this.config.teamName || !this.config.agentId) return;
|
|
392
|
+
this.heartbeat = new AgentHeartbeat();
|
|
393
|
+
this.heartbeat.start(
|
|
394
|
+
this.config.agentId,
|
|
395
|
+
this.config.teamName,
|
|
396
|
+
this.channel,
|
|
397
|
+
{ interval: this.config.heartbeatIntervalMs ?? 3e3 }
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
setupMailboxSubscription() {
|
|
401
|
+
if (!this.config.teamName || !this.config.agentId) return;
|
|
402
|
+
try {
|
|
403
|
+
const { getTeam } = require("../services/agentTeams/teamManager");
|
|
404
|
+
const teamEntry = getTeam(this.config.teamName);
|
|
405
|
+
if (teamEntry) {
|
|
406
|
+
this.unsubMailbox = teamEntry.mailbox.subscribe(
|
|
407
|
+
this.config.agentId,
|
|
408
|
+
(msg) => {
|
|
409
|
+
teamEntry.mailbox.markRead([msg.id]);
|
|
410
|
+
if (this._state === "idle") {
|
|
411
|
+
this.formatAndPushToChannel(msg);
|
|
412
|
+
} else {
|
|
413
|
+
this.pendingFromSubscription.push(msg);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
);
|
|
417
|
+
this.unsubWatcher = teamEntry.mailbox.startWatching(this.config.agentId);
|
|
418
|
+
}
|
|
419
|
+
} catch (err) {
|
|
420
|
+
debug.error("ENGINE_MAILBOX_SETUP_FAILED", { error: err });
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Format a team message and push to the injection channel.
|
|
425
|
+
* Replicates heartbeat formatting logic so subscription-delivered
|
|
426
|
+
* messages look identical to heartbeat-delivered ones.
|
|
427
|
+
*/
|
|
428
|
+
formatAndPushToChannel(msg) {
|
|
429
|
+
try {
|
|
430
|
+
const {
|
|
431
|
+
formatTeamMessage,
|
|
432
|
+
parseIdleNotification,
|
|
433
|
+
formatIdleNotificationForInjection,
|
|
434
|
+
humanizeProtocolMessage
|
|
435
|
+
} = require("../services/agentTeams/messageFormatter");
|
|
436
|
+
const idleNotif = parseIdleNotification(msg.text);
|
|
437
|
+
if (idleNotif) {
|
|
438
|
+
this.channel.push(formatIdleNotificationForInjection(idleNotif), "task");
|
|
439
|
+
} else {
|
|
440
|
+
const friendly = humanizeProtocolMessage(msg.text);
|
|
441
|
+
const formatted = formatTeamMessage(
|
|
442
|
+
friendly ? { ...msg, summary: friendly } : msg
|
|
443
|
+
);
|
|
444
|
+
this.channel.push(formatted, "team");
|
|
445
|
+
}
|
|
446
|
+
} catch {
|
|
447
|
+
this.channel.push(msg.text, "team");
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Ensure heartbeat is running. Called at query boundaries as a safety net.
|
|
452
|
+
*/
|
|
453
|
+
ensureHeartbeat() {
|
|
454
|
+
if (!this.config.teamName || !this.config.agentId) return;
|
|
455
|
+
try {
|
|
456
|
+
const { getActiveTeams } = require("../services/agentTeams/teamManager");
|
|
457
|
+
const activeTeams = getActiveTeams();
|
|
458
|
+
if (activeTeams.length > 0 && !this.heartbeat?.running) {
|
|
459
|
+
this.setupHeartbeat();
|
|
460
|
+
}
|
|
461
|
+
} catch {
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// ── Internal: Query loop ──────────────────────────────────
|
|
465
|
+
/**
|
|
466
|
+
* Core method: add messages and run the query loop with post-query drain.
|
|
467
|
+
*/
|
|
468
|
+
async runQueryWithMessages(newMessages) {
|
|
469
|
+
if (this._state === "stopping" || this._state === "stopped") return;
|
|
470
|
+
this.transition("querying");
|
|
471
|
+
this.reentryCount = 0;
|
|
472
|
+
this.turnStartTime = Date.now();
|
|
473
|
+
this.emit({ type: "turn_start" });
|
|
474
|
+
this.abortController = new AbortController();
|
|
475
|
+
this.ensureHeartbeat();
|
|
476
|
+
for (const msg of newMessages) {
|
|
477
|
+
this._messages.push(msg);
|
|
478
|
+
this.emit({ type: "message", message: msg });
|
|
479
|
+
this.config.onMessage?.(msg);
|
|
480
|
+
}
|
|
481
|
+
try {
|
|
482
|
+
await this.executeQueryLoop();
|
|
483
|
+
} catch (error) {
|
|
484
|
+
logError(error);
|
|
485
|
+
this.emit({
|
|
486
|
+
type: "error",
|
|
487
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
488
|
+
});
|
|
489
|
+
} finally {
|
|
490
|
+
const durationMs = this.turnStartTime ? Date.now() - this.turnStartTime : 0;
|
|
491
|
+
this.turnStartTime = null;
|
|
492
|
+
this.abortController = null;
|
|
493
|
+
this.emit({ type: "turn_end", durationMs });
|
|
494
|
+
this.ensureHeartbeat();
|
|
495
|
+
try {
|
|
496
|
+
if (!this.isAborted()) {
|
|
497
|
+
const reEntered = await this.postQueryDrain();
|
|
498
|
+
if (!reEntered) {
|
|
499
|
+
if (this.channel.hasMessages()) {
|
|
500
|
+
debug.trace("ENGINE_SAFETY_NET_DRAIN", {
|
|
501
|
+
channelSize: this.channel.size
|
|
502
|
+
});
|
|
503
|
+
const safetyDrained = this.channel.drain();
|
|
504
|
+
if (safetyDrained.length > 0) {
|
|
505
|
+
const injectionText = safetyDrained.map((m) => m.text).join("\n\n");
|
|
506
|
+
const injectedMsg = createUserMessage(injectionText);
|
|
507
|
+
this.emit({
|
|
508
|
+
type: "injected_messages",
|
|
509
|
+
count: safetyDrained.length
|
|
510
|
+
});
|
|
511
|
+
await this.runReentryTurn(injectedMsg);
|
|
512
|
+
const reEnteredAgain = await this.postQueryDrain();
|
|
513
|
+
if (reEnteredAgain) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
this.trimMessages();
|
|
519
|
+
this.transition("idle");
|
|
520
|
+
if (this.config.idleTimeoutMs && this.config.idleTimeoutMs > 0 && !this._idlePaused) {
|
|
521
|
+
const wokenUp = await this.startIdleTimeout();
|
|
522
|
+
if (wokenUp) {
|
|
523
|
+
this.drainAndReQuery();
|
|
524
|
+
} else {
|
|
525
|
+
this.emit({ type: "idle_timeout" });
|
|
526
|
+
this.transition("stopping");
|
|
527
|
+
this.transition("stopped");
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
} else {
|
|
532
|
+
this.transition("idle");
|
|
533
|
+
}
|
|
534
|
+
} catch (drainError) {
|
|
535
|
+
debug.error("ENGINE_POST_DRAIN_ERROR", {
|
|
536
|
+
error: drainError,
|
|
537
|
+
channelSize: this.channel.size
|
|
538
|
+
});
|
|
539
|
+
logError(drainError);
|
|
540
|
+
this.emit({
|
|
541
|
+
type: "error",
|
|
542
|
+
error: drainError instanceof Error ? drainError : new Error(String(drainError))
|
|
543
|
+
});
|
|
544
|
+
if (this.channel.hasMessages()) {
|
|
545
|
+
const salvaged = this.channel.drain();
|
|
546
|
+
if (salvaged.length > 0) {
|
|
547
|
+
debug.trace("ENGINE_SALVAGED_MESSAGES", { count: salvaged.length });
|
|
548
|
+
const txt = salvaged.map((m) => m.text).join("\n\n");
|
|
549
|
+
const msg = createUserMessage(txt);
|
|
550
|
+
this._messages.push(msg);
|
|
551
|
+
this.emit({ type: "message", message: msg });
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (this._state === "querying") {
|
|
555
|
+
this.transition("idle");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Execute the inner query loop with post-loop mailbox drain.
|
|
562
|
+
* When the model finishes (no more tool_use), checks the mailbox —
|
|
563
|
+
* if messages arrived, re-enters query() so the agent can respond.
|
|
564
|
+
*/
|
|
565
|
+
async executeQueryLoop() {
|
|
566
|
+
const maxTurns = this.config.maxTurns ?? 0;
|
|
567
|
+
let keepRunning = true;
|
|
568
|
+
while (keepRunning) {
|
|
569
|
+
keepRunning = false;
|
|
570
|
+
const [systemPrompt, context] = await this.resolvePromptAndContext();
|
|
571
|
+
const queryOptions = this.buildQueryOptions();
|
|
572
|
+
for await (const message of query(
|
|
573
|
+
this._messages.filter((m) => !("localOnly" in m && m.localOnly)),
|
|
574
|
+
systemPrompt,
|
|
575
|
+
context,
|
|
576
|
+
this._canUseTool,
|
|
577
|
+
{
|
|
578
|
+
abortController: this.abortController,
|
|
579
|
+
options: queryOptions,
|
|
580
|
+
messageId: getLastAssistantMessageId(this._messages),
|
|
581
|
+
agentId: this.config.agentId,
|
|
582
|
+
readFileTimestamps: this.config.readFileTimestamps ?? {},
|
|
583
|
+
setToolJSX: this._setToolJSX,
|
|
584
|
+
askUser: this._askUser,
|
|
585
|
+
injectionChannel: this.channel
|
|
586
|
+
},
|
|
587
|
+
this._getBinaryFeedbackResponse
|
|
588
|
+
)) {
|
|
589
|
+
this._messages.push(message);
|
|
590
|
+
this.emit({ type: "message", message });
|
|
591
|
+
this.config.onMessage?.(message);
|
|
592
|
+
if (message.type !== "assistant") {
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
if (maxTurns > 0 && this.getAssistantTurnCount() >= maxTurns) break;
|
|
596
|
+
}
|
|
597
|
+
debug.trace("ENGINE_QUERY_LOOP_POST", {
|
|
598
|
+
isAborted: this.isAborted(),
|
|
599
|
+
maxTurns,
|
|
600
|
+
assistantTurns: this.getAssistantTurnCount(),
|
|
601
|
+
channelSize: this.channel.size
|
|
602
|
+
});
|
|
603
|
+
if (!this.isAborted() && !(maxTurns > 0 && this.getAssistantTurnCount() >= maxTurns)) {
|
|
604
|
+
const drained = this.drainChannelInline();
|
|
605
|
+
debug.trace("ENGINE_DRAIN_INLINE", { drained });
|
|
606
|
+
if (drained) keepRunning = true;
|
|
607
|
+
if (this.config.teamName) {
|
|
608
|
+
if (this.drainMailbox()) keepRunning = true;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
/** Hard ceiling on total re-entries across all recursive postQueryDrain calls. */
|
|
614
|
+
static MAX_GLOBAL_REENTRY = 20;
|
|
615
|
+
/**
|
|
616
|
+
* Execute a single re-entry turn: push the injected message, emit events,
|
|
617
|
+
* create a fresh AbortController, run the query loop, and finalize.
|
|
618
|
+
* Consolidates the 15-line pattern that was duplicated in 4 places.
|
|
619
|
+
*/
|
|
620
|
+
async runReentryTurn(injectedMsg) {
|
|
621
|
+
this._messages.push(injectedMsg);
|
|
622
|
+
this.emit({ type: "message", message: injectedMsg });
|
|
623
|
+
this.config.onMessage?.(injectedMsg);
|
|
624
|
+
this.turnStartTime = Date.now();
|
|
625
|
+
this.abortController = new AbortController();
|
|
626
|
+
this.emit({ type: "turn_start" });
|
|
627
|
+
try {
|
|
628
|
+
await this.executeQueryLoop();
|
|
629
|
+
} catch (error) {
|
|
630
|
+
logError(error);
|
|
631
|
+
this.emit({
|
|
632
|
+
type: "error",
|
|
633
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
634
|
+
});
|
|
635
|
+
} finally {
|
|
636
|
+
const durationMs = this.turnStartTime ? Date.now() - this.turnStartTime : 0;
|
|
637
|
+
this.turnStartTime = null;
|
|
638
|
+
this.abortController = null;
|
|
639
|
+
this.emit({ type: "turn_end", durationMs });
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Post-query drain: check injection channel and direct mailbox.
|
|
644
|
+
* Uses iterative loop instead of recursion to bound call stack depth.
|
|
645
|
+
* Returns true if re-entry was triggered.
|
|
646
|
+
*/
|
|
647
|
+
async postQueryDrain() {
|
|
648
|
+
const maxReentry = this.config.maxReentry ?? 10;
|
|
649
|
+
let didReenter = false;
|
|
650
|
+
while (true) {
|
|
651
|
+
debug.trace("ENGINE_POST_QUERY_DRAIN", {
|
|
652
|
+
channelSize: this.channel.size,
|
|
653
|
+
reentryCount: this.reentryCount,
|
|
654
|
+
maxReentry
|
|
655
|
+
});
|
|
656
|
+
if (this.reentryCount >= AgentEngine.MAX_GLOBAL_REENTRY) {
|
|
657
|
+
debug.warn("ENGINE_GLOBAL_REENTRY_LIMIT", {
|
|
658
|
+
reentryCount: this.reentryCount,
|
|
659
|
+
limit: AgentEngine.MAX_GLOBAL_REENTRY
|
|
660
|
+
});
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
const pending = this.channel.drain();
|
|
664
|
+
if (pending.length > 0 && this.reentryCount < maxReentry) {
|
|
665
|
+
this.reentryCount++;
|
|
666
|
+
didReenter = true;
|
|
667
|
+
const injectionText = pending.map((m) => m.text).join("\n\n");
|
|
668
|
+
const injectedMsg = createUserMessage(injectionText);
|
|
669
|
+
this.emit({ type: "injected_messages", count: pending.length });
|
|
670
|
+
await this.runReentryTurn(injectedMsg);
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
if (this.config.teamName) {
|
|
674
|
+
try {
|
|
675
|
+
const { getTeam } = require("../services/agentTeams/teamManager");
|
|
676
|
+
const entry = getTeam(this.config.teamName);
|
|
677
|
+
if (entry && this.config.agentId) {
|
|
678
|
+
const unread = entry.mailbox.getUnread(this.config.agentId);
|
|
679
|
+
if (unread.length > 0 && this.reentryCount < maxReentry) {
|
|
680
|
+
this.reentryCount++;
|
|
681
|
+
didReenter = true;
|
|
682
|
+
const { formatTeamMessage } = require("../services/agentTeams/messageFormatter");
|
|
683
|
+
const formatted = unread.map((m) => formatTeamMessage(m)).join("\n\n");
|
|
684
|
+
entry.mailbox.markRead(unread.map((m) => m.id));
|
|
685
|
+
const injectedMsg = createUserMessage(formatted);
|
|
686
|
+
await this.runReentryTurn(injectedMsg);
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
} catch {
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
return didReenter;
|
|
696
|
+
}
|
|
697
|
+
// ── Internal: Channel + Mailbox drain ─────────────────────
|
|
698
|
+
/**
|
|
699
|
+
* Drain the injection channel inline: move pending channel messages
|
|
700
|
+
* into the conversation as a user message. Universal — works for
|
|
701
|
+
* any agent type (standalone, team-lead, teammate).
|
|
702
|
+
*
|
|
703
|
+
* Returns true if messages were injected.
|
|
704
|
+
*/
|
|
705
|
+
drainChannelInline() {
|
|
706
|
+
const pending = this.channel.drain();
|
|
707
|
+
if (pending.length === 0) return false;
|
|
708
|
+
const injectionText = pending.map((m) => m.text).join("\n\n");
|
|
709
|
+
const injectedMsg = createUserMessage(injectionText);
|
|
710
|
+
this._messages.push(injectedMsg);
|
|
711
|
+
this.emit({ type: "message", message: injectedMsg });
|
|
712
|
+
this.config.onMessage?.(injectedMsg);
|
|
713
|
+
this.emit({ type: "injected_messages", count: pending.length });
|
|
714
|
+
return true;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Drain pending mailbox messages and inject them into the conversation.
|
|
718
|
+
* Returns true if messages were injected.
|
|
719
|
+
*/
|
|
720
|
+
drainMailbox() {
|
|
721
|
+
if (!this.config.teamName) return false;
|
|
722
|
+
let injected = false;
|
|
723
|
+
try {
|
|
724
|
+
const { getTeam } = require("../services/agentTeams/teamManager");
|
|
725
|
+
const teamEntry = getTeam(this.config.teamName);
|
|
726
|
+
if (!teamEntry || !this.config.agentId) return false;
|
|
727
|
+
const fromSub = this.pendingFromSubscription.splice(0);
|
|
728
|
+
const unread = teamEntry.mailbox.getUnread(this.config.agentId);
|
|
729
|
+
const allNew = [...fromSub];
|
|
730
|
+
const seenIds = new Set(fromSub.map((m) => m.id));
|
|
731
|
+
for (const m of unread) {
|
|
732
|
+
if (!seenIds.has(m.id)) allNew.push(m);
|
|
733
|
+
}
|
|
734
|
+
if (allNew.length === 0) return false;
|
|
735
|
+
const {
|
|
736
|
+
formatTeamMessagesForInjection,
|
|
737
|
+
formatUserGuidanceForInjection,
|
|
738
|
+
humanizeProtocolMessage
|
|
739
|
+
} = require("../services/agentTeams/messageFormatter");
|
|
740
|
+
const userMsgs = allNew.filter((m) => m.from === "user");
|
|
741
|
+
const teamMsgs = allNew.filter((m) => m.from !== "user");
|
|
742
|
+
const humanizedTeamMsgs = teamMsgs.map((m) => {
|
|
743
|
+
const friendly = humanizeProtocolMessage(m.text);
|
|
744
|
+
return friendly ? { ...m, summary: friendly } : m;
|
|
745
|
+
});
|
|
746
|
+
if (userMsgs.length > 0) {
|
|
747
|
+
const guidanceText = userMsgs.map((m) => formatUserGuidanceForInjection(m.text)).join("\n\n");
|
|
748
|
+
const guidanceMsg = createUserMessage(guidanceText);
|
|
749
|
+
this._messages.push(guidanceMsg);
|
|
750
|
+
this.emit({ type: "message", message: guidanceMsg });
|
|
751
|
+
this.config.onMessage?.(guidanceMsg);
|
|
752
|
+
injected = true;
|
|
753
|
+
}
|
|
754
|
+
if (humanizedTeamMsgs.length > 0) {
|
|
755
|
+
const deliveryText = formatTeamMessagesForInjection(humanizedTeamMsgs);
|
|
756
|
+
const injectedMsg = createUserMessage(deliveryText);
|
|
757
|
+
this._messages.push(injectedMsg);
|
|
758
|
+
this.emit({ type: "message", message: injectedMsg });
|
|
759
|
+
this.config.onMessage?.(injectedMsg);
|
|
760
|
+
injected = true;
|
|
761
|
+
}
|
|
762
|
+
teamEntry.mailbox.markRead(allNew.map((m) => m.id));
|
|
763
|
+
} catch {
|
|
764
|
+
}
|
|
765
|
+
return injected;
|
|
766
|
+
}
|
|
767
|
+
// ── Internal: Idle timeout ────────────────────────────────
|
|
768
|
+
/**
|
|
769
|
+
* Wait for the configured idle timeout. Returns true if woken up
|
|
770
|
+
* by arriving messages, false if the timeout expired or was aborted.
|
|
771
|
+
*/
|
|
772
|
+
startIdleTimeout() {
|
|
773
|
+
const timeoutMs = this.config.idleTimeoutMs ?? 0;
|
|
774
|
+
if (timeoutMs <= 0) return Promise.resolve(false);
|
|
775
|
+
return new Promise((resolve) => {
|
|
776
|
+
if (this.pendingFromSubscription.length > 0) {
|
|
777
|
+
resolve(true);
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
this.idleResolve = resolve;
|
|
781
|
+
this.idleTimer = setTimeout(() => {
|
|
782
|
+
this.idleResolve = null;
|
|
783
|
+
this.idleTimer = null;
|
|
784
|
+
resolve(false);
|
|
785
|
+
}, timeoutMs);
|
|
786
|
+
if (this.config.teamName && this.config.agentId) {
|
|
787
|
+
try {
|
|
788
|
+
const { getTeam } = require("../services/agentTeams/teamManager");
|
|
789
|
+
const teamEntry = getTeam(this.config.teamName);
|
|
790
|
+
if (teamEntry) {
|
|
791
|
+
this.unsubMailbox?.();
|
|
792
|
+
this.unsubMailbox = teamEntry.mailbox.subscribe(
|
|
793
|
+
this.config.agentId,
|
|
794
|
+
(msg) => {
|
|
795
|
+
this.pendingFromSubscription.push(msg);
|
|
796
|
+
teamEntry.mailbox.markRead([msg.id]);
|
|
797
|
+
this.cancelIdleTimeout();
|
|
798
|
+
if (this.idleResolve) {
|
|
799
|
+
const r = this.idleResolve;
|
|
800
|
+
this.idleResolve = null;
|
|
801
|
+
r(true);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
} catch {
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
this.idleAbortController = new AbortController();
|
|
810
|
+
const handler = () => {
|
|
811
|
+
this.cancelIdleTimeout();
|
|
812
|
+
if (this.idleResolve) {
|
|
813
|
+
const r = this.idleResolve;
|
|
814
|
+
this.idleResolve = null;
|
|
815
|
+
r(false);
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
this.idleAbortController.signal.addEventListener("abort", handler, {
|
|
819
|
+
once: true
|
|
820
|
+
});
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
/** Dedicated abort controller for idle timeout, independent of query abort. */
|
|
824
|
+
idleAbortController = null;
|
|
825
|
+
cancelIdleTimeout() {
|
|
826
|
+
if (this.idleTimer) {
|
|
827
|
+
clearTimeout(this.idleTimer);
|
|
828
|
+
this.idleTimer = null;
|
|
829
|
+
}
|
|
830
|
+
if (this.idleAbortController) {
|
|
831
|
+
this.idleAbortController.abort();
|
|
832
|
+
this.idleAbortController = null;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Drain mailbox and re-enter query loop (used after idle wake-up).
|
|
837
|
+
* Uses a synthetic empty message as the trigger since drainMailbox()
|
|
838
|
+
* already pushed the actual messages into this._messages.
|
|
839
|
+
*/
|
|
840
|
+
async drainAndReQuery() {
|
|
841
|
+
const drained = this.drainMailbox();
|
|
842
|
+
if (!drained) return;
|
|
843
|
+
this.transition("querying");
|
|
844
|
+
this.turnStartTime = Date.now();
|
|
845
|
+
this.abortController = new AbortController();
|
|
846
|
+
this.emit({ type: "turn_start" });
|
|
847
|
+
try {
|
|
848
|
+
await this.executeQueryLoop();
|
|
849
|
+
} catch (error) {
|
|
850
|
+
logError(error);
|
|
851
|
+
this.emit({
|
|
852
|
+
type: "error",
|
|
853
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
854
|
+
});
|
|
855
|
+
} finally {
|
|
856
|
+
const durationMs = this.turnStartTime ? Date.now() - this.turnStartTime : 0;
|
|
857
|
+
this.turnStartTime = null;
|
|
858
|
+
this.abortController = null;
|
|
859
|
+
this.emit({ type: "turn_end", durationMs });
|
|
860
|
+
this.transition("idle");
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
// ── Internal: Helpers ─────────────────────────────────────
|
|
864
|
+
isAborted() {
|
|
865
|
+
return this.abortController?.signal.aborted ?? false;
|
|
866
|
+
}
|
|
867
|
+
getAssistantTurnCount() {
|
|
868
|
+
return this._messages.filter((m) => m.type === "assistant").length;
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Resolve system prompt and context (supports both pre-built and async factory).
|
|
872
|
+
*/
|
|
873
|
+
async resolvePromptAndContext() {
|
|
874
|
+
const [systemPrompt, context] = await Promise.all([
|
|
875
|
+
typeof this.config.systemPrompt === "function" ? this.config.systemPrompt() : Promise.resolve(this.config.systemPrompt),
|
|
876
|
+
typeof this.config.context === "function" ? this.config.context() : Promise.resolve(this.config.context)
|
|
877
|
+
]);
|
|
878
|
+
return [systemPrompt, context];
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Build query options from engine config.
|
|
882
|
+
*/
|
|
883
|
+
buildQueryOptions() {
|
|
884
|
+
return {
|
|
885
|
+
commands: this.config.commands ?? [],
|
|
886
|
+
forkNumber: this.config.forkNumber ?? 0,
|
|
887
|
+
messageLogName: this.config.messageLogName ?? "agent",
|
|
888
|
+
tools: this.config.tools,
|
|
889
|
+
verbose: this.config.verbose ?? false,
|
|
890
|
+
safeMode: this.config.safeMode,
|
|
891
|
+
safetyMode: this.config.safetyMode,
|
|
892
|
+
permissionMode: this.config.permissionMode,
|
|
893
|
+
maxThinkingTokens: this.config.maxThinkingTokens ?? 0,
|
|
894
|
+
isKodingRequest: this.config.isKodingRequest,
|
|
895
|
+
model: this.config.model
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
export {
|
|
900
|
+
AgentEngine
|
|
901
|
+
};
|
|
902
|
+
//# sourceMappingURL=AgentEngine.js.map
|