@within-7/minto 0.2.0 → 0.3.3

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.
Files changed (308) hide show
  1. package/dist/commands/agents/AgentsCommand.js +22 -24
  2. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  3. package/dist/commands/context.js +2 -1
  4. package/dist/commands/context.js.map +2 -2
  5. package/dist/commands/export.js +2 -1
  6. package/dist/commands/export.js.map +2 -2
  7. package/dist/commands/mcp-interactive.js +7 -6
  8. package/dist/commands/mcp-interactive.js.map +2 -2
  9. package/dist/commands/model.js +3 -2
  10. package/dist/commands/model.js.map +2 -2
  11. package/dist/commands/permissions.js +4 -3
  12. package/dist/commands/permissions.js.map +2 -2
  13. package/dist/commands/plugin/AddMarketplaceForm.js +3 -2
  14. package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
  15. package/dist/commands/plugin/ConfirmDialog.js +2 -1
  16. package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
  17. package/dist/commands/plugin/ErrorView.js +2 -1
  18. package/dist/commands/plugin/ErrorView.js.map +2 -2
  19. package/dist/commands/plugin/InstalledPluginsByMarketplace.js +5 -4
  20. package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
  21. package/dist/commands/plugin/InstalledPluginsManager.js +5 -4
  22. package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
  23. package/dist/commands/plugin/MainMenu.js +2 -1
  24. package/dist/commands/plugin/MainMenu.js.map +2 -2
  25. package/dist/commands/plugin/MarketplaceManager.js +5 -4
  26. package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
  27. package/dist/commands/plugin/MarketplaceSelector.js +4 -3
  28. package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
  29. package/dist/commands/plugin/PlaceholderScreen.js +3 -2
  30. package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
  31. package/dist/commands/plugin/PluginBrowser.js +6 -5
  32. package/dist/commands/plugin/PluginBrowser.js.map +2 -2
  33. package/dist/commands/plugin/PluginDetailsInstall.js +5 -4
  34. package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
  35. package/dist/commands/plugin/PluginDetailsManage.js +4 -3
  36. package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
  37. package/dist/commands/plugin.js +16 -15
  38. package/dist/commands/plugin.js.map +2 -2
  39. package/dist/commands/sandbox.js +4 -3
  40. package/dist/commands/sandbox.js.map +2 -2
  41. package/dist/commands/setup.js +2 -1
  42. package/dist/commands/setup.js.map +2 -2
  43. package/dist/commands/status.js +2 -1
  44. package/dist/commands/status.js.map +2 -2
  45. package/dist/commands/undo.js +245 -0
  46. package/dist/commands/undo.js.map +7 -0
  47. package/dist/commands.js +2 -0
  48. package/dist/commands.js.map +2 -2
  49. package/dist/components/AgentThinkingBlock.js +1 -1
  50. package/dist/components/AgentThinkingBlock.js.map +2 -2
  51. package/dist/components/AsciiLogo.js +7 -8
  52. package/dist/components/AsciiLogo.js.map +2 -2
  53. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
  54. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
  55. package/dist/components/AskUserQuestionDialog/QuestionView.js +2 -1
  56. package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
  57. package/dist/components/CollapsibleHint.js +2 -1
  58. package/dist/components/CollapsibleHint.js.map +2 -2
  59. package/dist/components/Config.js +3 -2
  60. package/dist/components/Config.js.map +2 -2
  61. package/dist/components/ConsoleOAuthFlow.js +2 -1
  62. package/dist/components/ConsoleOAuthFlow.js.map +2 -2
  63. package/dist/components/Cost.js +2 -1
  64. package/dist/components/Cost.js.map +2 -2
  65. package/dist/components/HeaderBar.js +13 -8
  66. package/dist/components/HeaderBar.js.map +2 -2
  67. package/dist/components/HistorySearchOverlay.js +4 -3
  68. package/dist/components/HistorySearchOverlay.js.map +2 -2
  69. package/dist/components/HotkeyHelpPanel.js +8 -11
  70. package/dist/components/HotkeyHelpPanel.js.map +2 -2
  71. package/dist/components/InvalidConfigDialog.js +2 -1
  72. package/dist/components/InvalidConfigDialog.js.map +2 -2
  73. package/dist/components/Logo.js +23 -67
  74. package/dist/components/Logo.js.map +2 -2
  75. package/dist/components/MCPServerApprovalDialog.js +2 -1
  76. package/dist/components/MCPServerApprovalDialog.js.map +2 -2
  77. package/dist/components/MCPServerDialogCopy.js +2 -1
  78. package/dist/components/MCPServerDialogCopy.js.map +2 -2
  79. package/dist/components/MCPServerMultiselectDialog.js +2 -1
  80. package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
  81. package/dist/components/MessageSelector.js +4 -3
  82. package/dist/components/MessageSelector.js.map +2 -2
  83. package/dist/components/ModeIndicator.js +2 -1
  84. package/dist/components/ModeIndicator.js.map +2 -2
  85. package/dist/components/ModelConfig.js +4 -3
  86. package/dist/components/ModelConfig.js.map +2 -2
  87. package/dist/components/ModelListManager.js +4 -3
  88. package/dist/components/ModelListManager.js.map +2 -2
  89. package/dist/components/ModelSelector/ModelSelector.js +26 -13
  90. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  91. package/dist/components/Onboarding.js +3 -2
  92. package/dist/components/Onboarding.js.map +2 -2
  93. package/dist/components/OperationSummary.js +130 -0
  94. package/dist/components/OperationSummary.js.map +7 -0
  95. package/dist/components/PromptInput.js +88 -75
  96. package/dist/components/PromptInput.js.map +2 -2
  97. package/dist/components/SensitiveFileWarning.js +31 -0
  98. package/dist/components/SensitiveFileWarning.js.map +7 -0
  99. package/dist/components/Spinner.js +71 -22
  100. package/dist/components/Spinner.js.map +2 -2
  101. package/dist/components/StructuredDiff.js +6 -8
  102. package/dist/components/StructuredDiff.js.map +2 -2
  103. package/dist/components/SubagentBlock.js +4 -2
  104. package/dist/components/SubagentBlock.js.map +2 -2
  105. package/dist/components/SubagentProgress.js +17 -6
  106. package/dist/components/SubagentProgress.js.map +2 -2
  107. package/dist/components/TaskCard.js +14 -11
  108. package/dist/components/TaskCard.js.map +2 -2
  109. package/dist/components/TextInput.js +9 -1
  110. package/dist/components/TextInput.js.map +2 -2
  111. package/dist/components/TodoPanel.js +44 -26
  112. package/dist/components/TodoPanel.js.map +2 -2
  113. package/dist/components/ToolUseLoader.js +2 -2
  114. package/dist/components/ToolUseLoader.js.map +2 -2
  115. package/dist/components/TreeConnector.js +4 -3
  116. package/dist/components/TreeConnector.js.map +2 -2
  117. package/dist/components/TrustDialog.js +2 -1
  118. package/dist/components/TrustDialog.js.map +2 -2
  119. package/dist/components/binary-feedback/BinaryFeedbackView.js +2 -1
  120. package/dist/components/binary-feedback/BinaryFeedbackView.js.map +2 -2
  121. package/dist/components/messages/AssistantTextMessage.js +17 -9
  122. package/dist/components/messages/AssistantTextMessage.js.map +2 -2
  123. package/dist/components/messages/AssistantToolUseMessage.js +8 -4
  124. package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
  125. package/dist/components/messages/GroupRenderer.js +2 -1
  126. package/dist/components/messages/GroupRenderer.js.map +2 -2
  127. package/dist/components/messages/NestedTasksPreview.js +13 -1
  128. package/dist/components/messages/NestedTasksPreview.js.map +2 -2
  129. package/dist/components/messages/ParallelTasksGroupView.js +4 -3
  130. package/dist/components/messages/ParallelTasksGroupView.js.map +2 -2
  131. package/dist/components/messages/TaskInModuleView.js +35 -15
  132. package/dist/components/messages/TaskInModuleView.js.map +2 -2
  133. package/dist/components/messages/TaskOutputContent.js +9 -6
  134. package/dist/components/messages/TaskOutputContent.js.map +2 -2
  135. package/dist/components/messages/UserPromptMessage.js +2 -2
  136. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  137. package/dist/constants/colors.js +90 -72
  138. package/dist/constants/colors.js.map +2 -2
  139. package/dist/constants/prompts.js +22 -1
  140. package/dist/constants/prompts.js.map +2 -2
  141. package/dist/constants/toolInputExamples.js +84 -0
  142. package/dist/constants/toolInputExamples.js.map +7 -0
  143. package/dist/core/backupManager.js +321 -0
  144. package/dist/core/backupManager.js.map +7 -0
  145. package/dist/core/costTracker.js +9 -18
  146. package/dist/core/costTracker.js.map +2 -2
  147. package/dist/core/gitAutoCommit.js +287 -0
  148. package/dist/core/gitAutoCommit.js.map +7 -0
  149. package/dist/core/index.js +3 -0
  150. package/dist/core/index.js.map +2 -2
  151. package/dist/core/operationTracker.js +212 -0
  152. package/dist/core/operationTracker.js.map +7 -0
  153. package/dist/core/permissions/rules/allowedToolsRule.js +1 -1
  154. package/dist/core/permissions/rules/allowedToolsRule.js.map +2 -2
  155. package/dist/core/permissions/rules/autoEscalationRule.js +5 -0
  156. package/dist/core/permissions/rules/autoEscalationRule.js.map +2 -2
  157. package/dist/core/permissions/rules/projectBoundaryRule.js +5 -0
  158. package/dist/core/permissions/rules/projectBoundaryRule.js.map +2 -2
  159. package/dist/core/permissions/rules/sensitivePathsRule.js +5 -0
  160. package/dist/core/permissions/rules/sensitivePathsRule.js.map +2 -2
  161. package/dist/core/tokenStats.js +9 -0
  162. package/dist/core/tokenStats.js.map +7 -0
  163. package/dist/core/tokenStatsManager.js +331 -0
  164. package/dist/core/tokenStatsManager.js.map +7 -0
  165. package/dist/entrypoints/cli.js +122 -88
  166. package/dist/entrypoints/cli.js.map +2 -2
  167. package/dist/hooks/useAgentTokenStats.js +72 -0
  168. package/dist/hooks/useAgentTokenStats.js.map +7 -0
  169. package/dist/hooks/useAgentTranscripts.js +30 -6
  170. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  171. package/dist/hooks/useLogMessages.js +12 -1
  172. package/dist/hooks/useLogMessages.js.map +2 -2
  173. package/dist/i18n/locales/en.js +6 -5
  174. package/dist/i18n/locales/en.js.map +2 -2
  175. package/dist/i18n/locales/zh-CN.js +6 -5
  176. package/dist/i18n/locales/zh-CN.js.map +2 -2
  177. package/dist/i18n/types.js.map +1 -1
  178. package/dist/permissions.js +147 -1
  179. package/dist/permissions.js.map +2 -2
  180. package/dist/query.js +78 -4
  181. package/dist/query.js.map +3 -3
  182. package/dist/screens/REPL.js +23 -3
  183. package/dist/screens/REPL.js.map +2 -2
  184. package/dist/screens/ResumeConversation.js +2 -0
  185. package/dist/screens/ResumeConversation.js.map +2 -2
  186. package/dist/services/claude.js +54 -3
  187. package/dist/services/claude.js.map +2 -2
  188. package/dist/services/intelligentCompactor.js +1 -1
  189. package/dist/services/intelligentCompactor.js.map +2 -2
  190. package/dist/services/mcpClient.js +81 -25
  191. package/dist/services/mcpClient.js.map +2 -2
  192. package/dist/services/sandbox/filesystemBoundary.js +58 -17
  193. package/dist/services/sandbox/filesystemBoundary.js.map +2 -2
  194. package/dist/services/taskStore.js +205 -0
  195. package/dist/services/taskStore.js.map +7 -0
  196. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +3 -2
  197. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  198. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +42 -4
  199. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
  200. package/dist/tools/BashTool/BashTool.js +43 -7
  201. package/dist/tools/BashTool/BashTool.js.map +2 -2
  202. package/dist/tools/BashTool/prompt.js +184 -34
  203. package/dist/tools/BashTool/prompt.js.map +2 -2
  204. package/dist/tools/FileEditTool/FileEditTool.js +24 -9
  205. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  206. package/dist/tools/FileEditTool/prompt.js +10 -4
  207. package/dist/tools/FileEditTool/prompt.js.map +2 -2
  208. package/dist/tools/FileEditTool/utils.js +10 -4
  209. package/dist/tools/FileEditTool/utils.js.map +2 -2
  210. package/dist/tools/FileReadTool/FileReadTool.js +1 -1
  211. package/dist/tools/FileReadTool/FileReadTool.js.map +1 -1
  212. package/dist/tools/FileReadTool/prompt.js +16 -1
  213. package/dist/tools/FileReadTool/prompt.js.map +2 -2
  214. package/dist/tools/FileWriteTool/FileWriteTool.js +1 -1
  215. package/dist/tools/FileWriteTool/FileWriteTool.js.map +1 -1
  216. package/dist/tools/FileWriteTool/prompt.js +12 -3
  217. package/dist/tools/FileWriteTool/prompt.js.map +2 -2
  218. package/dist/tools/GlobTool/prompt.js +12 -1
  219. package/dist/tools/GlobTool/prompt.js.map +2 -2
  220. package/dist/tools/GrepTool/GrepTool.js +333 -65
  221. package/dist/tools/GrepTool/GrepTool.js.map +2 -2
  222. package/dist/tools/GrepTool/prompt.js +15 -8
  223. package/dist/tools/GrepTool/prompt.js.map +2 -2
  224. package/dist/tools/MultiEditTool/prompt.js +5 -3
  225. package/dist/tools/MultiEditTool/prompt.js.map +2 -2
  226. package/dist/tools/NotebookEditTool/NotebookEditTool.js +59 -46
  227. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  228. package/dist/tools/NotebookEditTool/prompt.js +1 -1
  229. package/dist/tools/NotebookEditTool/prompt.js.map +1 -1
  230. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +3 -2
  231. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  232. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +3 -2
  233. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  234. package/dist/tools/PlanModeTool/prompt.js +1 -1
  235. package/dist/tools/PlanModeTool/prompt.js.map +1 -1
  236. package/dist/tools/SkillTool/SkillTool.js +4 -3
  237. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  238. package/dist/tools/SkillTool/prompt.js +1 -1
  239. package/dist/tools/SkillTool/prompt.js.map +1 -1
  240. package/dist/tools/TaskCreateTool/TaskCreateTool.js +102 -0
  241. package/dist/tools/TaskCreateTool/TaskCreateTool.js.map +7 -0
  242. package/dist/tools/TaskCreateTool/prompt.js +47 -0
  243. package/dist/tools/TaskCreateTool/prompt.js.map +7 -0
  244. package/dist/tools/TaskGetTool/TaskGetTool.js +115 -0
  245. package/dist/tools/TaskGetTool/TaskGetTool.js.map +7 -0
  246. package/dist/tools/TaskGetTool/prompt.js +28 -0
  247. package/dist/tools/TaskGetTool/prompt.js.map +7 -0
  248. package/dist/tools/TaskListTool/TaskListTool.js +102 -0
  249. package/dist/tools/TaskListTool/TaskListTool.js.map +7 -0
  250. package/dist/tools/TaskListTool/prompt.js +27 -0
  251. package/dist/tools/TaskListTool/prompt.js.map +7 -0
  252. package/dist/tools/TaskOutputTool/TaskOutputTool.js +3 -2
  253. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +2 -2
  254. package/dist/tools/TaskStopTool/TaskStopTool.js +150 -0
  255. package/dist/tools/TaskStopTool/TaskStopTool.js.map +7 -0
  256. package/dist/tools/TaskStopTool/prompt.js +15 -0
  257. package/dist/tools/TaskStopTool/prompt.js.map +7 -0
  258. package/dist/tools/TaskTool/TaskTool.js +49 -1
  259. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  260. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js +134 -0
  261. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +7 -0
  262. package/dist/tools/TaskUpdateTool/prompt.js +81 -0
  263. package/dist/tools/TaskUpdateTool/prompt.js.map +7 -0
  264. package/dist/tools/URLFetcherTool/prompt.js +1 -1
  265. package/dist/tools/URLFetcherTool/prompt.js.map +1 -1
  266. package/dist/tools.js +12 -0
  267. package/dist/tools.js.map +2 -2
  268. package/dist/utils/CircuitBreaker.js +242 -0
  269. package/dist/utils/CircuitBreaker.js.map +7 -0
  270. package/dist/utils/ask.js +2 -0
  271. package/dist/utils/ask.js.map +2 -2
  272. package/dist/utils/config.js +47 -5
  273. package/dist/utils/config.js.map +2 -2
  274. package/dist/utils/credentials/CredentialStore.js +1 -0
  275. package/dist/utils/credentials/CredentialStore.js.map +7 -0
  276. package/dist/utils/credentials/EncryptedFileStore.js +157 -0
  277. package/dist/utils/credentials/EncryptedFileStore.js.map +7 -0
  278. package/dist/utils/credentials/index.js +37 -0
  279. package/dist/utils/credentials/index.js.map +7 -0
  280. package/dist/utils/credentials/migration.js +82 -0
  281. package/dist/utils/credentials/migration.js.map +7 -0
  282. package/dist/utils/markdown.js +13 -1
  283. package/dist/utils/markdown.js.map +2 -2
  284. package/dist/utils/model.js +15 -2
  285. package/dist/utils/model.js.map +2 -2
  286. package/dist/utils/permissions/filesystem.js +5 -1
  287. package/dist/utils/permissions/filesystem.js.map +2 -2
  288. package/dist/utils/ripgrep.js +53 -1
  289. package/dist/utils/ripgrep.js.map +2 -2
  290. package/dist/utils/safePath.js +132 -0
  291. package/dist/utils/safePath.js.map +7 -0
  292. package/dist/utils/sensitiveFiles.js +125 -0
  293. package/dist/utils/sensitiveFiles.js.map +7 -0
  294. package/dist/utils/taskDisplayUtils.js +9 -9
  295. package/dist/utils/taskDisplayUtils.js.map +2 -2
  296. package/dist/utils/terminal.js +12 -0
  297. package/dist/utils/terminal.js.map +2 -2
  298. package/dist/utils/theme.js +6 -6
  299. package/dist/utils/theme.js.map +1 -1
  300. package/dist/utils/toolRiskClassification.js +207 -0
  301. package/dist/utils/toolRiskClassification.js.map +7 -0
  302. package/dist/utils/tooling/safeRender.js +17 -17
  303. package/dist/utils/tooling/safeRender.js.map +2 -2
  304. package/dist/version.js +2 -2
  305. package/dist/version.js.map +1 -1
  306. package/package.json +22 -28
  307. package/dist/hooks/useCancelRequest.js +0 -31
  308. package/dist/hooks/useCancelRequest.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/AskExpertModelTool/AskExpertModelTool.tsx"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { Box, Text } from 'ink'\nimport { z } from 'zod'\nimport { Tool, ValidationResult } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { getModelManager } from '@utils/model'\nimport { getTheme } from '@utils/theme'\nimport {\n createUserMessage,\n createAssistantMessage,\n INTERRUPT_MESSAGE,\n} from '@utils/messages'\nimport { logError } from '@utils/log'\nimport {\n createExpertChatSession,\n loadExpertChatSession,\n getSessionMessages,\n addMessageToSession,\n} from '@utils/expertChatStorage'\nimport { queryLLM } from '@services/claude'\nimport { debug as debugLogger } from '@utils/debugLogger'\nimport { applyMarkdown } from '@utils/markdown'\n\nexport const inputSchema = z.strictObject({\n question: z\n .string()\n .describe(\n 'COMPLETE SELF-CONTAINED QUESTION: Must include full background context, relevant details, and a clear independent question. The expert model will receive ONLY this content with no access to previous conversation or external context. Structure as: 1) Background/Context 2) Specific situation/problem 3) Clear question. Ensure the expert can fully understand and respond without needing additional information.',\n ),\n expert_model: z\n .string()\n .describe(\n 'The expert model to use (e.g., gpt-5, claude-3-5-sonnet-20241022)',\n ),\n chat_session_id: z\n .string()\n .describe(\n 'Chat session ID: use \"new\" for new session or existing session ID',\n ),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n chatSessionId: string\n expertModelName: string\n expertAnswer: string\n}\n\nexport const AskExpertModelTool = {\n name: 'AskExpertModel',\n async description() {\n return 'Consult external AI models for expert opinions and analysis'\n },\n async prompt() {\n return `Ask a question to a specific external AI model for expert analysis.\n\nThis tool allows you to consult different AI models for their unique perspectives and expertise.\n\nCRITICAL REQUIREMENT FOR QUESTION PARAMETER:\nThe question MUST be completely self-contained and include:\n1. FULL BACKGROUND CONTEXT - All relevant information the expert needs\n2. SPECIFIC SITUATION - Clear description of the current scenario/problem\n3. INDEPENDENT QUESTION - What exactly you want the expert to analyze/answer\n\nThe expert model receives ONLY your question content with NO access to:\n- Previous conversation history (unless using existing session) \n- Current codebase or file context\n- User's current task or project details\n\nIMPORTANT: This tool is for asking questions to models, not for task execution.\n- Use when you need a specific model's opinion or analysis\n- Use when you want to compare different models' responses\n- Use the @ask-[model] format when available\n\nThe expert_model parameter accepts:\n- OpenAI: gpt-4, gpt-5, o1-preview\n- Anthropic: claude-3-5-sonnet, claude-3-opus \n- Others: kimi, gemini-pro, mixtral\n\nExample of well-structured question:\n\"Background: I'm working on a React TypeScript application with performance issues. The app renders a large list of 10,000 items using a simple map() function, causing UI freezing.\n\nCurrent situation: Users report 3-5 second delays when scrolling through the list. The component re-renders the entire list on every state change.\n\nQuestion: What are the most effective React optimization techniques for handling large lists, and how should I prioritize implementing virtualization vs memoization vs other approaches?\"`\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true\n },\n inputSchema,\n userFacingName() {\n return 'AskExpertModel'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n return false\n },\n async validateInput(\n { question, expert_model, chat_session_id },\n context?: any,\n ): Promise<ValidationResult> {\n if (!question.trim()) {\n return { result: false, message: 'Question cannot be empty' }\n }\n\n if (!expert_model.trim()) {\n return { result: false, message: 'Expert model must be specified' }\n }\n\n if (!chat_session_id.trim()) {\n return {\n result: false,\n message:\n 'Chat session ID must be specified (use \"new\" for new session)',\n }\n }\n\n // Check if trying to consult the same model we're currently running\n try {\n const modelManager = getModelManager()\n\n // Get current model based on context\n let currentModel: string\n if (context?.agentId && context?.options?.model) {\n // In subagent context (Task tool)\n currentModel = context.options.model\n } else {\n // In main agent context or after model switch\n currentModel = modelManager.getModelName('main') || ''\n }\n\n // Normalize model names for comparison\n const normalizedExpert = expert_model\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '')\n const normalizedCurrent = currentModel\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '')\n\n if (normalizedExpert === normalizedCurrent) {\n return {\n result: false,\n message: `You are already running as ${currentModel}. Consulting the same model would be redundant. Please choose a different model or handle the task directly.`,\n }\n }\n } catch (e) {\n // If we can't determine current model, allow the request\n debugLogger.error('AskExpertModel', {\n message: 'Could not determine current model',\n error: e,\n })\n }\n\n // Validate that the model exists and is available\n try {\n const modelManager = getModelManager()\n const modelResolution = modelManager.resolveModelWithInfo(expert_model)\n\n if (!modelResolution.success) {\n const availableModels = modelManager.getAllAvailableModelNames()\n if (availableModels.length > 0) {\n return {\n result: false,\n message: `Model '${expert_model}' is not configured. Available models: ${availableModels.join(', ')}. Check if any available model closely matches the user's request (e.g., 'kimi' matches 'kimi-k2-0711-preview'). If there's a strong match, auto retry using the correct model name. If no close match exists, inform the user that '${expert_model}' needs to be configured using /model command.`,\n }\n } else {\n return {\n result: false,\n message: `Model '${expert_model}' not found and no models are currently configured in the system. Inform the user that models need to be configured first using the /model command.`,\n }\n }\n }\n } catch (error) {\n console.error('Model validation error in AskExpertModelTool:', error)\n logError(error)\n return {\n result: false,\n message: `Failed to validate expert model '${expert_model}'. Please check your model configuration.`,\n }\n }\n\n return { result: true }\n },\n\n renderToolUseMessage(\n { question, expert_model, chat_session_id },\n { verbose },\n ) {\n if (!question || !expert_model) return null\n const isNewSession = chat_session_id === 'new'\n const sessionDisplay = isNewSession\n ? 'new session'\n : `session ${chat_session_id.substring(0, 5)}...`\n const theme = getTheme()\n\n if (verbose) {\n return (\n <Box flexDirection=\"column\">\n <Text bold color=\"yellow\">\n {expert_model}\n </Text>\n <Text color={theme.secondaryText}>{sessionDisplay}</Text>\n <Box marginTop={1}>\n <Text color={theme.text}>\n {question.length > 300\n ? question.substring(0, 300) + '...'\n : question}\n </Text>\n </Box>\n </Box>\n )\n }\n return (\n <Box flexDirection=\"column\">\n <Text bold color=\"yellow\">\n {expert_model}{' '}\n </Text>\n <Text color={theme.secondaryText} dimColor>\n ({sessionDisplay})\n </Text>\n </Box>\n )\n },\n\n renderToolResultMessage(content) {\n const verbose = true // Show more content\n const theme = getTheme()\n\n // Guard against undefined or null content\n if (!content) {\n return (\n <Box flexDirection=\"row\">\n <Text color={theme.secondaryText}>Consultation completed</Text>\n </Box>\n )\n }\n\n if (typeof content === 'object' && content && 'expertAnswer' in content) {\n const expertResult = content as Out\n const isError =\n expertResult.expertAnswer.startsWith('Error') ||\n expertResult.expertAnswer.includes('failed')\n const isInterrupted = expertResult.chatSessionId === 'interrupted'\n\n if (isInterrupted) {\n return (\n <Box flexDirection=\"row\">\n <Text color={theme.secondaryText}>Consultation interrupted</Text>\n </Box>\n )\n }\n\n const answerText = verbose\n ? expertResult.expertAnswer.trim()\n : expertResult.expertAnswer.length > 500\n ? expertResult.expertAnswer.substring(0, 500) + '...'\n : expertResult.expertAnswer.trim()\n\n if (isError) {\n return (\n <Box flexDirection=\"column\">\n <Text color=\"red\">{answerText}</Text>\n </Box>\n )\n }\n\n return (\n <Box flexDirection=\"column\">\n <Text bold color={theme.text}>\n Response from {expertResult.expertModelName}:\n </Text>\n <Box marginTop={1}>\n <Text color={theme.text}>{applyMarkdown(answerText)}</Text>\n </Box>\n <Box marginTop={1}>\n <Text color={theme.secondaryText} dimColor>\n Session: {expertResult.chatSessionId.substring(0, 8)}\n </Text>\n </Box>\n </Box>\n )\n }\n\n return (\n <Box flexDirection=\"row\">\n <Text color={theme.secondaryText}>Consultation completed</Text>\n </Box>\n )\n },\n\n renderResultForAssistant(output: Out): string {\n return `[Expert consultation completed]\nExpert Model: ${output.expertModelName}\nSession ID: ${output.chatSessionId}\nTo continue this conversation with context preservation, use this Session ID in your next AskExpertModel call to maintain the full conversation history and context.\n\n${output.expertAnswer}`\n },\n\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n async *call(\n { question, expert_model, chat_session_id },\n { abortController, readFileTimestamps },\n ) {\n const expertModel = expert_model\n\n let sessionId: string\n let isInterrupted = false\n\n // Set up abort listener (following TaskTool pattern)\n const abortListener = () => {\n isInterrupted = true\n }\n abortController.signal.addEventListener('abort', abortListener)\n\n try {\n // Initial abort check\n if (abortController.signal.aborted) {\n return yield* this.handleInterrupt()\n }\n // Session management with error handling\n if (chat_session_id === 'new') {\n try {\n const session = createExpertChatSession(expertModel)\n sessionId = session.sessionId\n } catch (error) {\n console.error('Failed to create new expert chat session:', error)\n logError(error)\n throw new Error('Failed to create new chat session')\n }\n } else {\n sessionId = chat_session_id\n try {\n const session = loadExpertChatSession(sessionId)\n if (!session) {\n // Session doesn't exist, create new one\n const newSession = createExpertChatSession(expertModel)\n sessionId = newSession.sessionId\n }\n } catch (error) {\n console.error('Failed to load expert chat session:', error)\n logError(error)\n // Fallback: create new session\n try {\n const newSession = createExpertChatSession(expertModel)\n sessionId = newSession.sessionId\n } catch (createError) {\n console.error(\n 'Failed to create fallback expert chat session:',\n createError,\n )\n logError(createError)\n throw new Error('Unable to create or load chat session')\n }\n }\n }\n\n // Check for cancellation before loading history\n if (isInterrupted || abortController.signal.aborted) {\n return yield* this.handleInterrupt()\n }\n\n // Load history and prepare messages with error handling\n let historyMessages: Array<{ role: string; content: string }>\n try {\n historyMessages = getSessionMessages(sessionId)\n } catch (error) {\n console.error('Failed to load session messages:', error)\n logError(error)\n historyMessages = [] // Fallback to empty history\n }\n\n const messages = [...historyMessages, { role: 'user', content: question }]\n\n let systemMessages\n try {\n systemMessages = messages.map(msg =>\n msg.role === 'user'\n ? createUserMessage(msg.content)\n : createAssistantMessage(msg.content),\n )\n } catch (error) {\n console.error('Failed to create system messages:', error)\n logError(error)\n throw new Error('Failed to prepare conversation messages')\n }\n\n // Check for cancellation before model call\n if (isInterrupted || abortController.signal.aborted) {\n return yield* this.handleInterrupt()\n }\n\n // Yield progress message to show we're connecting\n yield {\n type: 'progress',\n content: createAssistantMessage(\n `Connecting to ${expertModel}... (timeout: 5 minutes)`,\n ),\n }\n\n // Call model with comprehensive error handling and timeout\n let response\n try {\n // Debug: Log the model we're trying to use (using global debug logger)\n const modelManager = getModelManager()\n const modelResolution = modelManager.resolveModelWithInfo(expertModel)\n\n debugLogger.api('EXPERT_MODEL_RESOLUTION', {\n requestedModel: expertModel,\n success: modelResolution.success,\n profileName: modelResolution.profile?.name,\n profileModelName: modelResolution.profile?.modelName,\n provider: modelResolution.profile?.provider,\n isActive: modelResolution.profile?.isActive,\n error: modelResolution.error,\n })\n\n // Create a timeout promise to prevent hanging\n const timeoutMs = 300000 // 300 seconds (5 minutes) timeout for external models\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => {\n reject(\n new Error(\n `Expert model query timed out after ${timeoutMs / 1000}s`,\n ),\n )\n }, timeoutMs)\n })\n\n // Race between the query and timeout\n response = await Promise.race([\n queryLLM(\n systemMessages,\n [], // no system prompt - let expert model use its default behavior\n 0, // no thinking tokens needed\n [], // no tools needed\n abortController.signal,\n {\n safeMode: false,\n model: expertModel,\n prependCLISysprompt: false, // KEY: avoid injecting CLI context\n },\n ),\n timeoutPromise,\n ])\n } catch (error: any) {\n console.error('Expert model query failed:', error)\n logError(error)\n\n // Check for specific error types\n if (\n error.name === 'AbortError' ||\n abortController.signal?.aborted ||\n isInterrupted\n ) {\n return yield* this.handleInterrupt()\n }\n\n if (error.message?.includes('timed out')) {\n throw new Error(\n `Expert model '${expertModel}' timed out after 5 minutes.\\n\\n` +\n `Suggestions:\\n` +\n ` - The model might be experiencing high load\\n` +\n ` - Try a different model or retry later\\n` +\n ` - Consider breaking down your question into smaller parts`,\n )\n }\n\n if (error.message?.includes('rate limit')) {\n throw new Error(\n `Rate limit exceeded for ${expertModel}.\\n\\n` +\n `Please wait a moment and try again, or use a different model.`,\n )\n }\n\n if (error.message?.includes('invalid api key')) {\n throw new Error(\n `Invalid API key for ${expertModel}.\\n\\n` +\n `Please check your model configuration with /model command.`,\n )\n }\n\n if (\n error.message?.includes('model not found') ||\n error.message?.includes('Failed to resolve model')\n ) {\n // Provide helpful model guidance in runtime errors too\n try {\n const modelManager = getModelManager()\n const availableModels = modelManager.getAllAvailableModelNames()\n if (availableModels.length > 0) {\n throw new Error(\n `Model '${expertModel}' is not configured. Available models: ${availableModels.join(', ')}. Check if any available model closely matches the user's request (e.g., 'kimi' matches 'kimi-k2-0711-preview'). If there's a strong match, auto retry using the correct model name. If no close match exists, inform the user that '${expertModel}' needs to be configured using /model command.`,\n )\n } else {\n throw new Error(\n `Model '${expertModel}' not found and no models are currently configured in the system. Inform the user that models need to be configured first using the /model command.`,\n )\n }\n } catch (modelError) {\n // If we can't get model list, fall back to simple error\n throw new Error(\n `Model '${expertModel}' not found. Please check model configuration or inform user about the issue.`,\n )\n }\n }\n\n // Generic fallback\n throw new Error(\n `Expert model query failed: ${error.message || 'Unknown error'}`,\n )\n }\n\n // Extract answer with error handling\n let expertAnswer: string\n try {\n if (!response?.message?.content) {\n throw new Error('No content in expert response')\n }\n\n expertAnswer = response.message.content\n .filter(block => block.type === 'text')\n .map(block => (block as any).text)\n .join('\\n')\n\n if (!expertAnswer.trim()) {\n throw new Error('Expert response was empty')\n }\n } catch (error) {\n console.error('Failed to extract expert answer:', error)\n logError(error)\n throw new Error('Failed to process expert response')\n }\n\n // Save conversation with error handling\n try {\n addMessageToSession(sessionId, 'user', question)\n addMessageToSession(sessionId, 'assistant', expertAnswer)\n } catch (error) {\n console.error('Failed to save conversation to session:', error)\n logError(error)\n // Don't throw here - we got a valid response, saving is non-critical\n }\n\n const result: Out = {\n chatSessionId: sessionId,\n expertModelName: expertModel,\n expertAnswer: expertAnswer,\n }\n\n yield {\n type: 'result',\n data: result,\n resultForAssistant: this.renderResultForAssistant(result),\n }\n } catch (error: any) {\n // Check if error is due to cancellation\n if (\n error.name === 'AbortError' ||\n abortController.signal?.aborted ||\n isInterrupted\n ) {\n return yield* this.handleInterrupt()\n }\n\n console.error('AskExpertModelTool execution failed:', error)\n logError(error)\n\n // Ensure we have a valid sessionId for error response\n const errorSessionId = sessionId || 'error-session'\n\n const errorMessage =\n error.message || 'Expert consultation failed with unknown error'\n const result: Out = {\n chatSessionId: errorSessionId,\n expertModelName: expertModel,\n expertAnswer: `\u274C ${errorMessage}`,\n }\n\n yield {\n type: 'result',\n data: result,\n resultForAssistant: this.renderResultForAssistant(result),\n }\n } finally {\n // Clean up event listener\n abortController.signal.removeEventListener('abort', abortListener)\n }\n },\n\n // Unified interrupt handling method (following TaskTool pattern)\n async *handleInterrupt() {\n yield {\n type: 'result',\n data: {\n chatSessionId: 'interrupted',\n expertModelName: 'cancelled',\n expertAnswer: INTERRUPT_MESSAGE,\n },\n resultForAssistant: INTERRUPT_MESSAGE,\n }\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react'\nimport { Box, Text } from 'ink'\nimport { z } from 'zod'\nimport { Tool, ValidationResult } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { getModelManager } from '@utils/model'\nimport { getTheme } from '@utils/theme'\nimport {\n createUserMessage,\n createAssistantMessage,\n INTERRUPT_MESSAGE,\n} from '@utils/messages'\nimport { logError } from '@utils/log'\nimport {\n createExpertChatSession,\n loadExpertChatSession,\n getSessionMessages,\n addMessageToSession,\n} from '@utils/expertChatStorage'\nimport { queryLLM } from '@services/claude'\nimport { SEMANTIC_COLORS } from '@constants/colors'\nimport { debug as debugLogger } from '@utils/debugLogger'\nimport { applyMarkdown } from '@utils/markdown'\n\nexport const inputSchema = z.strictObject({\n question: z\n .string()\n .describe(\n 'COMPLETE SELF-CONTAINED QUESTION: Must include full background context, relevant details, and a clear independent question. The expert model will receive ONLY this content with no access to previous conversation or external context. Structure as: 1) Background/Context 2) Specific situation/problem 3) Clear question. Ensure the expert can fully understand and respond without needing additional information.',\n ),\n expert_model: z\n .string()\n .describe(\n 'The expert model to use (e.g., gpt-5, claude-3-5-sonnet-20241022)',\n ),\n chat_session_id: z\n .string()\n .describe(\n 'Chat session ID: use \"new\" for new session or existing session ID',\n ),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n chatSessionId: string\n expertModelName: string\n expertAnswer: string\n}\n\nexport const AskExpertModelTool = {\n name: 'AskExpertModel',\n async description() {\n return 'Consult external AI models for expert opinions and analysis'\n },\n async prompt() {\n return `Ask a question to a specific external AI model for expert analysis.\n\nThis tool allows you to consult different AI models for their unique perspectives and expertise.\n\nCRITICAL REQUIREMENT FOR QUESTION PARAMETER:\nThe question MUST be completely self-contained and include:\n1. FULL BACKGROUND CONTEXT - All relevant information the expert needs\n2. SPECIFIC SITUATION - Clear description of the current scenario/problem\n3. INDEPENDENT QUESTION - What exactly you want the expert to analyze/answer\n\nThe expert model receives ONLY your question content with NO access to:\n- Previous conversation history (unless using existing session) \n- Current codebase or file context\n- User's current task or project details\n\nIMPORTANT: This tool is for asking questions to models, not for task execution.\n- Use when you need a specific model's opinion or analysis\n- Use when you want to compare different models' responses\n- Use the @ask-[model] format when available\n\nThe expert_model parameter accepts:\n- OpenAI: gpt-4, gpt-5, o1-preview\n- Anthropic: claude-3-5-sonnet, claude-3-opus \n- Others: kimi, gemini-pro, mixtral\n\nExample of well-structured question:\n\"Background: I'm working on a React TypeScript application with performance issues. The app renders a large list of 10,000 items using a simple map() function, causing UI freezing.\n\nCurrent situation: Users report 3-5 second delays when scrolling through the list. The component re-renders the entire list on every state change.\n\nQuestion: What are the most effective React optimization techniques for handling large lists, and how should I prioritize implementing virtualization vs memoization vs other approaches?\"`\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true\n },\n inputSchema,\n userFacingName() {\n return 'AskExpertModel'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n return false\n },\n async validateInput(\n { question, expert_model, chat_session_id },\n context?: any,\n ): Promise<ValidationResult> {\n if (!question.trim()) {\n return { result: false, message: 'Question cannot be empty' }\n }\n\n if (!expert_model.trim()) {\n return { result: false, message: 'Expert model must be specified' }\n }\n\n if (!chat_session_id.trim()) {\n return {\n result: false,\n message:\n 'Chat session ID must be specified (use \"new\" for new session)',\n }\n }\n\n // Check if trying to consult the same model we're currently running\n try {\n const modelManager = getModelManager()\n\n // Get current model based on context\n let currentModel: string\n if (context?.agentId && context?.options?.model) {\n // In subagent context (Task tool)\n currentModel = context.options.model\n } else {\n // In main agent context or after model switch\n currentModel = modelManager.getModelName('main') || ''\n }\n\n // Normalize model names for comparison\n const normalizedExpert = expert_model\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '')\n const normalizedCurrent = currentModel\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '')\n\n if (normalizedExpert === normalizedCurrent) {\n return {\n result: false,\n message: `You are already running as ${currentModel}. Consulting the same model would be redundant. Please choose a different model or handle the task directly.`,\n }\n }\n } catch (e) {\n // If we can't determine current model, allow the request\n debugLogger.error('AskExpertModel', {\n message: 'Could not determine current model',\n error: e,\n })\n }\n\n // Validate that the model exists and is available\n try {\n const modelManager = getModelManager()\n const modelResolution = modelManager.resolveModelWithInfo(expert_model)\n\n if (!modelResolution.success) {\n const availableModels = modelManager.getAllAvailableModelNames()\n if (availableModels.length > 0) {\n return {\n result: false,\n message: `Model '${expert_model}' is not configured. Available models: ${availableModels.join(', ')}. Check if any available model closely matches the user's request (e.g., 'kimi' matches 'kimi-k2-0711-preview'). If there's a strong match, auto retry using the correct model name. If no close match exists, inform the user that '${expert_model}' needs to be configured using /model command.`,\n }\n } else {\n return {\n result: false,\n message: `Model '${expert_model}' not found and no models are currently configured in the system. Inform the user that models need to be configured first using the /model command.`,\n }\n }\n }\n } catch (error) {\n console.error('Model validation error in AskExpertModelTool:', error)\n logError(error)\n return {\n result: false,\n message: `Failed to validate expert model '${expert_model}'. Please check your model configuration.`,\n }\n }\n\n return { result: true }\n },\n\n renderToolUseMessage(\n { question, expert_model, chat_session_id },\n { verbose },\n ) {\n if (!question || !expert_model) return null\n const isNewSession = chat_session_id === 'new'\n const sessionDisplay = isNewSession\n ? 'new session'\n : `session ${chat_session_id.substring(0, 5)}...`\n const theme = getTheme()\n\n if (verbose) {\n return (\n <Box flexDirection=\"column\">\n <Text bold color=\"yellow\">\n {expert_model}\n </Text>\n <Text color={theme.secondaryText}>{sessionDisplay}</Text>\n <Box marginTop={1}>\n <Text color={theme.text}>\n {question.length > 300\n ? question.substring(0, 300) + '...'\n : question}\n </Text>\n </Box>\n </Box>\n )\n }\n return (\n <Box flexDirection=\"column\">\n <Text bold color=\"yellow\">\n {expert_model}{' '}\n </Text>\n <Text color={SEMANTIC_COLORS.dim}>({sessionDisplay})</Text>\n </Box>\n )\n },\n\n renderToolResultMessage(content) {\n const verbose = true // Show more content\n const theme = getTheme()\n\n // Guard against undefined or null content\n if (!content) {\n return (\n <Box flexDirection=\"row\">\n <Text color={theme.secondaryText}>Consultation completed</Text>\n </Box>\n )\n }\n\n if (typeof content === 'object' && content && 'expertAnswer' in content) {\n const expertResult = content as Out\n const isError =\n expertResult.expertAnswer.startsWith('Error') ||\n expertResult.expertAnswer.includes('failed')\n const isInterrupted = expertResult.chatSessionId === 'interrupted'\n\n if (isInterrupted) {\n return (\n <Box flexDirection=\"row\">\n <Text color={theme.secondaryText}>Consultation interrupted</Text>\n </Box>\n )\n }\n\n const answerText = verbose\n ? expertResult.expertAnswer.trim()\n : expertResult.expertAnswer.length > 500\n ? expertResult.expertAnswer.substring(0, 500) + '...'\n : expertResult.expertAnswer.trim()\n\n if (isError) {\n return (\n <Box flexDirection=\"column\">\n <Text color=\"red\">{answerText}</Text>\n </Box>\n )\n }\n\n return (\n <Box flexDirection=\"column\">\n <Text bold color={theme.text}>\n Response from {expertResult.expertModelName}:\n </Text>\n <Box marginTop={1}>\n <Text color={theme.text}>{applyMarkdown(answerText)}</Text>\n </Box>\n <Box marginTop={1}>\n <Text color={SEMANTIC_COLORS.dim}>\n Session: {expertResult.chatSessionId.substring(0, 8)}\n </Text>\n </Box>\n </Box>\n )\n }\n\n return (\n <Box flexDirection=\"row\">\n <Text color={theme.secondaryText}>Consultation completed</Text>\n </Box>\n )\n },\n\n renderResultForAssistant(output: Out): string {\n return `[Expert consultation completed]\nExpert Model: ${output.expertModelName}\nSession ID: ${output.chatSessionId}\nTo continue this conversation with context preservation, use this Session ID in your next AskExpertModel call to maintain the full conversation history and context.\n\n${output.expertAnswer}`\n },\n\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n async *call(\n { question, expert_model, chat_session_id },\n { abortController, readFileTimestamps },\n ) {\n const expertModel = expert_model\n\n let sessionId: string\n let isInterrupted = false\n\n // Set up abort listener (following TaskTool pattern)\n const abortListener = () => {\n isInterrupted = true\n }\n abortController.signal.addEventListener('abort', abortListener)\n\n try {\n // Initial abort check\n if (abortController.signal.aborted) {\n return yield* this.handleInterrupt()\n }\n // Session management with error handling\n if (chat_session_id === 'new') {\n try {\n const session = createExpertChatSession(expertModel)\n sessionId = session.sessionId\n } catch (error) {\n console.error('Failed to create new expert chat session:', error)\n logError(error)\n throw new Error('Failed to create new chat session')\n }\n } else {\n sessionId = chat_session_id\n try {\n const session = loadExpertChatSession(sessionId)\n if (!session) {\n // Session doesn't exist, create new one\n const newSession = createExpertChatSession(expertModel)\n sessionId = newSession.sessionId\n }\n } catch (error) {\n console.error('Failed to load expert chat session:', error)\n logError(error)\n // Fallback: create new session\n try {\n const newSession = createExpertChatSession(expertModel)\n sessionId = newSession.sessionId\n } catch (createError) {\n console.error(\n 'Failed to create fallback expert chat session:',\n createError,\n )\n logError(createError)\n throw new Error('Unable to create or load chat session')\n }\n }\n }\n\n // Check for cancellation before loading history\n if (isInterrupted || abortController.signal.aborted) {\n return yield* this.handleInterrupt()\n }\n\n // Load history and prepare messages with error handling\n let historyMessages: Array<{ role: string; content: string }>\n try {\n historyMessages = getSessionMessages(sessionId)\n } catch (error) {\n console.error('Failed to load session messages:', error)\n logError(error)\n historyMessages = [] // Fallback to empty history\n }\n\n const messages = [...historyMessages, { role: 'user', content: question }]\n\n let systemMessages\n try {\n systemMessages = messages.map(msg =>\n msg.role === 'user'\n ? createUserMessage(msg.content)\n : createAssistantMessage(msg.content),\n )\n } catch (error) {\n console.error('Failed to create system messages:', error)\n logError(error)\n throw new Error('Failed to prepare conversation messages')\n }\n\n // Check for cancellation before model call\n if (isInterrupted || abortController.signal.aborted) {\n return yield* this.handleInterrupt()\n }\n\n // Yield progress message to show we're connecting\n yield {\n type: 'progress',\n content: createAssistantMessage(\n `Connecting to ${expertModel}... (timeout: 5 minutes)`,\n ),\n }\n\n // Call model with comprehensive error handling and timeout\n let response\n try {\n // Debug: Log the model we're trying to use (using global debug logger)\n const modelManager = getModelManager()\n const modelResolution = modelManager.resolveModelWithInfo(expertModel)\n\n debugLogger.api('EXPERT_MODEL_RESOLUTION', {\n requestedModel: expertModel,\n success: modelResolution.success,\n profileName: modelResolution.profile?.name,\n profileModelName: modelResolution.profile?.modelName,\n provider: modelResolution.profile?.provider,\n isActive: modelResolution.profile?.isActive,\n error: modelResolution.error,\n })\n\n // Create a timeout promise to prevent hanging\n const timeoutMs = 300000 // 300 seconds (5 minutes) timeout for external models\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => {\n reject(\n new Error(\n `Expert model query timed out after ${timeoutMs / 1000}s`,\n ),\n )\n }, timeoutMs)\n })\n\n // Race between the query and timeout\n response = await Promise.race([\n queryLLM(\n systemMessages,\n [], // no system prompt - let expert model use its default behavior\n 0, // no thinking tokens needed\n [], // no tools needed\n abortController.signal,\n {\n safeMode: false,\n model: expertModel,\n prependCLISysprompt: false, // KEY: avoid injecting CLI context\n },\n ),\n timeoutPromise,\n ])\n } catch (error: any) {\n console.error('Expert model query failed:', error)\n logError(error)\n\n // Check for specific error types\n if (\n error.name === 'AbortError' ||\n abortController.signal?.aborted ||\n isInterrupted\n ) {\n return yield* this.handleInterrupt()\n }\n\n if (error.message?.includes('timed out')) {\n throw new Error(\n `Expert model '${expertModel}' timed out after 5 minutes.\\n\\n` +\n `Suggestions:\\n` +\n ` - The model might be experiencing high load\\n` +\n ` - Try a different model or retry later\\n` +\n ` - Consider breaking down your question into smaller parts`,\n )\n }\n\n if (error.message?.includes('rate limit')) {\n throw new Error(\n `Rate limit exceeded for ${expertModel}.\\n\\n` +\n `Please wait a moment and try again, or use a different model.`,\n )\n }\n\n if (error.message?.includes('invalid api key')) {\n throw new Error(\n `Invalid API key for ${expertModel}.\\n\\n` +\n `Please check your model configuration with /model command.`,\n )\n }\n\n if (\n error.message?.includes('model not found') ||\n error.message?.includes('Failed to resolve model')\n ) {\n // Provide helpful model guidance in runtime errors too\n try {\n const modelManager = getModelManager()\n const availableModels = modelManager.getAllAvailableModelNames()\n if (availableModels.length > 0) {\n throw new Error(\n `Model '${expertModel}' is not configured. Available models: ${availableModels.join(', ')}. Check if any available model closely matches the user's request (e.g., 'kimi' matches 'kimi-k2-0711-preview'). If there's a strong match, auto retry using the correct model name. If no close match exists, inform the user that '${expertModel}' needs to be configured using /model command.`,\n )\n } else {\n throw new Error(\n `Model '${expertModel}' not found and no models are currently configured in the system. Inform the user that models need to be configured first using the /model command.`,\n )\n }\n } catch (modelError) {\n // If we can't get model list, fall back to simple error\n throw new Error(\n `Model '${expertModel}' not found. Please check model configuration or inform user about the issue.`,\n )\n }\n }\n\n // Generic fallback\n throw new Error(\n `Expert model query failed: ${error.message || 'Unknown error'}`,\n )\n }\n\n // Extract answer with error handling\n let expertAnswer: string\n try {\n if (!response?.message?.content) {\n throw new Error('No content in expert response')\n }\n\n expertAnswer = response.message.content\n .filter(block => block.type === 'text')\n .map(block => (block as any).text)\n .join('\\n')\n\n if (!expertAnswer.trim()) {\n throw new Error('Expert response was empty')\n }\n } catch (error) {\n console.error('Failed to extract expert answer:', error)\n logError(error)\n throw new Error('Failed to process expert response')\n }\n\n // Save conversation with error handling\n try {\n addMessageToSession(sessionId, 'user', question)\n addMessageToSession(sessionId, 'assistant', expertAnswer)\n } catch (error) {\n console.error('Failed to save conversation to session:', error)\n logError(error)\n // Don't throw here - we got a valid response, saving is non-critical\n }\n\n const result: Out = {\n chatSessionId: sessionId,\n expertModelName: expertModel,\n expertAnswer: expertAnswer,\n }\n\n yield {\n type: 'result',\n data: result,\n resultForAssistant: this.renderResultForAssistant(result),\n }\n } catch (error: any) {\n // Check if error is due to cancellation\n if (\n error.name === 'AbortError' ||\n abortController.signal?.aborted ||\n isInterrupted\n ) {\n return yield* this.handleInterrupt()\n }\n\n console.error('AskExpertModelTool execution failed:', error)\n logError(error)\n\n // Ensure we have a valid sessionId for error response\n const errorSessionId = sessionId || 'error-session'\n\n const errorMessage =\n error.message || 'Expert consultation failed with unknown error'\n const result: Out = {\n chatSessionId: errorSessionId,\n expertModelName: expertModel,\n expertAnswer: `\u274C ${errorMessage}`,\n }\n\n yield {\n type: 'result',\n data: result,\n resultForAssistant: this.renderResultForAssistant(result),\n }\n } finally {\n // Clean up event listener\n abortController.signal.removeEventListener('abort', abortListener)\n }\n },\n\n // Unified interrupt handling method (following TaskTool pattern)\n async *handleInterrupt() {\n yield {\n type: 'result',\n data: {\n chatSessionId: 'interrupted',\n expertModelName: 'cancelled',\n expertAnswer: INTERRUPT_MESSAGE,\n },\n resultForAssistant: INTERRUPT_MESSAGE,\n }\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,7 @@ import * as React from "react";
