@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
|
@@ -306,20 +306,28 @@ ${validation.violations.map((v) => `- ${v.details}`).join("\n")}`,
|
|
|
306
306
|
};
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Escape a path for use in seatbelt profile strings.
|
|
311
|
+
* Prevents injection via backslashes or double quotes in directory names.
|
|
312
|
+
*/
|
|
313
|
+
escapeSbplPath(path) {
|
|
314
|
+
return path.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
315
|
+
}
|
|
309
316
|
/**
|
|
310
317
|
* Generate macOS seatbelt profile
|
|
311
318
|
*/
|
|
312
319
|
generateSeatbeltProfile() {
|
|
313
320
|
const writeAllowed = this.config.filesystem.writeAllowed.map((p) => {
|
|
314
|
-
if (p === "./")
|
|
321
|
+
if (p === "./")
|
|
322
|
+
return `(subpath "${this.escapeSbplPath(this.workingDir)}")`;
|
|
315
323
|
if (p === "*") return '(subpath "/")';
|
|
316
324
|
const absPath = resolve(this.workingDir, p);
|
|
317
|
-
return `(subpath "${absPath}")`;
|
|
325
|
+
return `(subpath "${this.escapeSbplPath(absPath)}")`;
|
|
318
326
|
}).join("\n ");
|
|
319
327
|
const readAllowed = this.config.filesystem.readAllowed.map((p) => {
|
|
320
328
|
if (p === "*") return '(subpath "/")';
|
|
321
329
|
const absPath = resolve(this.workingDir, p);
|
|
322
|
-
return `(subpath "${absPath}")`;
|
|
330
|
+
return `(subpath "${this.escapeSbplPath(absPath)}")`;
|
|
323
331
|
}).join("\n ");
|
|
324
332
|
const networkRules = this.config.network.blockAll ? "(deny network*)" : this.config.network.allowedDomains.length > 0 ? `(allow network-outbound
|
|
325
333
|
(remote tcp "${this.config.network.allowedDomains.join('", "')}"))` : "(allow network-outbound)";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/services/sandbox/sandboxController.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Sandbox Controller\n *\n * Main controller for the sandbox execution system.\n * Coordinates filesystem, network, and process isolation.\n */\n\nimport { spawn, type ChildProcess } from 'child_process'\nimport { platform } from 'os'\nimport { existsSync } from 'fs'\nimport { resolve } from 'path'\nimport type {\n ISandboxController,\n SandboxConfig,\n SandboxExecutionResult,\n SandboxImplementation,\n SandboxViolation,\n} from './types'\nimport { DEFAULT_SANDBOX_CONFIG } from './types'\nimport { FilesystemBoundary } from './filesystemBoundary'\nimport { NetworkProxy } from './networkProxy'\n\n/**\n * Sandbox Controller Implementation\n *\n * Provides unified sandbox management with OS-specific implementations.\n */\nexport class SandboxController implements ISandboxController {\n private config: SandboxConfig\n private implementation: SandboxImplementation\n private filesystemBoundary: FilesystemBoundary\n private networkProxy: NetworkProxy\n private violations: SandboxViolation[] = []\n private workingDir: string\n\n constructor(workingDir: string, config?: Partial<SandboxConfig>) {\n this.workingDir = resolve(workingDir)\n this.config = { ...DEFAULT_SANDBOX_CONFIG, ...config }\n this.implementation = this.detectImplementation()\n this.filesystemBoundary = new FilesystemBoundary(\n this.config.filesystem,\n this.workingDir,\n )\n this.networkProxy = new NetworkProxy(this.config.network)\n }\n\n /**\n * Check if sandbox is available on this system\n */\n async isAvailable(): Promise<boolean> {\n if (!this.config.enabled) {\n return false\n }\n\n switch (this.implementation) {\n case 'seatbelt':\n return this.isSeatbeltAvailable()\n case 'bubblewrap':\n return this.isBubblewrapAvailable()\n case 'docker':\n return this.isDockerAvailable()\n default:\n return false\n }\n }\n\n /**\n * Get the implementation type\n */\n getImplementationType(): SandboxImplementation {\n return this.implementation\n }\n\n /**\n * Initialize the sandbox\n */\n async initialize(config: SandboxConfig): Promise<void> {\n this.config = config\n this.filesystemBoundary = new FilesystemBoundary(\n config.filesystem,\n this.workingDir,\n )\n this.networkProxy = new NetworkProxy(config.network)\n this.violations = []\n }\n\n /**\n * Pre-validate a command against policies\n */\n async validateCommand(command: string): Promise<{\n valid: boolean\n violations: SandboxViolation[]\n }> {\n const violations: SandboxViolation[] = []\n\n // Check if command is excluded from sandboxing\n if (this.isExcludedCommand(command)) {\n return { valid: true, violations: [] }\n }\n\n // Validate filesystem access\n const fsResult = this.filesystemBoundary.analyzeCommand(command)\n for (const violation of fsResult.violations) {\n violation.command = command\n violations.push(violation)\n }\n\n // Validate network access\n const netResult = this.networkProxy.analyzeCommand(command)\n for (const violation of netResult.violations) {\n violation.command = command\n violations.push(violation)\n }\n\n // Store all violations\n this.violations.push(...violations)\n\n return {\n valid: violations.length === 0,\n violations,\n }\n }\n\n /**\n * Execute a command in the sandbox\n */\n async execute(\n command: string,\n workingDir: string,\n signal?: AbortSignal,\n timeout?: number,\n ): Promise<SandboxExecutionResult> {\n const startTime = Date.now()\n this.workingDir = resolve(workingDir)\n this.filesystemBoundary.setWorkingDir(this.workingDir)\n\n // If sandbox is disabled, execute directly\n if (!this.config.enabled) {\n return this.executeDirectly(command, signal, timeout)\n }\n\n // Validate command first\n const validation = await this.validateCommand(command)\n if (!validation.valid) {\n return {\n stdout: '',\n stderr: `Sandbox blocked command:\\n${validation.violations\n .map(v => `- ${v.details}`)\n .join('\\n')}`,\n exitCode: 1,\n interrupted: false,\n blocked: true,\n blockReason: validation.violations[0]?.details,\n duration: Date.now() - startTime,\n }\n }\n\n // Check if command is excluded from sandboxing\n if (this.isExcludedCommand(command)) {\n return this.executeDirectly(command, signal, timeout)\n }\n\n // Execute with appropriate sandbox\n switch (this.implementation) {\n case 'seatbelt':\n return this.executeWithSeatbelt(command, signal, timeout, startTime)\n case 'bubblewrap':\n return this.executeWithBubblewrap(command, signal, timeout, startTime)\n case 'docker':\n return this.executeWithDocker(command, signal, timeout, startTime)\n default:\n // No sandbox available, execute directly with validation only\n return this.executeDirectly(command, signal, timeout)\n }\n }\n\n /**\n * Get current configuration\n */\n getConfig(): SandboxConfig {\n return { ...this.config }\n }\n\n /**\n * Update configuration\n */\n updateConfig(config: Partial<SandboxConfig>): void {\n this.config = { ...this.config, ...config }\n if (config.filesystem) {\n this.filesystemBoundary.updatePolicy(config.filesystem)\n }\n if (config.network) {\n this.networkProxy.updatePolicy(config.network)\n }\n }\n\n /**\n * Get violation history\n */\n getViolations(): SandboxViolation[] {\n return [\n ...this.violations,\n ...this.filesystemBoundary.getViolations(),\n ...this.networkProxy.getViolations(),\n ]\n }\n\n /**\n * Clear violation history\n */\n clearViolations(): void {\n this.violations = []\n this.filesystemBoundary.clearViolations()\n this.networkProxy.clearViolations()\n }\n\n /**\n * Get filesystem boundary instance\n */\n getFilesystemBoundary(): FilesystemBoundary {\n return this.filesystemBoundary\n }\n\n /**\n * Get network proxy instance\n */\n getNetworkProxy(): NetworkProxy {\n return this.networkProxy\n }\n\n /**\n * Detect the best available sandbox implementation\n */\n private detectImplementation(): SandboxImplementation {\n const os = platform()\n\n if (os === 'darwin') {\n // macOS - use seatbelt/sandbox-exec\n return 'seatbelt'\n } else if (os === 'linux') {\n // Linux - prefer bubblewrap, fallback to docker\n if (this.hasBubblewrap()) {\n return 'bubblewrap'\n } else if (this.hasDocker()) {\n return 'docker'\n }\n }\n\n return 'none'\n }\n\n /**\n * Check if bubblewrap is installed\n */\n private hasBubblewrap(): boolean {\n return existsSync('/usr/bin/bwrap') || existsSync('/usr/local/bin/bwrap')\n }\n\n /**\n * Check if docker is available\n */\n private hasDocker(): boolean {\n return existsSync('/usr/bin/docker') || existsSync('/usr/local/bin/docker')\n }\n\n /**\n * Check if seatbelt (macOS sandbox-exec) is available\n */\n private async isSeatbeltAvailable(): Promise<boolean> {\n return platform() === 'darwin' && existsSync('/usr/bin/sandbox-exec')\n }\n\n /**\n * Check if bubblewrap is available and working\n */\n private async isBubblewrapAvailable(): Promise<boolean> {\n if (!this.hasBubblewrap()) return false\n try {\n const result = await this.spawnAsync('bwrap', ['--version'])\n return result.exitCode === 0\n } catch {\n return false\n }\n }\n\n /**\n * Check if docker is available and running\n */\n private async isDockerAvailable(): Promise<boolean> {\n if (!this.hasDocker()) return false\n try {\n const result = await this.spawnAsync('docker', ['info'])\n return result.exitCode === 0\n } catch {\n return false\n }\n }\n\n /**\n * Check if a command is excluded from sandboxing\n */\n private isExcludedCommand(command: string): boolean {\n const firstWord = command.trim().split(/\\s+/)[0]?.toLowerCase()\n if (!firstWord) return false\n return this.config.process.excludedCommands.some(\n excluded => firstWord === excluded.toLowerCase(),\n )\n }\n\n /**\n * Execute command directly without sandboxing\n */\n private async executeDirectly(\n command: string,\n signal?: AbortSignal,\n timeout?: number,\n ): Promise<SandboxExecutionResult> {\n const startTime = Date.now()\n const effectiveTimeout = timeout || this.config.process.maxExecutionTime\n\n try {\n const result = await this.spawnAsync('sh', ['-c', command], {\n cwd: this.workingDir,\n signal,\n timeout: effectiveTimeout,\n })\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: result.exitCode,\n interrupted: result.interrupted,\n blocked: false,\n duration: Date.now() - startTime,\n }\n } catch (error) {\n const err = error as Error\n return {\n stdout: '',\n stderr: err.message,\n exitCode: 1,\n interrupted: signal?.aborted || false,\n blocked: false,\n duration: Date.now() - startTime,\n }\n }\n }\n\n /**\n * Execute command with macOS seatbelt sandbox\n */\n private async executeWithSeatbelt(\n command: string,\n signal?: AbortSignal,\n timeout?: number,\n startTime?: number,\n ): Promise<SandboxExecutionResult> {\n const start = startTime || Date.now()\n const effectiveTimeout = timeout || this.config.process.maxExecutionTime\n\n // Generate seatbelt profile\n const profile = this.generateSeatbeltProfile()\n\n try {\n const result = await this.spawnAsync(\n 'sandbox-exec',\n ['-p', profile, 'sh', '-c', command],\n {\n cwd: this.workingDir,\n signal,\n timeout: effectiveTimeout,\n },\n )\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: result.exitCode,\n interrupted: result.interrupted,\n blocked: false,\n duration: Date.now() - start,\n }\n } catch (error) {\n const err = error as Error\n const isBlocked =\n err.message.includes('sandbox') || err.message.includes('denied')\n return {\n stdout: '',\n stderr: err.message,\n exitCode: 1,\n interrupted: signal?.aborted || false,\n blocked: isBlocked,\n blockReason: isBlocked ? 'Sandbox policy violation' : undefined,\n duration: Date.now() - start,\n }\n }\n }\n\n /**\n * Generate macOS seatbelt profile\n */\n private generateSeatbeltProfile(): string {\n const writeAllowed = this.config.filesystem.writeAllowed\n .map(p => {\n if (p === './') return `(subpath \"${this.workingDir}\")`\n if (p === '*') return '(subpath \"/\")'\n const absPath = resolve(this.workingDir, p)\n return `(subpath \"${absPath}\")`\n })\n .join('\\n ')\n\n const readAllowed = this.config.filesystem.readAllowed\n .map(p => {\n if (p === '*') return '(subpath \"/\")'\n const absPath = resolve(this.workingDir, p)\n return `(subpath \"${absPath}\")`\n })\n .join('\\n ')\n\n const networkRules = this.config.network.blockAll\n ? '(deny network*)'\n : this.config.network.allowedDomains.length > 0\n ? `(allow network-outbound\n (remote tcp \"${this.config.network.allowedDomains.join('\", \"')}\"))`\n : '(allow network-outbound)'\n\n return `\n(version 1)\n(deny default)\n\n; Allow basic system operations\n(allow process-exec*)\n(allow process-fork)\n(allow file-read-metadata)\n(allow sysctl-read)\n\n; Allow reading system libraries and executables\n(allow file-read*\n (subpath \"/usr\")\n (subpath \"/bin\")\n (subpath \"/sbin\")\n (subpath \"/System\")\n (subpath \"/Library\")\n (subpath \"/private/var\")\n (subpath \"/private/tmp\")\n (subpath \"/dev\")\n (subpath \"/tmp\")\n ${readAllowed}\n)\n\n; Allow writing to specific paths\n(allow file-write*\n (subpath \"/dev\")\n (subpath \"/private/tmp\")\n (subpath \"/tmp\")\n ${writeAllowed}\n)\n\n; Network rules\n${networkRules}\n\n; Allow stdout/stderr\n(allow file-write-data\n (literal \"/dev/null\")\n (literal \"/dev/zero\")\n (literal \"/dev/random\")\n (literal \"/dev/urandom\")\n (literal \"/dev/tty\")\n (literal \"/dev/console\")\n)\n\n; Allow process management\n(allow signal)\n(allow mach-lookup)\n(allow ipc-posix-shm-read-data)\n(allow ipc-posix-shm-write-data)\n`\n }\n\n /**\n * Execute command with Linux bubblewrap sandbox\n */\n private async executeWithBubblewrap(\n command: string,\n signal?: AbortSignal,\n timeout?: number,\n startTime?: number,\n ): Promise<SandboxExecutionResult> {\n const start = startTime || Date.now()\n const effectiveTimeout = timeout || this.config.process.maxExecutionTime\n\n // Build bubblewrap arguments\n const bwrapArgs = this.buildBubblewrapArgs()\n\n try {\n const result = await this.spawnAsync(\n 'bwrap',\n [...bwrapArgs, 'sh', '-c', command],\n {\n cwd: this.workingDir,\n signal,\n timeout: effectiveTimeout,\n },\n )\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: result.exitCode,\n interrupted: result.interrupted,\n blocked: false,\n duration: Date.now() - start,\n }\n } catch (error) {\n const err = error as Error\n return {\n stdout: '',\n stderr: err.message,\n exitCode: 1,\n interrupted: signal?.aborted || false,\n blocked: false,\n duration: Date.now() - start,\n }\n }\n }\n\n /**\n * Build bubblewrap command arguments\n */\n private buildBubblewrapArgs(): string[] {\n const args: string[] = []\n\n // Basic sandboxing\n args.push('--unshare-all')\n args.push('--die-with-parent')\n\n // Mount system directories read-only\n args.push('--ro-bind', '/usr', '/usr')\n args.push('--ro-bind', '/bin', '/bin')\n args.push('--ro-bind', '/lib', '/lib')\n if (existsSync('/lib64')) {\n args.push('--ro-bind', '/lib64', '/lib64')\n }\n args.push('--ro-bind', '/etc', '/etc')\n\n // Allow /tmp\n args.push('--tmpfs', '/tmp')\n\n // Set working directory with read-write access\n args.push('--bind', this.workingDir, this.workingDir)\n args.push('--chdir', this.workingDir)\n\n // Add additional read paths\n for (const path of this.config.filesystem.readAllowed) {\n if (path !== '*' && path !== './' && existsSync(path)) {\n const absPath = resolve(this.workingDir, path)\n if (existsSync(absPath)) {\n args.push('--ro-bind', absPath, absPath)\n }\n }\n }\n\n // Add additional write paths\n for (const path of this.config.filesystem.writeAllowed) {\n if (path !== './' && path !== '*') {\n const absPath = resolve(this.workingDir, path)\n if (existsSync(absPath)) {\n args.push('--bind', absPath, absPath)\n }\n }\n }\n\n // Network isolation (if blocking all)\n if (this.config.network.blockAll) {\n args.push('--unshare-net')\n }\n\n // Process limits\n if (this.config.process.maxProcesses > 0) {\n args.push(\n '--setenv',\n 'MINTO_MAX_PROCS',\n String(this.config.process.maxProcesses),\n )\n }\n\n return args\n }\n\n /**\n * Execute command with Docker sandbox\n */\n private async executeWithDocker(\n command: string,\n signal?: AbortSignal,\n timeout?: number,\n startTime?: number,\n ): Promise<SandboxExecutionResult> {\n const start = startTime || Date.now()\n const effectiveTimeout = timeout || this.config.process.maxExecutionTime\n\n // Build docker run arguments\n const dockerArgs = [\n 'run',\n '--rm',\n '-i',\n '--network',\n this.config.network.blockAll ? 'none' : 'bridge',\n '-v',\n `${this.workingDir}:/workspace`,\n '-w',\n '/workspace',\n ]\n\n // Add memory limit\n if (this.config.process.maxMemory > 0) {\n dockerArgs.push('--memory', `${this.config.process.maxMemory}`)\n }\n\n // Use a minimal image\n dockerArgs.push('alpine:latest')\n dockerArgs.push('sh', '-c', command)\n\n try {\n const result = await this.spawnAsync('docker', dockerArgs, {\n cwd: this.workingDir,\n signal,\n timeout: effectiveTimeout,\n })\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: result.exitCode,\n interrupted: result.interrupted,\n blocked: false,\n duration: Date.now() - start,\n }\n } catch (error) {\n const err = error as Error\n return {\n stdout: '',\n stderr: err.message,\n exitCode: 1,\n interrupted: signal?.aborted || false,\n blocked: false,\n duration: Date.now() - start,\n }\n }\n }\n\n /**\n * Spawn a process with promise wrapper\n */\n private spawnAsync(\n cmd: string,\n args: string[],\n options?: {\n cwd?: string\n signal?: AbortSignal\n timeout?: number\n },\n ): Promise<{\n stdout: string\n stderr: string\n exitCode: number\n interrupted: boolean\n }> {\n return new Promise((resolve, reject) => {\n let stdout = ''\n let stderr = ''\n let interrupted = false\n let child: ChildProcess | null = null\n\n const timeoutId = options?.timeout\n ? setTimeout(() => {\n interrupted = true\n child?.kill('SIGTERM')\n }, options.timeout)\n : null\n\n // Handle abort signal\n if (options?.signal) {\n options.signal.addEventListener('abort', () => {\n interrupted = true\n child?.kill('SIGTERM')\n })\n }\n\n child = spawn(cmd, args, {\n cwd: options?.cwd || this.workingDir,\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n child.stdout?.on('data', (data: Buffer) => {\n stdout += data.toString()\n })\n\n child.stderr?.on('data', (data: Buffer) => {\n stderr += data.toString()\n })\n\n child.on('error', (error: Error) => {\n if (timeoutId) clearTimeout(timeoutId)\n reject(error)\n })\n\n child.on('close', (code: number | null) => {\n if (timeoutId) clearTimeout(timeoutId)\n resolve({\n stdout,\n stderr,\n exitCode: code ?? 1,\n interrupted,\n })\n })\n })\n }\n}\n\n// Global singleton instance\nlet globalSandboxController: SandboxController | null = null\n\n/**\n * Get the global sandbox controller instance\n */\nexport function getSandboxController(workingDir?: string): SandboxController {\n if (!globalSandboxController) {\n globalSandboxController = new SandboxController(workingDir || process.cwd())\n } else if (workingDir) {\n // Update working directory if provided\n globalSandboxController = new SandboxController(\n workingDir,\n globalSandboxController.getConfig(),\n )\n }\n return globalSandboxController\n}\n\n/**\n * Reset the global sandbox controller (for testing)\n */\nexport function resetSandboxController(): void {\n globalSandboxController = null\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Sandbox Controller\n *\n * Main controller for the sandbox execution system.\n * Coordinates filesystem, network, and process isolation.\n */\n\nimport { spawn, type ChildProcess } from 'child_process'\nimport { platform } from 'os'\nimport { existsSync } from 'fs'\nimport { resolve } from 'path'\nimport type {\n ISandboxController,\n SandboxConfig,\n SandboxExecutionResult,\n SandboxImplementation,\n SandboxViolation,\n} from './types'\nimport { DEFAULT_SANDBOX_CONFIG } from './types'\nimport { FilesystemBoundary } from './filesystemBoundary'\nimport { NetworkProxy } from './networkProxy'\n\n/**\n * Sandbox Controller Implementation\n *\n * Provides unified sandbox management with OS-specific implementations.\n */\nexport class SandboxController implements ISandboxController {\n private config: SandboxConfig\n private implementation: SandboxImplementation\n private filesystemBoundary: FilesystemBoundary\n private networkProxy: NetworkProxy\n private violations: SandboxViolation[] = []\n private workingDir: string\n\n constructor(workingDir: string, config?: Partial<SandboxConfig>) {\n this.workingDir = resolve(workingDir)\n this.config = { ...DEFAULT_SANDBOX_CONFIG, ...config }\n this.implementation = this.detectImplementation()\n this.filesystemBoundary = new FilesystemBoundary(\n this.config.filesystem,\n this.workingDir,\n )\n this.networkProxy = new NetworkProxy(this.config.network)\n }\n\n /**\n * Check if sandbox is available on this system\n */\n async isAvailable(): Promise<boolean> {\n if (!this.config.enabled) {\n return false\n }\n\n switch (this.implementation) {\n case 'seatbelt':\n return this.isSeatbeltAvailable()\n case 'bubblewrap':\n return this.isBubblewrapAvailable()\n case 'docker':\n return this.isDockerAvailable()\n default:\n return false\n }\n }\n\n /**\n * Get the implementation type\n */\n getImplementationType(): SandboxImplementation {\n return this.implementation\n }\n\n /**\n * Initialize the sandbox\n */\n async initialize(config: SandboxConfig): Promise<void> {\n this.config = config\n this.filesystemBoundary = new FilesystemBoundary(\n config.filesystem,\n this.workingDir,\n )\n this.networkProxy = new NetworkProxy(config.network)\n this.violations = []\n }\n\n /**\n * Pre-validate a command against policies\n */\n async validateCommand(command: string): Promise<{\n valid: boolean\n violations: SandboxViolation[]\n }> {\n const violations: SandboxViolation[] = []\n\n // Check if command is excluded from sandboxing\n if (this.isExcludedCommand(command)) {\n return { valid: true, violations: [] }\n }\n\n // Validate filesystem access\n const fsResult = this.filesystemBoundary.analyzeCommand(command)\n for (const violation of fsResult.violations) {\n violation.command = command\n violations.push(violation)\n }\n\n // Validate network access\n const netResult = this.networkProxy.analyzeCommand(command)\n for (const violation of netResult.violations) {\n violation.command = command\n violations.push(violation)\n }\n\n // Store all violations\n this.violations.push(...violations)\n\n return {\n valid: violations.length === 0,\n violations,\n }\n }\n\n /**\n * Execute a command in the sandbox\n */\n async execute(\n command: string,\n workingDir: string,\n signal?: AbortSignal,\n timeout?: number,\n ): Promise<SandboxExecutionResult> {\n const startTime = Date.now()\n this.workingDir = resolve(workingDir)\n this.filesystemBoundary.setWorkingDir(this.workingDir)\n\n // If sandbox is disabled, execute directly\n if (!this.config.enabled) {\n return this.executeDirectly(command, signal, timeout)\n }\n\n // Validate command first\n const validation = await this.validateCommand(command)\n if (!validation.valid) {\n return {\n stdout: '',\n stderr: `Sandbox blocked command:\\n${validation.violations\n .map(v => `- ${v.details}`)\n .join('\\n')}`,\n exitCode: 1,\n interrupted: false,\n blocked: true,\n blockReason: validation.violations[0]?.details,\n duration: Date.now() - startTime,\n }\n }\n\n // Check if command is excluded from sandboxing\n if (this.isExcludedCommand(command)) {\n return this.executeDirectly(command, signal, timeout)\n }\n\n // Execute with appropriate sandbox\n switch (this.implementation) {\n case 'seatbelt':\n return this.executeWithSeatbelt(command, signal, timeout, startTime)\n case 'bubblewrap':\n return this.executeWithBubblewrap(command, signal, timeout, startTime)\n case 'docker':\n return this.executeWithDocker(command, signal, timeout, startTime)\n default:\n // No sandbox available, execute directly with validation only\n return this.executeDirectly(command, signal, timeout)\n }\n }\n\n /**\n * Get current configuration\n */\n getConfig(): SandboxConfig {\n return { ...this.config }\n }\n\n /**\n * Update configuration\n */\n updateConfig(config: Partial<SandboxConfig>): void {\n this.config = { ...this.config, ...config }\n if (config.filesystem) {\n this.filesystemBoundary.updatePolicy(config.filesystem)\n }\n if (config.network) {\n this.networkProxy.updatePolicy(config.network)\n }\n }\n\n /**\n * Get violation history\n */\n getViolations(): SandboxViolation[] {\n return [\n ...this.violations,\n ...this.filesystemBoundary.getViolations(),\n ...this.networkProxy.getViolations(),\n ]\n }\n\n /**\n * Clear violation history\n */\n clearViolations(): void {\n this.violations = []\n this.filesystemBoundary.clearViolations()\n this.networkProxy.clearViolations()\n }\n\n /**\n * Get filesystem boundary instance\n */\n getFilesystemBoundary(): FilesystemBoundary {\n return this.filesystemBoundary\n }\n\n /**\n * Get network proxy instance\n */\n getNetworkProxy(): NetworkProxy {\n return this.networkProxy\n }\n\n /**\n * Detect the best available sandbox implementation\n */\n private detectImplementation(): SandboxImplementation {\n const os = platform()\n\n if (os === 'darwin') {\n // macOS - use seatbelt/sandbox-exec\n return 'seatbelt'\n } else if (os === 'linux') {\n // Linux - prefer bubblewrap, fallback to docker\n if (this.hasBubblewrap()) {\n return 'bubblewrap'\n } else if (this.hasDocker()) {\n return 'docker'\n }\n }\n\n return 'none'\n }\n\n /**\n * Check if bubblewrap is installed\n */\n private hasBubblewrap(): boolean {\n return existsSync('/usr/bin/bwrap') || existsSync('/usr/local/bin/bwrap')\n }\n\n /**\n * Check if docker is available\n */\n private hasDocker(): boolean {\n return existsSync('/usr/bin/docker') || existsSync('/usr/local/bin/docker')\n }\n\n /**\n * Check if seatbelt (macOS sandbox-exec) is available\n */\n private async isSeatbeltAvailable(): Promise<boolean> {\n return platform() === 'darwin' && existsSync('/usr/bin/sandbox-exec')\n }\n\n /**\n * Check if bubblewrap is available and working\n */\n private async isBubblewrapAvailable(): Promise<boolean> {\n if (!this.hasBubblewrap()) return false\n try {\n const result = await this.spawnAsync('bwrap', ['--version'])\n return result.exitCode === 0\n } catch {\n return false\n }\n }\n\n /**\n * Check if docker is available and running\n */\n private async isDockerAvailable(): Promise<boolean> {\n if (!this.hasDocker()) return false\n try {\n const result = await this.spawnAsync('docker', ['info'])\n return result.exitCode === 0\n } catch {\n return false\n }\n }\n\n /**\n * Check if a command is excluded from sandboxing\n */\n private isExcludedCommand(command: string): boolean {\n const firstWord = command.trim().split(/\\s+/)[0]?.toLowerCase()\n if (!firstWord) return false\n return this.config.process.excludedCommands.some(\n excluded => firstWord === excluded.toLowerCase(),\n )\n }\n\n /**\n * Execute command directly without sandboxing\n */\n private async executeDirectly(\n command: string,\n signal?: AbortSignal,\n timeout?: number,\n ): Promise<SandboxExecutionResult> {\n const startTime = Date.now()\n const effectiveTimeout = timeout || this.config.process.maxExecutionTime\n\n try {\n const result = await this.spawnAsync('sh', ['-c', command], {\n cwd: this.workingDir,\n signal,\n timeout: effectiveTimeout,\n })\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: result.exitCode,\n interrupted: result.interrupted,\n blocked: false,\n duration: Date.now() - startTime,\n }\n } catch (error) {\n const err = error as Error\n return {\n stdout: '',\n stderr: err.message,\n exitCode: 1,\n interrupted: signal?.aborted || false,\n blocked: false,\n duration: Date.now() - startTime,\n }\n }\n }\n\n /**\n * Execute command with macOS seatbelt sandbox\n */\n private async executeWithSeatbelt(\n command: string,\n signal?: AbortSignal,\n timeout?: number,\n startTime?: number,\n ): Promise<SandboxExecutionResult> {\n const start = startTime || Date.now()\n const effectiveTimeout = timeout || this.config.process.maxExecutionTime\n\n // Generate seatbelt profile\n const profile = this.generateSeatbeltProfile()\n\n try {\n const result = await this.spawnAsync(\n 'sandbox-exec',\n ['-p', profile, 'sh', '-c', command],\n {\n cwd: this.workingDir,\n signal,\n timeout: effectiveTimeout,\n },\n )\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: result.exitCode,\n interrupted: result.interrupted,\n blocked: false,\n duration: Date.now() - start,\n }\n } catch (error) {\n const err = error as Error\n const isBlocked =\n err.message.includes('sandbox') || err.message.includes('denied')\n return {\n stdout: '',\n stderr: err.message,\n exitCode: 1,\n interrupted: signal?.aborted || false,\n blocked: isBlocked,\n blockReason: isBlocked ? 'Sandbox policy violation' : undefined,\n duration: Date.now() - start,\n }\n }\n }\n\n /**\n * Escape a path for use in seatbelt profile strings.\n * Prevents injection via backslashes or double quotes in directory names.\n */\n private escapeSbplPath(path: string): string {\n return path.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n }\n\n /**\n * Generate macOS seatbelt profile\n */\n private generateSeatbeltProfile(): string {\n const writeAllowed = this.config.filesystem.writeAllowed\n .map(p => {\n if (p === './')\n return `(subpath \"${this.escapeSbplPath(this.workingDir)}\")`\n if (p === '*') return '(subpath \"/\")'\n const absPath = resolve(this.workingDir, p)\n return `(subpath \"${this.escapeSbplPath(absPath)}\")`\n })\n .join('\\n ')\n\n const readAllowed = this.config.filesystem.readAllowed\n .map(p => {\n if (p === '*') return '(subpath \"/\")'\n const absPath = resolve(this.workingDir, p)\n return `(subpath \"${this.escapeSbplPath(absPath)}\")`\n })\n .join('\\n ')\n\n const networkRules = this.config.network.blockAll\n ? '(deny network*)'\n : this.config.network.allowedDomains.length > 0\n ? `(allow network-outbound\n (remote tcp \"${this.config.network.allowedDomains.join('\", \"')}\"))`\n : '(allow network-outbound)'\n\n return `\n(version 1)\n(deny default)\n\n; Allow basic system operations\n(allow process-exec*)\n(allow process-fork)\n(allow file-read-metadata)\n(allow sysctl-read)\n\n; Allow reading system libraries and executables\n(allow file-read*\n (subpath \"/usr\")\n (subpath \"/bin\")\n (subpath \"/sbin\")\n (subpath \"/System\")\n (subpath \"/Library\")\n (subpath \"/private/var\")\n (subpath \"/private/tmp\")\n (subpath \"/dev\")\n (subpath \"/tmp\")\n ${readAllowed}\n)\n\n; Allow writing to specific paths\n(allow file-write*\n (subpath \"/dev\")\n (subpath \"/private/tmp\")\n (subpath \"/tmp\")\n ${writeAllowed}\n)\n\n; Network rules\n${networkRules}\n\n; Allow stdout/stderr\n(allow file-write-data\n (literal \"/dev/null\")\n (literal \"/dev/zero\")\n (literal \"/dev/random\")\n (literal \"/dev/urandom\")\n (literal \"/dev/tty\")\n (literal \"/dev/console\")\n)\n\n; Allow process management\n(allow signal)\n(allow mach-lookup)\n(allow ipc-posix-shm-read-data)\n(allow ipc-posix-shm-write-data)\n`\n }\n\n /**\n * Execute command with Linux bubblewrap sandbox\n */\n private async executeWithBubblewrap(\n command: string,\n signal?: AbortSignal,\n timeout?: number,\n startTime?: number,\n ): Promise<SandboxExecutionResult> {\n const start = startTime || Date.now()\n const effectiveTimeout = timeout || this.config.process.maxExecutionTime\n\n // Build bubblewrap arguments\n const bwrapArgs = this.buildBubblewrapArgs()\n\n try {\n const result = await this.spawnAsync(\n 'bwrap',\n [...bwrapArgs, 'sh', '-c', command],\n {\n cwd: this.workingDir,\n signal,\n timeout: effectiveTimeout,\n },\n )\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: result.exitCode,\n interrupted: result.interrupted,\n blocked: false,\n duration: Date.now() - start,\n }\n } catch (error) {\n const err = error as Error\n return {\n stdout: '',\n stderr: err.message,\n exitCode: 1,\n interrupted: signal?.aborted || false,\n blocked: false,\n duration: Date.now() - start,\n }\n }\n }\n\n /**\n * Build bubblewrap command arguments\n */\n private buildBubblewrapArgs(): string[] {\n const args: string[] = []\n\n // Basic sandboxing\n args.push('--unshare-all')\n args.push('--die-with-parent')\n\n // Mount system directories read-only\n args.push('--ro-bind', '/usr', '/usr')\n args.push('--ro-bind', '/bin', '/bin')\n args.push('--ro-bind', '/lib', '/lib')\n if (existsSync('/lib64')) {\n args.push('--ro-bind', '/lib64', '/lib64')\n }\n args.push('--ro-bind', '/etc', '/etc')\n\n // Allow /tmp\n args.push('--tmpfs', '/tmp')\n\n // Set working directory with read-write access\n args.push('--bind', this.workingDir, this.workingDir)\n args.push('--chdir', this.workingDir)\n\n // Add additional read paths\n for (const path of this.config.filesystem.readAllowed) {\n if (path !== '*' && path !== './' && existsSync(path)) {\n const absPath = resolve(this.workingDir, path)\n if (existsSync(absPath)) {\n args.push('--ro-bind', absPath, absPath)\n }\n }\n }\n\n // Add additional write paths\n for (const path of this.config.filesystem.writeAllowed) {\n if (path !== './' && path !== '*') {\n const absPath = resolve(this.workingDir, path)\n if (existsSync(absPath)) {\n args.push('--bind', absPath, absPath)\n }\n }\n }\n\n // Network isolation (if blocking all)\n if (this.config.network.blockAll) {\n args.push('--unshare-net')\n }\n\n // Process limits\n if (this.config.process.maxProcesses > 0) {\n args.push(\n '--setenv',\n 'MINTO_MAX_PROCS',\n String(this.config.process.maxProcesses),\n )\n }\n\n return args\n }\n\n /**\n * Execute command with Docker sandbox\n */\n private async executeWithDocker(\n command: string,\n signal?: AbortSignal,\n timeout?: number,\n startTime?: number,\n ): Promise<SandboxExecutionResult> {\n const start = startTime || Date.now()\n const effectiveTimeout = timeout || this.config.process.maxExecutionTime\n\n // Build docker run arguments\n const dockerArgs = [\n 'run',\n '--rm',\n '-i',\n '--network',\n this.config.network.blockAll ? 'none' : 'bridge',\n '-v',\n `${this.workingDir}:/workspace`,\n '-w',\n '/workspace',\n ]\n\n // Add memory limit\n if (this.config.process.maxMemory > 0) {\n dockerArgs.push('--memory', `${this.config.process.maxMemory}`)\n }\n\n // Use a minimal image\n dockerArgs.push('alpine:latest')\n dockerArgs.push('sh', '-c', command)\n\n try {\n const result = await this.spawnAsync('docker', dockerArgs, {\n cwd: this.workingDir,\n signal,\n timeout: effectiveTimeout,\n })\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: result.exitCode,\n interrupted: result.interrupted,\n blocked: false,\n duration: Date.now() - start,\n }\n } catch (error) {\n const err = error as Error\n return {\n stdout: '',\n stderr: err.message,\n exitCode: 1,\n interrupted: signal?.aborted || false,\n blocked: false,\n duration: Date.now() - start,\n }\n }\n }\n\n /**\n * Spawn a process with promise wrapper\n */\n private spawnAsync(\n cmd: string,\n args: string[],\n options?: {\n cwd?: string\n signal?: AbortSignal\n timeout?: number\n },\n ): Promise<{\n stdout: string\n stderr: string\n exitCode: number\n interrupted: boolean\n }> {\n return new Promise((resolve, reject) => {\n let stdout = ''\n let stderr = ''\n let interrupted = false\n let child: ChildProcess | null = null\n\n const timeoutId = options?.timeout\n ? setTimeout(() => {\n interrupted = true\n child?.kill('SIGTERM')\n }, options.timeout)\n : null\n\n // Handle abort signal\n if (options?.signal) {\n options.signal.addEventListener('abort', () => {\n interrupted = true\n child?.kill('SIGTERM')\n })\n }\n\n child = spawn(cmd, args, {\n cwd: options?.cwd || this.workingDir,\n shell: false,\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n child.stdout?.on('data', (data: Buffer) => {\n stdout += data.toString()\n })\n\n child.stderr?.on('data', (data: Buffer) => {\n stderr += data.toString()\n })\n\n child.on('error', (error: Error) => {\n if (timeoutId) clearTimeout(timeoutId)\n reject(error)\n })\n\n child.on('close', (code: number | null) => {\n if (timeoutId) clearTimeout(timeoutId)\n resolve({\n stdout,\n stderr,\n exitCode: code ?? 1,\n interrupted,\n })\n })\n })\n }\n}\n\n// Global singleton instance\nlet globalSandboxController: SandboxController | null = null\n\n/**\n * Get the global sandbox controller instance\n */\nexport function getSandboxController(workingDir?: string): SandboxController {\n if (!globalSandboxController) {\n globalSandboxController = new SandboxController(workingDir || process.cwd())\n } else if (workingDir) {\n // Update working directory if provided\n globalSandboxController = new SandboxController(\n workingDir,\n globalSandboxController.getConfig(),\n )\n }\n return globalSandboxController\n}\n\n/**\n * Reset the global sandbox controller (for testing)\n */\nexport function resetSandboxController(): void {\n globalSandboxController = null\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["resolve"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { getGlobalConfig } from "../utils/config.js";
|
|
2
|
+
import { getOriginalCwd } from "../utils/state.js";
|
|
3
|
+
import { debug as debugLogger } from "../utils/debugLogger.js";
|
|
4
|
+
import { SessionMemoryManager } from "./sessionMemory.js";
|
|
5
|
+
let sessionMemoryLoaded = false;
|
|
6
|
+
let cachedSessionMemoryBlock = null;
|
|
7
|
+
function getSessionMemoryBlock() {
|
|
8
|
+
if (sessionMemoryLoaded) {
|
|
9
|
+
return cachedSessionMemoryBlock;
|
|
10
|
+
}
|
|
11
|
+
sessionMemoryLoaded = true;
|
|
12
|
+
try {
|
|
13
|
+
const config = getGlobalConfig();
|
|
14
|
+
if (config.enableSessionMemory === false) {
|
|
15
|
+
debugLogger.state("SESSION_MEMORY", { status: "disabled_by_config" });
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const projectPath = getOriginalCwd();
|
|
19
|
+
if (!projectPath) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const manager = new SessionMemoryManager(projectPath);
|
|
23
|
+
try {
|
|
24
|
+
manager.cleanupOldSessions(5);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
const recentSession = manager.getMostRecentSession();
|
|
28
|
+
if (!recentSession?.memory?.summary) {
|
|
29
|
+
debugLogger.state("SESSION_MEMORY", { status: "no_previous_session" });
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const block = formatSessionMemoryBlock(recentSession.memory);
|
|
33
|
+
if (!block) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
cachedSessionMemoryBlock = block;
|
|
37
|
+
debugLogger.state("SESSION_MEMORY", {
|
|
38
|
+
status: "injected",
|
|
39
|
+
summaryLength: String(recentSession.memory.summary.length),
|
|
40
|
+
decisionsCount: String(recentSession.memory.decisions?.length ?? 0)
|
|
41
|
+
});
|
|
42
|
+
return cachedSessionMemoryBlock;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
debugLogger.warn(
|
|
45
|
+
"SESSION_MEMORY",
|
|
46
|
+
`Failed to load session memory: ${error instanceof Error ? error.message : String(error)}`
|
|
47
|
+
);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function formatSessionMemoryBlock(memory) {
|
|
52
|
+
const parts = [];
|
|
53
|
+
if (!memory.summary || memory.summary.trim().length === 0) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
parts.push(memory.summary);
|
|
57
|
+
if (memory.decisions && memory.decisions.length > 0) {
|
|
58
|
+
const decisionsText = memory.decisions.slice(0, 10).map((d) => `- ${d.description}`).join("\n");
|
|
59
|
+
parts.push(`Key decisions:
|
|
60
|
+
${decisionsText}`);
|
|
61
|
+
}
|
|
62
|
+
if (memory.focusAreas && memory.focusAreas.length > 0) {
|
|
63
|
+
parts.push(`Focus areas: ${memory.focusAreas.join(", ")}`);
|
|
64
|
+
}
|
|
65
|
+
return `<session-memory>
|
|
66
|
+
${parts.join("\n")}
|
|
67
|
+
</session-memory>`;
|
|
68
|
+
}
|
|
69
|
+
function resetSessionMemoryCache() {
|
|
70
|
+
sessionMemoryLoaded = false;
|
|
71
|
+
cachedSessionMemoryBlock = null;
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
getSessionMemoryBlock,
|
|
75
|
+
resetSessionMemoryCache
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=sessionMemoryInjector.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/services/sessionMemoryInjector.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Session Memory Injector\n *\n * Loads the most recent session memory for the current project\n * and formats it as a system prompt block for injection into new sessions.\n *\n * This mirrors Claude Code's behavior of injecting <session-memory> tags\n * from past sessions into new conversations.\n */\n\nimport { getGlobalConfig } from '@utils/config'\nimport { getOriginalCwd } from '@utils/state'\nimport { debug as debugLogger } from '@utils/debugLogger'\nimport { SessionMemoryManager, type SessionMemoryData } from './sessionMemory'\n\n/**\n * Whether session memory has been loaded for this process.\n * We only load once per session to avoid repeated disk I/O.\n */\nlet sessionMemoryLoaded = false\nlet cachedSessionMemoryBlock: string | null = null\n\n/**\n * Load and format the most recent session memory as a system prompt block.\n *\n * Called once per session (at first query). Returns null if:\n * - enableSessionMemory config is false\n * - No previous sessions exist for this project\n * - The most recent session has no meaningful content\n */\nexport function getSessionMemoryBlock(): string | null {\n if (sessionMemoryLoaded) {\n return cachedSessionMemoryBlock\n }\n\n sessionMemoryLoaded = true\n\n try {\n const config = getGlobalConfig()\n\n // enableSessionMemory defaults to true when not explicitly set\n if (config.enableSessionMemory === false) {\n debugLogger.state('SESSION_MEMORY', { status: 'disabled_by_config' })\n return null\n }\n\n const projectPath = getOriginalCwd()\n if (!projectPath) {\n return null\n }\n\n const manager = new SessionMemoryManager(projectPath)\n\n // Cleanup old sessions (keep last 5) - non-blocking best-effort\n try {\n manager.cleanupOldSessions(5)\n } catch {\n // Non-critical\n }\n\n const recentSession = manager.getMostRecentSession()\n if (!recentSession?.memory?.summary) {\n debugLogger.state('SESSION_MEMORY', { status: 'no_previous_session' })\n return null\n }\n\n const block = formatSessionMemoryBlock(recentSession.memory)\n if (!block) {\n return null\n }\n\n cachedSessionMemoryBlock = block\n\n debugLogger.state('SESSION_MEMORY', {\n status: 'injected',\n summaryLength: String(recentSession.memory.summary.length),\n decisionsCount: String(recentSession.memory.decisions?.length ?? 0),\n })\n\n return cachedSessionMemoryBlock\n } catch (error) {\n debugLogger.warn(\n 'SESSION_MEMORY',\n `Failed to load session memory: ${error instanceof Error ? error.message : String(error)}`,\n )\n return null\n }\n}\n\n/**\n * Format a SessionMemoryData into a <session-memory> prompt block.\n */\nfunction formatSessionMemoryBlock(memory: SessionMemoryData): string | null {\n const parts: string[] = []\n\n // Summary is required\n if (!memory.summary || memory.summary.trim().length === 0) {\n return null\n }\n\n parts.push(memory.summary)\n\n // Key decisions\n if (memory.decisions && memory.decisions.length > 0) {\n const decisionsText = memory.decisions\n .slice(0, 10) // Limit to 10 most recent decisions\n .map(d => `- ${d.description}`)\n .join('\\n')\n parts.push(`Key decisions:\\n${decisionsText}`)\n }\n\n // Focus areas\n if (memory.focusAreas && memory.focusAreas.length > 0) {\n parts.push(`Focus areas: ${memory.focusAreas.join(', ')}`)\n }\n\n return `<session-memory>\\n${parts.join('\\n')}\\n</session-memory>`\n}\n\n/**\n * Reset the cached session memory (for testing or session reset).\n */\nexport function resetSessionMemoryCache(): void {\n sessionMemoryLoaded = false\n cachedSessionMemoryBlock = null\n}\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;AAC/B,SAAS,SAAS,mBAAmB;AACrC,SAAS,4BAAoD;AAM7D,IAAI,sBAAsB;AAC1B,IAAI,2BAA0C;AAUvC,SAAS,wBAAuC;AACrD,MAAI,qBAAqB;AACvB,WAAO;AAAA,EACT;AAEA,wBAAsB;AAEtB,MAAI;AACF,UAAM,SAAS,gBAAgB;AAG/B,QAAI,OAAO,wBAAwB,OAAO;AACxC,kBAAY,MAAM,kBAAkB,EAAE,QAAQ,qBAAqB,CAAC;AACpE,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,eAAe;AACnC,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,IAAI,qBAAqB,WAAW;AAGpD,QAAI;AACF,cAAQ,mBAAmB,CAAC;AAAA,IAC9B,QAAQ;AAAA,IAER;AAEA,UAAM,gBAAgB,QAAQ,qBAAqB;AACnD,QAAI,CAAC,eAAe,QAAQ,SAAS;AACnC,kBAAY,MAAM,kBAAkB,EAAE,QAAQ,sBAAsB,CAAC;AACrE,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,yBAAyB,cAAc,MAAM;AAC3D,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,+BAA2B;AAE3B,gBAAY,MAAM,kBAAkB;AAAA,MAClC,QAAQ;AAAA,MACR,eAAe,OAAO,cAAc,OAAO,QAAQ,MAAM;AAAA,MACzD,gBAAgB,OAAO,cAAc,OAAO,WAAW,UAAU,CAAC;AAAA,IACpE,CAAC;AAED,WAAO;AAAA,EACT,SAAS,OAAO;AACd,gBAAY;AAAA,MACV;AAAA,MACA,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IAC1F;AACA,WAAO;AAAA,EACT;AACF;AAKA,SAAS,yBAAyB,QAA0C;AAC1E,QAAM,QAAkB,CAAC;AAGzB,MAAI,CAAC,OAAO,WAAW,OAAO,QAAQ,KAAK,EAAE,WAAW,GAAG;AACzD,WAAO;AAAA,EACT;AAEA,QAAM,KAAK,OAAO,OAAO;AAGzB,MAAI,OAAO,aAAa,OAAO,UAAU,SAAS,GAAG;AACnD,UAAM,gBAAgB,OAAO,UAC1B,MAAM,GAAG,EAAE,EACX,IAAI,OAAK,KAAK,EAAE,WAAW,EAAE,EAC7B,KAAK,IAAI;AACZ,UAAM,KAAK;AAAA,EAAmB,aAAa,EAAE;AAAA,EAC/C;AAGA,MAAI,OAAO,cAAc,OAAO,WAAW,SAAS,GAAG;AACrD,UAAM,KAAK,gBAAgB,OAAO,WAAW,KAAK,IAAI,CAAC,EAAE;AAAA,EAC3D;AAEA,SAAO;AAAA,EAAqB,MAAM,KAAK,IAAI,CAAC;AAAA;AAC9C;AAKO,SAAS,0BAAgC;AAC9C,wBAAsB;AACtB,6BAA2B;AAC7B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { homedir } from "os";
|
|
1
2
|
import { getTodos } from "../utils/todoStorage.js";
|
|
3
|
+
function escapeXml(s) {
|
|
4
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5
|
+
}
|
|
2
6
|
class SystemReminderService {
|
|
3
7
|
sessionState = {
|
|
4
8
|
lastTodoUpdate: 0,
|
|
@@ -33,13 +37,18 @@ class SystemReminderService {
|
|
|
33
37
|
}
|
|
34
38
|
const reminders = [];
|
|
35
39
|
const currentTime = Date.now();
|
|
40
|
+
if (this.sessionState.remindersSent.size > 100) {
|
|
41
|
+
this.pruneOldReminders(currentTime);
|
|
42
|
+
}
|
|
36
43
|
const reminderGenerators = [
|
|
44
|
+
() => this.getPlanModeActiveReminder(),
|
|
37
45
|
() => this.dispatchTodoEvent(agentId),
|
|
38
46
|
() => this.dispatchTaskToolsReminder(),
|
|
39
47
|
() => this.dispatchSecurityEvent(),
|
|
40
48
|
() => this.dispatchPerformanceEvent(),
|
|
41
49
|
() => this.getMentionReminders(),
|
|
42
|
-
() => this.getEventReminders()
|
|
50
|
+
() => this.getEventReminders(),
|
|
51
|
+
() => this.getTeamMessageReminders(agentId)
|
|
43
52
|
];
|
|
44
53
|
for (const generator of reminderGenerators) {
|
|
45
54
|
if (reminders.length >= 8) break;
|
|
@@ -52,6 +61,35 @@ class SystemReminderService {
|
|
|
52
61
|
}
|
|
53
62
|
return reminders;
|
|
54
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Plan mode persistent reminder — injected EVERY turn while plan mode is active (CC behavior).
|
|
66
|
+
* Not gated by remindersSent because it must appear on every generateReminders() call.
|
|
67
|
+
*/
|
|
68
|
+
getPlanModeActiveReminder() {
|
|
69
|
+
try {
|
|
70
|
+
const {
|
|
71
|
+
isPlanModeEnabled,
|
|
72
|
+
getPlanFilePath
|
|
73
|
+
} = require("../utils/plan/planMode");
|
|
74
|
+
if (!isPlanModeEnabled()) return null;
|
|
75
|
+
const planPath = getPlanFilePath();
|
|
76
|
+
return this.createReminderMessage(
|
|
77
|
+
"plan_mode_active",
|
|
78
|
+
"general",
|
|
79
|
+
"high",
|
|
80
|
+
`Plan mode is active. You MUST NOT make any edits or changes except to the plan file at: ${planPath}
|
|
81
|
+
|
|
82
|
+
End every turn with either:
|
|
83
|
+
- AskUserQuestion (to clarify requirements)
|
|
84
|
+
- ExitPlanMode (to present your plan for approval)
|
|
85
|
+
|
|
86
|
+
Do NOT write code, edit files, or run commands other than read-only exploration.`,
|
|
87
|
+
Date.now()
|
|
88
|
+
);
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
55
93
|
dispatchTodoEvent(agentId) {
|
|
56
94
|
if (!this.sessionState.config.todoEmptyReminder) return null;
|
|
57
95
|
const todos = getTodos(agentId);
|
|
@@ -103,9 +141,9 @@ ${todoContent}. Continue on with the tasks at hand if applicable.`,
|
|
|
103
141
|
if (this.sessionState.remindersSent.has(key)) return null;
|
|
104
142
|
const sessionDuration = Date.now() - this.sessionState.sessionStartTime;
|
|
105
143
|
if (sessionDuration < 5 * 60 * 1e3) return null;
|
|
106
|
-
const hasTaskActivity = Array.from(
|
|
107
|
-
|
|
108
|
-
)
|
|
144
|
+
const hasTaskActivity = Array.from(this.sessionState.remindersSent).some(
|
|
145
|
+
(k) => k.startsWith("task_")
|
|
146
|
+
);
|
|
109
147
|
if (hasTaskActivity) return null;
|
|
110
148
|
this.emitEvent("task:tools_reminder", {});
|
|
111
149
|
return null;
|
|
@@ -188,6 +226,8 @@ ${todoContent}. Continue on with the tasks at hand if applicable.`,
|
|
|
188
226
|
"plan_file_reference",
|
|
189
227
|
"plan_mode_reentry",
|
|
190
228
|
"plan_exited",
|
|
229
|
+
"plan_verify",
|
|
230
|
+
"plan_mode_active",
|
|
191
231
|
"skill_invoked",
|
|
192
232
|
"mcp_resource_no_content",
|
|
193
233
|
"compact_file_reference",
|
|
@@ -224,6 +264,47 @@ ${todoContent}. Continue on with the tasks at hand if applicable.`,
|
|
|
224
264
|
expiredKeys.forEach((key) => this.reminderCache.delete(key));
|
|
225
265
|
return reminders;
|
|
226
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Deliver pending team mailbox messages to the current agent as reminders.
|
|
269
|
+
* Checks unread messages for the agent's team-scoped ID and formats them
|
|
270
|
+
* as <team-message> blocks for conversation injection.
|
|
271
|
+
*/
|
|
272
|
+
getTeamMessageReminders(agentId) {
|
|
273
|
+
if (!agentId) return null;
|
|
274
|
+
const teamName = process.env.MINTO_TEAM_NAME || agentId.split("@")[1];
|
|
275
|
+
if (!teamName) return null;
|
|
276
|
+
try {
|
|
277
|
+
const { getTeam } = require("./agentTeams/teamManager");
|
|
278
|
+
const entry = getTeam(teamName);
|
|
279
|
+
if (!entry) return null;
|
|
280
|
+
const { mailbox } = entry;
|
|
281
|
+
const unread = mailbox.getUnread(agentId);
|
|
282
|
+
if (unread.length === 0) return null;
|
|
283
|
+
mailbox.markRead(unread.map((m) => m.id));
|
|
284
|
+
const currentTime = Date.now();
|
|
285
|
+
const reminders = [];
|
|
286
|
+
for (const msg of unread) {
|
|
287
|
+
const senderName = msg.from.split("@")[0] || msg.from;
|
|
288
|
+
const summaryText = msg.summary || msg.text.slice(0, 60).replace(/"/g, "'");
|
|
289
|
+
const colorAttr = msg.color ? ` color="${escapeXml(msg.color)}"` : "";
|
|
290
|
+
const content = `<team-message from="${escapeXml(senderName)}"${colorAttr} summary="${escapeXml(summaryText)}">
|
|
291
|
+
${escapeXml(msg.text)}
|
|
292
|
+
</team-message>`;
|
|
293
|
+
reminders.push(
|
|
294
|
+
this.createReminderMessage(
|
|
295
|
+
"team_message",
|
|
296
|
+
"task",
|
|
297
|
+
"high",
|
|
298
|
+
content,
|
|
299
|
+
currentTime
|
|
300
|
+
)
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
return reminders;
|
|
304
|
+
} catch {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
227
308
|
/**
|
|
228
309
|
* Generate reminders for external file changes
|
|
229
310
|
* Called when todo files are modified externally
|
|
@@ -349,7 +430,7 @@ ${content}
|
|
|
349
430
|
"hook_additional_context",
|
|
350
431
|
"general",
|
|
351
432
|
"medium",
|
|
352
|
-
`Additional context from hook "${context.hookName}": ${context.content}`,
|
|
433
|
+
`Additional context from hook "${escapeXml(context.hookName)}": ${escapeXml(String(context.content))}`,
|
|
353
434
|
Date.now()
|
|
354
435
|
);
|
|
355
436
|
this.reminderCache.set(key, reminder);
|
|
@@ -363,7 +444,7 @@ ${content}
|
|
|
363
444
|
"hook_blocking_error",
|
|
364
445
|
"general",
|
|
365
446
|
"high",
|
|
366
|
-
`A hook blocked the action: ${context.reason}`,
|
|
447
|
+
`A hook blocked the action: ${escapeXml(String(context.reason))}`,
|
|
367
448
|
Date.now()
|
|
368
449
|
);
|
|
369
450
|
this.reminderCache.set(key, reminder);
|
|
@@ -377,7 +458,7 @@ ${content}
|
|
|
377
458
|
"hook_stopped_continuation",
|
|
378
459
|
"general",
|
|
379
460
|
"high",
|
|
380
|
-
`A hook stopped continuation: ${context.reason}`,
|
|
461
|
+
`A hook stopped continuation: ${escapeXml(String(context.reason))}`,
|
|
381
462
|
Date.now()
|
|
382
463
|
);
|
|
383
464
|
this.reminderCache.set(key, reminder);
|
|
@@ -537,6 +618,20 @@ ${content}
|
|
|
537
618
|
this.reminderCache.set(key, reminder);
|
|
538
619
|
}
|
|
539
620
|
});
|
|
621
|
+
this.addEventListener("plan:verify", (context) => {
|
|
622
|
+
const key = `plan_verify_${context.planPath}`;
|
|
623
|
+
if (!this.sessionState.remindersSent.has(key)) {
|
|
624
|
+
this.sessionState.remindersSent.add(key);
|
|
625
|
+
const reminder = this.createReminderMessage(
|
|
626
|
+
"plan_verify",
|
|
627
|
+
"general",
|
|
628
|
+
"medium",
|
|
629
|
+
`Implementation is complete. Verify against the approved plan at: ${context.planPath}. Check that all planned changes were made, run tests, and confirm the verification procedures from the plan pass.`,
|
|
630
|
+
Date.now()
|
|
631
|
+
);
|
|
632
|
+
this.reminderCache.set(key, reminder);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
540
635
|
this.addEventListener("token:usage", (context) => {
|
|
541
636
|
const key = `token_usage_${context.threshold}`;
|
|
542
637
|
if (!this.sessionState.remindersSent.has(key)) {
|
|
@@ -569,11 +664,17 @@ ${content}
|
|
|
569
664
|
const key = `team_coord_${context.teamId}_${Date.now()}`;
|
|
570
665
|
if (!this.sessionState.remindersSent.has(key)) {
|
|
571
666
|
this.sessionState.remindersSent.add(key);
|
|
667
|
+
const teamId = context.teamId;
|
|
668
|
+
const homeDir = homedir();
|
|
669
|
+
const teamConfigPath = `${homeDir}/.minto/teams/${teamId}/config.json`;
|
|
670
|
+
const taskListPath = `${homeDir}/.minto/tasks/${teamId}`;
|
|
572
671
|
const reminder = this.createReminderMessage(
|
|
573
672
|
"team_coordination",
|
|
574
673
|
"task",
|
|
575
674
|
"medium",
|
|
576
|
-
`Team coordination: ${context.message}
|
|
675
|
+
`Team coordination: ${context.message}
|
|
676
|
+
teamConfigPath: ${teamConfigPath}
|
|
677
|
+
taskListPath: ${taskListPath}`,
|
|
577
678
|
Date.now()
|
|
578
679
|
);
|
|
579
680
|
this.reminderCache.set(key, reminder);
|
|
@@ -656,6 +757,27 @@ ${content}
|
|
|
656
757
|
this.reminderCache.set(params.key, reminder);
|
|
657
758
|
}
|
|
658
759
|
}
|
|
760
|
+
/**
|
|
761
|
+
* Prune old reminder keys to prevent unbounded Set growth.
|
|
762
|
+
* Keeps keys that contain timestamps or are "sticky" (security, performance).
|
|
763
|
+
* Removes generic keys when the set exceeds threshold.
|
|
764
|
+
*/
|
|
765
|
+
pruneOldReminders(_currentTime) {
|
|
766
|
+
const stickyPrefixes = ["file_security", "performance_", "todo_empty_"];
|
|
767
|
+
const toDelete = [];
|
|
768
|
+
for (const key of this.sessionState.remindersSent) {
|
|
769
|
+
if (!stickyPrefixes.some((p) => key.startsWith(p))) {
|
|
770
|
+
toDelete.push(key);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
const removeCount = Math.min(
|
|
774
|
+
toDelete.length,
|
|
775
|
+
Math.floor(toDelete.length / 2)
|
|
776
|
+
);
|
|
777
|
+
for (let i = 0; i < removeCount; i++) {
|
|
778
|
+
this.sessionState.remindersSent.delete(toDelete[i]);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
659
781
|
resetSession() {
|
|
660
782
|
this.sessionState = {
|
|
661
783
|
lastTodoUpdate: 0,
|