@within-7/minto 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Tool.js +7 -0
- package/dist/Tool.js.map +2 -2
- package/dist/commands/agents/AgentsCommand.js +1 -1
- package/dist/commands/agents/AgentsCommand.js.map +2 -2
- package/dist/commands/agents/constants.js +2 -2
- package/dist/commands/agents/constants.js.map +2 -2
- package/dist/commands/clear.js +4 -3
- package/dist/commands/clear.js.map +2 -2
- package/dist/commands/compact.js +2 -2
- package/dist/commands/compact.js.map +1 -1
- package/dist/commands/context.js +3 -1
- package/dist/commands/context.js.map +2 -2
- package/dist/commands/login.js +128 -0
- package/dist/commands/login.js.map +7 -0
- package/dist/commands/memory.js +33 -82
- package/dist/commands/memory.js.map +2 -2
- package/dist/commands/quit.js +3 -1
- package/dist/commands/quit.js.map +2 -2
- package/dist/commands/resume.js +39 -239
- package/dist/commands/resume.js.map +2 -2
- package/dist/commands/tasks.js +1 -1
- package/dist/commands/tasks.js.map +2 -2
- package/dist/commands/terminalSetup.js +6 -2
- package/dist/commands/terminalSetup.js.map +2 -2
- package/dist/commands.js +2 -0
- package/dist/commands.js.map +2 -2
- package/dist/components/AgentDetailView.js +126 -0
- package/dist/components/AgentDetailView.js.map +7 -0
- package/dist/components/AgentThinkingBlock.js +1 -1
- package/dist/components/AgentThinkingBlock.js.map +2 -2
- package/dist/components/AgentViewBanner.js +22 -0
- package/dist/components/AgentViewBanner.js.map +7 -0
- package/dist/components/HeaderBar.js +1 -1
- package/dist/components/HeaderBar.js.map +2 -2
- package/dist/components/Help.js +8 -1
- package/dist/components/Help.js.map +2 -2
- package/dist/components/HotkeyHelpPanel.js +26 -8
- package/dist/components/HotkeyHelpPanel.js.map +2 -2
- package/dist/components/IdleNotificationBar.js +10 -0
- package/dist/components/IdleNotificationBar.js.map +7 -0
- package/dist/components/ModelSelector/ModelSelector.js +55 -20
- package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
- package/dist/components/PromptInput.js +186 -115
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/RewindPanel.js +272 -0
- package/dist/components/RewindPanel.js.map +7 -0
- package/dist/components/Spinner.js +10 -21
- package/dist/components/Spinner.js.map +2 -2
- package/dist/components/StreamingTextPreview.js +29 -0
- package/dist/components/StreamingTextPreview.js.map +7 -0
- package/dist/components/SubagentBlock.js +3 -2
- package/dist/components/SubagentBlock.js.map +2 -2
- package/dist/components/SubagentProgress.js +4 -4
- package/dist/components/SubagentProgress.js.map +2 -2
- package/dist/components/TabbedListView/SearchInput.js +1 -1
- package/dist/components/TabbedListView/SearchInput.js.map +2 -2
- package/dist/components/TabbedListView/TabbedListView.js +87 -41
- package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
- package/dist/components/TaskCard.js +4 -4
- package/dist/components/TaskCard.js.map +2 -2
- package/dist/components/TeamMemberPanel.js +107 -0
- package/dist/components/TeamMemberPanel.js.map +7 -0
- package/dist/components/ThinkingSelector.js +84 -0
- package/dist/components/ThinkingSelector.js.map +7 -0
- package/dist/components/TitledDivider.js +26 -0
- package/dist/components/TitledDivider.js.map +7 -0
- package/dist/components/TodoPanel.js +31 -30
- package/dist/components/TodoPanel.js.map +2 -2
- package/dist/components/TokenWarning.js +28 -7
- package/dist/components/TokenWarning.js.map +2 -2
- package/dist/components/messages/AssistantTextMessage.js +5 -2
- package/dist/components/messages/AssistantTextMessage.js.map +2 -2
- package/dist/components/messages/AssistantToolUseMessage.js +9 -1
- package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
- package/dist/components/messages/DefaultToolResultFallback.js +11 -0
- package/dist/components/messages/DefaultToolResultFallback.js.map +7 -0
- package/dist/components/messages/ParallelTasksGroupView.js +14 -6
- package/dist/components/messages/ParallelTasksGroupView.js.map +2 -2
- package/dist/components/messages/TaskInModuleView.js +27 -27
- package/dist/components/messages/TaskInModuleView.js.map +2 -2
- package/dist/components/messages/UserGuidanceMessage.js +26 -0
- package/dist/components/messages/UserGuidanceMessage.js.map +7 -0
- package/dist/components/messages/UserPromptMessage.js +2 -1
- package/dist/components/messages/UserPromptMessage.js.map +2 -2
- package/dist/components/messages/UserTeamNotificationMessage.js +91 -0
- package/dist/components/messages/UserTeamNotificationMessage.js.map +7 -0
- package/dist/components/messages/UserTextMessage.js +8 -0
- package/dist/components/messages/UserTextMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolRejectMessage.js +4 -2
- package/dist/components/messages/UserToolResultMessage/UserToolRejectMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolResultMessage.js +18 -1
- package/dist/components/messages/UserToolResultMessage/UserToolResultMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js +12 -1
- package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js.map +2 -2
- package/dist/components/permissions/PermissionRequest.js +4 -0
- package/dist/components/permissions/PermissionRequest.js.map +2 -2
- package/dist/components/permissions/PlanApprovalRequest.js +164 -0
- package/dist/components/permissions/PlanApprovalRequest.js.map +7 -0
- package/dist/constants/agentTeams.js +17 -0
- package/dist/constants/agentTeams.js.map +7 -0
- package/dist/constants/macros.js +2 -1
- package/dist/constants/macros.js.map +2 -2
- package/dist/constants/prompts/agentPrompt.js +1 -0
- package/dist/constants/prompts/agentPrompt.js.map +2 -2
- package/dist/constants/prompts/autoMemory.js +39 -0
- package/dist/constants/prompts/autoMemory.js.map +7 -0
- package/dist/constants/prompts/codeConventions.js +1 -13
- package/dist/constants/prompts/codeConventions.js.map +2 -2
- package/dist/constants/prompts/doingTasks.js +21 -2
- package/dist/constants/prompts/doingTasks.js.map +2 -2
- package/dist/constants/prompts/envInfo.js +6 -7
- package/dist/constants/prompts/envInfo.js.map +2 -2
- package/dist/constants/prompts/index.js +27 -5
- package/dist/constants/prompts/index.js.map +2 -2
- package/dist/constants/prompts/taskManagement.js +2 -43
- package/dist/constants/prompts/taskManagement.js.map +2 -2
- package/dist/constants/prompts/teamOverlays.js +50 -0
- package/dist/constants/prompts/teamOverlays.js.map +7 -0
- package/dist/constants/prompts/toneAndStyle.js +4 -29
- package/dist/constants/prompts/toneAndStyle.js.map +2 -2
- package/dist/constants/prompts/toolUsagePolicy.js +7 -22
- package/dist/constants/prompts/toolUsagePolicy.js.map +2 -2
- package/dist/constants/toolInputExamples.js +2 -2
- package/dist/constants/toolInputExamples.js.map +2 -2
- package/dist/context.js +39 -6
- package/dist/context.js.map +2 -2
- package/dist/core/backupManager.js +1 -1
- package/dist/core/backupManager.js.map +2 -2
- package/dist/core/permissions/rules/planModeRule.js +1 -1
- package/dist/core/permissions/rules/planModeRule.js.map +1 -1
- package/dist/core/permissions/rules/safeModeRule.js +1 -1
- package/dist/core/permissions/rules/safeModeRule.js.map +1 -1
- package/dist/engine/AgentEngine.js +902 -0
- package/dist/engine/AgentEngine.js.map +7 -0
- package/dist/engine/EngineRegistry.js +89 -0
- package/dist/engine/EngineRegistry.js.map +7 -0
- package/dist/engine/foregroundAdapter.js +191 -0
- package/dist/engine/foregroundAdapter.js.map +7 -0
- package/dist/engine/index.js +15 -0
- package/dist/engine/index.js.map +7 -0
- package/dist/engine/types.js +1 -0
- package/dist/engine/types.js.map +7 -0
- package/dist/entrypoints/cli.js +410 -79
- package/dist/entrypoints/cli.js.map +3 -3
- package/dist/hooks/useAgentEngine.js +129 -0
- package/dist/hooks/useAgentEngine.js.map +7 -0
- package/dist/hooks/useAgentTokenStats.js +0 -16
- package/dist/hooks/useAgentTokenStats.js.map +2 -2
- package/dist/hooks/useCanUseTool.js +47 -2
- package/dist/hooks/useCanUseTool.js.map +2 -2
- package/dist/hooks/useDeferredLoading.js +4 -1
- package/dist/hooks/useDeferredLoading.js.map +2 -2
- package/dist/hooks/useIdleNotifications.js +66 -0
- package/dist/hooks/useIdleNotifications.js.map +7 -0
- package/dist/hooks/useSessionTracking.js +9 -7
- package/dist/hooks/useSessionTracking.js.map +2 -2
- package/dist/hooks/useTeamMembers.js +51 -0
- package/dist/hooks/useTeamMembers.js.map +7 -0
- package/dist/i18n/locales/en.js +77 -12
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +77 -12
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/messages.js.map +2 -2
- package/dist/permissions.js +113 -7
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +135 -37
- package/dist/query.js.map +2 -2
- package/dist/screens/REPL.js +504 -361
- package/dist/screens/REPL.js.map +3 -3
- package/dist/screens/ResumeConversation.js +199 -14
- package/dist/screens/ResumeConversation.js.map +2 -2
- package/dist/services/adapters/base.js.map +1 -1
- package/dist/services/agentTeams/backends/headless.js +108 -0
- package/dist/services/agentTeams/backends/headless.js.map +7 -0
- package/dist/services/agentTeams/backends/inProcess.js +102 -0
- package/dist/services/agentTeams/backends/inProcess.js.map +7 -0
- package/dist/services/agentTeams/backends/resolver.js +18 -0
- package/dist/services/agentTeams/backends/resolver.js.map +7 -0
- package/dist/services/agentTeams/backends/tmux.js +168 -0
- package/dist/services/agentTeams/backends/tmux.js.map +7 -0
- package/dist/services/agentTeams/backends/types.js +1 -0
- package/dist/services/agentTeams/backends/types.js.map +7 -0
- package/dist/services/agentTeams/heartbeat.js +88 -0
- package/dist/services/agentTeams/heartbeat.js.map +7 -0
- package/dist/services/agentTeams/index.js +42 -2
- package/dist/services/agentTeams/index.js.map +2 -2
- package/dist/services/agentTeams/injectionChannel.js +105 -0
- package/dist/services/agentTeams/injectionChannel.js.map +7 -0
- package/dist/services/agentTeams/mailbox.js +410 -30
- package/dist/services/agentTeams/mailbox.js.map +2 -2
- package/dist/services/agentTeams/messageFormatter.js +80 -0
- package/dist/services/agentTeams/messageFormatter.js.map +7 -0
- package/dist/services/agentTeams/permissionDelegation.js +71 -0
- package/dist/services/agentTeams/permissionDelegation.js.map +7 -0
- package/dist/services/agentTeams/teamEvents.js +45 -0
- package/dist/services/agentTeams/teamEvents.js.map +7 -0
- package/dist/services/agentTeams/teamManager.js +251 -34
- package/dist/services/agentTeams/teamManager.js.map +2 -2
- package/dist/services/agentTeams/teamTaskStore.js +290 -61
- package/dist/services/agentTeams/teamTaskStore.js.map +2 -2
- package/dist/services/agentTeams/teammateSpawner.js +99 -18
- package/dist/services/agentTeams/teammateSpawner.js.map +2 -2
- package/dist/services/hookExecutor.js +51 -8
- package/dist/services/hookExecutor.js.map +2 -2
- package/dist/services/llm/anthropicProvider.js +56 -59
- package/dist/services/llm/anthropicProvider.js.map +2 -2
- package/dist/services/llm/dispatch.js +24 -5
- package/dist/services/llm/dispatch.js.map +2 -2
- package/dist/services/llm/openaiProvider.js +115 -136
- package/dist/services/llm/openaiProvider.js.map +3 -3
- package/dist/services/llm/types.js +89 -15
- package/dist/services/llm/types.js.map +2 -2
- package/dist/services/mcpClient.js +80 -4
- package/dist/services/mcpClient.js.map +2 -2
- package/dist/services/mintoAuth.js +299 -0
- package/dist/services/mintoAuth.js.map +7 -0
- package/dist/services/oauth.js +3 -3
- package/dist/services/oauth.js.map +2 -2
- package/dist/services/openai.js +91 -20
- package/dist/services/openai.js.map +2 -2
- package/dist/services/plugins/pluginRuntime.js +11 -5
- package/dist/services/plugins/pluginRuntime.js.map +2 -2
- package/dist/services/plugins/pluginValidation.js +4 -2
- package/dist/services/plugins/pluginValidation.js.map +2 -2
- package/dist/services/sandbox/sandboxController.js +11 -3
- package/dist/services/sandbox/sandboxController.js.map +2 -2
- package/dist/services/sessionMemoryInjector.js +77 -0
- package/dist/services/sessionMemoryInjector.js.map +7 -0
- package/dist/services/systemReminder.js +130 -8
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/services/taskStore.js +199 -8
- package/dist/services/taskStore.js.map +3 -3
- package/dist/services/topicDetector.js +169 -0
- package/dist/services/topicDetector.js.map +7 -0
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +0 -13
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
- package/dist/tools/BashTool/BashTool.js +51 -28
- package/dist/tools/BashTool/BashTool.js.map +2 -2
- package/dist/tools/BashTool/prompt.js +95 -118
- package/dist/tools/BashTool/prompt.js.map +2 -2
- package/dist/tools/BashTool/utils.js +39 -1
- package/dist/tools/BashTool/utils.js.map +2 -2
- package/dist/tools/EnterWorktreeTool/EnterWorktreeTool.js +121 -0
- package/dist/tools/EnterWorktreeTool/EnterWorktreeTool.js.map +7 -0
- package/dist/tools/EnterWorktreeTool/prompt.js +22 -0
- package/dist/tools/EnterWorktreeTool/prompt.js.map +7 -0
- package/dist/tools/FileEditTool/FileEditTool.js +9 -4
- package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
- package/dist/tools/FileEditTool/prompt.js +3 -7
- package/dist/tools/FileEditTool/prompt.js.map +2 -2
- package/dist/tools/FileReadTool/FileReadTool.js +125 -3
- package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
- package/dist/tools/FileReadTool/prompt.js +1 -2
- package/dist/tools/FileReadTool/prompt.js.map +2 -2
- package/dist/tools/FileWriteTool/prompt.js +3 -5
- package/dist/tools/FileWriteTool/prompt.js.map +2 -2
- package/dist/tools/GlobTool/GlobTool.js +3 -2
- package/dist/tools/GlobTool/GlobTool.js.map +2 -2
- package/dist/tools/GrepTool/GrepTool.js +16 -5
- package/dist/tools/GrepTool/GrepTool.js.map +2 -2
- package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +2 -2
- package/dist/tools/MCPSearchTool/MCPSearchTool.js +172 -0
- package/dist/tools/MCPSearchTool/MCPSearchTool.js.map +7 -0
- package/dist/tools/MCPSearchTool/prompt.js +77 -0
- package/dist/tools/MCPSearchTool/prompt.js.map +7 -0
- package/dist/tools/MultiEditTool/prompt.js +4 -7
- package/dist/tools/MultiEditTool/prompt.js.map +2 -2
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js +12 -8
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js +54 -1
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
- package/dist/tools/PlanModeTool/prompt.js +23 -74
- package/dist/tools/PlanModeTool/prompt.js.map +2 -2
- package/dist/tools/SendMessageTool/SendMessageTool.js +341 -0
- package/dist/tools/SendMessageTool/SendMessageTool.js.map +7 -0
- package/dist/tools/SendMessageTool/prompt.js +44 -0
- package/dist/tools/SendMessageTool/prompt.js.map +7 -0
- package/dist/tools/TaskCreateTool/prompt.js +15 -4
- package/dist/tools/TaskCreateTool/prompt.js.map +2 -2
- package/dist/tools/TaskListTool/prompt.js +18 -3
- package/dist/tools/TaskListTool/prompt.js.map +2 -2
- package/dist/tools/TaskOutputTool/prompt.js +4 -3
- package/dist/tools/TaskOutputTool/prompt.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +762 -98
- package/dist/tools/TaskTool/TaskTool.js.map +3 -3
- package/dist/tools/TaskTool/constants.js +8 -2
- package/dist/tools/TaskTool/constants.js.map +2 -2
- package/dist/tools/TaskTool/prompt.js +74 -70
- package/dist/tools/TaskTool/prompt.js.map +2 -2
- package/dist/tools/TaskUpdateTool/TaskUpdateTool.js +15 -1
- package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +2 -2
- package/dist/tools/TeamCreateTool/TeamCreateTool.js +129 -0
- package/dist/tools/TeamCreateTool/TeamCreateTool.js.map +7 -0
- package/dist/tools/TeamCreateTool/prompt.js +58 -0
- package/dist/tools/TeamCreateTool/prompt.js.map +7 -0
- package/dist/tools/TeamDeleteTool/TeamDeleteTool.js +151 -0
- package/dist/tools/TeamDeleteTool/TeamDeleteTool.js.map +7 -0
- package/dist/tools/TeamDeleteTool/prompt.js +16 -0
- package/dist/tools/TeamDeleteTool/prompt.js.map +7 -0
- package/dist/tools/URLFetcherTool/URLFetcherTool.js +106 -15
- package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
- package/dist/tools/URLFetcherTool/prompt.js +3 -2
- package/dist/tools/URLFetcherTool/prompt.js.map +2 -2
- package/dist/tools/WebSearchTool/WebSearchTool.js +2 -1
- package/dist/tools/WebSearchTool/WebSearchTool.js.map +2 -2
- package/dist/tools/WebSearchTool/prompt.js +5 -4
- package/dist/tools/WebSearchTool/prompt.js.map +2 -2
- package/dist/tools.js +100 -20
- package/dist/tools.js.map +2 -2
- package/dist/types/PermissionMode.js +35 -6
- package/dist/types/PermissionMode.js.map +2 -2
- package/dist/types/hooks.js +2 -0
- package/dist/types/hooks.js.map +2 -2
- package/dist/types/plugin.js +2 -0
- package/dist/types/plugin.js.map +3 -3
- package/dist/utils/CircuitBreaker.js +15 -9
- package/dist/utils/CircuitBreaker.js.map +2 -2
- package/dist/utils/agentLoader.js +249 -112
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/animationManager.js +40 -3
- package/dist/utils/animationManager.js.map +2 -2
- package/dist/utils/ask.js +7 -6
- package/dist/utils/ask.js.map +2 -2
- package/dist/utils/atomicWrite.js +23 -0
- package/dist/utils/atomicWrite.js.map +7 -0
- package/dist/utils/autoCompactCore.js +73 -56
- package/dist/utils/autoCompactCore.js.map +2 -2
- package/dist/utils/autoMemoryPaths.js +89 -0
- package/dist/utils/autoMemoryPaths.js.map +7 -0
- package/dist/utils/config.js +63 -38
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/configSchema.js +13 -8
- package/dist/utils/configSchema.js.map +2 -2
- package/dist/utils/credentials/index.js +14 -0
- package/dist/utils/credentials/index.js.map +2 -2
- package/dist/utils/dualPath.js +24 -0
- package/dist/utils/dualPath.js.map +7 -0
- package/dist/utils/exit.js +66 -7
- package/dist/utils/exit.js.map +2 -2
- package/dist/utils/externalEditor.js +155 -0
- package/dist/utils/externalEditor.js.map +7 -0
- package/dist/utils/fileLock.js +67 -0
- package/dist/utils/fileLock.js.map +7 -0
- package/dist/utils/format.js +24 -14
- package/dist/utils/format.js.map +2 -2
- package/dist/utils/globalErrorHandler.js +5 -96
- package/dist/utils/globalErrorHandler.js.map +3 -3
- package/dist/utils/groupHandlers/parallelTasksHandler.js +5 -3
- package/dist/utils/groupHandlers/parallelTasksHandler.js.map +2 -2
- package/dist/utils/groupHandlers/taskHandler.js +2 -2
- package/dist/utils/groupHandlers/taskHandler.js.map +2 -2
- package/dist/utils/hookManager.js +64 -6
- package/dist/utils/hookManager.js.map +2 -2
- package/dist/utils/log.js +6 -2
- package/dist/utils/log.js.map +2 -2
- package/dist/utils/markdown.js +237 -19
- package/dist/utils/markdown.js.map +2 -2
- package/dist/utils/messageContextManager.js +18 -5
- package/dist/utils/messageContextManager.js.map +2 -2
- package/dist/utils/messageGroupManager.js +1 -1
- package/dist/utils/messageGroupManager.js.map +2 -2
- package/dist/utils/messages.js +104 -46
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/model.js +2 -2
- package/dist/utils/model.js.map +2 -2
- package/dist/utils/pasteCache.js +8 -4
- package/dist/utils/pasteCache.js.map +2 -2
- package/dist/utils/pluginLoader.js +18 -0
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/secureKeyStorage.js +36 -7
- package/dist/utils/secureKeyStorage.js.map +2 -2
- package/dist/utils/simpleMode.js +7 -0
- package/dist/utils/simpleMode.js.map +7 -0
- package/dist/utils/streamingState.js +11 -1
- package/dist/utils/streamingState.js.map +2 -2
- package/dist/utils/taskDisplayUtils.js +2 -1
- package/dist/utils/taskDisplayUtils.js.map +2 -2
- package/dist/utils/teamConfig.js +2 -2
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/thinking.js +6 -2
- package/dist/utils/thinking.js.map +3 -3
- package/dist/utils/tokenProgress.js +55 -0
- package/dist/utils/tokenProgress.js.map +7 -0
- package/dist/utils/toolRiskClassification.js +26 -17
- package/dist/utils/toolRiskClassification.js.map +2 -2
- package/dist/utils/tooling/toolError.js +12 -0
- package/dist/utils/tooling/toolError.js.map +7 -0
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +10 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/services/systemReminder.ts"],
|
|
4
|
-
"sourcesContent": ["import { getTodos, TodoItem } from '@utils/todoStorage'\n\nexport interface ReminderMessage {\n role: 'system'\n content: string\n isMeta: boolean\n timestamp: number\n type: string\n priority: 'low' | 'medium' | 'high'\n category: 'task' | 'security' | 'performance' | 'general'\n}\n\ninterface ReminderConfig {\n todoEmptyReminder: boolean\n securityReminder: boolean\n performanceReminder: boolean\n maxRemindersPerSession: number\n}\n\ninterface SessionReminderState {\n lastTodoUpdate: number\n lastFileAccess: number\n sessionStartTime: number\n remindersSent: Set<string>\n contextPresent: boolean\n reminderCount: number\n config: ReminderConfig\n}\n\nclass SystemReminderService {\n private sessionState: SessionReminderState = {\n lastTodoUpdate: 0,\n lastFileAccess: 0,\n sessionStartTime: Date.now(),\n remindersSent: new Set(),\n contextPresent: false,\n reminderCount: 0,\n config: {\n todoEmptyReminder: true,\n securityReminder: true,\n performanceReminder: true,\n maxRemindersPerSession: 10,\n },\n }\n\n private eventDispatcher = new Map<string, Array<(context: any) => void>>()\n private reminderCache = new Map<string, ReminderMessage>()\n\n constructor() {\n this.setupEventDispatcher()\n }\n\n /**\n * Conditional reminder injection - only when context is present\n * Enhanced with performance optimizations and priority management\n */\n public generateReminders(\n hasContext: boolean = false,\n agentId?: string,\n ): ReminderMessage[] {\n this.sessionState.contextPresent = hasContext\n\n // Only inject when context is present (matching original behavior)\n if (!hasContext) {\n return []\n }\n\n // Check session reminder limit to prevent overload\n if (\n this.sessionState.reminderCount >=\n this.sessionState.config.maxRemindersPerSession\n ) {\n return []\n }\n\n const reminders: ReminderMessage[] = []\n const currentTime = Date.now()\n\n // Use lazy evaluation for performance with agent context\n const reminderGenerators = [\n () => this.dispatchTodoEvent(agentId),\n () => this.dispatchTaskToolsReminder(),\n () => this.dispatchSecurityEvent(),\n () => this.dispatchPerformanceEvent(),\n () => this.getMentionReminders(),\n () => this.getEventReminders(),\n ]\n\n for (const generator of reminderGenerators) {\n if (reminders.length >= 8) break // Accommodate mentions + event reminders\n\n const result = generator()\n if (result) {\n // Handle both single reminders and arrays\n const remindersToAdd = Array.isArray(result) ? result : [result]\n reminders.push(...remindersToAdd)\n this.sessionState.reminderCount += remindersToAdd.length\n }\n }\n\n // Log aggregated metrics instead of individual events for performance\n\n return reminders\n }\n\n private dispatchTodoEvent(agentId?: string): ReminderMessage | null {\n if (!this.sessionState.config.todoEmptyReminder) return null\n\n // Use agent-scoped todo access\n const todos = getTodos(agentId)\n const currentTime = Date.now()\n const agentKey = agentId || 'default'\n\n // Check if this is a fresh session (no todos seen yet)\n if (\n todos.length === 0 &&\n !this.sessionState.remindersSent.has(`todo_empty_${agentKey}`)\n ) {\n this.sessionState.remindersSent.add(`todo_empty_${agentKey}`)\n return this.createReminderMessage(\n 'todo',\n 'task',\n 'medium',\n 'This is a reminder that your todo list is currently empty. DO NOT mention this to the user explicitly because they are already aware. If you are working on tasks that would benefit from a todo list please use the TodoWrite tool to create one. If not, please feel free to ignore. Again do not mention this message to the user.',\n currentTime,\n )\n }\n\n // Check for todo updates since last seen\n if (todos.length > 0) {\n const reminderKey = `todo_updated_${agentKey}_${todos.length}_${this.getTodoStateHash(todos)}`\n\n // Use cache for performance optimization\n if (this.reminderCache.has(reminderKey)) {\n return this.reminderCache.get(reminderKey)!\n }\n\n if (!this.sessionState.remindersSent.has(reminderKey)) {\n this.sessionState.remindersSent.add(reminderKey)\n // Clear previous todo state reminders for this agent\n this.clearTodoReminders(agentKey)\n\n // Optimize: only include essential todo data\n const todoContent = JSON.stringify(\n todos.map(todo => ({\n content:\n todo.content.length > 100\n ? todo.content.substring(0, 100) + '...'\n : todo.content,\n status: todo.status,\n priority: todo.priority,\n id: todo.id,\n })),\n )\n\n const reminder = this.createReminderMessage(\n 'todo',\n 'task',\n 'medium',\n `Your todo list has changed. DO NOT mention this explicitly to the user. Here are the latest contents of your todo list:\\n\\n${todoContent}. Continue on with the tasks at hand if applicable.`,\n currentTime,\n )\n\n // Cache the reminder for reuse\n this.reminderCache.set(reminderKey, reminder)\n return reminder\n }\n }\n\n return null\n }\n\n private dispatchTaskToolsReminder(): ReminderMessage | null {\n const key = 'task_tools_reminder'\n if (this.sessionState.remindersSent.has(key)) return null\n\n // Only trigger after 5 minutes of session time\n const sessionDuration =\n Date.now() - this.sessionState.sessionStartTime\n if (sessionDuration < 5 * 60 * 1000) return null\n\n // Check if tasks exist via the event cache \u2014 if task:created or task:status\n // events have fired, the user is already using task tools\n const hasTaskActivity = Array.from(\n this.sessionState.remindersSent,\n ).some(k => k.startsWith('task_'))\n if (hasTaskActivity) return null\n\n // Emit the reminder event so the listener caches the message\n this.emitEvent('task:tools_reminder', {})\n return null\n }\n\n private dispatchSecurityEvent(): ReminderMessage | null {\n if (!this.sessionState.config.securityReminder) return null\n\n const currentTime = Date.now()\n\n // Only inject security reminder once per session when file operations occur\n if (\n this.sessionState.lastFileAccess > 0 &&\n !this.sessionState.remindersSent.has('file_security')\n ) {\n this.sessionState.remindersSent.add('file_security')\n return this.createReminderMessage(\n 'security',\n 'security',\n 'high',\n 'Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.',\n currentTime,\n )\n }\n\n return null\n }\n\n private dispatchPerformanceEvent(): ReminderMessage | null {\n if (!this.sessionState.config.performanceReminder) return null\n\n const currentTime = Date.now()\n const sessionDuration = currentTime - this.sessionState.sessionStartTime\n\n // Remind about performance after long sessions (30 minutes)\n if (\n sessionDuration > 30 * 60 * 1000 &&\n !this.sessionState.remindersSent.has('performance_long_session')\n ) {\n this.sessionState.remindersSent.add('performance_long_session')\n return this.createReminderMessage(\n 'performance',\n 'performance',\n 'low',\n 'Long session detected. Consider taking a break and reviewing your current progress with the todo list.',\n currentTime,\n )\n }\n\n return null\n }\n\n /**\n * Retrieve cached mention reminders\n * Returns recent mentions (within 5 seconds) that haven't expired\n */\n private getMentionReminders(): ReminderMessage[] {\n const currentTime = Date.now()\n const MENTION_FRESHNESS_WINDOW = 5000 // 5 seconds\n const reminders: ReminderMessage[] = []\n const expiredKeys: string[] = []\n\n // Single pass through cache for both collection and cleanup identification\n for (const [key, reminder] of this.reminderCache.entries()) {\n if (this.isMentionReminder(reminder)) {\n const age = currentTime - reminder.timestamp\n if (age <= MENTION_FRESHNESS_WINDOW) {\n reminders.push(reminder)\n } else {\n expiredKeys.push(key)\n }\n }\n }\n\n // Clean up expired mention reminders in separate pass for performance\n expiredKeys.forEach(key => this.reminderCache.delete(key))\n\n return reminders\n }\n\n /**\n * Type guard for mention reminders - centralized type checking\n * Eliminates hardcoded type strings scattered throughout the code\n */\n private isMentionReminder(reminder: ReminderMessage): boolean {\n const mentionTypes = ['agent_mention', 'file_mention', 'ask_model_mention']\n return mentionTypes.includes(reminder.type)\n }\n\n /**\n * Event-based reminder types for hook results, file status, task status\n */\n private static readonly EVENT_REMINDER_TYPES = new Set([\n // Batch A\n 'hook_additional_context',\n 'hook_blocking_error',\n 'hook_stopped_continuation',\n 'file_empty',\n 'file_truncated',\n 'task_status',\n 'task_tools_reminder',\n // Batch B\n 'output_style_active',\n 'plan_file_reference',\n 'plan_mode_reentry',\n 'plan_exited',\n 'skill_invoked',\n 'mcp_resource_no_content',\n 'compact_file_reference',\n // Batch C\n 'token_usage',\n 'budget_usd',\n 'team_coordination',\n 'team_shutdown',\n 'diagnostics_new',\n 'session_continuation',\n ])\n\n private isEventReminder(reminder: ReminderMessage): boolean {\n return SystemReminderService.EVENT_REMINDER_TYPES.has(reminder.type)\n }\n\n /**\n * Retrieve cached event-based reminders (hook results, file status, task status)\n * Returns recent event reminders within freshness window\n */\n private getEventReminders(): ReminderMessage[] {\n const currentTime = Date.now()\n const EVENT_FRESHNESS_WINDOW = 10000 // 10 seconds\n const reminders: ReminderMessage[] = []\n const expiredKeys: string[] = []\n\n for (const [key, reminder] of this.reminderCache.entries()) {\n if (this.isEventReminder(reminder)) {\n const age = currentTime - reminder.timestamp\n if (age <= EVENT_FRESHNESS_WINDOW) {\n reminders.push(reminder)\n } else {\n expiredKeys.push(key)\n }\n }\n }\n\n expiredKeys.forEach(key => this.reminderCache.delete(key))\n\n return reminders\n }\n\n /**\n * Generate reminders for external file changes\n * Called when todo files are modified externally\n */\n public generateFileChangeReminder(context: any): ReminderMessage | null {\n const { agentId, filePath, reminder } = context\n\n if (!reminder) {\n return null\n }\n\n const currentTime = Date.now()\n const reminderKey = `file_changed_${agentId}_${filePath}_${currentTime}`\n\n // Ensure this specific file change reminder is only shown once\n if (this.sessionState.remindersSent.has(reminderKey)) {\n return null\n }\n\n this.sessionState.remindersSent.add(reminderKey)\n\n return this.createReminderMessage(\n 'file_changed',\n 'general',\n 'medium',\n reminder,\n currentTime,\n )\n }\n\n private createReminderMessage(\n type: string,\n category: ReminderMessage['category'],\n priority: ReminderMessage['priority'],\n content: string,\n timestamp: number,\n ): ReminderMessage {\n return {\n role: 'system',\n content: `<system-reminder>\\n${content}\\n</system-reminder>`,\n isMeta: true,\n timestamp,\n type,\n priority,\n category,\n }\n }\n\n private getTodoStateHash(todos: TodoItem[]): string {\n return todos\n .map(t => `${t.id}:${t.status}`)\n .sort()\n .join('|')\n }\n\n private clearTodoReminders(agentId?: string): void {\n const agentKey = agentId || 'default'\n for (const key of this.sessionState.remindersSent) {\n if (key.startsWith(`todo_updated_${agentKey}_`)) {\n this.sessionState.remindersSent.delete(key)\n }\n }\n }\n\n private setupEventDispatcher(): void {\n // Session startup events\n this.addEventListener('session:startup', context => {\n // Reset session state on startup\n this.resetSession()\n\n // Initialize session tracking\n this.sessionState.sessionStartTime = Date.now()\n this.sessionState.contextPresent =\n Object.keys(context.context || {}).length > 0\n })\n\n // Todo change events\n this.addEventListener('todo:changed', context => {\n this.sessionState.lastTodoUpdate = Date.now()\n this.clearTodoReminders(context.agentId)\n })\n\n // Todo file changed externally\n this.addEventListener('todo:file_changed', context => {\n // External file change detected, cache reminder for next generateReminders() call\n const agentId = context.agentId || 'default'\n this.clearTodoReminders(agentId)\n this.sessionState.lastTodoUpdate = Date.now()\n\n // Generate and cache file change reminder directly\n const reminder = this.generateFileChangeReminder(context)\n if (reminder) {\n const cacheKey = `file_changed_${agentId}_${Date.now()}`\n this.reminderCache.set(cacheKey, reminder)\n }\n })\n\n // File conflict events (external modification between reads)\n this.addEventListener('file:conflict', context => {\n const filePath = context.filePath || context.path || 'unknown'\n const cacheKey = `file_conflict_${filePath}_${Date.now()}`\n\n if (!this.sessionState.remindersSent.has(cacheKey)) {\n this.sessionState.remindersSent.add(cacheKey)\n\n const reminder = this.createReminderMessage(\n 'file_conflict',\n 'general',\n 'high',\n `File ${filePath} was modified externally since last read. Re-read the file before editing to avoid overwriting changes.`,\n Date.now(),\n )\n this.reminderCache.set(cacheKey, reminder)\n }\n })\n\n // File access events\n this.addEventListener('file:read', context => {\n this.sessionState.lastFileAccess = Date.now()\n })\n\n // File edit events for freshness detection\n this.addEventListener('file:edited', context => {\n // File edit handling\n })\n\n // Unified mention event handlers - eliminates code duplication\n this.addEventListener('agent:mentioned', context => {\n this.createMentionReminder({\n type: 'agent_mention',\n key: `agent_mention_${context.agentType}_${context.timestamp}`,\n category: 'task',\n priority: 'high',\n content: `The user mentioned @${context.originalMention}. You MUST use the Task tool with subagent_type=\"${context.agentType}\" to delegate this task to the specified agent. Provide a detailed, self-contained task description that fully captures the user's intent for the ${context.agentType} agent to execute.`,\n timestamp: context.timestamp,\n })\n })\n\n this.addEventListener('file:mentioned', context => {\n this.createMentionReminder({\n type: 'file_mention',\n key: `file_mention_${context.filePath}_${context.timestamp}`,\n category: 'general',\n priority: 'high',\n content: `The user mentioned @${context.originalMention}. You MUST read the entire content of the file at path: ${context.filePath} using the Read tool to understand the full context before proceeding with the user's request.`,\n timestamp: context.timestamp,\n })\n })\n\n this.addEventListener('ask-model:mentioned', context => {\n this.createMentionReminder({\n type: 'ask_model_mention',\n key: `ask_model_mention_${context.modelName}_${context.timestamp}`,\n category: 'task',\n priority: 'high',\n content: `The user mentioned @${context.modelName}. You MUST use the AskExpertModelTool to consult this specific model for expert opinions and analysis. Provide the user's question or context clearly to get the most relevant response from ${context.modelName}.`,\n timestamp: context.timestamp,\n })\n })\n\n // Hook result events (Batch A system reminders)\n this.addEventListener('hook:additional_context', context => {\n const key = `hook_ctx_${context.hookName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'hook_additional_context',\n 'general',\n 'medium',\n `Additional context from hook \"${context.hookName}\": ${context.content}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('hook:blocking_error', context => {\n const key = `hook_block_${context.hookName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'hook_blocking_error',\n 'general',\n 'high',\n `A hook blocked the action: ${context.reason}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('hook:stopped_continuation', context => {\n const key = `hook_stop_${context.hookName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'hook_stopped_continuation',\n 'general',\n 'high',\n `A hook stopped continuation: ${context.reason}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // File status events\n this.addEventListener('file:empty', context => {\n const key = `file_empty_${context.filePath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'file_empty',\n 'general',\n 'low',\n `File exists but is empty: ${context.filePath}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('file:truncated', context => {\n const key = `file_truncated_${context.filePath}_${context.limit}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'file_truncated',\n 'general',\n 'low',\n `File content was truncated at ${context.limit} lines. Total lines: ${context.totalLines}. Use offset and limit to read more.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Task status events\n this.addEventListener('task:status', context => {\n const key = `task_status_${context.taskId}_${context.status}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'task_status',\n 'task',\n 'medium',\n `Task ${context.taskId} (${context.description || 'unnamed'}): status changed to ${context.status}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('task:tools_reminder', context => {\n const key = 'task_tools_reminder'\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'task_tools_reminder',\n 'task',\n 'low',\n \"The task tools haven't been used recently. If you're working on tasks that would benefit from tracking progress, consider using TaskCreate to add new tasks and TaskUpdate to update task status (set to in_progress when starting, completed when done). Also consider cleaning up the task list if it has become stale. Only use these if relevant to the current work. This is just a gentle reminder - ignore if not applicable. Make sure that you NEVER mention this reminder to the user\",\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Batch B: Output style events\n this.addEventListener('output_style:active', context => {\n const key = `output_style_active_${context.styleName}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'output_style_active',\n 'general',\n 'low',\n `Output style \"${context.styleName}\" is active. Follow its formatting instructions for your responses.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Plan mode events\n this.addEventListener('plan:file_reference', context => {\n const key = `plan_file_ref_${context.planPath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_file_reference',\n 'general',\n 'medium',\n `A plan file exists at: ${context.planPath}. Review it before making implementation decisions.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('plan:mode_reentry', context => {\n const key = `plan_reentry_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_mode_reentry',\n 'general',\n 'medium',\n 'Re-entering plan mode. Review the existing plan and update as needed before implementation.',\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('plan:exited', context => {\n const key = `plan_exited_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_exited',\n 'general',\n 'medium',\n 'Exited plan mode. Proceed with implementation following the approved plan.',\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Skill invocation events\n this.addEventListener('skill:invoked', context => {\n const key = `skill_invoked_${context.skillName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'skill_invoked',\n 'general',\n 'low',\n `Skill \"${context.skillName}\" was invoked. Follow the skill's instructions in the expanded prompt.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // MCP resource events\n this.addEventListener('mcp:resource_no_content', context => {\n const key = `mcp_no_content_${context.resourceUri}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'mcp_resource_no_content',\n 'general',\n 'low',\n `MCP resource \"${context.resourceUri}\" returned no content. It may be unavailable or empty.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Compact file reference events\n this.addEventListener('compact:file_reference', context => {\n const key = `compact_file_ref_${context.filePath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'compact_file_reference',\n 'general',\n 'low',\n `Note: ${context.filePath} was read before the last conversation was summarized, but the contents are too large to include. Use Read tool if you need to access it.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Batch C: Token usage tracking\n this.addEventListener('token:usage', context => {\n const key = `token_usage_${context.threshold}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'token_usage',\n 'performance',\n 'low',\n `Token usage has reached ${context.percentage}% of context limit (${context.used}/${context.total} tokens).`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Budget tracking\n this.addEventListener('budget:usd', context => {\n const key = `budget_usd_${context.threshold}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'budget_usd',\n 'performance',\n 'medium',\n `USD budget usage: $${context.used.toFixed(2)} of $${context.limit.toFixed(2)} (${context.percentage}%).`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Team coordination\n this.addEventListener('team:coordination', context => {\n const key = `team_coord_${context.teamId}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'team_coordination',\n 'task',\n 'medium',\n `Team coordination: ${context.message}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Team shutdown\n this.addEventListener('team:shutdown', context => {\n const key = `team_shutdown_${context.teamId}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'team_shutdown',\n 'task',\n 'high',\n `Team \"${context.teamId}\" is shutting down. Reason: ${context.reason || 'completed'}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // New diagnostics\n this.addEventListener('diagnostics:new', context => {\n const key = `diagnostics_${context.source}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const count = context.count || 1\n const reminder = this.createReminderMessage(\n 'diagnostics_new',\n 'general',\n 'low',\n `${count} new diagnostic${count > 1 ? 's' : ''} detected from ${context.source}. Review before proceeding.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Session continuation\n this.addEventListener('session:continuation', context => {\n const key = 'session_continuation'\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'session_continuation',\n 'general',\n 'medium',\n `This session is a continuation of a previous conversation. Key context has been preserved.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n }\n\n public addEventListener(\n event: string,\n callback: (context: any) => void,\n ): void {\n if (!this.eventDispatcher.has(event)) {\n this.eventDispatcher.set(event, [])\n }\n this.eventDispatcher.get(event)!.push(callback)\n }\n\n public emitEvent(event: string, context: any): void {\n const listeners = this.eventDispatcher.get(event) || []\n listeners.forEach(callback => {\n try {\n callback(context)\n } catch (error) {\n console.error(`Error in event listener for ${event}:`, error)\n }\n })\n }\n\n /**\n * Unified mention reminder creation - eliminates duplicate logic\n * Centralizes reminder creation with consistent deduplication\n */\n private createMentionReminder(params: {\n type: string\n key: string\n category: ReminderMessage['category']\n priority: ReminderMessage['priority']\n content: string\n timestamp: number\n }): void {\n if (!this.sessionState.remindersSent.has(params.key)) {\n this.sessionState.remindersSent.add(params.key)\n\n const reminder = this.createReminderMessage(\n params.type,\n params.category,\n params.priority,\n params.content,\n params.timestamp,\n )\n\n this.reminderCache.set(params.key, reminder)\n }\n }\n\n public resetSession(): void {\n this.sessionState = {\n lastTodoUpdate: 0,\n lastFileAccess: 0,\n sessionStartTime: Date.now(),\n remindersSent: new Set(),\n contextPresent: false,\n reminderCount: 0,\n config: { ...this.sessionState.config }, // Preserve config across resets\n }\n this.reminderCache.clear() // Clear cache on session reset\n }\n\n public updateConfig(config: Partial<ReminderConfig>): void {\n this.sessionState.config = { ...this.sessionState.config, ...config }\n }\n\n public getSessionState(): SessionReminderState {\n return { ...this.sessionState }\n }\n}\n\nexport const systemReminderService = new SystemReminderService()\n\nexport const generateSystemReminders = (\n hasContext: boolean = false,\n agentId?: string,\n) => systemReminderService.generateReminders(hasContext, agentId)\n\nexport const generateFileChangeReminder = (context: any) =>\n systemReminderService.generateFileChangeReminder(context)\n\nexport const emitReminderEvent = (event: string, context: any) =>\n systemReminderService.emitEvent(event, context)\n\nexport const resetReminderSession = () => systemReminderService.resetSession()\nexport const getReminderSessionState = () =>\n systemReminderService.getSessionState()\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { homedir } from 'os'\nimport { getTodos, TodoItem } from '@utils/todoStorage'\n\n/**\n * Escape XML special characters to prevent injection in XML-tagged content.\n * Used for team messages and hook results injected into system reminders.\n */\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\nexport interface ReminderMessage {\n role: 'system'\n content: string\n isMeta: boolean\n timestamp: number\n type: string\n priority: 'low' | 'medium' | 'high'\n category: 'task' | 'security' | 'performance' | 'general'\n}\n\ninterface ReminderConfig {\n todoEmptyReminder: boolean\n securityReminder: boolean\n performanceReminder: boolean\n maxRemindersPerSession: number\n}\n\ninterface SessionReminderState {\n lastTodoUpdate: number\n lastFileAccess: number\n sessionStartTime: number\n remindersSent: Set<string>\n contextPresent: boolean\n reminderCount: number\n config: ReminderConfig\n}\n\nclass SystemReminderService {\n private sessionState: SessionReminderState = {\n lastTodoUpdate: 0,\n lastFileAccess: 0,\n sessionStartTime: Date.now(),\n remindersSent: new Set(),\n contextPresent: false,\n reminderCount: 0,\n config: {\n todoEmptyReminder: true,\n securityReminder: true,\n performanceReminder: true,\n maxRemindersPerSession: 10,\n },\n }\n\n private eventDispatcher = new Map<string, Array<(context: any) => void>>()\n private reminderCache = new Map<string, ReminderMessage>()\n\n constructor() {\n this.setupEventDispatcher()\n }\n\n /**\n * Conditional reminder injection - only when context is present\n * Enhanced with performance optimizations and priority management\n */\n public generateReminders(\n hasContext: boolean = false,\n agentId?: string,\n ): ReminderMessage[] {\n this.sessionState.contextPresent = hasContext\n\n // Only inject when context is present (matching original behavior)\n if (!hasContext) {\n return []\n }\n\n // Check session reminder limit to prevent overload\n if (\n this.sessionState.reminderCount >=\n this.sessionState.config.maxRemindersPerSession\n ) {\n return []\n }\n\n const reminders: ReminderMessage[] = []\n const currentTime = Date.now()\n\n // Periodic pruning of remindersSent to prevent unbounded growth\n // Prune every 100 reminders: remove entries older than 30 minutes\n if (this.sessionState.remindersSent.size > 100) {\n this.pruneOldReminders(currentTime)\n }\n\n // Use lazy evaluation for performance with agent context\n const reminderGenerators = [\n () => this.getPlanModeActiveReminder(),\n () => this.dispatchTodoEvent(agentId),\n () => this.dispatchTaskToolsReminder(),\n () => this.dispatchSecurityEvent(),\n () => this.dispatchPerformanceEvent(),\n () => this.getMentionReminders(),\n () => this.getEventReminders(),\n () => this.getTeamMessageReminders(agentId),\n ]\n\n for (const generator of reminderGenerators) {\n if (reminders.length >= 8) break // Accommodate mentions + event reminders\n\n const result = generator()\n if (result) {\n // Handle both single reminders and arrays\n const remindersToAdd = Array.isArray(result) ? result : [result]\n reminders.push(...remindersToAdd)\n this.sessionState.reminderCount += remindersToAdd.length\n }\n }\n\n // Log aggregated metrics instead of individual events for performance\n\n return reminders\n }\n\n /**\n * Plan mode persistent reminder \u2014 injected EVERY turn while plan mode is active (CC behavior).\n * Not gated by remindersSent because it must appear on every generateReminders() call.\n */\n private getPlanModeActiveReminder(): ReminderMessage | null {\n try {\n // Lazy import to avoid circular dependency\n const {\n isPlanModeEnabled,\n getPlanFilePath,\n } = require('@utils/plan/planMode')\n if (!isPlanModeEnabled()) return null\n\n const planPath = getPlanFilePath()\n return this.createReminderMessage(\n 'plan_mode_active',\n 'general',\n 'high',\n `Plan mode is active. You MUST NOT make any edits or changes except to the plan file at: ${planPath}\\n\\nEnd every turn with either:\\n- AskUserQuestion (to clarify requirements)\\n- ExitPlanMode (to present your plan for approval)\\n\\nDo NOT write code, edit files, or run commands other than read-only exploration.`,\n Date.now(),\n )\n } catch {\n return null\n }\n }\n\n private dispatchTodoEvent(agentId?: string): ReminderMessage | null {\n if (!this.sessionState.config.todoEmptyReminder) return null\n\n // Use agent-scoped todo access\n const todos = getTodos(agentId)\n const currentTime = Date.now()\n const agentKey = agentId || 'default'\n\n // Check if this is a fresh session (no todos seen yet)\n if (\n todos.length === 0 &&\n !this.sessionState.remindersSent.has(`todo_empty_${agentKey}`)\n ) {\n this.sessionState.remindersSent.add(`todo_empty_${agentKey}`)\n return this.createReminderMessage(\n 'todo',\n 'task',\n 'medium',\n 'This is a reminder that your todo list is currently empty. DO NOT mention this to the user explicitly because they are already aware. If you are working on tasks that would benefit from a todo list please use the TodoWrite tool to create one. If not, please feel free to ignore. Again do not mention this message to the user.',\n currentTime,\n )\n }\n\n // Check for todo updates since last seen\n if (todos.length > 0) {\n const reminderKey = `todo_updated_${agentKey}_${todos.length}_${this.getTodoStateHash(todos)}`\n\n // Use cache for performance optimization\n if (this.reminderCache.has(reminderKey)) {\n return this.reminderCache.get(reminderKey)!\n }\n\n if (!this.sessionState.remindersSent.has(reminderKey)) {\n this.sessionState.remindersSent.add(reminderKey)\n // Clear previous todo state reminders for this agent\n this.clearTodoReminders(agentKey)\n\n // Optimize: only include essential todo data\n const todoContent = JSON.stringify(\n todos.map(todo => ({\n content:\n todo.content.length > 100\n ? todo.content.substring(0, 100) + '...'\n : todo.content,\n status: todo.status,\n priority: todo.priority,\n id: todo.id,\n })),\n )\n\n const reminder = this.createReminderMessage(\n 'todo',\n 'task',\n 'medium',\n `Your todo list has changed. DO NOT mention this explicitly to the user. Here are the latest contents of your todo list:\\n\\n${todoContent}. Continue on with the tasks at hand if applicable.`,\n currentTime,\n )\n\n // Cache the reminder for reuse\n this.reminderCache.set(reminderKey, reminder)\n return reminder\n }\n }\n\n return null\n }\n\n private dispatchTaskToolsReminder(): ReminderMessage | null {\n const key = 'task_tools_reminder'\n if (this.sessionState.remindersSent.has(key)) return null\n\n // Only trigger after 5 minutes of session time\n const sessionDuration = Date.now() - this.sessionState.sessionStartTime\n if (sessionDuration < 5 * 60 * 1000) return null\n\n // Check if tasks exist via the event cache \u2014 if task:created or task:status\n // events have fired, the user is already using task tools\n const hasTaskActivity = Array.from(this.sessionState.remindersSent).some(\n k => k.startsWith('task_'),\n )\n if (hasTaskActivity) return null\n\n // Emit the reminder event so the listener caches the message\n this.emitEvent('task:tools_reminder', {})\n return null\n }\n\n private dispatchSecurityEvent(): ReminderMessage | null {\n if (!this.sessionState.config.securityReminder) return null\n\n const currentTime = Date.now()\n\n // Only inject security reminder once per session when file operations occur\n if (\n this.sessionState.lastFileAccess > 0 &&\n !this.sessionState.remindersSent.has('file_security')\n ) {\n this.sessionState.remindersSent.add('file_security')\n return this.createReminderMessage(\n 'security',\n 'security',\n 'high',\n 'Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.',\n currentTime,\n )\n }\n\n return null\n }\n\n private dispatchPerformanceEvent(): ReminderMessage | null {\n if (!this.sessionState.config.performanceReminder) return null\n\n const currentTime = Date.now()\n const sessionDuration = currentTime - this.sessionState.sessionStartTime\n\n // Remind about performance after long sessions (30 minutes)\n if (\n sessionDuration > 30 * 60 * 1000 &&\n !this.sessionState.remindersSent.has('performance_long_session')\n ) {\n this.sessionState.remindersSent.add('performance_long_session')\n return this.createReminderMessage(\n 'performance',\n 'performance',\n 'low',\n 'Long session detected. Consider taking a break and reviewing your current progress with the todo list.',\n currentTime,\n )\n }\n\n return null\n }\n\n /**\n * Retrieve cached mention reminders\n * Returns recent mentions (within 5 seconds) that haven't expired\n */\n private getMentionReminders(): ReminderMessage[] {\n const currentTime = Date.now()\n const MENTION_FRESHNESS_WINDOW = 5000 // 5 seconds\n const reminders: ReminderMessage[] = []\n const expiredKeys: string[] = []\n\n // Single pass through cache for both collection and cleanup identification\n for (const [key, reminder] of this.reminderCache.entries()) {\n if (this.isMentionReminder(reminder)) {\n const age = currentTime - reminder.timestamp\n if (age <= MENTION_FRESHNESS_WINDOW) {\n reminders.push(reminder)\n } else {\n expiredKeys.push(key)\n }\n }\n }\n\n // Clean up expired mention reminders in separate pass for performance\n expiredKeys.forEach(key => this.reminderCache.delete(key))\n\n return reminders\n }\n\n /**\n * Type guard for mention reminders - centralized type checking\n * Eliminates hardcoded type strings scattered throughout the code\n */\n private isMentionReminder(reminder: ReminderMessage): boolean {\n const mentionTypes = ['agent_mention', 'file_mention', 'ask_model_mention']\n return mentionTypes.includes(reminder.type)\n }\n\n /**\n * Event-based reminder types for hook results, file status, task status\n */\n private static readonly EVENT_REMINDER_TYPES = new Set([\n // Batch A\n 'hook_additional_context',\n 'hook_blocking_error',\n 'hook_stopped_continuation',\n 'file_empty',\n 'file_truncated',\n 'task_status',\n 'task_tools_reminder',\n // Batch B\n 'output_style_active',\n 'plan_file_reference',\n 'plan_mode_reentry',\n 'plan_exited',\n 'plan_verify',\n 'plan_mode_active',\n 'skill_invoked',\n 'mcp_resource_no_content',\n 'compact_file_reference',\n // Batch C\n 'token_usage',\n 'budget_usd',\n 'team_coordination',\n 'team_shutdown',\n 'diagnostics_new',\n 'session_continuation',\n ])\n\n private isEventReminder(reminder: ReminderMessage): boolean {\n return SystemReminderService.EVENT_REMINDER_TYPES.has(reminder.type)\n }\n\n /**\n * Retrieve cached event-based reminders (hook results, file status, task status)\n * Returns recent event reminders within freshness window\n */\n private getEventReminders(): ReminderMessage[] {\n const currentTime = Date.now()\n const EVENT_FRESHNESS_WINDOW = 10000 // 10 seconds\n const reminders: ReminderMessage[] = []\n const expiredKeys: string[] = []\n\n for (const [key, reminder] of this.reminderCache.entries()) {\n if (this.isEventReminder(reminder)) {\n const age = currentTime - reminder.timestamp\n if (age <= EVENT_FRESHNESS_WINDOW) {\n reminders.push(reminder)\n } else {\n expiredKeys.push(key)\n }\n }\n }\n\n expiredKeys.forEach(key => this.reminderCache.delete(key))\n\n return reminders\n }\n\n /**\n * Deliver pending team mailbox messages to the current agent as reminders.\n * Checks unread messages for the agent's team-scoped ID and formats them\n * as <team-message> blocks for conversation injection.\n */\n private getTeamMessageReminders(agentId?: string): ReminderMessage[] | null {\n if (!agentId) return null\n\n // Only deliver for team-scoped agents (format: name@team)\n const teamName = process.env.MINTO_TEAM_NAME || agentId.split('@')[1]\n if (!teamName) return null\n\n try {\n // Lazy import to avoid circular dependency (teamManager imports systemReminder)\n const { getTeam } = require('./agentTeams/teamManager')\n const entry = getTeam(teamName)\n if (!entry) return null\n\n const { mailbox } = entry\n const unread = mailbox.getUnread(agentId)\n if (unread.length === 0) return null\n\n // Mark as read so they aren't re-delivered\n mailbox.markRead(unread.map(m => m.id))\n\n const currentTime = Date.now()\n const reminders: ReminderMessage[] = []\n\n for (const msg of unread) {\n const senderName = msg.from.split('@')[0] || msg.from\n const summaryText =\n msg.summary || msg.text.slice(0, 60).replace(/\"/g, \"'\")\n const colorAttr = msg.color ? ` color=\"${escapeXml(msg.color)}\"` : ''\n const content = `<team-message from=\"${escapeXml(senderName)}\"${colorAttr} summary=\"${escapeXml(summaryText)}\">\\n${escapeXml(msg.text)}\\n</team-message>`\n\n reminders.push(\n this.createReminderMessage(\n 'team_message',\n 'task',\n 'high',\n content,\n currentTime,\n ),\n )\n }\n\n return reminders\n } catch {\n return null\n }\n }\n\n /**\n * Generate reminders for external file changes\n * Called when todo files are modified externally\n */\n public generateFileChangeReminder(context: any): ReminderMessage | null {\n const { agentId, filePath, reminder } = context\n\n if (!reminder) {\n return null\n }\n\n const currentTime = Date.now()\n const reminderKey = `file_changed_${agentId}_${filePath}_${currentTime}`\n\n // Ensure this specific file change reminder is only shown once\n if (this.sessionState.remindersSent.has(reminderKey)) {\n return null\n }\n\n this.sessionState.remindersSent.add(reminderKey)\n\n return this.createReminderMessage(\n 'file_changed',\n 'general',\n 'medium',\n reminder,\n currentTime,\n )\n }\n\n private createReminderMessage(\n type: string,\n category: ReminderMessage['category'],\n priority: ReminderMessage['priority'],\n content: string,\n timestamp: number,\n ): ReminderMessage {\n return {\n role: 'system',\n content: `<system-reminder>\\n${content}\\n</system-reminder>`,\n isMeta: true,\n timestamp,\n type,\n priority,\n category,\n }\n }\n\n private getTodoStateHash(todos: TodoItem[]): string {\n return todos\n .map(t => `${t.id}:${t.status}`)\n .sort()\n .join('|')\n }\n\n private clearTodoReminders(agentId?: string): void {\n const agentKey = agentId || 'default'\n for (const key of this.sessionState.remindersSent) {\n if (key.startsWith(`todo_updated_${agentKey}_`)) {\n this.sessionState.remindersSent.delete(key)\n }\n }\n }\n\n private setupEventDispatcher(): void {\n // Session startup events\n this.addEventListener('session:startup', context => {\n // Reset session state on startup\n this.resetSession()\n\n // Initialize session tracking\n this.sessionState.sessionStartTime = Date.now()\n this.sessionState.contextPresent =\n Object.keys(context.context || {}).length > 0\n })\n\n // Todo change events\n this.addEventListener('todo:changed', context => {\n this.sessionState.lastTodoUpdate = Date.now()\n this.clearTodoReminders(context.agentId)\n })\n\n // Todo file changed externally\n this.addEventListener('todo:file_changed', context => {\n // External file change detected, cache reminder for next generateReminders() call\n const agentId = context.agentId || 'default'\n this.clearTodoReminders(agentId)\n this.sessionState.lastTodoUpdate = Date.now()\n\n // Generate and cache file change reminder directly\n const reminder = this.generateFileChangeReminder(context)\n if (reminder) {\n const cacheKey = `file_changed_${agentId}_${Date.now()}`\n this.reminderCache.set(cacheKey, reminder)\n }\n })\n\n // File conflict events (external modification between reads)\n this.addEventListener('file:conflict', context => {\n const filePath = context.filePath || context.path || 'unknown'\n const cacheKey = `file_conflict_${filePath}_${Date.now()}`\n\n if (!this.sessionState.remindersSent.has(cacheKey)) {\n this.sessionState.remindersSent.add(cacheKey)\n\n const reminder = this.createReminderMessage(\n 'file_conflict',\n 'general',\n 'high',\n `File ${filePath} was modified externally since last read. Re-read the file before editing to avoid overwriting changes.`,\n Date.now(),\n )\n this.reminderCache.set(cacheKey, reminder)\n }\n })\n\n // File access events\n this.addEventListener('file:read', context => {\n this.sessionState.lastFileAccess = Date.now()\n })\n\n // File edit events for freshness detection\n this.addEventListener('file:edited', context => {\n // File edit handling\n })\n\n // Unified mention event handlers - eliminates code duplication\n this.addEventListener('agent:mentioned', context => {\n this.createMentionReminder({\n type: 'agent_mention',\n key: `agent_mention_${context.agentType}_${context.timestamp}`,\n category: 'task',\n priority: 'high',\n content: `The user mentioned @${context.originalMention}. You MUST use the Task tool with subagent_type=\"${context.agentType}\" to delegate this task to the specified agent. Provide a detailed, self-contained task description that fully captures the user's intent for the ${context.agentType} agent to execute.`,\n timestamp: context.timestamp,\n })\n })\n\n this.addEventListener('file:mentioned', context => {\n this.createMentionReminder({\n type: 'file_mention',\n key: `file_mention_${context.filePath}_${context.timestamp}`,\n category: 'general',\n priority: 'high',\n content: `The user mentioned @${context.originalMention}. You MUST read the entire content of the file at path: ${context.filePath} using the Read tool to understand the full context before proceeding with the user's request.`,\n timestamp: context.timestamp,\n })\n })\n\n this.addEventListener('ask-model:mentioned', context => {\n this.createMentionReminder({\n type: 'ask_model_mention',\n key: `ask_model_mention_${context.modelName}_${context.timestamp}`,\n category: 'task',\n priority: 'high',\n content: `The user mentioned @${context.modelName}. You MUST use the AskExpertModelTool to consult this specific model for expert opinions and analysis. Provide the user's question or context clearly to get the most relevant response from ${context.modelName}.`,\n timestamp: context.timestamp,\n })\n })\n\n // Hook result events (Batch A system reminders)\n this.addEventListener('hook:additional_context', context => {\n const key = `hook_ctx_${context.hookName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'hook_additional_context',\n 'general',\n 'medium',\n `Additional context from hook \"${escapeXml(context.hookName)}\": ${escapeXml(String(context.content))}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('hook:blocking_error', context => {\n const key = `hook_block_${context.hookName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'hook_blocking_error',\n 'general',\n 'high',\n `A hook blocked the action: ${escapeXml(String(context.reason))}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('hook:stopped_continuation', context => {\n const key = `hook_stop_${context.hookName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'hook_stopped_continuation',\n 'general',\n 'high',\n `A hook stopped continuation: ${escapeXml(String(context.reason))}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // File status events\n this.addEventListener('file:empty', context => {\n const key = `file_empty_${context.filePath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'file_empty',\n 'general',\n 'low',\n `File exists but is empty: ${context.filePath}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('file:truncated', context => {\n const key = `file_truncated_${context.filePath}_${context.limit}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'file_truncated',\n 'general',\n 'low',\n `File content was truncated at ${context.limit} lines. Total lines: ${context.totalLines}. Use offset and limit to read more.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Task status events\n this.addEventListener('task:status', context => {\n const key = `task_status_${context.taskId}_${context.status}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'task_status',\n 'task',\n 'medium',\n `Task ${context.taskId} (${context.description || 'unnamed'}): status changed to ${context.status}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('task:tools_reminder', context => {\n const key = 'task_tools_reminder'\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'task_tools_reminder',\n 'task',\n 'low',\n \"The task tools haven't been used recently. If you're working on tasks that would benefit from tracking progress, consider using TaskCreate to add new tasks and TaskUpdate to update task status (set to in_progress when starting, completed when done). Also consider cleaning up the task list if it has become stale. Only use these if relevant to the current work. This is just a gentle reminder - ignore if not applicable. Make sure that you NEVER mention this reminder to the user\",\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Batch B: Output style events\n this.addEventListener('output_style:active', context => {\n const key = `output_style_active_${context.styleName}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'output_style_active',\n 'general',\n 'low',\n `Output style \"${context.styleName}\" is active. Follow its formatting instructions for your responses.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Plan mode events\n this.addEventListener('plan:file_reference', context => {\n const key = `plan_file_ref_${context.planPath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_file_reference',\n 'general',\n 'medium',\n `A plan file exists at: ${context.planPath}. Review it before making implementation decisions.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('plan:mode_reentry', context => {\n const key = `plan_reentry_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_mode_reentry',\n 'general',\n 'medium',\n 'Re-entering plan mode. Review the existing plan and update as needed before implementation.',\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('plan:exited', context => {\n const key = `plan_exited_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_exited',\n 'general',\n 'medium',\n 'Exited plan mode. Proceed with implementation following the approved plan.',\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Skill invocation events\n this.addEventListener('skill:invoked', context => {\n const key = `skill_invoked_${context.skillName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'skill_invoked',\n 'general',\n 'low',\n `Skill \"${context.skillName}\" was invoked. Follow the skill's instructions in the expanded prompt.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // MCP resource events\n this.addEventListener('mcp:resource_no_content', context => {\n const key = `mcp_no_content_${context.resourceUri}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'mcp_resource_no_content',\n 'general',\n 'low',\n `MCP resource \"${context.resourceUri}\" returned no content. It may be unavailable or empty.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Compact file reference events\n this.addEventListener('compact:file_reference', context => {\n const key = `compact_file_ref_${context.filePath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'compact_file_reference',\n 'general',\n 'low',\n `Note: ${context.filePath} was read before the last conversation was summarized, but the contents are too large to include. Use Read tool if you need to access it.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Plan verify: post-execution verification reminder\n this.addEventListener('plan:verify', context => {\n const key = `plan_verify_${context.planPath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_verify',\n 'general',\n 'medium',\n `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.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Batch C: Token usage tracking\n this.addEventListener('token:usage', context => {\n const key = `token_usage_${context.threshold}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'token_usage',\n 'performance',\n 'low',\n `Token usage has reached ${context.percentage}% of context limit (${context.used}/${context.total} tokens).`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Budget tracking\n this.addEventListener('budget:usd', context => {\n const key = `budget_usd_${context.threshold}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'budget_usd',\n 'performance',\n 'medium',\n `USD budget usage: $${context.used.toFixed(2)} of $${context.limit.toFixed(2)} (${context.percentage}%).`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Team coordination (CC-compatible: include file paths for agent self-service)\n this.addEventListener('team:coordination', context => {\n const key = `team_coord_${context.teamId}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const teamId = context.teamId\n const homeDir = homedir()\n const teamConfigPath = `${homeDir}/.minto/teams/${teamId}/config.json`\n const taskListPath = `${homeDir}/.minto/tasks/${teamId}`\n const reminder = this.createReminderMessage(\n 'team_coordination',\n 'task',\n 'medium',\n `Team coordination: ${context.message}\\nteamConfigPath: ${teamConfigPath}\\ntaskListPath: ${taskListPath}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Team shutdown\n this.addEventListener('team:shutdown', context => {\n const key = `team_shutdown_${context.teamId}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'team_shutdown',\n 'task',\n 'high',\n `Team \"${context.teamId}\" is shutting down. Reason: ${context.reason || 'completed'}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // New diagnostics\n this.addEventListener('diagnostics:new', context => {\n const key = `diagnostics_${context.source}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const count = context.count || 1\n const reminder = this.createReminderMessage(\n 'diagnostics_new',\n 'general',\n 'low',\n `${count} new diagnostic${count > 1 ? 's' : ''} detected from ${context.source}. Review before proceeding.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Session continuation\n this.addEventListener('session:continuation', context => {\n const key = 'session_continuation'\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'session_continuation',\n 'general',\n 'medium',\n `This session is a continuation of a previous conversation. Key context has been preserved.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n }\n\n public addEventListener(\n event: string,\n callback: (context: any) => void,\n ): void {\n if (!this.eventDispatcher.has(event)) {\n this.eventDispatcher.set(event, [])\n }\n this.eventDispatcher.get(event)!.push(callback)\n }\n\n public emitEvent(event: string, context: any): void {\n const listeners = this.eventDispatcher.get(event) || []\n listeners.forEach(callback => {\n try {\n callback(context)\n } catch (error) {\n console.error(`Error in event listener for ${event}:`, error)\n }\n })\n }\n\n /**\n * Unified mention reminder creation - eliminates duplicate logic\n * Centralizes reminder creation with consistent deduplication\n */\n private createMentionReminder(params: {\n type: string\n key: string\n category: ReminderMessage['category']\n priority: ReminderMessage['priority']\n content: string\n timestamp: number\n }): void {\n if (!this.sessionState.remindersSent.has(params.key)) {\n this.sessionState.remindersSent.add(params.key)\n\n const reminder = this.createReminderMessage(\n params.type,\n params.category,\n params.priority,\n params.content,\n params.timestamp,\n )\n\n this.reminderCache.set(params.key, reminder)\n }\n }\n\n /**\n * Prune old reminder keys to prevent unbounded Set growth.\n * Keeps keys that contain timestamps or are \"sticky\" (security, performance).\n * Removes generic keys when the set exceeds threshold.\n */\n private pruneOldReminders(_currentTime: number): void {\n // Keep essential/sticky keys, remove timestamped transient ones\n const stickyPrefixes = ['file_security', 'performance_', 'todo_empty_']\n const toDelete: string[] = []\n for (const key of this.sessionState.remindersSent) {\n if (!stickyPrefixes.some(p => key.startsWith(p))) {\n toDelete.push(key)\n }\n }\n // Remove up to half of non-sticky keys\n const removeCount = Math.min(\n toDelete.length,\n Math.floor(toDelete.length / 2),\n )\n for (let i = 0; i < removeCount; i++) {\n this.sessionState.remindersSent.delete(toDelete[i]!)\n }\n }\n\n public resetSession(): void {\n this.sessionState = {\n lastTodoUpdate: 0,\n lastFileAccess: 0,\n sessionStartTime: Date.now(),\n remindersSent: new Set(),\n contextPresent: false,\n reminderCount: 0,\n config: { ...this.sessionState.config }, // Preserve config across resets\n }\n this.reminderCache.clear() // Clear cache on session reset\n }\n\n public updateConfig(config: Partial<ReminderConfig>): void {\n this.sessionState.config = { ...this.sessionState.config, ...config }\n }\n\n public getSessionState(): SessionReminderState {\n return { ...this.sessionState }\n }\n}\n\nexport const systemReminderService = new SystemReminderService()\n\nexport const generateSystemReminders = (\n hasContext: boolean = false,\n agentId?: string,\n) => systemReminderService.generateReminders(hasContext, agentId)\n\nexport const generateFileChangeReminder = (context: any) =>\n systemReminderService.generateFileChangeReminder(context)\n\nexport const emitReminderEvent = (event: string, context: any) =>\n systemReminderService.emitEvent(event, context)\n\nexport const resetReminderSession = () => systemReminderService.resetSession()\nexport const getReminderSessionState = () =>\n systemReminderService.getSessionState()\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { getSessionState, setSessionState } from "../utils/sessionState.js";
|
|
2
2
|
import { emitReminderEvent } from "./systemReminder.js";
|
|
3
3
|
import { getHookManager } from "../utils/hookManager.js";
|
|
4
|
+
import { TeamTaskStore } from "./agentTeams/teamTaskStore.js";
|
|
5
|
+
function normalizeTaskId(taskId) {
|
|
6
|
+
return taskId.startsWith("#") ? taskId.slice(1) : taskId;
|
|
7
|
+
}
|
|
8
|
+
function getTeamStore() {
|
|
9
|
+
const teamName = process.env.MINTO_TEAM_NAME;
|
|
10
|
+
if (!teamName) return null;
|
|
11
|
+
try {
|
|
12
|
+
const { getTeam } = require("./agentTeams/teamManager");
|
|
13
|
+
const entry = getTeam(teamName);
|
|
14
|
+
if (entry?.taskStore) return entry.taskStore;
|
|
15
|
+
const store = new TeamTaskStore(teamName);
|
|
16
|
+
store.load();
|
|
17
|
+
return store;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
4
22
|
const TASK_STORAGE_KEY = "claudeCodeTasks";
|
|
5
23
|
let taskCache = null;
|
|
6
24
|
let cacheTimestamp = 0;
|
|
@@ -53,6 +71,22 @@ function generateActiveForm(subject) {
|
|
|
53
71
|
return `Working on: ${trimmed}`;
|
|
54
72
|
}
|
|
55
73
|
function getAllTasks() {
|
|
74
|
+
const teamStore = getTeamStore();
|
|
75
|
+
if (teamStore) {
|
|
76
|
+
return teamStore.getAll().map((t) => ({
|
|
77
|
+
id: t.id,
|
|
78
|
+
subject: t.subject,
|
|
79
|
+
description: t.description,
|
|
80
|
+
status: t.status,
|
|
81
|
+
owner: t.assignee,
|
|
82
|
+
activeForm: t.activeForm,
|
|
83
|
+
metadata: t.metadata,
|
|
84
|
+
blocks: t.blocks,
|
|
85
|
+
blockedBy: t.blockedBy,
|
|
86
|
+
createdAt: t.createdAt,
|
|
87
|
+
updatedAt: t.updatedAt
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
56
90
|
const now = Date.now();
|
|
57
91
|
if (taskCache && now - cacheTimestamp < CACHE_TTL) {
|
|
58
92
|
return taskCache;
|
|
@@ -71,6 +105,29 @@ function saveTasks(tasks) {
|
|
|
71
105
|
invalidateCache();
|
|
72
106
|
}
|
|
73
107
|
function createTask(input) {
|
|
108
|
+
const teamStore = getTeamStore();
|
|
109
|
+
if (teamStore) {
|
|
110
|
+
const activeForm = input.activeForm || generateActiveForm(input.subject);
|
|
111
|
+
const teamTask = teamStore.create(input.subject, input.description, {
|
|
112
|
+
activeForm,
|
|
113
|
+
metadata: input.metadata
|
|
114
|
+
});
|
|
115
|
+
const now2 = Date.now();
|
|
116
|
+
const task = {
|
|
117
|
+
id: teamTask.id,
|
|
118
|
+
subject: teamTask.subject,
|
|
119
|
+
description: teamTask.description,
|
|
120
|
+
status: teamTask.status,
|
|
121
|
+
activeForm: teamTask.activeForm || activeForm,
|
|
122
|
+
metadata: teamTask.metadata || input.metadata,
|
|
123
|
+
blocks: teamTask.blocks,
|
|
124
|
+
blockedBy: teamTask.blockedBy,
|
|
125
|
+
createdAt: teamTask.createdAt,
|
|
126
|
+
updatedAt: teamTask.updatedAt
|
|
127
|
+
};
|
|
128
|
+
emitReminderEvent("task:created", { task, timestamp: now2 });
|
|
129
|
+
return task;
|
|
130
|
+
}
|
|
74
131
|
const tasks = getAllTasks();
|
|
75
132
|
const now = Date.now();
|
|
76
133
|
const newTask = {
|
|
@@ -91,7 +148,124 @@ function createTask(input) {
|
|
|
91
148
|
});
|
|
92
149
|
return newTask;
|
|
93
150
|
}
|
|
94
|
-
function updateTask(input) {
|
|
151
|
+
async function updateTask(input) {
|
|
152
|
+
input = { ...input, taskId: normalizeTaskId(input.taskId) };
|
|
153
|
+
const teamStore = getTeamStore();
|
|
154
|
+
if (teamStore) {
|
|
155
|
+
if (input.status === "deleted") {
|
|
156
|
+
teamStore.delete(input.taskId);
|
|
157
|
+
emitReminderEvent("task:deleted", {
|
|
158
|
+
taskId: input.taskId,
|
|
159
|
+
timestamp: Date.now()
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
id: input.taskId,
|
|
163
|
+
subject: "",
|
|
164
|
+
description: "",
|
|
165
|
+
status: "completed",
|
|
166
|
+
createdAt: 0,
|
|
167
|
+
updatedAt: Date.now()
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const updated = teamStore.update(input.taskId, {
|
|
171
|
+
status: input.status,
|
|
172
|
+
subject: input.subject,
|
|
173
|
+
description: input.description,
|
|
174
|
+
assignee: input.owner,
|
|
175
|
+
activeForm: input.activeForm,
|
|
176
|
+
metadata: input.metadata,
|
|
177
|
+
addBlocks: input.addBlocks,
|
|
178
|
+
addBlockedBy: input.addBlockedBy
|
|
179
|
+
});
|
|
180
|
+
if (!updated) return null;
|
|
181
|
+
const task = {
|
|
182
|
+
id: updated.id,
|
|
183
|
+
subject: updated.subject,
|
|
184
|
+
description: updated.description,
|
|
185
|
+
status: updated.status,
|
|
186
|
+
owner: updated.assignee,
|
|
187
|
+
activeForm: updated.activeForm,
|
|
188
|
+
metadata: updated.metadata,
|
|
189
|
+
blocks: updated.blocks,
|
|
190
|
+
blockedBy: updated.blockedBy,
|
|
191
|
+
createdAt: updated.createdAt,
|
|
192
|
+
updatedAt: updated.updatedAt
|
|
193
|
+
};
|
|
194
|
+
if (input.owner && updated.assignee) {
|
|
195
|
+
const teamName = process.env.MINTO_TEAM_NAME;
|
|
196
|
+
if (teamName) {
|
|
197
|
+
try {
|
|
198
|
+
const { getTeam } = require("./agentTeams/teamManager");
|
|
199
|
+
const entry = getTeam(teamName);
|
|
200
|
+
if (entry?.mailbox) {
|
|
201
|
+
const agentId = process.env.MINTO_AGENT_ID || `team-lead@${teamName}`;
|
|
202
|
+
const assignmentMsg = JSON.stringify({
|
|
203
|
+
type: "task_assignment",
|
|
204
|
+
taskId: task.id,
|
|
205
|
+
subject: task.subject,
|
|
206
|
+
description: task.description,
|
|
207
|
+
assignedBy: agentId,
|
|
208
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
209
|
+
});
|
|
210
|
+
entry.mailbox.send(agentId, updated.assignee, assignmentMsg);
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (input.status) {
|
|
217
|
+
emitReminderEvent("task:status", {
|
|
218
|
+
taskId: task.id,
|
|
219
|
+
status: task.status,
|
|
220
|
+
subject: task.subject
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (updated.status === "completed") {
|
|
224
|
+
const hookMgr = getHookManager();
|
|
225
|
+
if (hookMgr) {
|
|
226
|
+
try {
|
|
227
|
+
const hookResult = await hookMgr.executeTaskCompleted(
|
|
228
|
+
task.id,
|
|
229
|
+
task.subject,
|
|
230
|
+
task.description
|
|
231
|
+
);
|
|
232
|
+
if (hookResult && !hookResult.shouldContinue) {
|
|
233
|
+
teamStore.update(input.taskId, { status: "in_progress" });
|
|
234
|
+
return {
|
|
235
|
+
...task,
|
|
236
|
+
status: "in_progress",
|
|
237
|
+
metadata: {
|
|
238
|
+
...task.metadata,
|
|
239
|
+
_hookRejected: true,
|
|
240
|
+
_hookFeedback: hookResult.reason || "Blocked by hook"
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
} catch {
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const teamName = process.env.MINTO_TEAM_NAME;
|
|
248
|
+
if (teamName) {
|
|
249
|
+
try {
|
|
250
|
+
const { getTeam } = require("./agentTeams/teamManager");
|
|
251
|
+
const entry = getTeam(teamName);
|
|
252
|
+
if (entry?.mailbox) {
|
|
253
|
+
const agentId = process.env.MINTO_AGENT_ID || `unknown@${teamName}`;
|
|
254
|
+
const completedMsg = JSON.stringify({
|
|
255
|
+
type: "task_completed",
|
|
256
|
+
taskId: task.id,
|
|
257
|
+
taskSubject: task.subject,
|
|
258
|
+
from: agentId,
|
|
259
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
260
|
+
});
|
|
261
|
+
entry.mailbox.send(agentId, `team-lead@${teamName}`, completedMsg);
|
|
262
|
+
}
|
|
263
|
+
} catch {
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return task;
|
|
268
|
+
}
|
|
95
269
|
const tasks = getAllTasks();
|
|
96
270
|
const taskIndex = tasks.findIndex((t) => t.id === input.taskId);
|
|
97
271
|
if (taskIndex === -1) {
|
|
@@ -166,19 +340,36 @@ function updateTask(input) {
|
|
|
166
340
|
if (input.status === "completed") {
|
|
167
341
|
const hookMgr = getHookManager();
|
|
168
342
|
if (hookMgr) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
343
|
+
try {
|
|
344
|
+
const hookResult = await hookMgr.executeTaskCompleted(
|
|
345
|
+
updatedTask.id,
|
|
346
|
+
updatedTask.subject,
|
|
347
|
+
updatedTask.description
|
|
348
|
+
);
|
|
349
|
+
if (hookResult && !hookResult.shouldContinue) {
|
|
350
|
+
updatedTask.status = "in_progress";
|
|
351
|
+
const revertedTasks = [...tasks];
|
|
352
|
+
revertedTasks[taskIndex] = updatedTask;
|
|
353
|
+
saveTasks(revertedTasks);
|
|
354
|
+
return {
|
|
355
|
+
...updatedTask,
|
|
356
|
+
metadata: {
|
|
357
|
+
...updatedTask.metadata,
|
|
358
|
+
_hookRejected: true,
|
|
359
|
+
_hookFeedback: hookResult.reason || "Blocked by hook"
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
} catch {
|
|
364
|
+
}
|
|
175
365
|
}
|
|
176
366
|
}
|
|
177
367
|
return updatedTask;
|
|
178
368
|
}
|
|
179
369
|
function getTaskById(taskId) {
|
|
370
|
+
const id = normalizeTaskId(taskId);
|
|
180
371
|
const tasks = getAllTasks();
|
|
181
|
-
return tasks.find((t) => t.id ===
|
|
372
|
+
return tasks.find((t) => t.id === id) || null;
|
|
182
373
|
}
|
|
183
374
|
function getTaskList() {
|
|
184
375
|
const tasks = getAllTasks();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/services/taskStore.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Task Store Service - Claude Code Compatible\n *\n * Provides task management functionality compatible with Claude Code's\n * TaskCreate, TaskUpdate, TaskGet, TaskList tools.\n *\n * Task lifecycle: pending \u2192 in_progress \u2192 completed\n * Tasks can also be deleted (soft delete with 'deleted' status)\n */\n\nimport { getSessionState, setSessionState } from '@utils/sessionState'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { getHookManager } from '@utils/hookManager'\n\n/**\n * Task interface matching Claude Code's task structure\n */\nexport interface Task {\n id: string\n subject: string\n description: string\n status: 'pending' | 'in_progress' | 'completed'\n activeForm?: string\n owner?: string\n metadata?: Record<string, unknown>\n blocks?: string[] // Task IDs that this task blocks\n blockedBy?: string[] // Task IDs that block this task\n createdAt: number\n updatedAt: number\n}\n\n/**\n * Task creation input (without auto-generated fields)\n */\nexport interface TaskCreateInput {\n subject: string\n description: string\n activeForm?: string\n metadata?: Record<string, unknown>\n}\n\n/**\n * Task update input (all fields optional except taskId)\n */\nexport interface TaskUpdateInput {\n taskId: string\n status?: 'pending' | 'in_progress' | 'completed' | 'deleted'\n subject?: string\n description?: string\n activeForm?: string\n owner?: string\n metadata?: Record<string, unknown>\n addBlocks?: string[]\n addBlockedBy?: string[]\n}\n\nconst TASK_STORAGE_KEY = 'claudeCodeTasks'\n\n// In-memory cache\nlet taskCache: Task[] | null = null\nlet cacheTimestamp = 0\nconst CACHE_TTL = 5000 // 5 seconds\n\n// Auto-incrementing ID counter\nlet nextTaskId = 1\n\nfunction invalidateCache(): void {\n taskCache = null\n cacheTimestamp = 0\n}\n\nfunction getNextTaskId(): string {\n // Find the highest existing ID to avoid conflicts\n const tasks = getAllTasks()\n const existingIds = tasks\n .map(t => parseInt(t.id, 10))\n .filter(id => !isNaN(id))\n\n if (existingIds.length > 0) {\n nextTaskId = Math.max(...existingIds) + 1\n }\n\n return String(nextTaskId++)\n}\n\n/**\n * Generate activeForm from subject if not provided\n * Converts imperative form to present continuous\n */\nfunction generateActiveForm(subject: string): string {\n const trimmed = subject.trim()\n\n const patterns = [\n { regex: /^(Run|run)\\s+(.+)$/i, replacement: 'Running $2' },\n { regex: /^(Build|build)\\s+(.+)$/i, replacement: 'Building $2' },\n { regex: /^(Fix|fix)\\s+(.+)$/i, replacement: 'Fixing $2' },\n { regex: /^(Add|add)\\s+(.+)$/i, replacement: 'Adding $2' },\n { regex: /^(Create|create)\\s+(.+)$/i, replacement: 'Creating $2' },\n { regex: /^(Update|update)\\s+(.+)$/i, replacement: 'Updating $2' },\n { regex: /^(Delete|delete)\\s+(.+)$/i, replacement: 'Deleting $2' },\n { regex: /^(Test|test)\\s+(.+)$/i, replacement: 'Testing $2' },\n { regex: /^(Deploy|deploy)\\s+(.+)$/i, replacement: 'Deploying $2' },\n { regex: /^(Analyze|analyze)\\s+(.+)$/i, replacement: 'Analyzing $2' },\n { regex: /^(Review|review)\\s+(.+)$/i, replacement: 'Reviewing $2' },\n { regex: /^(Write|write)\\s+(.+)$/i, replacement: 'Writing $2' },\n {\n regex: /^(Implement|implement)\\s+(.+)$/i,\n replacement: 'Implementing $2',\n },\n { regex: /^(Refactor|refactor)\\s+(.+)$/i, replacement: 'Refactoring $2' },\n { regex: /^(Debug|debug)\\s+(.+)$/i, replacement: 'Debugging $2' },\n { regex: /^(Configure|configure)\\s+(.+)$/i, replacement: 'Configuring $2' },\n { regex: /^(Install|install)\\s+(.+)$/i, replacement: 'Installing $2' },\n { regex: /^(Remove|remove)\\s+(.+)$/i, replacement: 'Removing $2' },\n { regex: /^(Verify|verify)\\s+(.+)$/i, replacement: 'Verifying $2' },\n { regex: /^(Check|check)\\s+(.+)$/i, replacement: 'Checking $2' },\n ]\n\n for (const { regex, replacement } of patterns) {\n if (regex.test(trimmed)) {\n return trimmed.replace(regex, replacement)\n }\n }\n\n return `Working on: ${trimmed}`\n}\n\n/**\n * Get all tasks from storage\n */\nexport function getAllTasks(): Task[] {\n const now = Date.now()\n\n // Check cache first\n if (taskCache && now - cacheTimestamp < CACHE_TTL) {\n return taskCache\n }\n\n const sessionState = getSessionState() as Record<string, unknown>\n const tasks = (sessionState[TASK_STORAGE_KEY] as Task[]) || []\n\n // Update cache\n taskCache = [...tasks]\n cacheTimestamp = now\n\n return tasks\n}\n\n/**\n * Save tasks to storage\n */\nfunction saveTasks(tasks: Task[]): void {\n setSessionState({\n ...getSessionState(),\n [TASK_STORAGE_KEY]: tasks,\n } as Record<string, unknown>)\n\n invalidateCache()\n}\n\n/**\n * Create a new task\n */\nexport function createTask(input: TaskCreateInput): Task {\n const tasks = getAllTasks()\n const now = Date.now()\n\n const newTask: Task = {\n id: getNextTaskId(),\n subject: input.subject,\n description: input.description,\n status: 'pending',\n activeForm: input.activeForm || generateActiveForm(input.subject),\n metadata: input.metadata,\n createdAt: now,\n updatedAt: now,\n }\n\n const updatedTasks = [...tasks, newTask]\n saveTasks(updatedTasks)\n\n // Emit event for system reminders\n emitReminderEvent('task:created', {\n task: newTask,\n timestamp: now,\n })\n\n return newTask\n}\n\n/**\n * Update an existing task\n */\nexport function updateTask(input: TaskUpdateInput): Task | null {\n const tasks = getAllTasks()\n const taskIndex = tasks.findIndex(t => t.id === input.taskId)\n\n if (taskIndex === -1) {\n return null\n }\n\n const existingTask = tasks[taskIndex]\n const now = Date.now()\n\n // Handle deletion\n if (input.status === 'deleted') {\n const updatedTasks = tasks.filter(t => t.id !== input.taskId)\n saveTasks(updatedTasks)\n\n emitReminderEvent('task:deleted', {\n taskId: input.taskId,\n timestamp: now,\n })\n\n return existingTask\n }\n\n // Build updated task\n const updatedTask: Task = {\n ...existingTask,\n updatedAt: now,\n }\n\n if (input.status !== undefined) {\n updatedTask.status = input.status\n }\n if (input.subject !== undefined) {\n updatedTask.subject = input.subject\n }\n if (input.description !== undefined) {\n updatedTask.description = input.description\n }\n if (input.activeForm !== undefined) {\n updatedTask.activeForm = input.activeForm\n }\n if (input.owner !== undefined) {\n updatedTask.owner = input.owner\n }\n if (input.metadata !== undefined) {\n // Merge metadata, null values delete keys\n const newMetadata = { ...existingTask.metadata }\n for (const [key, value] of Object.entries(input.metadata)) {\n if (value === null) {\n delete newMetadata[key]\n } else {\n newMetadata[key] = value\n }\n }\n updatedTask.metadata =\n Object.keys(newMetadata).length > 0 ? newMetadata : undefined\n }\n\n // Handle dependency updates\n if (input.addBlocks && input.addBlocks.length > 0) {\n const currentBlocks = new Set(existingTask.blocks || [])\n input.addBlocks.forEach(id => currentBlocks.add(id))\n updatedTask.blocks = Array.from(currentBlocks)\n }\n if (input.addBlockedBy && input.addBlockedBy.length > 0) {\n const currentBlockedBy = new Set(existingTask.blockedBy || [])\n input.addBlockedBy.forEach(id => currentBlockedBy.add(id))\n updatedTask.blockedBy = Array.from(currentBlockedBy)\n }\n\n // Update tasks array\n const updatedTasks = [...tasks]\n updatedTasks[taskIndex] = updatedTask\n saveTasks(updatedTasks)\n\n // Emit event\n emitReminderEvent('task:updated', {\n previousTask: existingTask,\n updatedTask,\n timestamp: now,\n })\n\n // Emit task:status when status changes\n if (input.status && input.status !== existingTask.status) {\n emitReminderEvent('task:status', {\n taskId: updatedTask.id,\n status: updatedTask.status,\n subject: updatedTask.subject,\n })\n }\n\n // Fire TaskCompleted hook when status changes to 'completed'\n if (input.status === 'completed') {\n const hookMgr = getHookManager()\n if (hookMgr) {\n hookMgr\n .executeTaskCompleted(\n updatedTask.id,\n updatedTask.subject,\n updatedTask.description,\n )\n .catch(() => {\n // Hook errors don't break task completion\n })\n }\n }\n\n return updatedTask\n}\n\n/**\n * Get a task by ID\n */\nexport function getTaskById(taskId: string): Task | null {\n const tasks = getAllTasks()\n return tasks.find(t => t.id === taskId) || null\n}\n\n/**\n * Get task list summary for display\n * Returns tasks with summary info suitable for listing\n */\nexport function getTaskList(): Array<{\n id: string\n subject: string\n status: 'pending' | 'in_progress' | 'completed'\n owner?: string\n blockedBy?: string[]\n}> {\n const tasks = getAllTasks()\n\n // Filter out tasks blocked by incomplete tasks\n return tasks.map(task => {\n // Check which blockedBy tasks are still open\n const openBlockers = (task.blockedBy || []).filter(blockerId => {\n const blocker = tasks.find(t => t.id === blockerId)\n return blocker && blocker.status !== 'completed'\n })\n\n return {\n id: task.id,\n subject: task.subject,\n status: task.status,\n owner: task.owner,\n blockedBy: openBlockers.length > 0 ? openBlockers : undefined,\n }\n })\n}\n\n/**\n * Clear all tasks (for testing or reset)\n */\nexport function clearAllTasks(): void {\n saveTasks([])\n nextTaskId = 1\n\n emitReminderEvent('task:cleared', {\n timestamp: Date.now(),\n })\n}\n\n/**\n * Get task statistics\n */\nexport function getTaskStatistics(): {\n total: number\n pending: number\n inProgress: number\n completed: number\n} {\n const tasks = getAllTasks()\n\n return {\n total: tasks.length,\n pending: tasks.filter(t => t.status === 'pending').length,\n inProgress: tasks.filter(t => t.status === 'in_progress').length,\n completed: tasks.filter(t => t.status === 'completed').length,\n }\n}\n"],
|
|
5
|
-
"mappings": "AAUA,SAAS,iBAAiB,uBAAuB;AACjD,SAAS,yBAAyB;AAClC,SAAS,sBAAsB;
|
|
6
|
-
"names": ["updatedTasks"]
|
|
4
|
+
"sourcesContent": ["/**\n * Task Store Service - Claude Code Compatible\n *\n * Provides task management functionality compatible with Claude Code's\n * TaskCreate, TaskUpdate, TaskGet, TaskList tools.\n *\n * Task lifecycle: pending \u2192 in_progress \u2192 completed\n * Tasks can also be deleted (soft delete with 'deleted' status)\n */\n\nimport { getSessionState, setSessionState } from '@utils/sessionState'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { getHookManager } from '@utils/hookManager'\nimport { TeamTaskStore } from '@services/agentTeams/teamTaskStore'\n\n/**\n * Normalize taskId by stripping leading '#' that the model may include\n * (TaskCreate returns \"Task #1 created\", model echoes \"#1\" as the ID).\n */\nfunction normalizeTaskId(taskId: string): string {\n return taskId.startsWith('#') ? taskId.slice(1) : taskId\n}\n\n/**\n * Get the active team task store when running in a team context.\n * Returns null when not in a team \u2014 callers fall through to session store.\n */\nfunction getTeamStore(): TeamTaskStore | null {\n const teamName = process.env.MINTO_TEAM_NAME\n if (!teamName) return null\n\n try {\n const { getTeam } = require('./agentTeams/teamManager')\n const entry = getTeam(teamName)\n if (entry?.taskStore) return entry.taskStore\n // Fallback: create a standalone store (for tmux subprocesses)\n const store = new TeamTaskStore(teamName)\n store.load()\n return store\n } catch {\n return null\n }\n}\n\n/**\n * Task interface matching Claude Code's task structure\n */\nexport interface Task {\n id: string\n subject: string\n description: string\n status: 'pending' | 'in_progress' | 'completed'\n activeForm?: string\n owner?: string\n metadata?: Record<string, unknown>\n blocks?: string[] // Task IDs that this task blocks\n blockedBy?: string[] // Task IDs that block this task\n createdAt: number\n updatedAt: number\n}\n\n/**\n * Task creation input (without auto-generated fields)\n */\nexport interface TaskCreateInput {\n subject: string\n description: string\n activeForm?: string\n metadata?: Record<string, unknown>\n}\n\n/**\n * Task update input (all fields optional except taskId)\n */\nexport interface TaskUpdateInput {\n taskId: string\n status?: 'pending' | 'in_progress' | 'completed' | 'deleted'\n subject?: string\n description?: string\n activeForm?: string\n owner?: string\n metadata?: Record<string, unknown>\n addBlocks?: string[]\n addBlockedBy?: string[]\n}\n\nconst TASK_STORAGE_KEY = 'claudeCodeTasks'\n\n// In-memory cache\nlet taskCache: Task[] | null = null\nlet cacheTimestamp = 0\nconst CACHE_TTL = 5000 // 5 seconds\n\n// Auto-incrementing ID counter\nlet nextTaskId = 1\n\nfunction invalidateCache(): void {\n taskCache = null\n cacheTimestamp = 0\n}\n\nfunction getNextTaskId(): string {\n // Find the highest existing ID to avoid conflicts\n const tasks = getAllTasks()\n const existingIds = tasks\n .map(t => parseInt(t.id, 10))\n .filter(id => !isNaN(id))\n\n if (existingIds.length > 0) {\n nextTaskId = Math.max(...existingIds) + 1\n }\n\n return String(nextTaskId++)\n}\n\n/**\n * Generate activeForm from subject if not provided\n * Converts imperative form to present continuous\n */\nfunction generateActiveForm(subject: string): string {\n const trimmed = subject.trim()\n\n const patterns = [\n { regex: /^(Run|run)\\s+(.+)$/i, replacement: 'Running $2' },\n { regex: /^(Build|build)\\s+(.+)$/i, replacement: 'Building $2' },\n { regex: /^(Fix|fix)\\s+(.+)$/i, replacement: 'Fixing $2' },\n { regex: /^(Add|add)\\s+(.+)$/i, replacement: 'Adding $2' },\n { regex: /^(Create|create)\\s+(.+)$/i, replacement: 'Creating $2' },\n { regex: /^(Update|update)\\s+(.+)$/i, replacement: 'Updating $2' },\n { regex: /^(Delete|delete)\\s+(.+)$/i, replacement: 'Deleting $2' },\n { regex: /^(Test|test)\\s+(.+)$/i, replacement: 'Testing $2' },\n { regex: /^(Deploy|deploy)\\s+(.+)$/i, replacement: 'Deploying $2' },\n { regex: /^(Analyze|analyze)\\s+(.+)$/i, replacement: 'Analyzing $2' },\n { regex: /^(Review|review)\\s+(.+)$/i, replacement: 'Reviewing $2' },\n { regex: /^(Write|write)\\s+(.+)$/i, replacement: 'Writing $2' },\n {\n regex: /^(Implement|implement)\\s+(.+)$/i,\n replacement: 'Implementing $2',\n },\n { regex: /^(Refactor|refactor)\\s+(.+)$/i, replacement: 'Refactoring $2' },\n { regex: /^(Debug|debug)\\s+(.+)$/i, replacement: 'Debugging $2' },\n { regex: /^(Configure|configure)\\s+(.+)$/i, replacement: 'Configuring $2' },\n { regex: /^(Install|install)\\s+(.+)$/i, replacement: 'Installing $2' },\n { regex: /^(Remove|remove)\\s+(.+)$/i, replacement: 'Removing $2' },\n { regex: /^(Verify|verify)\\s+(.+)$/i, replacement: 'Verifying $2' },\n { regex: /^(Check|check)\\s+(.+)$/i, replacement: 'Checking $2' },\n ]\n\n for (const { regex, replacement } of patterns) {\n if (regex.test(trimmed)) {\n return trimmed.replace(regex, replacement)\n }\n }\n\n return `Working on: ${trimmed}`\n}\n\n/**\n * Get all tasks from storage.\n * In a team context, reads from the file-based TeamTaskStore.\n */\nexport function getAllTasks(): Task[] {\n const teamStore = getTeamStore()\n if (teamStore) {\n return teamStore.getAll().map(t => ({\n id: t.id,\n subject: t.subject,\n description: t.description,\n status: t.status,\n owner: t.assignee,\n activeForm: t.activeForm,\n metadata: t.metadata,\n blocks: t.blocks,\n blockedBy: t.blockedBy,\n createdAt: t.createdAt,\n updatedAt: t.updatedAt,\n }))\n }\n\n const now = Date.now()\n\n // Check cache first\n if (taskCache && now - cacheTimestamp < CACHE_TTL) {\n return taskCache\n }\n\n const sessionState = getSessionState() as Record<string, unknown>\n const tasks = (sessionState[TASK_STORAGE_KEY] as Task[]) || []\n\n // Update cache\n taskCache = [...tasks]\n cacheTimestamp = now\n\n return tasks\n}\n\n/**\n * Save tasks to storage\n */\nfunction saveTasks(tasks: Task[]): void {\n setSessionState({\n ...getSessionState(),\n [TASK_STORAGE_KEY]: tasks,\n } as Record<string, unknown>)\n\n invalidateCache()\n}\n\n/**\n * Create a new task.\n * When running in a team context (MINTO_TEAM_NAME set), delegates to\n * the file-based TeamTaskStore for cross-process visibility.\n */\nexport function createTask(input: TaskCreateInput): Task {\n const teamStore = getTeamStore()\n if (teamStore) {\n const activeForm = input.activeForm || generateActiveForm(input.subject)\n const teamTask = teamStore.create(input.subject, input.description, {\n activeForm,\n metadata: input.metadata,\n })\n const now = Date.now()\n const task: Task = {\n id: teamTask.id,\n subject: teamTask.subject,\n description: teamTask.description,\n status: teamTask.status,\n activeForm: teamTask.activeForm || activeForm,\n metadata: teamTask.metadata || input.metadata,\n blocks: teamTask.blocks,\n blockedBy: teamTask.blockedBy,\n createdAt: teamTask.createdAt,\n updatedAt: teamTask.updatedAt,\n }\n emitReminderEvent('task:created', { task, timestamp: now })\n return task\n }\n\n const tasks = getAllTasks()\n const now = Date.now()\n\n const newTask: Task = {\n id: getNextTaskId(),\n subject: input.subject,\n description: input.description,\n status: 'pending',\n activeForm: input.activeForm || generateActiveForm(input.subject),\n metadata: input.metadata,\n createdAt: now,\n updatedAt: now,\n }\n\n const updatedTasks = [...tasks, newTask]\n saveTasks(updatedTasks)\n\n // Emit event for system reminders\n emitReminderEvent('task:created', {\n task: newTask,\n timestamp: now,\n })\n\n return newTask\n}\n\n/**\n * Update an existing task.\n * Delegates to TeamTaskStore when in a team context.\n */\nexport async function updateTask(input: TaskUpdateInput): Promise<Task | null> {\n // Normalize taskId \u2014 model may include '#' prefix from TaskCreate output\n input = { ...input, taskId: normalizeTaskId(input.taskId) }\n\n const teamStore = getTeamStore()\n if (teamStore) {\n if (input.status === 'deleted') {\n teamStore.delete(input.taskId)\n emitReminderEvent('task:deleted', {\n taskId: input.taskId,\n timestamp: Date.now(),\n })\n return {\n id: input.taskId,\n subject: '',\n description: '',\n status: 'completed',\n createdAt: 0,\n updatedAt: Date.now(),\n }\n }\n const updated = teamStore.update(input.taskId, {\n status: input.status as\n | 'pending'\n | 'in_progress'\n | 'completed'\n | undefined,\n subject: input.subject,\n description: input.description,\n assignee: input.owner,\n activeForm: input.activeForm,\n metadata: input.metadata,\n addBlocks: input.addBlocks,\n addBlockedBy: input.addBlockedBy,\n })\n if (!updated) return null\n const task: Task = {\n id: updated.id,\n subject: updated.subject,\n description: updated.description,\n status: updated.status,\n owner: updated.assignee,\n activeForm: updated.activeForm,\n metadata: updated.metadata,\n blocks: updated.blocks,\n blockedBy: updated.blockedBy,\n createdAt: updated.createdAt,\n updatedAt: updated.updatedAt,\n }\n\n // Send task_assignment protocol message when assignee changes\n if (input.owner && updated.assignee) {\n const teamName = process.env.MINTO_TEAM_NAME\n if (teamName) {\n try {\n const { getTeam } = require('./agentTeams/teamManager')\n const entry = getTeam(teamName)\n if (entry?.mailbox) {\n const agentId =\n process.env.MINTO_AGENT_ID || `team-lead@${teamName}`\n const assignmentMsg = JSON.stringify({\n type: 'task_assignment',\n taskId: task.id,\n subject: task.subject,\n description: task.description,\n assignedBy: agentId,\n timestamp: new Date().toISOString(),\n })\n entry.mailbox.send(agentId, updated.assignee, assignmentMsg)\n }\n } catch {\n // Best-effort notification\n }\n }\n }\n\n if (input.status) {\n emitReminderEvent('task:status', {\n taskId: task.id,\n status: task.status,\n subject: task.subject,\n })\n }\n if (updated.status === 'completed') {\n const hookMgr = getHookManager()\n if (hookMgr) {\n try {\n const hookResult = await hookMgr.executeTaskCompleted(\n task.id,\n task.subject,\n task.description,\n )\n if (hookResult && !hookResult.shouldContinue) {\n // Hook blocked completion \u2014 revert status\n teamStore.update(input.taskId, { status: 'in_progress' })\n return {\n ...task,\n status: 'in_progress',\n metadata: {\n ...task.metadata,\n _hookRejected: true,\n _hookFeedback: hookResult.reason || 'Blocked by hook',\n },\n }\n }\n } catch {\n // Hook errors don't block \u2014 graceful degradation\n }\n }\n\n // Send task_completed protocol message to team lead\n const teamName = process.env.MINTO_TEAM_NAME\n if (teamName) {\n try {\n const { getTeam } = require('./agentTeams/teamManager')\n const entry = getTeam(teamName)\n if (entry?.mailbox) {\n const agentId = process.env.MINTO_AGENT_ID || `unknown@${teamName}`\n const completedMsg = JSON.stringify({\n type: 'task_completed',\n taskId: task.id,\n taskSubject: task.subject,\n from: agentId,\n timestamp: new Date().toISOString(),\n })\n entry.mailbox.send(agentId, `team-lead@${teamName}`, completedMsg)\n }\n } catch {\n // Best-effort notification\n }\n }\n }\n return task\n }\n\n const tasks = getAllTasks()\n const taskIndex = tasks.findIndex(t => t.id === input.taskId)\n\n if (taskIndex === -1) {\n return null\n }\n\n const existingTask = tasks[taskIndex]\n const now = Date.now()\n\n // Handle deletion\n if (input.status === 'deleted') {\n const updatedTasks = tasks.filter(t => t.id !== input.taskId)\n saveTasks(updatedTasks)\n\n emitReminderEvent('task:deleted', {\n taskId: input.taskId,\n timestamp: now,\n })\n\n return existingTask\n }\n\n // Build updated task\n const updatedTask: Task = {\n ...existingTask,\n updatedAt: now,\n }\n\n if (input.status !== undefined) {\n updatedTask.status = input.status\n }\n if (input.subject !== undefined) {\n updatedTask.subject = input.subject\n }\n if (input.description !== undefined) {\n updatedTask.description = input.description\n }\n if (input.activeForm !== undefined) {\n updatedTask.activeForm = input.activeForm\n }\n if (input.owner !== undefined) {\n updatedTask.owner = input.owner\n }\n if (input.metadata !== undefined) {\n // Merge metadata, null values delete keys\n const newMetadata = { ...existingTask.metadata }\n for (const [key, value] of Object.entries(input.metadata)) {\n if (value === null) {\n delete newMetadata[key]\n } else {\n newMetadata[key] = value\n }\n }\n updatedTask.metadata =\n Object.keys(newMetadata).length > 0 ? newMetadata : undefined\n }\n\n // Handle dependency updates\n if (input.addBlocks && input.addBlocks.length > 0) {\n const currentBlocks = new Set(existingTask.blocks || [])\n input.addBlocks.forEach(id => currentBlocks.add(id))\n updatedTask.blocks = Array.from(currentBlocks)\n }\n if (input.addBlockedBy && input.addBlockedBy.length > 0) {\n const currentBlockedBy = new Set(existingTask.blockedBy || [])\n input.addBlockedBy.forEach(id => currentBlockedBy.add(id))\n updatedTask.blockedBy = Array.from(currentBlockedBy)\n }\n\n // Update tasks array\n const updatedTasks = [...tasks]\n updatedTasks[taskIndex] = updatedTask\n saveTasks(updatedTasks)\n\n // Emit event\n emitReminderEvent('task:updated', {\n previousTask: existingTask,\n updatedTask,\n timestamp: now,\n })\n\n // Emit task:status when status changes\n if (input.status && input.status !== existingTask.status) {\n emitReminderEvent('task:status', {\n taskId: updatedTask.id,\n status: updatedTask.status,\n subject: updatedTask.subject,\n })\n }\n\n // Fire TaskCompleted hook when status changes to 'completed'\n if (input.status === 'completed') {\n const hookMgr = getHookManager()\n if (hookMgr) {\n try {\n const hookResult = await hookMgr.executeTaskCompleted(\n updatedTask.id,\n updatedTask.subject,\n updatedTask.description,\n )\n if (hookResult && !hookResult.shouldContinue) {\n // Hook blocked completion \u2014 revert status\n updatedTask.status = 'in_progress'\n const revertedTasks = [...tasks]\n revertedTasks[taskIndex] = updatedTask\n saveTasks(revertedTasks)\n return {\n ...updatedTask,\n metadata: {\n ...updatedTask.metadata,\n _hookRejected: true,\n _hookFeedback: hookResult.reason || 'Blocked by hook',\n },\n }\n }\n } catch {\n // Hook errors don't block \u2014 graceful degradation\n }\n }\n }\n\n return updatedTask\n}\n\n/**\n * Get a task by ID\n */\nexport function getTaskById(taskId: string): Task | null {\n const id = normalizeTaskId(taskId)\n const tasks = getAllTasks()\n return tasks.find(t => t.id === id) || null\n}\n\n/**\n * Get task list summary for display\n * Returns tasks with summary info suitable for listing\n */\nexport function getTaskList(): Array<{\n id: string\n subject: string\n status: 'pending' | 'in_progress' | 'completed'\n owner?: string\n blockedBy?: string[]\n}> {\n const tasks = getAllTasks()\n\n // Filter out tasks blocked by incomplete tasks\n return tasks.map(task => {\n // Check which blockedBy tasks are still open\n const openBlockers = (task.blockedBy || []).filter(blockerId => {\n const blocker = tasks.find(t => t.id === blockerId)\n return blocker && blocker.status !== 'completed'\n })\n\n return {\n id: task.id,\n subject: task.subject,\n status: task.status,\n owner: task.owner,\n blockedBy: openBlockers.length > 0 ? openBlockers : undefined,\n }\n })\n}\n\n/**\n * Clear all tasks (for testing or reset)\n */\nexport function clearAllTasks(): void {\n saveTasks([])\n nextTaskId = 1\n\n emitReminderEvent('task:cleared', {\n timestamp: Date.now(),\n })\n}\n\n/**\n * Get task statistics\n */\nexport function getTaskStatistics(): {\n total: number\n pending: number\n inProgress: number\n completed: number\n} {\n const tasks = getAllTasks()\n\n return {\n total: tasks.length,\n pending: tasks.filter(t => t.status === 'pending').length,\n inProgress: tasks.filter(t => t.status === 'in_progress').length,\n completed: tasks.filter(t => t.status === 'completed').length,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["now", "updatedTasks"]
|
|
7
7
|
}
|