3
3
  import { z } from "zod";
4
4
  import { FallbackToolUseRejectedMessage } from "../../components/FallbackToolUseRejectedMessage.js";
5
5
  import { DESCRIPTION, PROMPT } from "./prompt.js";
6
+ import { SEMANTIC_COLORS } from "../../constants/colors.js";
6
7
  const inputSchema = z.strictObject({
7
8
  questions: z.array(
8
9
  z.object({
@@ -16,7 +17,15 @@ const inputSchema = z.strictObject({
16
17
  ).min(2).max(4).optional().describe("List of options (2-4 recommended)"),
17
18
  multiSelect: z.boolean().optional().default(false).describe("Allow multiple selections")
18
19
  })
19
- ).min(1).max(4).describe("Questions to ask (1-4 questions)")
20
+ ).min(1).max(4).describe("Questions to ask (1-4 questions)"),
21
+ answers: z.record(z.string(), z.string()).optional().describe("User answers collected by the permission component"),
22
+ metadata: z.object({
23
+ source: z.string().optional().describe(
24
+ 'Optional identifier for the source of this question (e.g., "remember" for /remember command). Used for analytics tracking.'
25
+ )
26
+ }).optional().describe(
27
+ "Optional metadata for tracking and analytics purposes. Not displayed to user."
28
+ )
20
29
  });
