@within-7/minto 0.4.1 → 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
|
@@ -1,33 +1,69 @@
|
|
|
1
|
-
import { existsSync, readFileSync,
|
|
1
|
+
import { existsSync, readFileSync, mkdirSync, watch } from "fs";
|
|
2
|
+
import { acquireFileLock, releaseFileLock } from "../../utils/fileLock.js";
|
|
2
3
|
import { join } from "path";
|
|
3
4
|
import { homedir } from "os";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { atomicWriteFileSync } from "../../utils/atomicWrite.js";
|
|
7
|
+
import { debug as debugLog } from "../../utils/debugLogger.js";
|
|
8
|
+
const TeamMessageSchema = z.object({
|
|
9
|
+
id: z.string(),
|
|
10
|
+
from: z.string(),
|
|
11
|
+
to: z.string(),
|
|
12
|
+
text: z.string(),
|
|
13
|
+
summary: z.string().optional(),
|
|
14
|
+
color: z.string().optional(),
|
|
15
|
+
timestamp: z.number(),
|
|
16
|
+
type: z.enum(["direct", "broadcast"]),
|
|
17
|
+
read: z.boolean().optional()
|
|
18
|
+
});
|
|
19
|
+
const LOCK_MAX_RETRIES = 2;
|
|
20
|
+
const LOCK_RETRY_DELAY_MS = 500;
|
|
21
|
+
function cooperativeDelay(ms) {
|
|
22
|
+
try {
|
|
23
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
24
|
+
} catch {
|
|
25
|
+
const waitUntil = Date.now() + ms;
|
|
26
|
+
while (Date.now() < waitUntil) {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const MAX_MESSAGES_PER_INBOX = 1e3;
|
|
31
|
+
const READ_MESSAGE_TTL_MS = 5 * 60 * 1e3;
|
|
4
32
|
class Mailbox {
|
|
5
33
|
messages = [];
|
|
6
|
-
|
|
34
|
+
teamDir;
|
|
35
|
+
inboxesDir;
|
|
7
36
|
listeners = /* @__PURE__ */ new Map();
|
|
37
|
+
/** Whether load() has been called. Disk I/O is skipped until initialized. */
|
|
38
|
+
initialized = false;
|
|
39
|
+
/** Active fs.watch cleanups keyed by agentId */
|
|
40
|
+
watcherCleanups = /* @__PURE__ */ new Map();
|
|
8
41
|
constructor(teamName) {
|
|
9
|
-
this.
|
|
10
|
-
|
|
11
|
-
".minto",
|
|
12
|
-
"teams",
|
|
13
|
-
teamName,
|
|
14
|
-
"mailbox.json"
|
|
15
|
-
);
|
|
42
|
+
this.teamDir = join(homedir(), ".minto", "teams", teamName);
|
|
43
|
+
this.inboxesDir = join(this.teamDir, "inboxes");
|
|
16
44
|
}
|
|
17
45
|
/**
|
|
18
46
|
* Send a message to a specific teammate or broadcast to all
|
|
19
47
|
*/
|
|
20
|
-
send(from, to,
|
|
48
|
+
send(from, to, text, options) {
|
|
21
49
|
const msg = {
|
|
22
50
|
id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
23
51
|
from,
|
|
24
52
|
to,
|
|
25
|
-
|
|
53
|
+
text,
|
|
54
|
+
...options?.summary && { summary: options.summary },
|
|
55
|
+
...options?.color && { color: options.color },
|
|
26
56
|
timestamp: Date.now(),
|
|
27
57
|
type: to === "all" ? "broadcast" : "direct"
|
|
28
58
|
};
|
|
29
59
|
this.messages.push(msg);
|
|
30
|
-
this.
|
|
60
|
+
if (this.initialized) {
|
|
61
|
+
if (to === "all") {
|
|
62
|
+
this.broadcastToInboxes(msg);
|
|
63
|
+
} else {
|
|
64
|
+
this.appendToInbox(to, msg);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
31
67
|
const targetListeners = to === "all" ? Array.from(this.listeners.values()).flat() : this.listeners.get(to) || [];
|
|
32
68
|
for (const listener of targetListeners) {
|
|
33
69
|
try {
|
|
@@ -35,12 +71,15 @@ class Mailbox {
|
|
|
35
71
|
} catch {
|
|
36
72
|
}
|
|
37
73
|
}
|
|
74
|
+
this.prune();
|
|
38
75
|
return msg;
|
|
39
76
|
}
|
|
40
77
|
/**
|
|
41
|
-
* Get unread messages for a teammate
|
|
78
|
+
* Get unread messages for a teammate.
|
|
79
|
+
* Reloads from disk first to pick up cross-process messages.
|
|
42
80
|
*/
|
|
43
81
|
getUnread(recipientId) {
|
|
82
|
+
if (this.initialized) this.reloadInbox(recipientId);
|
|
44
83
|
return this.messages.filter(
|
|
45
84
|
(m) => !m.read && (m.to === recipientId || m.to === "all") && m.from !== recipientId
|
|
46
85
|
);
|
|
@@ -50,12 +89,21 @@ class Mailbox {
|
|
|
50
89
|
*/
|
|
51
90
|
markRead(messageIds) {
|
|
52
91
|
const idSet = new Set(messageIds);
|
|
92
|
+
const affectedRecipients = /* @__PURE__ */ new Set();
|
|
53
93
|
for (const msg of this.messages) {
|
|
54
94
|
if (idSet.has(msg.id)) {
|
|
55
95
|
msg.read = true;
|
|
96
|
+
if (msg.to !== "all") {
|
|
97
|
+
affectedRecipients.add(msg.to);
|
|
98
|
+
}
|
|
56
99
|
}
|
|
57
100
|
}
|
|
58
|
-
this.
|
|
101
|
+
if (this.initialized) {
|
|
102
|
+
for (const recipientId of affectedRecipients) {
|
|
103
|
+
this.persistInbox(recipientId);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
this.prune();
|
|
59
107
|
}
|
|
60
108
|
/**
|
|
61
109
|
* Subscribe to messages for a teammate
|
|
@@ -79,34 +127,366 @@ class Mailbox {
|
|
|
79
127
|
return [...this.messages];
|
|
80
128
|
}
|
|
81
129
|
/**
|
|
82
|
-
*
|
|
130
|
+
* Get all messages involving a specific agent (sent or received)
|
|
83
131
|
*/
|
|
84
|
-
|
|
132
|
+
getMessagesFor(agentId) {
|
|
133
|
+
if (this.initialized) this.reloadInbox(agentId);
|
|
134
|
+
return this.messages.filter(
|
|
135
|
+
(m) => m.from === agentId || m.to === agentId || m.to === "all"
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Load mailbox from disk (initial load).
|
|
140
|
+
*/
|
|
141
|
+
load() {
|
|
142
|
+
this.initialized = true;
|
|
143
|
+
this.ensureInboxesDir();
|
|
144
|
+
this.loadAllInboxes();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Prune stale messages to prevent unbounded memory growth.
|
|
148
|
+
* Removes read messages older than READ_MESSAGE_TTL_MS,
|
|
149
|
+
* then hard-caps at MAX_MESSAGES_PER_INBOX.
|
|
150
|
+
*/
|
|
151
|
+
prune() {
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
this.messages = this.messages.filter(
|
|
154
|
+
(m) => !m.read || now - m.timestamp < READ_MESSAGE_TTL_MS
|
|
155
|
+
);
|
|
156
|
+
if (this.messages.length > MAX_MESSAGES_PER_INBOX) {
|
|
157
|
+
this.messages = this.messages.slice(-MAX_MESSAGES_PER_INBOX);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// ── Per-agent inbox persistence ──────────────────────────────
|
|
161
|
+
ensureInboxesDir() {
|
|
85
162
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
writeFileSync(
|
|
91
|
-
this.storePath,
|
|
92
|
-
JSON.stringify(this.messages, null, 2),
|
|
93
|
-
"utf-8"
|
|
94
|
-
);
|
|
163
|
+
if (!existsSync(this.inboxesDir)) {
|
|
164
|
+
mkdirSync(this.inboxesDir, { recursive: true, mode: 448 });
|
|
165
|
+
}
|
|
95
166
|
} catch {
|
|
96
167
|
}
|
|
97
168
|
}
|
|
98
169
|
/**
|
|
99
|
-
*
|
|
170
|
+
* Get the file path for an agent's inbox.
|
|
171
|
+
* Uses the agent name portion before '@' for readability.
|
|
100
172
|
*/
|
|
101
|
-
|
|
102
|
-
|
|
173
|
+
getInboxPath(agentId) {
|
|
174
|
+
const name = agentId.split("@")[0] || agentId;
|
|
175
|
+
const safeName = name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
176
|
+
return join(this.inboxesDir, `${safeName}.json`);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Append a message to a recipient's inbox file.
|
|
180
|
+
* Retries up to LOCK_MAX_RETRIES times on lock timeout.
|
|
181
|
+
*/
|
|
182
|
+
appendToInbox(recipientId, msg) {
|
|
183
|
+
this.ensureInboxesDir();
|
|
184
|
+
const path = this.getInboxPath(recipientId);
|
|
185
|
+
const lockPath = path + ".lock";
|
|
186
|
+
for (let attempt = 0; attempt <= LOCK_MAX_RETRIES; attempt++) {
|
|
187
|
+
if (attempt > 0) {
|
|
188
|
+
cooperativeDelay(LOCK_RETRY_DELAY_MS);
|
|
189
|
+
}
|
|
190
|
+
if (!acquireFileLock(lockPath, 3e3)) {
|
|
191
|
+
if (attempt === LOCK_MAX_RETRIES) {
|
|
192
|
+
debugLog.error("MAILBOX_LOCK_TIMEOUT", {
|
|
193
|
+
recipientId,
|
|
194
|
+
attempts: attempt + 1
|
|
195
|
+
});
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
debugLog.warn("MAILBOX_LOCK_RETRY", {
|
|
199
|
+
recipientId,
|
|
200
|
+
attempt: attempt + 1
|
|
201
|
+
});
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
let inbox = [];
|
|
206
|
+
if (existsSync(path)) {
|
|
207
|
+
const content = readFileSync(path, "utf-8");
|
|
208
|
+
const parsed = z.array(TeamMessageSchema).safeParse(JSON.parse(content));
|
|
209
|
+
if (parsed.success) {
|
|
210
|
+
inbox = parsed.data;
|
|
211
|
+
} else {
|
|
212
|
+
debugLog.warn("MAILBOX_VALIDATION_FAILED", {
|
|
213
|
+
recipientId,
|
|
214
|
+
error: parsed.error.message
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
inbox.push(msg);
|
|
219
|
+
atomicWriteFileSync(path, JSON.stringify(inbox), 384);
|
|
220
|
+
return;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
debugLog.warn("MAILBOX_APPEND_FAILED", { recipientId, error });
|
|
223
|
+
return;
|
|
224
|
+
} finally {
|
|
225
|
+
releaseFileLock(lockPath);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Write all messages for a recipient to their inbox file.
|
|
231
|
+
* Retries up to LOCK_MAX_RETRIES times on lock timeout.
|
|
232
|
+
*/
|
|
233
|
+
persistInbox(recipientId) {
|
|
234
|
+
this.ensureInboxesDir();
|
|
235
|
+
const path = this.getInboxPath(recipientId);
|
|
236
|
+
const lockPath = path + ".lock";
|
|
237
|
+
for (let attempt = 0; attempt <= LOCK_MAX_RETRIES; attempt++) {
|
|
238
|
+
if (attempt > 0) {
|
|
239
|
+
cooperativeDelay(LOCK_RETRY_DELAY_MS);
|
|
240
|
+
}
|
|
241
|
+
if (!acquireFileLock(lockPath, 3e3)) {
|
|
242
|
+
if (attempt === LOCK_MAX_RETRIES) {
|
|
243
|
+
debugLog.error("MAILBOX_LOCK_TIMEOUT", {
|
|
244
|
+
recipientId,
|
|
245
|
+
op: "persist",
|
|
246
|
+
attempts: attempt + 1
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
debugLog.warn("MAILBOX_LOCK_RETRY", {
|
|
251
|
+
recipientId,
|
|
252
|
+
op: "persist",
|
|
253
|
+
attempt: attempt + 1
|
|
254
|
+
});
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
const inbox = this.messages.filter(
|
|
259
|
+
(m) => m.to === recipientId || m.to === "all" && m.from !== recipientId
|
|
260
|
+
);
|
|
261
|
+
atomicWriteFileSync(path, JSON.stringify(inbox), 384);
|
|
262
|
+
return;
|
|
263
|
+
} catch (error) {
|
|
264
|
+
debugLog.warn("MAILBOX_PERSIST_FAILED", { recipientId, error });
|
|
265
|
+
return;
|
|
266
|
+
} finally {
|
|
267
|
+
releaseFileLock(lockPath);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Broadcast a message by appending it to every known inbox.
|
|
273
|
+
*/
|
|
274
|
+
broadcastToInboxes(msg) {
|
|
275
|
+
this.ensureInboxesDir();
|
|
276
|
+
try {
|
|
277
|
+
const { readdirSync } = require("fs");
|
|
278
|
+
const files = readdirSync(this.inboxesDir).filter(
|
|
279
|
+
(f) => f.endsWith(".json")
|
|
280
|
+
);
|
|
281
|
+
for (const file of files) {
|
|
282
|
+
const filePath = join(this.inboxesDir, file);
|
|
283
|
+
const lockFilePath = filePath + ".lock";
|
|
284
|
+
const agentName = file.replace(".json", "");
|
|
285
|
+
const senderName = msg.from.split("@")[0] || msg.from;
|
|
286
|
+
if (senderName === agentName) continue;
|
|
287
|
+
let lockAcquired = false;
|
|
288
|
+
for (let attempt = 0; attempt <= LOCK_MAX_RETRIES; attempt++) {
|
|
289
|
+
if (attempt > 0) {
|
|
290
|
+
cooperativeDelay(LOCK_RETRY_DELAY_MS);
|
|
291
|
+
}
|
|
292
|
+
if (acquireFileLock(lockFilePath, 3e3)) {
|
|
293
|
+
lockAcquired = true;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (!lockAcquired) {
|
|
298
|
+
debugLog.error("MAILBOX_BROADCAST_LOCK_TIMEOUT", { file });
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
let inbox = [];
|
|
303
|
+
const content = readFileSync(filePath, "utf-8");
|
|
304
|
+
const parsed = z.array(TeamMessageSchema).safeParse(JSON.parse(content));
|
|
305
|
+
if (parsed.success) {
|
|
306
|
+
inbox = parsed.data;
|
|
307
|
+
} else {
|
|
308
|
+
debugLog.warn("MAILBOX_BROADCAST_VALIDATION_FAILED", {
|
|
309
|
+
file,
|
|
310
|
+
error: parsed.error.message
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
inbox.push(msg);
|
|
314
|
+
atomicWriteFileSync(filePath, JSON.stringify(inbox), 384);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
debugLog.warn("MAILBOX_BROADCAST_WRITE_FAILED", { file, error });
|
|
317
|
+
} finally {
|
|
318
|
+
releaseFileLock(lockFilePath);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} catch (error) {
|
|
322
|
+
debugLog.warn("MAILBOX_BROADCAST_FAILED", { error });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Reload a specific agent's inbox from disk, merging new messages.
|
|
327
|
+
* Throttled to at most once per 100ms per agent.
|
|
328
|
+
*/
|
|
329
|
+
inboxReloadTimestamps = /* @__PURE__ */ new Map();
|
|
330
|
+
reloadInbox(agentId) {
|
|
331
|
+
const now = Date.now();
|
|
332
|
+
const lastReload = this.inboxReloadTimestamps.get(agentId) ?? 0;
|
|
333
|
+
if (now - lastReload < 100) return;
|
|
334
|
+
this.inboxReloadTimestamps.set(agentId, now);
|
|
335
|
+
const path = this.getInboxPath(agentId);
|
|
336
|
+
if (!existsSync(path)) return;
|
|
337
|
+
try {
|
|
338
|
+
const content = readFileSync(path, "utf-8");
|
|
339
|
+
const parsed = z.array(TeamMessageSchema).safeParse(JSON.parse(content));
|
|
340
|
+
if (!parsed.success) {
|
|
341
|
+
debugLog.warn("MAILBOX_RELOAD_VALIDATION_FAILED", {
|
|
342
|
+
agentId,
|
|
343
|
+
error: parsed.error.message
|
|
344
|
+
});
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const diskMessages = parsed.data;
|
|
348
|
+
const knownIds = new Set(this.messages.map((m) => m.id));
|
|
349
|
+
let merged = false;
|
|
350
|
+
for (const msg of diskMessages) {
|
|
351
|
+
if (!knownIds.has(msg.id)) {
|
|
352
|
+
this.messages.push(msg);
|
|
353
|
+
merged = true;
|
|
354
|
+
} else {
|
|
355
|
+
const local = this.messages.find((m) => m.id === msg.id);
|
|
356
|
+
if (local && msg.read && !local.read) {
|
|
357
|
+
local.read = true;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (merged) {
|
|
362
|
+
this.messages.sort((a, b) => a.timestamp - b.timestamp);
|
|
363
|
+
}
|
|
364
|
+
} catch (error) {
|
|
365
|
+
debugLog.warn("MAILBOX_RELOAD_FAILED", { agentId, error });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Load all inbox files into memory (initial load).
|
|
370
|
+
*/
|
|
371
|
+
loadAllInboxes() {
|
|
103
372
|
try {
|
|
104
|
-
const
|
|
105
|
-
|
|
373
|
+
const { readdirSync } = require("fs");
|
|
374
|
+
const files = readdirSync(this.inboxesDir).filter(
|
|
375
|
+
(f) => f.endsWith(".json")
|
|
376
|
+
);
|
|
377
|
+
const allMessages = [];
|
|
378
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
379
|
+
for (const file of files) {
|
|
380
|
+
try {
|
|
381
|
+
const content = readFileSync(join(this.inboxesDir, file), "utf-8");
|
|
382
|
+
const parsed = z.array(TeamMessageSchema).safeParse(JSON.parse(content));
|
|
383
|
+
if (!parsed.success) {
|
|
384
|
+
debugLog.warn("MAILBOX_LOAD_VALIDATION_FAILED", {
|
|
385
|
+
file,
|
|
386
|
+
error: parsed.error.message
|
|
387
|
+
});
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
const inbox = parsed.data;
|
|
391
|
+
for (const msg of inbox) {
|
|
392
|
+
if (!seenIds.has(msg.id)) {
|
|
393
|
+
allMessages.push(msg);
|
|
394
|
+
seenIds.add(msg.id);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
} catch {
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
this.messages = allMessages.sort((a, b) => a.timestamp - b.timestamp);
|
|
106
401
|
} catch {
|
|
107
402
|
this.messages = [];
|
|
108
403
|
}
|
|
109
404
|
}
|
|
405
|
+
// ── fs.watch for real-time cross-process delivery ─────────
|
|
406
|
+
/**
|
|
407
|
+
* Watch an agent's inbox file for cross-process changes.
|
|
408
|
+
* When the file is modified by another process, reloads it
|
|
409
|
+
* and fires subscribe() listeners for any new messages.
|
|
410
|
+
*
|
|
411
|
+
* This enables near-instant (~30ms) cross-process message delivery,
|
|
412
|
+
* replacing heartbeat polling as the primary detection mechanism.
|
|
413
|
+
* Returns an unsubscribe function that stops the watcher.
|
|
414
|
+
*/
|
|
415
|
+
startWatching(agentId) {
|
|
416
|
+
if (this.watcherCleanups.has(agentId)) {
|
|
417
|
+
return () => this.stopWatching(agentId);
|
|
418
|
+
}
|
|
419
|
+
this.ensureInboxesDir();
|
|
420
|
+
const inboxPath = this.getInboxPath(agentId);
|
|
421
|
+
const expectedFilename = inboxPath.split("/").pop();
|
|
422
|
+
let debounceTimer = null;
|
|
423
|
+
try {
|
|
424
|
+
const watcher = watch(
|
|
425
|
+
this.inboxesDir,
|
|
426
|
+
{ persistent: false },
|
|
427
|
+
(_event, filename) => {
|
|
428
|
+
if (!filename || filename !== expectedFilename) return;
|
|
429
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
430
|
+
debounceTimer = setTimeout(() => {
|
|
431
|
+
debounceTimer = null;
|
|
432
|
+
this.reloadAndNotify(agentId);
|
|
433
|
+
}, 30);
|
|
434
|
+
}
|
|
435
|
+
);
|
|
436
|
+
watcher.on("error", () => {
|
|
437
|
+
this.stopWatching(agentId);
|
|
438
|
+
});
|
|
439
|
+
this.watcherCleanups.set(agentId, () => {
|
|
440
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
441
|
+
try {
|
|
442
|
+
watcher.close();
|
|
443
|
+
} catch {
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
} catch {
|
|
447
|
+
}
|
|
448
|
+
return () => this.stopWatching(agentId);
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Stop watching an agent's inbox file.
|
|
452
|
+
*/
|
|
453
|
+
stopWatching(agentId) {
|
|
454
|
+
const cleanup = this.watcherCleanups.get(agentId);
|
|
455
|
+
if (cleanup) {
|
|
456
|
+
cleanup();
|
|
457
|
+
this.watcherCleanups.delete(agentId);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Stop all active file watchers.
|
|
462
|
+
*/
|
|
463
|
+
stopAllWatchers() {
|
|
464
|
+
for (const [agentId] of this.watcherCleanups) {
|
|
465
|
+
this.stopWatching(agentId);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Reload an inbox from disk (bypassing throttle) and fire
|
|
470
|
+
* subscribe() listeners for any genuinely new messages.
|
|
471
|
+
*/
|
|
472
|
+
reloadAndNotify(agentId) {
|
|
473
|
+
this.inboxReloadTimestamps.set(agentId, 0);
|
|
474
|
+
const beforeIds = new Set(this.messages.map((m) => m.id));
|
|
475
|
+
this.reloadInbox(agentId);
|
|
476
|
+
const newMessages = this.messages.filter(
|
|
477
|
+
(m) => !beforeIds.has(m.id) && !m.read && (m.to === agentId || m.to === "all") && m.from !== agentId
|
|
478
|
+
);
|
|
479
|
+
if (newMessages.length === 0) return;
|
|
480
|
+
const targetListeners = this.listeners.get(agentId) || [];
|
|
481
|
+
for (const msg of newMessages) {
|
|
482
|
+
for (const listener of targetListeners) {
|
|
483
|
+
try {
|
|
484
|
+
listener(msg);
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
110
490
|
}
|
|
111
491
|
export {
|
|
112
492
|
Mailbox
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/services/agentTeams/mailbox.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Agent Teams Mailbox\n *\n * Message passing system between teammates.\n * In-memory for in-process mode; file-based for cross-process (tmux).\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'\nimport { join } from 'path'\nimport { homedir } from 'os'\nimport type { TeamMessage } from '../../types/agentTeams'\n\n/**\n * Mailbox for a team's inter-agent messaging\n */\nexport class Mailbox {\n private messages: TeamMessage[] = []\n private storePath: string\n private listeners = new Map<string, ((msg: TeamMessage) => void)[]>()\n\n constructor(teamName: string) {\n this.storePath = join(\n homedir(),\n '.minto',\n 'teams',\n teamName,\n 'mailbox.json',\n )\n }\n\n /**\n * Send a message to a specific teammate or broadcast to all\n */\n send(from: string, to: string | 'all', content: string): TeamMessage {\n const msg: TeamMessage = {\n id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n from,\n to,\n content,\n timestamp: Date.now(),\n type: to === 'all' ? 'broadcast' : 'direct',\n }\n\n this.messages.push(msg)\n this.persist()\n\n // Notify listeners\n const targetListeners =\n to === 'all'\n ? Array.from(this.listeners.values()).flat()\n : this.listeners.get(to) || []\n\n for (const listener of targetListeners) {\n try {\n listener(msg)\n } catch {\n // Don't let listener errors break sending\n }\n }\n\n return msg\n }\n\n /**\n * Get unread messages for a teammate\n */\n getUnread(recipientId: string): TeamMessage[] {\n return this.messages.filter(\n m =>\n !m.read &&\n (m.to === recipientId || m.to === 'all') &&\n m.from !== recipientId,\n )\n }\n\n /**\n * Mark messages as read\n */\n markRead(messageIds: string[]): void {\n const idSet = new Set(messageIds)\n for (const msg of this.messages) {\n if (idSet.has(msg.id)) {\n msg.read = true\n }\n }\n this.persist()\n }\n\n /**\n * Subscribe to messages for a teammate\n */\n subscribe(\n recipientId: string,\n callback: (msg: TeamMessage) => void,\n ): () => void {\n const existing = this.listeners.get(recipientId) || []\n existing.push(callback)\n this.listeners.set(recipientId, existing)\n\n return () => {\n const list = this.listeners.get(recipientId)\n if (list) {\n const idx = list.indexOf(callback)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n }\n\n /**\n * Get all messages (for debugging/display)\n */\n getAll(): TeamMessage[] {\n return [...this.messages]\n }\n\n /**\n * Persist mailbox to disk (for cross-process teams)\n */\n private persist(): void {\n try {\n const dir = join(this.storePath, '..')\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n writeFileSync(\n this.storePath,\n JSON.stringify(this.messages, null, 2),\n 'utf-8',\n )\n } catch {\n // Best-effort persistence\n }\n }\n\n /**\n * Load mailbox from disk\n */\n load(): void {\n if (!existsSync(this.storePath)) return\n\n try {\n const content = readFileSync(this.storePath, 'utf-8')\n this.messages = JSON.parse(content)\n } catch {\n // Start fresh on parse error\n this.messages = []\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * Agent Teams Mailbox\n *\n * Message passing system between teammates.\n * Uses per-agent inbox files to reduce lock contention in multi-process mode.\n *\n * Directory structure:\n * ~/.minto/teams/<team>/\n * \u2514\u2500\u2500 inboxes/\n * \u251C\u2500\u2500 team-lead.json # team-lead's inbox\n * \u251C\u2500\u2500 researcher.json # researcher's inbox\n * \u2514\u2500\u2500 implementer.json # implementer's inbox\n */\n\nimport { existsSync, readFileSync, mkdirSync, watch } from 'fs'\nimport { randomUUID } from 'crypto'\nimport { acquireFileLock, releaseFileLock } from '@utils/fileLock'\nimport { join } from 'path'\nimport { homedir } from 'os'\nimport { z } from 'zod'\nimport type { TeamMessage } from '../../types/agentTeams'\nimport { atomicWriteFileSync } from '../../utils/atomicWrite'\nimport { debug as debugLog } from '../../utils/debugLogger'\n\n/** Zod schema for TeamMessage \u2014 used to validate JSON parsed from inbox files */\nconst TeamMessageSchema = z.object({\n id: z.string(),\n from: z.string(),\n to: z.string(),\n text: z.string(),\n summary: z.string().optional(),\n color: z.string().optional(),\n timestamp: z.number(),\n type: z.enum(['direct', 'broadcast']),\n read: z.boolean().optional(),\n})\n\n/** Max retries when file lock acquisition fails */\nconst LOCK_MAX_RETRIES = 2\n/** Delay between lock retries (ms) */\nconst LOCK_RETRY_DELAY_MS = 500\n\n/**\n * Cooperative delay that yields the thread instead of spin-waiting.\n * Uses Atomics.wait (true thread sleep) when available,\n * falls back to spin-wait for environments without SharedArrayBuffer.\n */\nfunction cooperativeDelay(ms: number): void {\n try {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms)\n } catch {\n const waitUntil = Date.now() + ms\n while (Date.now() < waitUntil) {\n /* fallback for environments without SharedArrayBuffer */\n }\n }\n}\n\n/**\n * Mailbox for a team's inter-agent messaging\n *\n * Uses per-agent inbox files for cross-process safety.\n */\n/** Max messages kept per inbox before hard-pruning */\nconst MAX_MESSAGES_PER_INBOX = 1000\n/** Read messages older than this are eligible for pruning */\nconst READ_MESSAGE_TTL_MS = 5 * 60 * 1000 // 5 minutes\n\nexport class Mailbox {\n private messages: TeamMessage[] = []\n private teamDir: string\n private inboxesDir: string\n private listeners = new Map<string, ((msg: TeamMessage) => void)[]>()\n /** Whether load() has been called. Disk I/O is skipped until initialized. */\n private initialized = false\n /** Active fs.watch cleanups keyed by agentId */\n private watcherCleanups = new Map<string, () => void>()\n\n constructor(teamName: string) {\n this.teamDir = join(homedir(), '.minto', 'teams', teamName)\n this.inboxesDir = join(this.teamDir, 'inboxes')\n }\n\n /**\n * Send a message to a specific teammate or broadcast to all\n */\n send(\n from: string,\n to: string | 'all',\n text: string,\n options?: {\n summary?: string\n color?: string\n },\n ): TeamMessage {\n const msg: TeamMessage = {\n id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n from,\n to,\n text,\n ...(options?.summary && { summary: options.summary }),\n ...(options?.color && { color: options.color }),\n timestamp: Date.now(),\n type: to === 'all' ? 'broadcast' : 'direct',\n }\n\n this.messages.push(msg)\n\n if (this.initialized) {\n if (to === 'all') {\n this.broadcastToInboxes(msg)\n } else {\n this.appendToInbox(to, msg)\n }\n }\n\n // Notify in-process listeners\n const targetListeners =\n to === 'all'\n ? Array.from(this.listeners.values()).flat()\n : this.listeners.get(to) || []\n\n for (const listener of targetListeners) {\n try {\n listener(msg)\n } catch {\n // Don't let listener errors break sending\n }\n }\n\n this.prune()\n return msg\n }\n\n /**\n * Get unread messages for a teammate.\n * Reloads from disk first to pick up cross-process messages.\n */\n getUnread(recipientId: string): TeamMessage[] {\n if (this.initialized) this.reloadInbox(recipientId)\n return this.messages.filter(\n m =>\n !m.read &&\n (m.to === recipientId || m.to === 'all') &&\n m.from !== recipientId,\n )\n }\n\n /**\n * Mark messages as read\n */\n markRead(messageIds: string[]): void {\n const idSet = new Set(messageIds)\n const affectedRecipients = new Set<string>()\n\n for (const msg of this.messages) {\n if (idSet.has(msg.id)) {\n msg.read = true\n if (msg.to !== 'all') {\n affectedRecipients.add(msg.to)\n }\n }\n }\n\n if (this.initialized) {\n for (const recipientId of affectedRecipients) {\n this.persistInbox(recipientId)\n }\n }\n this.prune()\n }\n\n /**\n * Subscribe to messages for a teammate\n */\n subscribe(\n recipientId: string,\n callback: (msg: TeamMessage) => void,\n ): () => void {\n const existing = this.listeners.get(recipientId) || []\n existing.push(callback)\n this.listeners.set(recipientId, existing)\n\n return () => {\n const list = this.listeners.get(recipientId)\n if (list) {\n const idx = list.indexOf(callback)\n if (idx >= 0) list.splice(idx, 1)\n }\n }\n }\n\n /**\n * Get all messages (for debugging/display)\n */\n getAll(): TeamMessage[] {\n return [...this.messages]\n }\n\n /**\n * Get all messages involving a specific agent (sent or received)\n */\n getMessagesFor(agentId: string): TeamMessage[] {\n if (this.initialized) this.reloadInbox(agentId)\n return this.messages.filter(\n m => m.from === agentId || m.to === agentId || m.to === 'all',\n )\n }\n\n /**\n * Load mailbox from disk (initial load).\n */\n load(): void {\n this.initialized = true\n this.ensureInboxesDir()\n this.loadAllInboxes()\n }\n\n /**\n * Prune stale messages to prevent unbounded memory growth.\n * Removes read messages older than READ_MESSAGE_TTL_MS,\n * then hard-caps at MAX_MESSAGES_PER_INBOX.\n */\n private prune(): void {\n const now = Date.now()\n // Remove read messages that have expired\n this.messages = this.messages.filter(\n m => !m.read || now - m.timestamp < READ_MESSAGE_TTL_MS,\n )\n // Hard cap: keep most recent messages\n if (this.messages.length > MAX_MESSAGES_PER_INBOX) {\n this.messages = this.messages.slice(-MAX_MESSAGES_PER_INBOX)\n }\n }\n\n // \u2500\u2500 Per-agent inbox persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private ensureInboxesDir(): void {\n try {\n if (!existsSync(this.inboxesDir)) {\n mkdirSync(this.inboxesDir, { recursive: true, mode: 0o700 })\n }\n } catch {\n // Best-effort\n }\n }\n\n /**\n * Get the file path for an agent's inbox.\n * Uses the agent name portion before '@' for readability.\n */\n private getInboxPath(agentId: string): string {\n // \"researcher@my-team\" \u2192 \"researcher.json\"\n const name = agentId.split('@')[0] || agentId\n // Sanitize for filesystem safety\n const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '_')\n return join(this.inboxesDir, `${safeName}.json`)\n }\n\n /**\n * Append a message to a recipient's inbox file.\n * Retries up to LOCK_MAX_RETRIES times on lock timeout.\n */\n private appendToInbox(recipientId: string, msg: TeamMessage): void {\n this.ensureInboxesDir()\n const path = this.getInboxPath(recipientId)\n const lockPath = path + '.lock'\n\n for (let attempt = 0; attempt <= LOCK_MAX_RETRIES; attempt++) {\n if (attempt > 0) {\n cooperativeDelay(LOCK_RETRY_DELAY_MS)\n }\n\n if (!acquireFileLock(lockPath, 3000)) {\n if (attempt === LOCK_MAX_RETRIES) {\n debugLog.error('MAILBOX_LOCK_TIMEOUT', {\n recipientId,\n attempts: attempt + 1,\n })\n return\n }\n debugLog.warn('MAILBOX_LOCK_RETRY', {\n recipientId,\n attempt: attempt + 1,\n })\n continue\n }\n\n try {\n let inbox: TeamMessage[] = []\n if (existsSync(path)) {\n const content = readFileSync(path, 'utf-8')\n const parsed = z\n .array(TeamMessageSchema)\n .safeParse(JSON.parse(content))\n if (parsed.success) {\n inbox = parsed.data as TeamMessage[]\n } else {\n debugLog.warn('MAILBOX_VALIDATION_FAILED', {\n recipientId,\n error: parsed.error.message,\n })\n // Fall back to empty inbox rather than corrupting further\n }\n }\n inbox.push(msg)\n atomicWriteFileSync(path, JSON.stringify(inbox), 0o600)\n return // success\n } catch (error) {\n debugLog.warn('MAILBOX_APPEND_FAILED', { recipientId, error })\n return\n } finally {\n releaseFileLock(lockPath)\n }\n }\n }\n\n /**\n * Write all messages for a recipient to their inbox file.\n * Retries up to LOCK_MAX_RETRIES times on lock timeout.\n */\n private persistInbox(recipientId: string): void {\n this.ensureInboxesDir()\n const path = this.getInboxPath(recipientId)\n const lockPath = path + '.lock'\n\n for (let attempt = 0; attempt <= LOCK_MAX_RETRIES; attempt++) {\n if (attempt > 0) {\n cooperativeDelay(LOCK_RETRY_DELAY_MS)\n }\n\n if (!acquireFileLock(lockPath, 3000)) {\n if (attempt === LOCK_MAX_RETRIES) {\n debugLog.error('MAILBOX_LOCK_TIMEOUT', {\n recipientId,\n op: 'persist',\n attempts: attempt + 1,\n })\n return\n }\n debugLog.warn('MAILBOX_LOCK_RETRY', {\n recipientId,\n op: 'persist',\n attempt: attempt + 1,\n })\n continue\n }\n\n try {\n const inbox = this.messages.filter(\n m =>\n m.to === recipientId || (m.to === 'all' && m.from !== recipientId),\n )\n atomicWriteFileSync(path, JSON.stringify(inbox), 0o600)\n return // success\n } catch (error) {\n debugLog.warn('MAILBOX_PERSIST_FAILED', { recipientId, error })\n return\n } finally {\n releaseFileLock(lockPath)\n }\n }\n }\n\n /**\n * Broadcast a message by appending it to every known inbox.\n */\n private broadcastToInboxes(msg: TeamMessage): void {\n this.ensureInboxesDir()\n try {\n const { readdirSync } = require('fs') as typeof import('fs')\n const files = readdirSync(this.inboxesDir).filter((f: string) =>\n f.endsWith('.json'),\n )\n for (const file of files) {\n const filePath = join(this.inboxesDir, file)\n const lockFilePath = filePath + '.lock'\n // Don't add to sender's own inbox\n const agentName = file.replace('.json', '')\n const senderName = msg.from.split('@')[0] || msg.from\n if (senderName === agentName) continue\n\n let lockAcquired = false\n for (let attempt = 0; attempt <= LOCK_MAX_RETRIES; attempt++) {\n if (attempt > 0) {\n cooperativeDelay(LOCK_RETRY_DELAY_MS)\n }\n if (acquireFileLock(lockFilePath, 3000)) {\n lockAcquired = true\n break\n }\n }\n if (!lockAcquired) {\n debugLog.error('MAILBOX_BROADCAST_LOCK_TIMEOUT', { file })\n continue\n }\n\n try {\n let inbox: TeamMessage[] = []\n const content = readFileSync(filePath, 'utf-8')\n const parsed = z\n .array(TeamMessageSchema)\n .safeParse(JSON.parse(content))\n if (parsed.success) {\n inbox = parsed.data as TeamMessage[]\n } else {\n debugLog.warn('MAILBOX_BROADCAST_VALIDATION_FAILED', {\n file,\n error: parsed.error.message,\n })\n }\n inbox.push(msg)\n atomicWriteFileSync(filePath, JSON.stringify(inbox), 0o600)\n } catch (error) {\n debugLog.warn('MAILBOX_BROADCAST_WRITE_FAILED', { file, error })\n } finally {\n releaseFileLock(lockFilePath)\n }\n }\n } catch (error) {\n debugLog.warn('MAILBOX_BROADCAST_FAILED', { error })\n }\n }\n\n /**\n * Reload a specific agent's inbox from disk, merging new messages.\n * Throttled to at most once per 100ms per agent.\n */\n private inboxReloadTimestamps = new Map<string, number>()\n private reloadInbox(agentId: string): void {\n const now = Date.now()\n const lastReload = this.inboxReloadTimestamps.get(agentId) ?? 0\n if (now - lastReload < 100) return\n this.inboxReloadTimestamps.set(agentId, now)\n\n const path = this.getInboxPath(agentId)\n if (!existsSync(path)) return\n\n try {\n const content = readFileSync(path, 'utf-8')\n const parsed = z.array(TeamMessageSchema).safeParse(JSON.parse(content))\n if (!parsed.success) {\n debugLog.warn('MAILBOX_RELOAD_VALIDATION_FAILED', {\n agentId,\n error: parsed.error.message,\n })\n return\n }\n const diskMessages = parsed.data as TeamMessage[]\n\n const knownIds = new Set(this.messages.map(m => m.id))\n let merged = false\n for (const msg of diskMessages) {\n if (!knownIds.has(msg.id)) {\n this.messages.push(msg)\n merged = true\n } else {\n // Sync read state from disk\n const local = this.messages.find(m => m.id === msg.id)\n if (local && msg.read && !local.read) {\n local.read = true\n }\n }\n }\n if (merged) {\n this.messages.sort((a, b) => a.timestamp - b.timestamp)\n }\n } catch (error) {\n debugLog.warn('MAILBOX_RELOAD_FAILED', { agentId, error })\n }\n }\n\n /**\n * Load all inbox files into memory (initial load).\n */\n private loadAllInboxes(): void {\n try {\n const { readdirSync } = require('fs') as typeof import('fs')\n const files = readdirSync(this.inboxesDir).filter((f: string) =>\n f.endsWith('.json'),\n )\n const allMessages: TeamMessage[] = []\n const seenIds = new Set<string>()\n\n for (const file of files) {\n try {\n const content = readFileSync(join(this.inboxesDir, file), 'utf-8')\n const parsed = z\n .array(TeamMessageSchema)\n .safeParse(JSON.parse(content))\n if (!parsed.success) {\n debugLog.warn('MAILBOX_LOAD_VALIDATION_FAILED', {\n file,\n error: parsed.error.message,\n })\n continue // Skip files that fail validation\n }\n const inbox = parsed.data as TeamMessage[]\n for (const msg of inbox) {\n if (!seenIds.has(msg.id)) {\n allMessages.push(msg)\n seenIds.add(msg.id)\n }\n }\n } catch {\n // Skip corrupt inbox files\n }\n }\n\n this.messages = allMessages.sort((a, b) => a.timestamp - b.timestamp)\n } catch {\n this.messages = []\n }\n }\n\n // \u2500\u2500 fs.watch for real-time cross-process delivery \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Watch an agent's inbox file for cross-process changes.\n * When the file is modified by another process, reloads it\n * and fires subscribe() listeners for any new messages.\n *\n * This enables near-instant (~30ms) cross-process message delivery,\n * replacing heartbeat polling as the primary detection mechanism.\n * Returns an unsubscribe function that stops the watcher.\n */\n startWatching(agentId: string): () => void {\n if (this.watcherCleanups.has(agentId)) {\n return () => this.stopWatching(agentId)\n }\n\n this.ensureInboxesDir()\n\n // Derive the expected filename (same logic as getInboxPath)\n const inboxPath = this.getInboxPath(agentId)\n const expectedFilename = inboxPath.split('/').pop()!\n\n let debounceTimer: ReturnType<typeof setTimeout> | null = null\n\n try {\n const watcher = watch(\n this.inboxesDir,\n { persistent: false },\n (_event, filename) => {\n if (!filename || filename !== expectedFilename) return\n\n // Debounce: batch rapid writes (e.g. multiple sends in quick succession)\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n debounceTimer = null\n this.reloadAndNotify(agentId)\n }, 30)\n },\n )\n\n watcher.on('error', () => {\n // Watcher failed \u2014 heartbeat safety net will continue polling\n this.stopWatching(agentId)\n })\n\n this.watcherCleanups.set(agentId, () => {\n if (debounceTimer) clearTimeout(debounceTimer)\n try {\n watcher.close()\n } catch {\n // Best-effort cleanup\n }\n })\n } catch {\n // fs.watch not available on this platform \u2014 fall back to heartbeat polling\n }\n\n return () => this.stopWatching(agentId)\n }\n\n /**\n * Stop watching an agent's inbox file.\n */\n stopWatching(agentId: string): void {\n const cleanup = this.watcherCleanups.get(agentId)\n if (cleanup) {\n cleanup()\n this.watcherCleanups.delete(agentId)\n }\n }\n\n /**\n * Stop all active file watchers.\n */\n stopAllWatchers(): void {\n for (const [agentId] of this.watcherCleanups) {\n this.stopWatching(agentId)\n }\n }\n\n /**\n * Reload an inbox from disk (bypassing throttle) and fire\n * subscribe() listeners for any genuinely new messages.\n */\n private reloadAndNotify(agentId: string): void {\n // Force fresh read by setting timestamp far enough in the past\n this.inboxReloadTimestamps.set(agentId, 0)\n\n // Snapshot current message IDs before reload\n const beforeIds = new Set(this.messages.map(m => m.id))\n\n // Reload from disk (reloadInbox sets timestamp to Date.now() after read)\n this.reloadInbox(agentId)\n\n // Find genuinely new, unread messages for this agent\n const newMessages = this.messages.filter(\n m =>\n !beforeIds.has(m.id) &&\n !m.read &&\n (m.to === agentId || m.to === 'all') &&\n m.from !== agentId,\n )\n\n if (newMessages.length === 0) return\n\n // Fire subscribe() listeners \u2014 same callback path as in-process sends\n const targetListeners = this.listeners.get(agentId) || []\n for (const msg of newMessages) {\n for (const listener of targetListeners) {\n try {\n listener(msg)\n } catch {\n // Don't let listener errors break the watcher\n }\n }\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
function sanitizeTeamMessageText(text) {
|
|
2
|
+
return text.replace(/<\/team-message>/gi, "[team-message-close]").replace(/<system-reminder>/gi, "[system-reminder-open]").replace(/<\/system-reminder>/gi, "[system-reminder-close]").replace(/<tool_use>/gi, "[tool-use-open]").replace(/<\/tool_use>/gi, "[tool-use-close]").replace(/<tool_result>/gi, "[tool-result-open]").replace(/<\/tool_result>/gi, "[tool-result-close]").replace(/<team-message\b/gi, "[team-message-open").replace(/<user-guidance>/gi, "[user-guidance-open]").replace(/<\/user-guidance>/gi, "[user-guidance-close]").replace(/<task-notification>/gi, "[task-notification-open]").replace(/<\/task-notification>/gi, "[task-notification-close]").replace(/<preloaded-skill>/gi, "[preloaded-skill-open]").replace(/<\/preloaded-skill>/gi, "[preloaded-skill-close]");
|
|
3
|
+
}
|
|
4
|
+
function formatTeamMessage(msg) {
|
|
5
|
+
const senderName = msg.from.split("@")[0] || msg.from;
|
|
6
|
+
const summaryText = msg.summary || msg.text.slice(0, 60).replace(/"/g, "'");
|
|
7
|
+
const colorAttr = msg.color ? ` color="${msg.color}"` : "";
|
|
8
|
+
const sanitizedText = sanitizeTeamMessageText(msg.text);
|
|
9
|
+
return `<team-message from="${senderName}"${colorAttr} summary="${summaryText}">
|
|
10
|
+
${sanitizedText}
|
|
11
|
+
</team-message>`;
|
|
12
|
+
}
|
|
13
|
+
function formatTeamMessagesForInjection(messages) {
|
|
14
|
+
return messages.map(formatTeamMessage).join("\n\n");
|
|
15
|
+
}
|
|
16
|
+
function formatUserGuidanceForInjection(input) {
|
|
17
|
+
const sanitizedInput = sanitizeTeamMessageText(input);
|
|
18
|
+
return `<user-guidance>
|
|
19
|
+
${sanitizedInput}
|
|
20
|
+
</user-guidance>`;
|
|
21
|
+
}
|
|
22
|
+
function formatIdleNotificationForInjection(notification) {
|
|
23
|
+
const parts = ["<task-notification>"];
|
|
24
|
+
if (notification.taskId) {
|
|
25
|
+
parts.push(` <task-id>${notification.taskId}</task-id>`);
|
|
26
|
+
}
|
|
27
|
+
parts.push(` <status>${notification.status}</status>`);
|
|
28
|
+
parts.push(
|
|
29
|
+
` <message>${notification.message || `Teammate "${notification.agentName}" is now ${notification.status}.`}</message>`
|
|
30
|
+
);
|
|
31
|
+
if (notification.summary) {
|
|
32
|
+
parts.push(` <summary>${notification.summary}</summary>`);
|
|
33
|
+
}
|
|
34
|
+
parts.push("</task-notification>");
|
|
35
|
+
return parts.join("\n");
|
|
36
|
+
}
|
|
37
|
+
const PROTOCOL_SUMMARIES = {
|
|
38
|
+
shutdown_approved: "Shutdown approved",
|
|
39
|
+
shutdown_rejected: "Shutdown rejected",
|
|
40
|
+
shutdown_request: "Shutdown requested",
|
|
41
|
+
plan_approved: "Plan approved",
|
|
42
|
+
plan_rejected: "Plan rejected",
|
|
43
|
+
plan_approval_request: "Plan review requested"
|
|
44
|
+
};
|
|
45
|
+
function humanizeProtocolMessage(text) {
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(text);
|
|
48
|
+
if (parsed && typeof parsed === "object" && parsed.type in PROTOCOL_SUMMARIES) {
|
|
49
|
+
return PROTOCOL_SUMMARIES[parsed.type] || parsed.type;
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function parseIdleNotification(text) {
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(text);
|
|
58
|
+
if (parsed && typeof parsed === "object" && (parsed.type === "idle_notification" || parsed.type === "task_completed" || parsed.type === "idle")) {
|
|
59
|
+
return {
|
|
60
|
+
agentName: parsed.agentName || parsed.from || "unknown",
|
|
61
|
+
status: parsed.status || parsed.completedStatus || "idle",
|
|
62
|
+
taskId: parsed.taskId,
|
|
63
|
+
message: parsed.message,
|
|
64
|
+
summary: parsed.summary
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
formatIdleNotificationForInjection,
|
|
73
|
+
formatTeamMessage,
|
|
74
|
+
formatTeamMessagesForInjection,
|
|
75
|
+
formatUserGuidanceForInjection,
|
|
76
|
+
humanizeProtocolMessage,
|
|
77
|
+
parseIdleNotification,
|
|
78
|
+
sanitizeTeamMessageText
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=messageFormatter.js.map
|