21
30
  const AskUserQuestionTool = {
22
31
  name: "AskUserQuestion",
@@ -70,10 +79,38 @@ ${formattedAnswers}`;
70
79
  const answers = output.answers || [];
71
80
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: "#10B981" }, " ", "\u23BF "), /* @__PURE__ */ React.createElement(Text, null, "User provided answers")), answers.map((answer, index) => {
72
81
  if (!answer) return null;
73
- return /* @__PURE__ */ React.createElement(Box, { key: index, flexDirection: "row", paddingLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { color: "#6B7280" }, "\u2022 "), answer.customInput ? /* @__PURE__ */ React.createElement(Text, null, "Q", (answer.questionIndex ?? index) + 1, ': "', answer.customInput, '"') : answer.selectedOptions ? /* @__PURE__ */ React.createElement(Text, null, "Q", (answer.questionIndex ?? index) + 1, ": Option", " ", answer.selectedOptions.map((i) => i + 1).join(", ")) : /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Q", (answer.questionIndex ?? index) + 1, ": No answer"));
82
+ return /* @__PURE__ */ React.createElement(Box, { key: index, flexDirection: "row", paddingLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { color: "#6B7280" }, "\u2022 "), answer.customInput ? /* @__PURE__ */ React.createElement(Text, null, "Q", (answer.questionIndex ?? index) + 1, ': "', answer.customInput, '"') : answer.selectedOptions ? /* @__PURE__ */ React.createElement(Text, null, "Q", (answer.questionIndex ?? index) + 1, ": Option", " ", answer.selectedOptions.map((i) => i + 1).join(", ")) : /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Q", (answer.questionIndex ?? index) + 1, ": No answer"));
74
83
  }));
75
84
  },
76
- async *call({ questions }, context) {
85
+ async *call({
86
+ questions,
87
+ answers: providedAnswers,
88
+ metadata
89
+ }, context) {
90
+ if (providedAnswers) {
91
+ const convertedAnswers = Object.entries(
92
+ providedAnswers
93
+ ).map(([key, value], index) => ({
94
+ questionIndex: index,
95
+ customInput: value
96
+ }));
97
+ yield {
98
+ type: "result",
99
+ data: {
100
+ answers: convertedAnswers,
101
+ timestamp: Date.now(),
102
+ ...metadata && { metadata }
103
+ },
104
+ resultForAssistant: `User answered:
105
+ ${convertedAnswers.map((answer) => {
106
+ if (answer.customInput) {
107
+ return `Q${answer.questionIndex + 1}: "${answer.customInput}"`;
108
+ }
109
+ return `Q${answer.questionIndex + 1}: No answer`;
110
+ }).join("\n")}`
111
+ };
112
+ return;
113
+ }
77
114
  if (!context.askUser) {
78
115
  throw new Error(
79
116
  "AskUserQuestion tool requires askUser function in context"
@@ -94,7 +131,8 @@ ${formattedAnswers}`;
94
131
  type: "result",
95
132
  data: {
96
133
  answers,
97
- timestamp: Date.now()
134
+ timestamp: Date.now(),
135
+ ...metadata && { metadata }
98
136
  },
99
137
  resultForAssistant: `User answered:
100
138
  ${answers.map((answer) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { Tool, type ExtendedToolUseContext } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport type { UserAnswer } from '@minto-types/askUserQuestion'\n\nconst inputSchema = z.strictObject({\n questions: z\n .array(\n z.object({\n question: z.string().describe('The question to ask the user'),\n header: z\n .string()\n .max(12)\n .optional()\n .describe('Short category label (max 12 chars)'),\n options: z\n .array(\n z.object({\n label: z.string().describe('Option display text'),\n description: z.string().optional().describe('Option explanation'),\n }),\n )\n .min(2)\n .max(4)\n .optional()\n .describe('List of options (2-4 recommended)'),\n multiSelect: z\n .boolean()\n .optional()\n .default(false)\n .describe('Allow multiple selections'),\n }),\n )\n .min(1)\n .max(4)\n .describe('Questions to ask (1-4 questions)'),\n})\n\nexport type AskUserQuestionOutput = {\n answers: UserAnswer[]\n timestamp: number\n}\n\nexport const AskUserQuestionTool = {\n name: 'AskUserQuestion',\n\n async description() {\n return DESCRIPTION\n },\n\n async prompt() {\n return PROMPT\n },\n\n inputSchema,\n\n userFacingName() {\n return 'Ask User Question'\n },\n\n async isEnabled() {\n return true\n },\n\n isReadOnly() {\n return true // Doesn't modify files\n },\n\n isConcurrencySafe() {\n return false // User interaction is not concurrent-safe\n },\n\n needsPermissions() {\n return false // No special permissions needed\n },\n\n renderResultForAssistant(output: AskUserQuestionOutput) {\n const formattedAnswers = output.answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')\n\n return `User answered:\\n${formattedAnswers}`\n },\n\n renderToolUseMessage(input: z.infer<typeof inputSchema>) {\n const questionCount = input.questions.length\n const questionText =\n questionCount === 1\n ? input.questions[0]!.question\n : `${questionCount} questions`\n return `Asking user: ${questionText}`\n },\n\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(output: AskUserQuestionOutput) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>Question completed</Text>\n </Box>\n </Box>\n )\n }\n\n const answers = output.answers || []\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>User provided answers</Text>\n </Box>\n {answers.map((answer, index) => {\n // Guard against null/undefined answers\n if (!answer) return null\n return (\n <Box key={index} flexDirection=\"row\" paddingLeft={4}>\n <Text color=\"#6B7280\">\u2022 </Text>\n {answer.customInput ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: \"{answer.customInput}\"\n </Text>\n ) : answer.selectedOptions ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: Option{' '}\n {answer.selectedOptions.map(i => i + 1).join(', ')}\n </Text>\n ) : (\n <Text dimColor>\n Q{(answer.questionIndex ?? index) + 1}: No answer\n </Text>\n )}\n </Box>\n )\n })}\n </Box>\n )\n },\n\n async *call(\n { questions }: z.infer<typeof inputSchema>,\n context: ExtendedToolUseContext,\n ) {\n // Check if askUser function is available\n if (!context.askUser) {\n throw new Error(\n 'AskUserQuestion tool requires askUser function in context',\n )\n }\n\n try {\n // Convert parsed questions to UserQuestion type\n const userQuestions = questions.map(q => ({\n question: q.question,\n header: q.header,\n options: q.options?.map(opt => ({\n label: opt.label,\n description: opt.description,\n })),\n multiSelect: q.multiSelect ?? false,\n }))\n\n // Call askUser and wait for user response\n const answers = await context.askUser(userQuestions)\n\n // Return the result\n yield {\n type: 'result',\n data: {\n answers,\n timestamp: Date.now(),\n },\n resultForAssistant: `User answered:\\n${answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')}`,\n }\n } catch (error) {\n // User cancelled or error occurred\n throw new Error(\n `Failed to get user response: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n },\n} satisfies Tool<typeof inputSchema, AskUserQuestionOutput>\n"],
5
- "mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AACvB,SAAS,SAAS;AAElB,SAAS,sCAAsC;AAC/C,SAAS,aAAa,cAAc;AAGpC,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,WAAW,EACR;AAAA,IACC,EAAE,OAAO;AAAA,MACP,UAAU,EAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC5D,QAAQ,EACL,OAAO,EACP,IAAI,EAAE,EACN,SAAS,EACT,SAAS,qCAAqC;AAAA,MACjD,SAAS,EACN;AAAA,QACC,EAAE,OAAO;AAAA,UACP,OAAO,EAAE,OAAO,EAAE,SAAS,qBAAqB;AAAA,UAChD,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,QAClE,CAAC;AAAA,MACH,EACC,IAAI,CAAC,EACL,IAAI,CAAC,EACL,SAAS,EACT,SAAS,mCAAmC;AAAA,MAC/C,aAAa,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,KAAK,EACb,SAAS,2BAA2B;AAAA,IACzC,CAAC;AAAA,EACH,EACC,IAAI,CAAC,EACL,IAAI,CAAC,EACL,SAAS,kCAAkC;AAChD,CAAC;AAOM,MAAM,sBAAsB;AAAA,EACjC,MAAM;AAAA,EAEN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EAEA;AAAA,EAEA,iBAAiB;AACf,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EAEA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EAEA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,mBAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,yBAAyB,QAA+B;AACtD,UAAM,mBAAmB,OAAO,QAC7B,IAAI,YAAU;AACb,UAAI,OAAO,aAAa;AACtB,eAAO,IAAI,OAAO,gBAAgB,CAAC,MAAM,OAAO,WAAW;AAAA,MAC7D;AACA,UAAI,OAAO,iBAAiB;AAC1B,eAAO,IAAI,OAAO,gBAAgB,CAAC,YAAY,OAAO,gBAAgB,IAAI,OAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAClG;AACA,aAAO,IAAI,OAAO,gBAAgB,CAAC;AAAA,IACrC,CAAC,EACA,KAAK,IAAI;AAEZ,WAAO;AAAA,EAAmB,gBAAgB;AAAA,EAC5C;AAAA,EAEA,qBAAqB,OAAoC;AACvD,UAAM,gBAAgB,MAAM,UAAU;AACtC,UAAM,eACJ,kBAAkB,IACd,MAAM,UAAU,CAAC,EAAG,WACpB,GAAG,aAAa;AACtB,WAAO,gBAAgB,YAAY;AAAA,EACrC;AAAA,EAEA,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EAEA,wBAAwB,QAA+B;AAErD,QAAI,CAAC,QAAQ;AACX,aACE,oCAAC,OAAI,eAAc,UAAS,aAAa,KACvC,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAM,aAAW,MAAK,SAAE,GAC9B,oCAAC,YAAK,oBAAkB,CAC1B,CACF;AAAA,IAEJ;AAEA,UAAM,UAAU,OAAO,WAAW,CAAC;AAEnC,WACE,oCAAC,OAAI,eAAc,UAAS,aAAa,KACvC,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAM,aAAW,MAAK,SAAE,GAC9B,oCAAC,YAAK,uBAAqB,CAC7B,GACC,QAAQ,IAAI,CAAC,QAAQ,UAAU;AAE9B,UAAI,CAAC,OAAQ,QAAO;AACpB,aACE,oCAAC,OAAI,KAAK,OAAO,eAAc,OAAM,aAAa,KAChD,oCAAC,QAAK,OAAM,aAAU,SAAE,GACvB,OAAO,cACN,oCAAC,YAAK,MACD,OAAO,iBAAiB,SAAS,GAAE,OAAI,OAAO,aAAY,GAC/D,IACE,OAAO,kBACT,oCAAC,YAAK,MACD,OAAO,iBAAiB,SAAS,GAAE,YAAS,KAC9C,OAAO,gBAAgB,IAAI,OAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CACnD,IAEA,oCAAC,QAAK,UAAQ,QAAC,MACV,OAAO,iBAAiB,SAAS,GAAE,aACxC,CAEJ;AAAA,IAEJ,CAAC,CACH;AAAA,EAEJ;AAAA,EAEA,OAAO,KACL,EAAE,UAAU,GACZ,SACA;AAEA,QAAI,CAAC,QAAQ,SAAS;AACpB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AAEF,YAAM,gBAAgB,UAAU,IAAI,QAAM;AAAA,QACxC,UAAU,EAAE;AAAA,QACZ,QAAQ,EAAE;AAAA,QACV,SAAS,EAAE,SAAS,IAAI,UAAQ;AAAA,UAC9B,OAAO,IAAI;AAAA,UACX,aAAa,IAAI;AAAA,QACnB,EAAE;AAAA,QACF,aAAa,EAAE,eAAe;AAAA,MAChC,EAAE;AAGF,YAAM,UAAU,MAAM,QAAQ,QAAQ,aAAa;AAGnD,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB;AAAA,QACA,oBAAoB;AAAA,EAAmB,QACpC,IAAI,YAAU;AACb,cAAI,OAAO,aAAa;AACtB,mBAAO,IAAI,OAAO,gBAAgB,CAAC,MAAM,OAAO,WAAW;AAAA,UAC7D;AACA,cAAI,OAAO,iBAAiB;AAC1B,mBAAO,IAAI,OAAO,gBAAgB,CAAC,YAAY,OAAO,gBAAgB,IAAI,OAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,UAClG;AACA,iBAAO,IAAI,OAAO,gBAAgB,CAAC;AAAA,QACrC,CAAC,EACA,KAAK,IAAI,CAAC;AAAA,MACf;AAAA,IACF,SAAS,OAAO;AAEd,YAAM,IAAI;AAAA,QACR,gCAAgC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MAC1F;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { Tool, type ExtendedToolUseContext } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport type { UserAnswer } from '@minto-types/askUserQuestion'\nimport { SEMANTIC_COLORS } from '@constants/colors'\n\nconst inputSchema = z.strictObject({\n questions: z\n .array(\n z.object({\n question: z.string().describe('The question to ask the user'),\n header: z\n .string()\n .max(12)\n .optional()\n .describe('Short category label (max 12 chars)'),\n options: z\n .array(\n z.object({\n label: z.string().describe('Option display text'),\n description: z.string().optional().describe('Option explanation'),\n }),\n )\n .min(2)\n .max(4)\n .optional()\n .describe('List of options (2-4 recommended)'),\n multiSelect: z\n .boolean()\n .optional()\n .default(false)\n .describe('Allow multiple selections'),\n }),\n )\n .min(1)\n .max(4)\n .describe('Questions to ask (1-4 questions)'),\n answers: z\n .record(z.string(), z.string())\n .optional()\n .describe('User answers collected by the permission component'),\n metadata: z\n .object({\n source: z\n .string()\n .optional()\n .describe(\n 'Optional identifier for the source of this question (e.g., \"remember\" for /remember command). Used for analytics tracking.',\n ),\n })\n .optional()\n .describe(\n 'Optional metadata for tracking and analytics purposes. Not displayed to user.',\n ),\n})\n\nexport type AskUserQuestionOutput = {\n answers: UserAnswer[]\n timestamp: number\n metadata?: {\n source?: string\n }\n}\n\nexport const AskUserQuestionTool = {\n name: 'AskUserQuestion',\n\n async description() {\n return DESCRIPTION\n },\n\n async prompt() {\n return PROMPT\n },\n\n inputSchema,\n\n userFacingName() {\n return 'Ask User Question'\n },\n\n async isEnabled() {\n return true\n },\n\n isReadOnly() {\n return true // Doesn't modify files\n },\n\n isConcurrencySafe() {\n return false // User interaction is not concurrent-safe\n },\n\n needsPermissions() {\n return false // No special permissions needed\n },\n\n renderResultForAssistant(output: AskUserQuestionOutput) {\n const formattedAnswers = output.answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')\n\n return `User answered:\\n${formattedAnswers}`\n },\n\n renderToolUseMessage(input: z.infer<typeof inputSchema>) {\n const questionCount = input.questions.length\n const questionText =\n questionCount === 1\n ? input.questions[0]!.question\n : `${questionCount} questions`\n return `Asking user: ${questionText}`\n },\n\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(output: AskUserQuestionOutput) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>Question completed</Text>\n </Box>\n </Box>\n )\n }\n\n const answers = output.answers || []\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>User provided answers</Text>\n </Box>\n {answers.map((answer, index) => {\n // Guard against null/undefined answers\n if (!answer) return null\n return (\n <Box key={index} flexDirection=\"row\" paddingLeft={4}>\n <Text color=\"#6B7280\">\u2022 </Text>\n {answer.customInput ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: \"{answer.customInput}\"\n </Text>\n ) : answer.selectedOptions ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: Option{' '}\n {answer.selectedOptions.map(i => i + 1).join(', ')}\n </Text>\n ) : (\n <Text color={SEMANTIC_COLORS.dim}>\n Q{(answer.questionIndex ?? index) + 1}: No answer\n </Text>\n )}\n </Box>\n )\n })}\n </Box>\n )\n },\n\n async *call(\n {\n questions,\n answers: providedAnswers,\n metadata,\n }: z.infer<typeof inputSchema>,\n context: ExtendedToolUseContext,\n ) {\n // If answers are already provided (from permission component), use them directly\n if (providedAnswers) {\n // Convert provided answers (Record<string, string>) to UserAnswer[]\n const convertedAnswers: UserAnswer[] = Object.entries(\n providedAnswers,\n ).map(([key, value], index) => ({\n questionIndex: index,\n customInput: value,\n }))\n\n yield {\n type: 'result',\n data: {\n answers: convertedAnswers,\n timestamp: Date.now(),\n ...(metadata && { metadata }),\n },\n resultForAssistant: `User answered:\\n${convertedAnswers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')}`,\n }\n return\n }\n\n // Check if askUser function is available\n if (!context.askUser) {\n throw new Error(\n 'AskUserQuestion tool requires askUser function in context',\n )\n }\n\n try {\n // Convert parsed questions to UserQuestion type\n const userQuestions = questions.map(q => ({\n question: q.question,\n header: q.header,\n options: q.options?.map(opt => ({\n label: opt.label,\n description: opt.description,\n })),\n multiSelect: q.multiSelect ?? false,\n }))\n\n // Call askUser and wait for user response\n const answers = await context.askUser(userQuestions)\n\n // Return the result\n yield {\n type: 'result',\n data: {\n answers,\n timestamp: Date.now(),\n ...(metadata && { metadata }),\n },\n resultForAssistant: `User answered:\\n${answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')}`,\n }\n } catch (error) {\n // User cancelled or error occurred\n throw new Error(\n `Failed to get user response: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n },\n} satisfies Tool<typeof inputSchema, AskUserQuestionOutput>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -16,12 +16,29 @@ import { getGlobalConfig } from "../../utils/config.js";
16
16
  import { getModelManager } from "../../utils/model.js";
17
17
  import { BackgroundShellManager } from "../../utils/BackgroundShellManager.js";
18
18
  import BashToolResultMessage from "./BashToolResultMessage.js";
19
- import { BANNED_COMMANDS, PROMPT, matchesDangerousPattern } from "./prompt.js";
19
+ import {
20
+ BANNED_COMMANDS,
21
+ PROMPT,
22
+ detectDangerousPatterns
23
+ } from "./prompt.js";
20
24
  import { formatOutput, getCommandFilePaths } from "./utils.js";
21
25
  const inputSchema = z.strictObject({
22
26
  command: z.string().describe("The command to execute"),
23
27
  timeout: z.number().optional().describe("Optional timeout in milliseconds (max 600000)"),
24
- run_in_background: z.boolean().optional().describe("Set to true to run this command in the background")
28
+ run_in_background: z.boolean().optional().describe("Set to true to run this command in the background"),
29
+ description: z.string().optional().describe(
30
+ `Clear, concise description of what this command does in active voice. Never use words like "complex" or "risk" in the description - just describe what it does.
31
+
32
+ For simple commands (git, npm, standard CLI tools), keep it brief (5-10 words):
33
+ - ls \u2192 "List files in current directory"
34
+ - git status \u2192 "Show working tree status"
35
+ - npm install \u2192 "Install package dependencies"
36
+
37
+ For commands that are harder to parse at a glance (piped commands, obscure flags, etc.), add enough context to clarify what it does:
38
+ - find . -name "*.tmp" -exec rm {} \\; \u2192 "Find and delete all .tmp files recursively"
39
+ - git reset --hard origin/main \u2192 "Discard all local changes and match remote main"
40
+ - curl -s url | jq '.data[]' \u2192 "Fetch JSON from URL and extract data array elements"`
41
+ )
25
42
  });
26
43
  const BashTool = {
27
44
  name: "Bash",
@@ -51,12 +68,27 @@ const BashTool = {
51
68
  return true;
52
69
  },
53
70
  async validateInput({ command }) {
54
- if (matchesDangerousPattern(command)) {
71
+ const dangerousPatterns = detectDangerousPatterns(command);
72
+ const criticalPatterns = dangerousPatterns.filter(
73
+ (p) => p.severity === "critical"
74
+ );
75
+ if (criticalPatterns.length > 0) {
76
+ const patternNames = criticalPatterns.map((p) => p.name).join(", ");
77
+ logError(
78
+ `SECURITY: Critical dangerous patterns detected in command: ${patternNames}`
79
+ );
55
80
  return {
56
81
  result: false,
57
- message: `Command contains dangerous patterns and has been blocked for security reasons`
82
+ message: `Command contains critical security risks: ${patternNames}. This command has been blocked for security reasons.`
58
83
  };
59
84
  }
85
+ const highPatterns = dangerousPatterns.filter((p) => p.severity === "high");
86
+ if (highPatterns.length > 0) {
87
+ const patternNames = highPatterns.map((p) => p.name).join(", ");
88
+ logError(
89
+ `SECURITY WARNING: High-severity patterns detected in command: ${patternNames}. User confirmation required.`
90
+ );
91
+ }
60
92
  const commands = splitCommand(command);
61
93
  for (const cmd of commands) {
62
94
  const parts = cmd.split(" ");
@@ -92,7 +124,8 @@ const BashTool = {
92
124
  }
93
125
  return { result: true };
94
126
  },
95
- renderToolUseMessage({ command }) {
127
+ renderToolUseMessage({ command, description }) {
128
+ let displayCommand = command;
96
129
  if (command.includes(`"$(cat <<'EOF'`)) {
97
130
  const match = command.match(
98
131
  /^(.*?)"?\$\(cat <<'EOF'\n([\s\S]*?)\n\s*EOF\n\s*\)"(.*)$/
@@ -101,10 +134,13 @@ const BashTool = {
101
134
  const prefix = match[1];
102
135
  const content = match[2];
103
136
  const suffix = match[3] || "";
104
- return `${prefix.trim()} "${content.trim()}"${suffix.trim()}`;
137
+ displayCommand = `${prefix.trim()} "${content.trim()}"${suffix.trim()}`;
105
138
  }
106
139
  }
107
- return command;
140
+ if (description) {
141
+ return description;
142
+ }
143
+ return displayCommand;
108
144
  },
109
145
  renderToolUseRejectedMessage() {
110
146
  return /* @__PURE__ */ React.createElement(FallbackToolUseRejectedMessage, null);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/BashTool/BashTool.tsx"],
4
- "sourcesContent": ["import { statSync } from 'fs'\nimport { EOL } from 'os'\nimport { isAbsolute, relative, resolve } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { PRODUCT_NAME } from '@constants/product'\nimport { queryQuick } from '@services/claude'\nimport { Tool, ValidationResult } from '@tool'\nimport { splitCommand } from '@utils/commands'\nimport { isInDirectory } from '@utils/file'\nimport { logError } from '@utils/log'\nimport {\n PersistentShell,\n type StreamingYield,\n type StreamingResult,\n} from '@utils/PersistentShell'\nimport { getCwd, getOriginalCwd } from '@utils/state'\nimport { getGlobalConfig } from '@utils/config'\nimport { getModelManager } from '@utils/model'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\nimport BashToolResultMessage from './BashToolResultMessage'\nimport { BANNED_COMMANDS, PROMPT, matchesDangerousPattern } from './prompt'\nimport { formatOutput, getCommandFilePaths } from './utils'\n\nexport const inputSchema = z.strictObject({\n command: z.string().describe('The command to execute'),\n timeout: z\n .number()\n .optional()\n .describe('Optional timeout in milliseconds (max 600000)'),\n run_in_background: z\n .boolean()\n .optional()\n .describe('Set to true to run this command in the background'),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n stdout: string\n stdoutLines: number // Total number of lines in original stdout, even if `stdout` is now truncated\n stderr: string\n stderrLines: number // Total number of lines in original stderr, even if `stderr` is now truncated\n interrupted: boolean\n shellId?: string // Present if run_in_background is true\n}\n\nexport const BashTool = {\n name: 'Bash',\n async description() {\n return 'Executes shell commands on your computer'\n },\n async prompt() {\n const config = getGlobalConfig()\n // \uD83D\uDD27 Fix: Use ModelManager to get actual current model\n const modelManager = getModelManager()\n const modelName =\n modelManager.getModelName('main') || '<No Model Configured>'\n // Substitute the placeholder in the static PROMPT string\n return PROMPT.replace(/{MODEL_NAME}/g, modelName)\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // BashTool modifies state/files, not safe for concurrent execution\n },\n inputSchema,\n userFacingName() {\n return 'Bash'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n // Always check per-project permissions for BashTool\n return true\n },\n async validateInput({ command }): Promise<ValidationResult> {\n // Check for dangerous patterns in the entire command first\n if (matchesDangerousPattern(command)) {\n return {\n result: false,\n message: `Command contains dangerous patterns and has been blocked for security reasons`,\n }\n }\n\n const commands = splitCommand(command)\n for (const cmd of commands) {\n const parts = cmd.split(' ')\n const baseCmd = parts[0]\n\n // Check if command is banned\n if (baseCmd && BANNED_COMMANDS.includes(baseCmd.toLowerCase())) {\n return {\n result: false,\n message: `Command '${baseCmd}' is not allowed for security reasons`,\n }\n }\n\n // Also check for commands that might be invoked with full path\n // e.g., /usr/bin/curl, /bin/rm\n if (baseCmd && (baseCmd.startsWith('/') || baseCmd.startsWith('./'))) {\n const cmdName = baseCmd.split('/').pop()?.toLowerCase()\n if (cmdName && BANNED_COMMANDS.includes(cmdName)) {\n return {\n result: false,\n message: `Command '${cmdName}' is not allowed for security reasons (full path: ${baseCmd})`,\n }\n }\n }\n\n // Special handling for cd command\n if (baseCmd === 'cd' && parts[1]) {\n const targetDir = parts[1]!.replace(/^['\"]|['\"]$/g, '') // Remove quotes if present\n const fullTargetDir = isAbsolute(targetDir)\n ? targetDir\n : resolve(getCwd(), targetDir)\n if (\n !isInDirectory(\n relative(getOriginalCwd(), fullTargetDir),\n relative(getCwd(), getOriginalCwd()),\n )\n ) {\n return {\n result: false,\n message: `ERROR: cd to '${fullTargetDir}' was blocked. For security, ${PRODUCT_NAME} may only change directories to child directories of the original working directory (${getOriginalCwd()}) for this session.`,\n }\n }\n }\n }\n\n return { result: true }\n },\n renderToolUseMessage({ command }) {\n // Clean up any command that uses the quoted HEREDOC pattern\n if (command.includes(\"\\\"$(cat <<'EOF'\")) {\n const match = command.match(\n /^(.*?)\"?\\$\\(cat <<'EOF'\\n([\\s\\S]*?)\\n\\s*EOF\\n\\s*\\)\"(.*)$/,\n )\n if (match && match[1] && match[2]) {\n const prefix = match[1]\n const content = match[2]\n const suffix = match[3] || ''\n return `${prefix.trim()} \"${content.trim()}\"${suffix.trim()}`\n }\n }\n return command\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(content) {\n return <BashToolResultMessage content={content} verbose={false} />\n },\n renderResultForAssistant({ interrupted, stdout, stderr }) {\n let errorMessage = stderr.trim()\n if (interrupted) {\n if (stderr) errorMessage += EOL\n errorMessage += '<error>Command was aborted before completion</error>'\n }\n const hasBoth = stdout.trim() && errorMessage\n return `${stdout.trim()}${hasBoth ? '\\n' : ''}${errorMessage.trim()}`\n },\n async *call(\n { command, timeout = 120000, run_in_background = false },\n { abortController, readFileTimestamps },\n ) {\n // Handle background execution\n if (run_in_background) {\n const shellId = BackgroundShellManager.getInstance().create(\n command,\n getCwd(),\n )\n\n const data: Out = {\n stdout: `Background shell started with ID: ${shellId}`,\n stdoutLines: 1,\n stderr: '',\n stderrLines: 0,\n interrupted: false,\n shellId,\n }\n\n yield {\n type: 'result',\n resultForAssistant: `Started background shell: ${shellId}\\nCommand: ${command}`,\n data,\n }\n return\n }\n\n let stdout = ''\n let stderr = ''\n\n // \uD83D\uDD27 CRITICAL FIX: Track whether this tool invocation has completed\n // to prevent async callbacks from accessing stale context\n let isCompleted = false\n\n // \uD83D\uDD27 Check if already cancelled before starting execution\n if (abortController.signal.aborted) {\n const data: Out = {\n stdout: '',\n stdoutLines: 0,\n stderr: 'Command cancelled before execution',\n stderrLines: 1,\n interrupted: true,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n try {\n // Execute commands with streaming output\n let result: StreamingResult | null = null\n let streamedStdout = ''\n let streamedStderr = ''\n\n // Use streaming execution for real-time output\n const streamingGenerator = PersistentShell.getInstance().execStreaming(\n command,\n abortController.signal,\n timeout,\n )\n\n for await (const chunk of streamingGenerator) {\n if (chunk.type === 'chunk') {\n // Accumulate streamed output\n if (chunk.stdout) {\n streamedStdout += chunk.stdout\n }\n if (chunk.stderr) {\n streamedStderr += chunk.stderr\n }\n\n // Yield progress update for real-time UI feedback\n // Use StreamingProgressContent format for proper rendering\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Bash',\n stdout: streamedStdout,\n stderr: streamedStderr,\n isStreaming: true,\n },\n }\n } else if (chunk.type === 'result') {\n // Final result\n result = chunk\n }\n }\n\n // Use the final result (or construct from streamed data)\n if (!result) {\n result = {\n type: 'result',\n stdout: streamedStdout,\n stderr: streamedStderr,\n code: 0,\n interrupted: false,\n }\n }\n\n stdout += (result.stdout || '').trim() + EOL\n stderr += (result.stderr || '').trim() + EOL\n if (result.code !== 0) {\n stderr += `Exit code ${result.code}`\n }\n\n if (!isInDirectory(getCwd(), getOriginalCwd())) {\n // Shell directory is outside original working directory, reset it\n await PersistentShell.getInstance().setCwd(getOriginalCwd())\n stderr = `${stderr.trim()}${EOL}Shell cwd was reset to ${getOriginalCwd()}`\n }\n\n // Update read timestamps for any files referenced by the command\n // Don't block the main thread!\n // Skip this in tests because it makes fixtures non-deterministic (they might not always get written),\n // so will be missing in CI.\n if (process.env.NODE_ENV !== 'test') {\n getCommandFilePaths(command, stdout).then(filePaths => {\n // \uD83D\uDD27 CRITICAL FIX: Check if tool invocation has already completed\n // to prevent accessing potentially stale context\n if (isCompleted) {\n return // Tool has finished, don't access readFileTimestamps\n }\n\n for (const filePath of filePaths) {\n const fullFilePath = isAbsolute(filePath)\n ? filePath\n : resolve(getCwd(), filePath)\n\n // Try/catch in case the file doesn't exist (because Haiku didn't properly extract it)\n try {\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n } catch (e) {\n logError(e)\n }\n }\n })\n }\n\n const { totalLines: stdoutLines, truncatedContent: stdoutContent } =\n formatOutput(stdout.trim())\n const { totalLines: stderrLines, truncatedContent: stderrContent } =\n formatOutput(stderr.trim())\n\n const data: Out = {\n stdout: stdoutContent,\n stdoutLines,\n stderr: stderrContent,\n stderrLines,\n interrupted: result.interrupted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } catch (error) {\n // \uD83D\uDD27 Handle cancellation or other errors properly\n const isAborted = abortController.signal.aborted\n const errorMessage = isAborted\n ? 'Command was cancelled by user'\n : `Command failed: ${error instanceof Error ? error.message : String(error)}`\n\n const data: Out = {\n stdout: stdout.trim(),\n stdoutLines: stdout.split('\\n').length,\n stderr: errorMessage,\n stderrLines: 1,\n interrupted: isAborted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } finally {\n // \uD83D\uDD27 CRITICAL FIX: Mark tool invocation as completed\n // This prevents async callbacks (like getCommandFilePaths) from\n // accessing potentially stale context after this tool returns\n isCompleted = true\n }\n },\n} satisfies Tool<In, Out>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { statSync } from 'fs'\nimport { EOL } from 'os'\nimport { isAbsolute, relative, resolve } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { PRODUCT_NAME } from '@constants/product'\nimport { queryQuick } from '@services/claude'\nimport { Tool, ValidationResult } from '@tool'\nimport { splitCommand } from '@utils/commands'\nimport { isInDirectory } from '@utils/file'\nimport { logError } from '@utils/log'\nimport {\n PersistentShell,\n type StreamingYield,\n type StreamingResult,\n} from '@utils/PersistentShell'\nimport { getCwd, getOriginalCwd } from '@utils/state'\nimport { getGlobalConfig } from '@utils/config'\nimport { getModelManager } from '@utils/model'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\nimport BashToolResultMessage from './BashToolResultMessage'\nimport {\n BANNED_COMMANDS,\n PROMPT,\n matchesDangerousPattern,\n detectDangerousPatterns,\n} from './prompt'\nimport { formatOutput, getCommandFilePaths } from './utils'\n\nexport const inputSchema = z.strictObject({\n command: z.string().describe('The command to execute'),\n timeout: z\n .number()\n .optional()\n .describe('Optional timeout in milliseconds (max 600000)'),\n run_in_background: z\n .boolean()\n .optional()\n .describe('Set to true to run this command in the background'),\n description: z\n .string()\n .optional()\n .describe(\n `Clear, concise description of what this command does in active voice. Never use words like \"complex\" or \"risk\" in the description - just describe what it does.\n\nFor simple commands (git, npm, standard CLI tools), keep it brief (5-10 words):\n- ls \u2192 \"List files in current directory\"\n- git status \u2192 \"Show working tree status\"\n- npm install \u2192 \"Install package dependencies\"\n\nFor commands that are harder to parse at a glance (piped commands, obscure flags, etc.), add enough context to clarify what it does:\n- find . -name \"*.tmp\" -exec rm {} \\\\; \u2192 \"Find and delete all .tmp files recursively\"\n- git reset --hard origin/main \u2192 \"Discard all local changes and match remote main\"\n- curl -s url | jq '.data[]' \u2192 \"Fetch JSON from URL and extract data array elements\"`,\n ),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n stdout: string\n stdoutLines: number // Total number of lines in original stdout, even if `stdout` is now truncated\n stderr: string\n stderrLines: number // Total number of lines in original stderr, even if `stderr` is now truncated\n interrupted: boolean\n shellId?: string // Present if run_in_background is true\n}\n\nexport const BashTool = {\n name: 'Bash',\n async description() {\n return 'Executes shell commands on your computer'\n },\n async prompt() {\n const config = getGlobalConfig()\n // \uD83D\uDD27 Fix: Use ModelManager to get actual current model\n const modelManager = getModelManager()\n const modelName =\n modelManager.getModelName('main') || '<No Model Configured>'\n // Substitute the placeholder in the static PROMPT string\n return PROMPT.replace(/{MODEL_NAME}/g, modelName)\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // BashTool modifies state/files, not safe for concurrent execution\n },\n inputSchema,\n userFacingName() {\n return 'Bash'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n // Always check per-project permissions for BashTool\n return true\n },\n async validateInput({ command }): Promise<ValidationResult> {\n // Detect dangerous patterns (critical only block, others warn)\n const dangerousPatterns = detectDangerousPatterns(command)\n const criticalPatterns = dangerousPatterns.filter(\n p => p.severity === 'critical',\n )\n\n if (criticalPatterns.length > 0) {\n const patternNames = criticalPatterns.map(p => p.name).join(', ')\n logError(\n `SECURITY: Critical dangerous patterns detected in command: ${patternNames}`,\n )\n return {\n result: false,\n message: `Command contains critical security risks: ${patternNames}. This command has been blocked for security reasons.`,\n }\n }\n\n // Log warnings for high-severity patterns (non-blocking)\n const highPatterns = dangerousPatterns.filter(p => p.severity === 'high')\n if (highPatterns.length > 0) {\n const patternNames = highPatterns.map(p => p.name).join(', ')\n logError(\n `SECURITY WARNING: High-severity patterns detected in command: ${patternNames}. User confirmation required.`,\n )\n }\n\n const commands = splitCommand(command)\n for (const cmd of commands) {\n const parts = cmd.split(' ')\n const baseCmd = parts[0]\n\n // Check if command is banned\n if (baseCmd && BANNED_COMMANDS.includes(baseCmd.toLowerCase())) {\n return {\n result: false,\n message: `Command '${baseCmd}' is not allowed for security reasons`,\n }\n }\n\n // Also check for commands that might be invoked with full path\n // e.g., /usr/bin/curl, /bin/rm\n if (baseCmd && (baseCmd.startsWith('/') || baseCmd.startsWith('./'))) {\n const cmdName = baseCmd.split('/').pop()?.toLowerCase()\n if (cmdName && BANNED_COMMANDS.includes(cmdName)) {\n return {\n result: false,\n message: `Command '${cmdName}' is not allowed for security reasons (full path: ${baseCmd})`,\n }\n }\n }\n\n // Special handling for cd command\n if (baseCmd === 'cd' && parts[1]) {\n const targetDir = parts[1]!.replace(/^['\"]|['\"]$/g, '') // Remove quotes if present\n const fullTargetDir = isAbsolute(targetDir)\n ? targetDir\n : resolve(getCwd(), targetDir)\n if (\n !isInDirectory(\n relative(getOriginalCwd(), fullTargetDir),\n relative(getCwd(), getOriginalCwd()),\n )\n ) {\n return {\n result: false,\n message: `ERROR: cd to '${fullTargetDir}' was blocked. For security, ${PRODUCT_NAME} may only change directories to child directories of the original working directory (${getOriginalCwd()}) for this session.`,\n }\n }\n }\n }\n\n return { result: true }\n },\n renderToolUseMessage({ command, description }) {\n // Clean up any command that uses the quoted HEREDOC pattern\n let displayCommand = command\n if (command.includes(\"\\\"$(cat <<'EOF'\")) {\n const match = command.match(\n /^(.*?)\"?\\$\\(cat <<'EOF'\\n([\\s\\S]*?)\\n\\s*EOF\\n\\s*\\)\"(.*)$/,\n )\n if (match && match[1] && match[2]) {\n const prefix = match[1]\n const content = match[2]\n const suffix = match[3] || ''\n displayCommand = `${prefix.trim()} \"${content.trim()}\"${suffix.trim()}`\n }\n }\n\n // If description is provided, show it instead of (or alongside) the command\n if (description) {\n return description\n }\n\n return displayCommand\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(content) {\n return <BashToolResultMessage content={content} verbose={false} />\n },\n renderResultForAssistant({ interrupted, stdout, stderr }) {\n let errorMessage = stderr.trim()\n if (interrupted) {\n if (stderr) errorMessage += EOL\n errorMessage += '<error>Command was aborted before completion</error>'\n }\n const hasBoth = stdout.trim() && errorMessage\n return `${stdout.trim()}${hasBoth ? '\\n' : ''}${errorMessage.trim()}`\n },\n async *call(\n { command, timeout = 120000, run_in_background = false },\n { abortController, readFileTimestamps },\n ) {\n // Handle background execution\n if (run_in_background) {\n const shellId = BackgroundShellManager.getInstance().create(\n command,\n getCwd(),\n )\n\n const data: Out = {\n stdout: `Background shell started with ID: ${shellId}`,\n stdoutLines: 1,\n stderr: '',\n stderrLines: 0,\n interrupted: false,\n shellId,\n }\n\n yield {\n type: 'result',\n resultForAssistant: `Started background shell: ${shellId}\\nCommand: ${command}`,\n data,\n }\n return\n }\n\n let stdout = ''\n let stderr = ''\n\n // \uD83D\uDD27 CRITICAL FIX: Track whether this tool invocation has completed\n // to prevent async callbacks from accessing stale context\n let isCompleted = false\n\n // \uD83D\uDD27 Check if already cancelled before starting execution\n if (abortController.signal.aborted) {\n const data: Out = {\n stdout: '',\n stdoutLines: 0,\n stderr: 'Command cancelled before execution',\n stderrLines: 1,\n interrupted: true,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n try {\n // Execute commands with streaming output\n let result: StreamingResult | null = null\n let streamedStdout = ''\n let streamedStderr = ''\n\n // Use streaming execution for real-time output\n const streamingGenerator = PersistentShell.getInstance().execStreaming(\n command,\n abortController.signal,\n timeout,\n )\n\n for await (const chunk of streamingGenerator) {\n if (chunk.type === 'chunk') {\n // Accumulate streamed output\n if (chunk.stdout) {\n streamedStdout += chunk.stdout\n }\n if (chunk.stderr) {\n streamedStderr += chunk.stderr\n }\n\n // Yield progress update for real-time UI feedback\n // Use StreamingProgressContent format for proper rendering\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Bash',\n stdout: streamedStdout,\n stderr: streamedStderr,\n isStreaming: true,\n },\n }\n } else if (chunk.type === 'result') {\n // Final result\n result = chunk\n }\n }\n\n // Use the final result (or construct from streamed data)\n if (!result) {\n result = {\n type: 'result',\n stdout: streamedStdout,\n stderr: streamedStderr,\n code: 0,\n interrupted: false,\n }\n }\n\n stdout += (result.stdout || '').trim() + EOL\n stderr += (result.stderr || '').trim() + EOL\n if (result.code !== 0) {\n stderr += `Exit code ${result.code}`\n }\n\n if (!isInDirectory(getCwd(), getOriginalCwd())) {\n // Shell directory is outside original working directory, reset it\n await PersistentShell.getInstance().setCwd(getOriginalCwd())\n stderr = `${stderr.trim()}${EOL}Shell cwd was reset to ${getOriginalCwd()}`\n }\n\n // Update read timestamps for any files referenced by the command\n // Don't block the main thread!\n // Skip this in tests because it makes fixtures non-deterministic (they might not always get written),\n // so will be missing in CI.\n if (process.env.NODE_ENV !== 'test') {\n getCommandFilePaths(command, stdout).then(filePaths => {\n // \uD83D\uDD27 CRITICAL FIX: Check if tool invocation has already completed\n // to prevent accessing potentially stale context\n if (isCompleted) {\n return // Tool has finished, don't access readFileTimestamps\n }\n\n for (const filePath of filePaths) {\n const fullFilePath = isAbsolute(filePath)\n ? filePath\n : resolve(getCwd(), filePath)\n\n // Try/catch in case the file doesn't exist (because Haiku didn't properly extract it)\n try {\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n } catch (e) {\n logError(e)\n }\n }\n })\n }\n\n const { totalLines: stdoutLines, truncatedContent: stdoutContent } =\n formatOutput(stdout.trim())\n const { totalLines: stderrLines, truncatedContent: stderrContent } =\n formatOutput(stderr.trim())\n\n const data: Out = {\n stdout: stdoutContent,\n stdoutLines,\n stderr: stderrContent,\n stderrLines,\n interrupted: result.interrupted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } catch (error) {\n // \uD83D\uDD27 Handle cancellation or other errors properly\n const isAborted = abortController.signal.aborted\n const errorMessage = isAborted\n ? 'Command was cancelled by user'\n : `Command failed: ${error instanceof Error ? error.message : String(error)}`\n\n const data: Out = {\n stdout: stdout.trim(),\n stdoutLines: stdout.split('\\n').length,\n stderr: errorMessage,\n stderrLines: 1,\n interrupted: isAborted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } finally {\n // \uD83D\uDD27 CRITICAL FIX: Mark tool invocation as completed\n // This prevents async callbacks (like getCommandFilePaths) from\n // accessing potentially stale context after this tool returns\n isCompleted = true\n }\n },\n} satisfies Tool<In, Out>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }