@within-7/minto 0.2.0 → 0.3.0

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 (254) 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 +7 -4
  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/toolInputExamples.js +84 -0
  140. package/dist/constants/toolInputExamples.js.map +7 -0
  141. package/dist/core/backupManager.js +321 -0
  142. package/dist/core/backupManager.js.map +7 -0
  143. package/dist/core/costTracker.js +9 -18
  144. package/dist/core/costTracker.js.map +2 -2
  145. package/dist/core/gitAutoCommit.js +287 -0
  146. package/dist/core/gitAutoCommit.js.map +7 -0
  147. package/dist/core/index.js +3 -0
  148. package/dist/core/index.js.map +2 -2
  149. package/dist/core/operationTracker.js +212 -0
  150. package/dist/core/operationTracker.js.map +7 -0
  151. package/dist/core/permissions/rules/allowedToolsRule.js +1 -1
  152. package/dist/core/permissions/rules/allowedToolsRule.js.map +2 -2
  153. package/dist/core/permissions/rules/autoEscalationRule.js +5 -0
  154. package/dist/core/permissions/rules/autoEscalationRule.js.map +2 -2
  155. package/dist/core/permissions/rules/projectBoundaryRule.js +5 -0
  156. package/dist/core/permissions/rules/projectBoundaryRule.js.map +2 -2
  157. package/dist/core/permissions/rules/sensitivePathsRule.js +5 -0
  158. package/dist/core/permissions/rules/sensitivePathsRule.js.map +2 -2
  159. package/dist/core/tokenStats.js +9 -0
  160. package/dist/core/tokenStats.js.map +7 -0
  161. package/dist/core/tokenStatsManager.js +331 -0
  162. package/dist/core/tokenStatsManager.js.map +7 -0
  163. package/dist/entrypoints/cli.js +115 -87
  164. package/dist/entrypoints/cli.js.map +2 -2
  165. package/dist/hooks/useAgentTokenStats.js +72 -0
  166. package/dist/hooks/useAgentTokenStats.js.map +7 -0
  167. package/dist/hooks/useAgentTranscripts.js +30 -6
  168. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  169. package/dist/hooks/useLogMessages.js +12 -1
  170. package/dist/hooks/useLogMessages.js.map +2 -2
  171. package/dist/i18n/locales/en.js +6 -5
  172. package/dist/i18n/locales/en.js.map +2 -2
  173. package/dist/i18n/locales/zh-CN.js +6 -5
  174. package/dist/i18n/locales/zh-CN.js.map +2 -2
  175. package/dist/i18n/types.js.map +1 -1
  176. package/dist/permissions.js +28 -1
  177. package/dist/permissions.js.map +2 -2
  178. package/dist/query.js +78 -4
  179. package/dist/query.js.map +3 -3
  180. package/dist/screens/REPL.js +23 -3
  181. package/dist/screens/REPL.js.map +2 -2
  182. package/dist/services/claude.js +54 -3
  183. package/dist/services/claude.js.map +2 -2
  184. package/dist/services/intelligentCompactor.js +1 -1
  185. package/dist/services/intelligentCompactor.js.map +2 -2
  186. package/dist/services/mcpClient.js +81 -25
  187. package/dist/services/mcpClient.js.map +2 -2
  188. package/dist/services/sandbox/filesystemBoundary.js +58 -17
  189. package/dist/services/sandbox/filesystemBoundary.js.map +2 -2
  190. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +3 -2
  191. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  192. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +2 -1
  193. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
  194. package/dist/tools/BashTool/BashTool.js +22 -3
  195. package/dist/tools/BashTool/BashTool.js.map +2 -2
  196. package/dist/tools/BashTool/prompt.js +178 -34
  197. package/dist/tools/BashTool/prompt.js.map +2 -2
  198. package/dist/tools/FileEditTool/prompt.js +6 -3
  199. package/dist/tools/FileEditTool/prompt.js.map +2 -2
  200. package/dist/tools/FileWriteTool/prompt.js +4 -2
  201. package/dist/tools/FileWriteTool/prompt.js.map +2 -2
  202. package/dist/tools/MultiEditTool/prompt.js +5 -3
  203. package/dist/tools/MultiEditTool/prompt.js.map +2 -2
  204. package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -1
  205. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  206. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +3 -2
  207. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  208. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +3 -2
  209. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  210. package/dist/tools/PlanModeTool/prompt.js +1 -1
  211. package/dist/tools/PlanModeTool/prompt.js.map +1 -1
  212. package/dist/tools/SkillTool/SkillTool.js +4 -3
  213. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  214. package/dist/tools/SkillTool/prompt.js +1 -1
  215. package/dist/tools/SkillTool/prompt.js.map +1 -1
  216. package/dist/tools/TaskOutputTool/TaskOutputTool.js +3 -2
  217. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +2 -2
  218. package/dist/tools/TaskTool/TaskTool.js +8 -0
  219. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  220. package/dist/utils/CircuitBreaker.js +242 -0
  221. package/dist/utils/CircuitBreaker.js.map +7 -0
  222. package/dist/utils/ask.js +2 -0
  223. package/dist/utils/ask.js.map +2 -2
  224. package/dist/utils/config.js +47 -5
  225. package/dist/utils/config.js.map +2 -2
  226. package/dist/utils/credentials/CredentialStore.js +1 -0
  227. package/dist/utils/credentials/CredentialStore.js.map +7 -0
  228. package/dist/utils/credentials/EncryptedFileStore.js +157 -0
  229. package/dist/utils/credentials/EncryptedFileStore.js.map +7 -0
  230. package/dist/utils/credentials/index.js +37 -0
  231. package/dist/utils/credentials/index.js.map +7 -0
  232. package/dist/utils/credentials/migration.js +82 -0
  233. package/dist/utils/credentials/migration.js.map +7 -0
  234. package/dist/utils/markdown.js +13 -1
  235. package/dist/utils/markdown.js.map +2 -2
  236. package/dist/utils/permissions/filesystem.js +5 -1
  237. package/dist/utils/permissions/filesystem.js.map +2 -2
  238. package/dist/utils/safePath.js +132 -0
  239. package/dist/utils/safePath.js.map +7 -0
  240. package/dist/utils/sensitiveFiles.js +125 -0
  241. package/dist/utils/sensitiveFiles.js.map +7 -0
  242. package/dist/utils/taskDisplayUtils.js +9 -9
  243. package/dist/utils/taskDisplayUtils.js.map +2 -2
  244. package/dist/utils/theme.js +6 -6
  245. package/dist/utils/theme.js.map +1 -1
  246. package/dist/utils/toolRiskClassification.js +207 -0
  247. package/dist/utils/toolRiskClassification.js.map +7 -0
  248. package/dist/utils/tooling/safeRender.js +5 -4
  249. package/dist/utils/tooling/safeRender.js.map +2 -2
  250. package/dist/version.js +2 -2
  251. package/dist/version.js.map +1 -1
  252. package/package.json +9 -7
  253. package/dist/hooks/useCancelRequest.js +0 -31
  254. package/dist/hooks/useCancelRequest.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/TaskTool/TaskTool.tsx"],
4
- "sourcesContent": ["import { TextBlock } from '@anthropic-ai/sdk/resources/index.mjs'\nimport chalk from 'chalk'\nimport { last, memoize } from 'lodash-es'\nimport { EOL } from 'os'\nimport React, { useState, useEffect } from 'react'\nimport { Box, Text } from 'ink'\nimport { z } from 'zod'\nimport { Tool, ValidationResult } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { getAgentPrompt } from '@constants/prompts'\nimport { getContext } from '@context'\nimport { hasPermissionsToUseTool } from '@permissions'\nimport { AssistantMessage, Message as MessageType, query } from '@query'\nimport { formatDuration, formatNumber } from '@utils/format'\nimport {\n getMessagesPath,\n getNextAvailableLogSidechainNumber,\n overwriteLog,\n logError,\n} from '@utils/log'\nimport { applyMarkdown } from '@utils/markdown'\nimport {\n createAssistantMessage,\n createUserMessage,\n getLastAssistantMessageId,\n INTERRUPT_MESSAGE,\n normalizeMessages,\n} from '@utils/messages'\nimport { getModelManager } from '@utils/model'\nimport { getMaxThinkingTokens } from '@utils/thinking'\nimport { getTheme } from '@utils/theme'\nimport { generateAgentId } from '@utils/agentStorage'\nimport { debug as debugLogger } from '@utils/debugLogger'\nimport { getTaskTools, getPrompt } from './prompt'\nimport { TOOL_NAME } from './constants'\nimport {\n getActiveAgents,\n getAgentByType,\n getAvailableAgentTypes,\n} from '@utils/agentLoader'\nimport {\n createAgentTranscript,\n getAgentTranscript,\n getResumableTranscript,\n appendMessageToTranscript,\n updateAgentTranscript,\n completeAgentTranscript,\n failAgentTranscript,\n interruptAgentTranscript,\n canResumeTranscript,\n registerToolUseAgent,\n type AgentTranscript,\n} from '@utils/agentTranscripts'\n// Background agent imports removed - run_in_background feature deprecated\nimport { UserFriendlyError, formatErrorForUser } from '@utils/userFriendlyError'\n\nconst inputSchema = z.object({\n description: z\n .string()\n .describe('A short (3-5 word) description of the task'),\n prompt: z.string().describe('The task for the agent to perform'),\n model_name: z\n .string()\n .optional()\n .describe(\n 'Optional: Specific model name to use for this task. If not provided, uses the default task model pointer.',\n ),\n subagent_type: z\n .string()\n .optional()\n .describe(\n 'The specialized agent type to use. MUST be an exact name from the available agents list in the tool description. Defaults to \"general-purpose\" if not specified.',\n ),\n resume: z\n .string()\n .optional()\n .describe(\n 'Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.',\n ),\n // run_in_background parameter removed - concurrent execution is automatic\n})\n\nexport const TaskTool = {\n async prompt({ safeMode }) {\n return await getPrompt(safeMode)\n },\n name: TOOL_NAME,\n async description() {\n // Dynamically load available agent types from plugins\n const availableTypes = await getAvailableAgentTypes()\n const typesList = availableTypes.join(', ')\n return `Launch a specialized agent to handle tasks autonomously. You MUST use an exact agent type name from this list: ${typesList}. Default to \"general-purpose\" if unsure.`\n },\n inputSchema,\n\n async *call(\n { description, prompt, model_name, subagent_type, resume },\n {\n abortController,\n options: { safeMode = false, forkNumber, messageLogName, verbose },\n readFileTimestamps,\n toolUseId,\n },\n ): AsyncGenerator<\n | { type: 'result'; data: TextBlock[]; resultForAssistant?: string }\n | {\n type: 'progress'\n content: any\n normalizedMessages?: any[]\n tools?: any[]\n },\n void,\n unknown\n > {\n const startTime = Date.now()\n\n // Default to general-purpose if no subagent_type specified\n const agentType = subagent_type || 'general-purpose'\n\n // Handle resume: check if we're resuming from a previous transcript\n let resumedTranscript: AgentTranscript | null = null\n let agentId: string\n\n if (resume) {\n resumedTranscript = getResumableTranscript(resume)\n if (!resumedTranscript) {\n yield {\n type: 'result',\n data: [\n {\n type: 'text',\n text: `Cannot resume agent '${resume}': transcript not found or not resumable (status must be 'running' or 'interrupted')`,\n },\n ] as TextBlock[],\n resultForAssistant: `Cannot resume agent '${resume}': transcript not found or not resumable`,\n }\n return\n }\n agentId = resume\n debugLogger.info('TASK_AGENT_RESUMING', {\n agentId,\n agentType: resumedTranscript.agentType,\n messageCount: resumedTranscript.messages.length,\n previousStatus: resumedTranscript.status,\n })\n } else {\n agentId = generateAgentId()\n }\n\n // Register toolUseId -> agentId mapping for UI association\n if (toolUseId) {\n registerToolUseAgent(toolUseId, agentId)\n }\n\n // \uD83D\uDD27 CRITICAL FIX: Track whether we've yielded a result to prevent UI stuck state\n let hasYieldedResult = false\n\n // Apply subagent configuration\n let effectivePrompt = prompt\n let effectiveModel = model_name || 'task'\n let toolFilter = null\n let temperature = undefined\n\n // Load agent configuration dynamically\n if (agentType) {\n const agentConfig = await getAgentByType(agentType)\n\n if (!agentConfig) {\n // If agent type not found, return helpful message instead of throwing\n const availableTypes = await getAvailableAgentTypes()\n const helpMessage = `Agent type '${agentType}' not found.\\n\\nAvailable agents:\\n${availableTypes.map(t => ` \u2022 ${t}`).join('\\n')}\\n\\nUse /agents command to manage agent configurations.`\n\n yield {\n type: 'result',\n data: [{ type: 'text', text: helpMessage }] as TextBlock[],\n resultForAssistant: helpMessage,\n }\n hasYieldedResult = true\n return\n }\n\n // Apply system prompt if configured\n if (agentConfig.systemPrompt) {\n effectivePrompt = `${agentConfig.systemPrompt}\\n\\n${prompt}`\n }\n\n // Apply model if not overridden by model_name parameter\n if (!model_name && agentConfig.model_name) {\n // Support inherit: keep pointer-based default\n if (agentConfig.model_name !== 'inherit') {\n effectiveModel = agentConfig.model_name as string\n }\n }\n\n // Store tool filter for later application\n toolFilter = agentConfig.tools\n\n // Note: temperature is not currently in our agent configs\n // but could be added in the future\n }\n\n // Initialize messages: either resume from transcript or start fresh\n const messages: MessageType[] = resumedTranscript\n ? [...resumedTranscript.messages]\n : [createUserMessage(effectivePrompt)]\n\n let tools = await getTaskTools(safeMode)\n\n // Apply tool filtering if specified by subagent config\n if (toolFilter) {\n // Back-compat: ['*'] means all tools\n const isAllArray =\n Array.isArray(toolFilter) &&\n toolFilter.length === 1 &&\n toolFilter[0] === '*'\n if (toolFilter === '*' || isAllArray) {\n // no-op, keep all tools\n } else if (Array.isArray(toolFilter)) {\n tools = tools.filter(tool => toolFilter.includes(tool.name))\n }\n }\n\n // Model already resolved in effectiveModel variable above\n const modelToUse = effectiveModel\n\n // Display initial task information with separate progress lines\n if (resumedTranscript) {\n yield {\n type: 'progress',\n content: createAssistantMessage(\n `Resuming agent: ${agentType} (${agentId})`,\n ),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n\n yield {\n type: 'progress',\n content: createAssistantMessage(\n `Restored ${resumedTranscript.messages.length} messages, ${resumedTranscript.toolUseCount} tool uses`,\n ),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n } else {\n yield {\n type: 'progress',\n content: createAssistantMessage(`Starting agent: ${agentType}`),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n }\n\n yield {\n type: 'progress',\n content: createAssistantMessage(`Using model: ${modelToUse}`),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n\n yield {\n type: 'progress',\n content: createAssistantMessage(`Task: ${description}`),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n\n yield {\n type: 'progress',\n content: createAssistantMessage(\n `Prompt: ${prompt.length > 150 ? prompt.substring(0, 150) + '...' : prompt}`,\n ),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n\n const [taskPrompt, context, maxThinkingTokens] = await Promise.all([\n getAgentPrompt(),\n getContext(),\n getMaxThinkingTokens(messages),\n ])\n\n // Inject model context to prevent self-referential expert consultations\n taskPrompt.push(\n `\\nIMPORTANT: You are currently running as ${modelToUse}. You do not need to consult ${modelToUse} via AskExpertModel since you ARE ${modelToUse}. Complete tasks directly using your capabilities.`,\n )\n\n // Initialize tool use count (restore from transcript if resuming)\n let toolUseCount = resumedTranscript?.toolUseCount || 0\n\n const getSidechainNumber = memoize(() =>\n getNextAvailableLogSidechainNumber(messageLogName, forkNumber),\n )\n\n // Create or update transcript for this agent execution\n if (!resumedTranscript) {\n createAgentTranscript({\n agentId,\n agentType,\n toolUseId, // Include toolUseId for event-driven UI updates\n description,\n prompt,\n model: modelToUse,\n forkNumber,\n messageLogName,\n messages: [...messages],\n toolUseCount: 0,\n })\n } else {\n // Update transcript status to running when resuming\n appendMessageToTranscript(\n agentId,\n createUserMessage(`[Resumed] ${prompt}`),\n )\n }\n\n // Build query options, adding temperature if specified\n const queryOptions = {\n safeMode,\n forkNumber,\n messageLogName,\n tools,\n commands: [],\n verbose,\n maxThinkingTokens,\n model: modelToUse,\n }\n\n // Add temperature if specified by subagent config\n if (temperature !== undefined) {\n queryOptions['temperature'] = temperature\n }\n\n // \uD83D\uDD27 CRITICAL FIX: Wrap entire query execution in try-catch-finally\n // This ensures we ALWAYS yield a result, even if query() throws an error\n try {\n for await (const message of query(\n messages,\n taskPrompt,\n context,\n hasPermissionsToUseTool,\n {\n abortController,\n options: queryOptions,\n messageId: getLastAssistantMessageId(messages),\n agentId,\n readFileTimestamps,\n setToolJSX: () => {}, // No-op implementation for TaskTool\n },\n )) {\n messages.push(message)\n\n // Persist message to transcript for resume capability\n appendMessageToTranscript(agentId, message)\n\n overwriteLog(\n getMessagesPath(messageLogName, forkNumber, getSidechainNumber()),\n messages.filter(_ => _.type !== 'progress'),\n )\n\n if (message.type !== 'assistant') {\n continue\n }\n\n // Real-time token usage update for running task display\n if (message.message.usage) {\n updateAgentTranscript(agentId, {\n tokenUsage: {\n inputTokens: message.message.usage.input_tokens,\n outputTokens: message.message.usage.output_tokens,\n cacheReadTokens: message.message.usage.cache_read_input_tokens,\n cacheCreationTokens:\n message.message.usage.cache_creation_input_tokens,\n },\n })\n }\n\n const normalizedMessages = normalizeMessages(messages)\n\n // Process tool uses and text content for better visibility\n for (const content of message.message.content) {\n if (\n content.type === 'text' &&\n content.text &&\n content.text !== INTERRUPT_MESSAGE\n ) {\n // Show agent's reasoning/responses\n const preview =\n content.text.length > 200\n ? content.text.substring(0, 200) + '...'\n : content.text\n yield {\n type: 'progress',\n content: createAssistantMessage(`${preview}`),\n normalizedMessages,\n tools,\n }\n } else if (content.type === 'tool_use') {\n toolUseCount++\n\n // Show which tool is being used with agent context\n const toolMessage = normalizedMessages.find(\n _ =>\n _.type === 'assistant' &&\n _.message.content[0]?.type === 'tool_use' &&\n _.message.content[0].id === content.id,\n ) as AssistantMessage\n\n if (toolMessage) {\n // Clone and modify the message to show agent context\n const modifiedMessage = {\n ...toolMessage,\n message: {\n ...toolMessage.message,\n content: toolMessage.message.content.map(c => {\n if (c.type === 'tool_use' && c.id === content.id) {\n // Add agent context to tool name display\n return {\n ...c,\n name: c.name, // Keep original name, UI will handle display\n }\n }\n return c\n }),\n },\n }\n\n yield {\n type: 'progress',\n content: modifiedMessage,\n normalizedMessages,\n tools,\n }\n }\n }\n }\n }\n\n const normalizedMessages = normalizeMessages(messages)\n const lastMessage = last(messages)\n if (lastMessage?.type !== 'assistant') {\n throw new Error('Last message was not an assistant message')\n }\n\n // Calculate token usage for transcript\n const tokenUsage = {\n inputTokens: lastMessage.message.usage.input_tokens,\n outputTokens: lastMessage.message.usage.output_tokens,\n cacheReadTokens: lastMessage.message.usage.cache_read_input_tokens,\n cacheCreationTokens:\n lastMessage.message.usage.cache_creation_input_tokens,\n }\n\n // Check for interrupt\n const isInterrupted = lastMessage.message.content.some(\n _ => _.type === 'text' && _.text === INTERRUPT_MESSAGE,\n )\n\n if (isInterrupted) {\n // Mark transcript as interrupted (can be resumed later)\n interruptAgentTranscript(agentId)\n debugLogger.info('TASK_AGENT_INTERRUPTED', { agentId, toolUseCount })\n } else {\n // Mark transcript as completed\n completeAgentTranscript(agentId, tokenUsage)\n debugLogger.info('TASK_AGENT_COMPLETED', { agentId, toolUseCount })\n\n const result = [\n toolUseCount === 1 ? '1 tool use' : `${toolUseCount} tool uses`,\n formatNumber(\n (lastMessage.message.usage.cache_creation_input_tokens ?? 0) +\n (lastMessage.message.usage.cache_read_input_tokens ?? 0) +\n lastMessage.message.usage.input_tokens +\n lastMessage.message.usage.output_tokens,\n ) + ' tokens',\n formatDuration(Date.now() - startTime),\n ]\n yield {\n type: 'progress',\n content: createAssistantMessage(`Done (${result.join(' \u00B7 ')})`),\n normalizedMessages,\n tools,\n }\n }\n\n // Output is an AssistantMessage, but since TaskTool is a tool, it needs\n // to serialize its response to UserMessage-compatible content.\n const data = lastMessage.message.content.filter(_ => _.type === 'text')\n\n // Include agentId in result for potential resume\n const resultWithAgentId = isInterrupted\n ? `[Interrupted - Agent ID: ${agentId}]\\n${this.renderResultForAssistant(data)}`\n : this.renderResultForAssistant(data)\n\n yield {\n type: 'result',\n data,\n resultForAssistant: resultWithAgentId,\n }\n hasYieldedResult = true\n } catch (error) {\n // \uD83D\uDD27 CRITICAL: On error, we MUST yield a result to clear UI state\n const errorMessage =\n error instanceof Error ? error.message : String(error)\n\n // \uD83D\uDD27 FIX: Check if this is an AbortError (ESC interrupt)\n // AbortError indicates user interrupted, should mark as 'interrupted' not 'failed'\n const isAbortError =\n error instanceof Error &&\n (error.name === 'AbortError' ||\n error.message.includes('aborted') ||\n error.message.includes('abort'))\n\n if (isAbortError) {\n // Mark transcript as interrupted (can be resumed later)\n interruptAgentTranscript(agentId)\n debugLogger.info('TASK_AGENT_INTERRUPTED_BY_ABORT', {\n agentId,\n toolUseCount,\n })\n\n yield {\n type: 'result',\n data: [\n {\n type: 'text',\n text: `Task [${agentType}] interrupted\\n[Agent ID: ${agentId}]`,\n },\n ] as TextBlock[],\n resultForAssistant: `Task [${agentType}] interrupted by user\\n[Agent ID: ${agentId} - can be resumed]`,\n }\n } else {\n // Convert to user-friendly error for better UX\n const friendlyError =\n error instanceof UserFriendlyError\n ? error\n : new UserFriendlyError(error, {\n operation: 'agent task',\n model: agentType,\n })\n const friendlyMessage = friendlyError.getUserMessage()\n\n logError(`TaskTool [${agentType}] error: ${errorMessage}`)\n\n // Mark transcript as failed with friendly message\n failAgentTranscript(agentId, friendlyMessage)\n debugLogger.error('TASK_AGENT_FAILED', {\n agentId,\n error: errorMessage,\n errorId: friendlyError.id,\n })\n\n yield {\n type: 'result',\n data: [\n {\n type: 'text',\n text: `${friendlyMessage}\\n[Agent ID: ${agentId}]`,\n },\n ] as TextBlock[],\n resultForAssistant: `Task [${agentType}] failed: ${errorMessage}\\n[Agent ID: ${agentId} - can be resumed]`,\n }\n }\n hasYieldedResult = true\n } finally {\n // \uD83D\uDD27 SAFETY NET: Ensure we always yield a result even in unexpected scenarios\n if (!hasYieldedResult) {\n // Mark as interrupted if we haven't yielded a result\n interruptAgentTranscript(agentId)\n\n yield {\n type: 'result',\n data: [\n {\n type: 'text',\n text: `Task [${agentType}] terminated unexpectedly\\n[Agent ID: ${agentId}]`,\n },\n ] as TextBlock[],\n resultForAssistant: `Task [${agentType}] terminated unexpectedly\\n[Agent ID: ${agentId} - can be resumed]`,\n }\n }\n }\n },\n\n isReadOnly() {\n return true // for now...\n },\n isConcurrencySafe() {\n return true // Task tool supports concurrent execution in official implementation\n },\n async validateInput(input, context) {\n if (!input.description || typeof input.description !== 'string') {\n return {\n result: false,\n message: 'Description is required and must be a string',\n }\n }\n if (!input.prompt || typeof input.prompt !== 'string') {\n return {\n result: false,\n message: 'Prompt is required and must be a string',\n }\n }\n\n // Model validation - similar to Edit tool error handling\n if (input.model_name) {\n const modelManager = getModelManager()\n const availableModels = modelManager.getAllAvailableModelNames()\n\n if (!availableModels.includes(input.model_name)) {\n return {\n result: false,\n message: `Model '${input.model_name}' does not exist. Available models: ${availableModels.join(', ')}`,\n meta: {\n model_name: input.model_name,\n availableModels,\n },\n }\n }\n }\n\n // Validate subagent_type if provided\n if (input.subagent_type) {\n const availableTypes = await getAvailableAgentTypes()\n if (!availableTypes.includes(input.subagent_type)) {\n return {\n result: false,\n message: `Agent type '${input.subagent_type}' does not exist. Available types: ${availableTypes.join(', ')}`,\n meta: {\n subagent_type: input.subagent_type,\n availableTypes,\n },\n }\n }\n }\n\n // Validate resume parameter if provided\n if (input.resume) {\n if (!canResumeTranscript(input.resume)) {\n return {\n result: false,\n message: `Cannot resume agent '${input.resume}': transcript not found or not resumable (status must be 'running' or 'interrupted')`,\n meta: {\n resume: input.resume,\n },\n }\n }\n }\n\n return { result: true }\n },\n async isEnabled() {\n return true\n },\n userFacingName(input?: any) {\n // Return agent name with proper prefix\n const agentType = input?.subagent_type || 'general-purpose'\n return `agent-${agentType}`\n },\n needsPermissions() {\n return false\n },\n renderResultForAssistant(data: TextBlock[]) {\n return data\n .map(block => (block.type === 'text' ? block.text : ''))\n .join('\\n')\n },\n renderToolUseMessage(\n { description, prompt, model_name, subagent_type },\n { verbose },\n ) {\n if (!description || !prompt) return null\n\n const modelManager = getModelManager()\n const defaultTaskModel = modelManager.getModelName('task')\n const actualModel = model_name || defaultTaskModel\n const agentType = subagent_type || 'general-purpose'\n const promptPreview =\n prompt.length > 80 ? prompt.substring(0, 80) + '...' : prompt\n\n const theme = getTheme()\n\n if (verbose) {\n return (\n <Box flexDirection=\"column\">\n <Text>\n [{agentType}] {actualModel}: {description}\n </Text>\n <Box\n paddingLeft={2}\n borderLeftStyle=\"single\"\n borderLeftColor={theme.secondaryBorder}\n >\n <Text color={theme.secondaryText}>{promptPreview}</Text>\n </Box>\n </Box>\n )\n }\n\n // Simple display: agent type, model and description\n return `[${agentType}] ${actualModel}: ${description}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(content, options?: { verbose?: boolean }) {\n const theme = getTheme()\n const verbose = options?.verbose ?? false\n\n // Guard against undefined or null content\n if (!content) {\n return (\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={theme.success}>Done</Text>\n </Box>\n )\n }\n\n if (Array.isArray(content)) {\n const textBlocks = content.filter(\n (block): block is { type: 'text'; text: string } =>\n block && block.type === 'text',\n )\n const totalLength = textBlocks.reduce(\n (sum, block) => sum + (block.text?.length || 0),\n 0,\n )\n // Use exact match for interrupt detection\n const isInterrupted = content.some(\n block =>\n block && block.type === 'text' && block.text === INTERRUPT_MESSAGE,\n )\n\n if (isInterrupted) {\n return (\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={theme.error}>Interrupted by user</Text>\n </Box>\n )\n }\n\n // Extract output content for display based on verbosity\n const outputText = textBlocks.map(b => b.text).join('\\n')\n\n // Determine how much content to show based on displayMode\n // minimal/compact (verbose=false): Just show \"Task completed (X characters)\"\n // detailed (verbose=true): Show preview of the actual output\n const getOutputPreview = () => {\n if (!verbose || !outputText) return null\n const maxLen = 500\n if (outputText.length <= maxLen) return outputText\n return outputText.substring(0, maxLen) + '...'\n }\n\n const outputPreview = getOutputPreview()\n\n return (\n <Box flexDirection=\"column\">\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={theme.success}>Done</Text>\n {textBlocks.length > 0 && verbose && (\n <Text color={theme.secondaryText}> ({totalLength} chars)</Text>\n )}\n </Box>\n </Box>\n {outputPreview && (\n <Box marginTop={1} paddingLeft={4}>\n <Text color={theme.mutedText}>{outputPreview}</Text>\n </Box>\n )}\n </Box>\n )\n }\n\n return (\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={theme.success}>Done</Text>\n </Box>\n )\n },\n} satisfies Tool<typeof inputSchema, TextBlock[]>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { TextBlock } from '@anthropic-ai/sdk/resources/index.mjs'\nimport chalk from 'chalk'\nimport { last, memoize } from 'lodash-es'\nimport { EOL } from 'os'\nimport React, { useState, useEffect } from 'react'\nimport { Box, Text } from 'ink'\nimport { z } from 'zod'\nimport { Tool, ValidationResult } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { getAgentPrompt } from '@constants/prompts'\nimport { getContext } from '@context'\nimport { hasPermissionsToUseTool } from '@permissions'\nimport { AssistantMessage, Message as MessageType, query } from '@query'\nimport { formatDuration, formatNumber } from '@utils/format'\nimport {\n getMessagesPath,\n getNextAvailableLogSidechainNumber,\n overwriteLog,\n logError,\n} from '@utils/log'\nimport { applyMarkdown } from '@utils/markdown'\nimport {\n createAssistantMessage,\n createUserMessage,\n getLastAssistantMessageId,\n INTERRUPT_MESSAGE,\n normalizeMessages,\n} from '@utils/messages'\nimport { getModelManager } from '@utils/model'\nimport { getMaxThinkingTokens } from '@utils/thinking'\nimport { getTheme } from '@utils/theme'\nimport { generateAgentId } from '@utils/agentStorage'\nimport { debug as debugLogger } from '@utils/debugLogger'\nimport { getTaskTools, getPrompt } from './prompt'\nimport { TOOL_NAME } from './constants'\nimport {\n getActiveAgents,\n getAgentByType,\n getAvailableAgentTypes,\n} from '@utils/agentLoader'\nimport {\n createAgentTranscript,\n getAgentTranscript,\n getResumableTranscript,\n appendMessageToTranscript,\n updateAgentTranscript,\n completeAgentTranscript,\n failAgentTranscript,\n interruptAgentTranscript,\n canResumeTranscript,\n registerToolUseAgent,\n type AgentTranscript,\n} from '@utils/agentTranscripts'\n// Background agent imports removed - run_in_background feature deprecated\nimport { UserFriendlyError, formatErrorForUser } from '@utils/userFriendlyError'\nimport {\n pushAgentContext,\n popAgentContext,\n cleanupAgentStreamingState,\n} from '@components/Spinner'\n\nconst inputSchema = z.object({\n description: z\n .string()\n .describe('A short (3-5 word) description of the task'),\n prompt: z.string().describe('The task for the agent to perform'),\n model_name: z\n .string()\n .optional()\n .describe(\n 'Optional: Specific model name to use for this task. If not provided, uses the default task model pointer.',\n ),\n subagent_type: z\n .string()\n .optional()\n .describe(\n 'The specialized agent type to use. MUST be an exact name from the available agents list in the tool description. Defaults to \"general-purpose\" if not specified.',\n ),\n resume: z\n .string()\n .optional()\n .describe(\n 'Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.',\n ),\n // run_in_background parameter removed - concurrent execution is automatic\n})\n\nexport const TaskTool = {\n async prompt({ safeMode }) {\n return await getPrompt(safeMode)\n },\n name: TOOL_NAME,\n async description() {\n // Dynamically load available agent types from plugins\n const availableTypes = await getAvailableAgentTypes()\n const typesList = availableTypes.join(', ')\n return `Launch a specialized agent to handle tasks autonomously. You MUST use an exact agent type name from this list: ${typesList}. Default to \"general-purpose\" if unsure.`\n },\n inputSchema,\n\n async *call(\n { description, prompt, model_name, subagent_type, resume },\n {\n abortController,\n options: { safeMode = false, forkNumber, messageLogName, verbose },\n readFileTimestamps,\n toolUseId,\n },\n ): AsyncGenerator<\n | { type: 'result'; data: TextBlock[]; resultForAssistant?: string }\n | {\n type: 'progress'\n content: any\n normalizedMessages?: any[]\n tools?: any[]\n },\n void,\n unknown\n > {\n const startTime = Date.now()\n\n // Default to general-purpose if no subagent_type specified\n const agentType = subagent_type || 'general-purpose'\n\n // Handle resume: check if we're resuming from a previous transcript\n let resumedTranscript: AgentTranscript | null = null\n let agentId: string\n\n if (resume) {\n resumedTranscript = getResumableTranscript(resume)\n if (!resumedTranscript) {\n yield {\n type: 'result',\n data: [\n {\n type: 'text',\n text: `Cannot resume agent '${resume}': transcript not found or not resumable (status must be 'running' or 'interrupted')`,\n },\n ] as TextBlock[],\n resultForAssistant: `Cannot resume agent '${resume}': transcript not found or not resumable`,\n }\n return\n }\n agentId = resume\n debugLogger.info('TASK_AGENT_RESUMING', {\n agentId,\n agentType: resumedTranscript.agentType,\n messageCount: resumedTranscript.messages.length,\n previousStatus: resumedTranscript.status,\n })\n } else {\n agentId = generateAgentId()\n }\n\n // Register toolUseId -> agentId mapping for UI association\n if (toolUseId) {\n registerToolUseAgent(toolUseId, agentId)\n }\n\n // \uD83D\uDD27 CRITICAL FIX: Track whether we've yielded a result to prevent UI stuck state\n let hasYieldedResult = false\n\n // Apply subagent configuration\n let effectivePrompt = prompt\n let effectiveModel = model_name || 'task'\n let toolFilter = null\n let temperature = undefined\n\n // Load agent configuration dynamically\n if (agentType) {\n const agentConfig = await getAgentByType(agentType)\n\n if (!agentConfig) {\n // If agent type not found, return helpful message instead of throwing\n const availableTypes = await getAvailableAgentTypes()\n const helpMessage = `Agent type '${agentType}' not found.\\n\\nAvailable agents:\\n${availableTypes.map(t => ` \u2022 ${t}`).join('\\n')}\\n\\nUse /agents command to manage agent configurations.`\n\n yield {\n type: 'result',\n data: [{ type: 'text', text: helpMessage }] as TextBlock[],\n resultForAssistant: helpMessage,\n }\n hasYieldedResult = true\n return\n }\n\n // Apply system prompt if configured\n if (agentConfig.systemPrompt) {\n effectivePrompt = `${agentConfig.systemPrompt}\\n\\n${prompt}`\n }\n\n // Apply model if not overridden by model_name parameter\n if (!model_name && agentConfig.model_name) {\n // Support inherit: keep pointer-based default\n if (agentConfig.model_name !== 'inherit') {\n effectiveModel = agentConfig.model_name as string\n }\n }\n\n // Store tool filter for later application\n toolFilter = agentConfig.tools\n\n // Note: temperature is not currently in our agent configs\n // but could be added in the future\n }\n\n // Initialize messages: either resume from transcript or start fresh\n const messages: MessageType[] = resumedTranscript\n ? [...resumedTranscript.messages]\n : [createUserMessage(effectivePrompt)]\n\n let tools = await getTaskTools(safeMode)\n\n // Apply tool filtering if specified by subagent config\n if (toolFilter) {\n // Back-compat: ['*'] means all tools\n const isAllArray =\n Array.isArray(toolFilter) &&\n toolFilter.length === 1 &&\n toolFilter[0] === '*'\n if (toolFilter === '*' || isAllArray) {\n // no-op, keep all tools\n } else if (Array.isArray(toolFilter)) {\n tools = tools.filter(tool => toolFilter.includes(tool.name))\n }\n }\n\n // Model already resolved in effectiveModel variable above\n const modelToUse = effectiveModel\n\n // Display initial task information with separate progress lines\n if (resumedTranscript) {\n yield {\n type: 'progress',\n content: createAssistantMessage(\n `Resuming agent: ${agentType} (${agentId})`,\n ),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n\n yield {\n type: 'progress',\n content: createAssistantMessage(\n `Restored ${resumedTranscript.messages.length} messages, ${resumedTranscript.toolUseCount} tool uses`,\n ),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n } else {\n yield {\n type: 'progress',\n content: createAssistantMessage(`Starting agent: ${agentType}`),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n }\n\n yield {\n type: 'progress',\n content: createAssistantMessage(`Using model: ${modelToUse}`),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n\n yield {\n type: 'progress',\n content: createAssistantMessage(`Task: ${description}`),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n\n yield {\n type: 'progress',\n content: createAssistantMessage(\n `Prompt: ${prompt.length > 150 ? prompt.substring(0, 150) + '...' : prompt}`,\n ),\n normalizedMessages: normalizeMessages(messages),\n tools,\n }\n\n const [taskPrompt, context, maxThinkingTokens] = await Promise.all([\n getAgentPrompt(),\n getContext(),\n getMaxThinkingTokens(messages),\n ])\n\n // Inject model context to prevent self-referential expert consultations\n taskPrompt.push(\n `\\nIMPORTANT: You are currently running as ${modelToUse}. You do not need to consult ${modelToUse} via AskExpertModel since you ARE ${modelToUse}. Complete tasks directly using your capabilities.`,\n )\n\n // Initialize tool use count (restore from transcript if resuming)\n let toolUseCount = resumedTranscript?.toolUseCount || 0\n\n const getSidechainNumber = memoize(() =>\n getNextAvailableLogSidechainNumber(messageLogName, forkNumber),\n )\n\n // Create or update transcript for this agent execution\n if (!resumedTranscript) {\n createAgentTranscript({\n agentId,\n agentType,\n toolUseId, // Include toolUseId for event-driven UI updates\n description,\n prompt,\n model: modelToUse,\n forkNumber,\n messageLogName,\n messages: [...messages],\n toolUseCount: 0,\n })\n } else {\n // Update transcript status to running when resuming\n appendMessageToTranscript(\n agentId,\n createUserMessage(`[Resumed] ${prompt}`),\n )\n }\n\n // Build query options, adding temperature if specified\n const queryOptions = {\n safeMode,\n forkNumber,\n messageLogName,\n tools,\n commands: [],\n verbose,\n maxThinkingTokens,\n model: modelToUse,\n }\n\n // Add temperature if specified by subagent config\n if (temperature !== undefined) {\n queryOptions['temperature'] = temperature\n }\n\n // \uD83D\uDD27 CRITICAL FIX: Wrap entire query execution in try-catch-finally\n // This ensures we ALWAYS yield a result, even if query() throws an error\n\n // Push agent context so streaming state is isolated for this subagent\n pushAgentContext(agentId)\n\n try {\n for await (const message of query(\n messages,\n taskPrompt,\n context,\n hasPermissionsToUseTool,\n {\n abortController,\n options: queryOptions,\n messageId: getLastAssistantMessageId(messages),\n agentId,\n readFileTimestamps,\n setToolJSX: () => {}, // No-op implementation for TaskTool\n },\n )) {\n messages.push(message)\n\n // Persist message to transcript for resume capability\n appendMessageToTranscript(agentId, message)\n\n overwriteLog(\n getMessagesPath(messageLogName, forkNumber, getSidechainNumber()),\n messages.filter(_ => _.type !== 'progress'),\n )\n\n if (message.type !== 'assistant') {\n continue\n }\n\n // Real-time token usage update for running task display\n if (message.message.usage) {\n updateAgentTranscript(agentId, {\n tokenUsage: {\n inputTokens: message.message.usage.input_tokens,\n outputTokens: message.message.usage.output_tokens,\n cacheReadTokens: message.message.usage.cache_read_input_tokens,\n cacheCreationTokens:\n message.message.usage.cache_creation_input_tokens,\n },\n })\n }\n\n const normalizedMessages = normalizeMessages(messages)\n\n // Process tool uses and text content for better visibility\n for (const content of message.message.content) {\n if (\n content.type === 'text' &&\n content.text &&\n content.text !== INTERRUPT_MESSAGE\n ) {\n // Show agent's reasoning/responses\n const preview =\n content.text.length > 200\n ? content.text.substring(0, 200) + '...'\n : content.text\n yield {\n type: 'progress',\n content: createAssistantMessage(`${preview}`),\n normalizedMessages,\n tools,\n }\n } else if (content.type === 'tool_use') {\n toolUseCount++\n\n // Show which tool is being used with agent context\n const toolMessage = normalizedMessages.find(\n _ =>\n _.type === 'assistant' &&\n _.message.content[0]?.type === 'tool_use' &&\n _.message.content[0].id === content.id,\n ) as AssistantMessage\n\n if (toolMessage) {\n // Clone and modify the message to show agent context\n const modifiedMessage = {\n ...toolMessage,\n message: {\n ...toolMessage.message,\n content: toolMessage.message.content.map(c => {\n if (c.type === 'tool_use' && c.id === content.id) {\n // Add agent context to tool name display\n return {\n ...c,\n name: c.name, // Keep original name, UI will handle display\n }\n }\n return c\n }),\n },\n }\n\n yield {\n type: 'progress',\n content: modifiedMessage,\n normalizedMessages,\n tools,\n }\n }\n }\n }\n }\n\n const normalizedMessages = normalizeMessages(messages)\n const lastMessage = last(messages)\n if (lastMessage?.type !== 'assistant') {\n throw new Error('Last message was not an assistant message')\n }\n\n // Calculate token usage for transcript\n const tokenUsage = {\n inputTokens: lastMessage.message.usage.input_tokens,\n outputTokens: lastMessage.message.usage.output_tokens,\n cacheReadTokens: lastMessage.message.usage.cache_read_input_tokens,\n cacheCreationTokens:\n lastMessage.message.usage.cache_creation_input_tokens,\n }\n\n // Check for interrupt\n const isInterrupted = lastMessage.message.content.some(\n _ => _.type === 'text' && _.text === INTERRUPT_MESSAGE,\n )\n\n if (isInterrupted) {\n // Mark transcript as interrupted (can be resumed later)\n interruptAgentTranscript(agentId)\n debugLogger.info('TASK_AGENT_INTERRUPTED', { agentId, toolUseCount })\n } else {\n // Mark transcript as completed\n completeAgentTranscript(agentId, tokenUsage)\n debugLogger.info('TASK_AGENT_COMPLETED', { agentId, toolUseCount })\n\n const result = [\n toolUseCount === 1 ? '1 tool use' : `${toolUseCount} tool uses`,\n formatNumber(\n (lastMessage.message.usage.cache_creation_input_tokens ?? 0) +\n (lastMessage.message.usage.cache_read_input_tokens ?? 0) +\n lastMessage.message.usage.input_tokens +\n lastMessage.message.usage.output_tokens,\n ) + ' tokens',\n formatDuration(Date.now() - startTime),\n ]\n yield {\n type: 'progress',\n content: createAssistantMessage(`Done (${result.join(' \u00B7 ')})`),\n normalizedMessages,\n tools,\n }\n }\n\n // Output is an AssistantMessage, but since TaskTool is a tool, it needs\n // to serialize its response to UserMessage-compatible content.\n const data = lastMessage.message.content.filter(_ => _.type === 'text')\n\n // Include agentId in result for potential resume\n const resultWithAgentId = isInterrupted\n ? `[Interrupted - Agent ID: ${agentId}]\\n${this.renderResultForAssistant(data)}`\n : this.renderResultForAssistant(data)\n\n yield {\n type: 'result',\n data,\n resultForAssistant: resultWithAgentId,\n }\n hasYieldedResult = true\n } catch (error) {\n // \uD83D\uDD27 CRITICAL: On error, we MUST yield a result to clear UI state\n const errorMessage =\n error instanceof Error ? error.message : String(error)\n\n // \uD83D\uDD27 FIX: Check if this is an AbortError (ESC interrupt)\n // AbortError indicates user interrupted, should mark as 'interrupted' not 'failed'\n const isAbortError =\n error instanceof Error &&\n (error.name === 'AbortError' ||\n error.message.includes('aborted') ||\n error.message.includes('abort'))\n\n if (isAbortError) {\n // Mark transcript as interrupted (can be resumed later)\n interruptAgentTranscript(agentId)\n debugLogger.info('TASK_AGENT_INTERRUPTED_BY_ABORT', {\n agentId,\n toolUseCount,\n })\n\n yield {\n type: 'result',\n data: [\n {\n type: 'text',\n text: `Task [${agentType}] interrupted\\n[Agent ID: ${agentId}]`,\n },\n ] as TextBlock[],\n resultForAssistant: `Task [${agentType}] interrupted by user\\n[Agent ID: ${agentId} - can be resumed]`,\n }\n } else {\n // Convert to user-friendly error for better UX\n const friendlyError =\n error instanceof UserFriendlyError\n ? error\n : new UserFriendlyError(error, {\n operation: 'agent task',\n model: agentType,\n })\n const friendlyMessage = friendlyError.getUserMessage()\n\n logError(`TaskTool [${agentType}] error: ${errorMessage}`)\n\n // Mark transcript as failed with friendly message\n failAgentTranscript(agentId, friendlyMessage)\n debugLogger.error('TASK_AGENT_FAILED', {\n agentId,\n error: errorMessage,\n errorId: friendlyError.id,\n })\n\n yield {\n type: 'result',\n data: [\n {\n type: 'text',\n text: `${friendlyMessage}\\n[Agent ID: ${agentId}]`,\n },\n ] as TextBlock[],\n resultForAssistant: `Task [${agentType}] failed: ${errorMessage}\\n[Agent ID: ${agentId} - can be resumed]`,\n }\n }\n hasYieldedResult = true\n } finally {\n // Pop agent context and clean up streaming state\n popAgentContext()\n cleanupAgentStreamingState(agentId)\n\n // \uD83D\uDD27 SAFETY NET: Ensure we always yield a result even in unexpected scenarios\n if (!hasYieldedResult) {\n // Mark as interrupted if we haven't yielded a result\n interruptAgentTranscript(agentId)\n\n yield {\n type: 'result',\n data: [\n {\n type: 'text',\n text: `Task [${agentType}] terminated unexpectedly\\n[Agent ID: ${agentId}]`,\n },\n ] as TextBlock[],\n resultForAssistant: `Task [${agentType}] terminated unexpectedly\\n[Agent ID: ${agentId} - can be resumed]`,\n }\n }\n }\n },\n\n isReadOnly() {\n return true // for now...\n },\n isConcurrencySafe() {\n return true // Task tool supports concurrent execution in official implementation\n },\n async validateInput(input, context) {\n if (!input.description || typeof input.description !== 'string') {\n return {\n result: false,\n message: 'Description is required and must be a string',\n }\n }\n if (!input.prompt || typeof input.prompt !== 'string') {\n return {\n result: false,\n message: 'Prompt is required and must be a string',\n }\n }\n\n // Model validation - similar to Edit tool error handling\n if (input.model_name) {\n const modelManager = getModelManager()\n const availableModels = modelManager.getAllAvailableModelNames()\n\n if (!availableModels.includes(input.model_name)) {\n return {\n result: false,\n message: `Model '${input.model_name}' does not exist. Available models: ${availableModels.join(', ')}`,\n meta: {\n model_name: input.model_name,\n availableModels,\n },\n }\n }\n }\n\n // Validate subagent_type if provided\n if (input.subagent_type) {\n const availableTypes = await getAvailableAgentTypes()\n if (!availableTypes.includes(input.subagent_type)) {\n return {\n result: false,\n message: `Agent type '${input.subagent_type}' does not exist. Available types: ${availableTypes.join(', ')}`,\n meta: {\n subagent_type: input.subagent_type,\n availableTypes,\n },\n }\n }\n }\n\n // Validate resume parameter if provided\n if (input.resume) {\n if (!canResumeTranscript(input.resume)) {\n return {\n result: false,\n message: `Cannot resume agent '${input.resume}': transcript not found or not resumable (status must be 'running' or 'interrupted')`,\n meta: {\n resume: input.resume,\n },\n }\n }\n }\n\n return { result: true }\n },\n async isEnabled() {\n return true\n },\n userFacingName(input?: any) {\n // Return agent name with proper prefix\n const agentType = input?.subagent_type || 'general-purpose'\n return `agent-${agentType}`\n },\n needsPermissions() {\n return false\n },\n renderResultForAssistant(data: TextBlock[]) {\n return data\n .map(block => (block.type === 'text' ? block.text : ''))\n .join('\\n')\n },\n renderToolUseMessage(\n { description, prompt, model_name, subagent_type },\n { verbose },\n ) {\n if (!description || !prompt) return null\n\n const modelManager = getModelManager()\n const defaultTaskModel = modelManager.getModelName('task')\n const actualModel = model_name || defaultTaskModel\n const agentType = subagent_type || 'general-purpose'\n const promptPreview =\n prompt.length > 80 ? prompt.substring(0, 80) + '...' : prompt\n\n const theme = getTheme()\n\n if (verbose) {\n return (\n <Box flexDirection=\"column\">\n <Text>\n [{agentType}] {actualModel}: {description}\n </Text>\n <Box\n paddingLeft={2}\n borderLeftStyle=\"single\"\n borderLeftColor={theme.secondaryBorder}\n >\n <Text color={theme.secondaryText}>{promptPreview}</Text>\n </Box>\n </Box>\n )\n }\n\n // Simple display: agent type, model and description\n return `[${agentType}] ${actualModel}: ${description}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(content, options?: { verbose?: boolean }) {\n const theme = getTheme()\n const verbose = options?.verbose ?? false\n\n // Guard against undefined or null content\n if (!content) {\n return (\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={theme.success}>Done</Text>\n </Box>\n )\n }\n\n if (Array.isArray(content)) {\n const textBlocks = content.filter(\n (block): block is { type: 'text'; text: string } =>\n block && block.type === 'text',\n )\n const totalLength = textBlocks.reduce(\n (sum, block) => sum + (block.text?.length || 0),\n 0,\n )\n // Use exact match for interrupt detection\n const isInterrupted = content.some(\n block =>\n block && block.type === 'text' && block.text === INTERRUPT_MESSAGE,\n )\n\n if (isInterrupted) {\n return (\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={theme.error}>Interrupted by user</Text>\n </Box>\n )\n }\n\n // Extract output content for display based on verbosity\n const outputText = textBlocks.map(b => b.text).join('\\n')\n\n // Determine how much content to show based on displayMode\n // minimal/compact (verbose=false): Just show \"Task completed (X characters)\"\n // detailed (verbose=true): Show preview of the actual output\n const getOutputPreview = () => {\n if (!verbose || !outputText) return null\n const maxLen = 500\n if (outputText.length <= maxLen) return outputText\n return outputText.substring(0, maxLen) + '...'\n }\n\n const outputPreview = getOutputPreview()\n\n return (\n <Box flexDirection=\"column\">\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={theme.success}>Done</Text>\n {textBlocks.length > 0 && verbose && (\n <Text color={theme.secondaryText}> ({totalLength} chars)</Text>\n )}\n </Box>\n </Box>\n {outputPreview && (\n <Box marginTop={1} paddingLeft={4}>\n <Text color={theme.mutedText}>{outputPreview}</Text>\n </Box>\n )}\n </Box>\n )\n }\n\n return (\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={theme.success}>Done</Text>\n </Box>\n )\n },\n} satisfies Tool<typeof inputSchema, TextBlock[]>\n"],
5
+ "mappings": "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",
6
6
  "names": ["normalizedMessages"]
7
7
  }
@@ -0,0 +1,242 @@
1
+ class CircuitOpenError extends Error {
2
+ constructor(remainingCooldown, serverName) {
3
+ const message = serverName ? `Circuit breaker for "${serverName}" is open. Retry in ${Math.ceil(remainingCooldown / 1e3)}s` : `Circuit breaker is open. Retry in ${Math.ceil(remainingCooldown / 1e3)}s`;
4
+ super(message);
5
+ this.remainingCooldown = remainingCooldown;
6
+ this.name = "CircuitOpenError";
7
+ }
8
+ }
9
+ class CircuitBreaker {
10
+ state = "closed";
11
+ failureCount = 0;
12
+ successCount = 0;
13
+ lastFailureTime = null;
14
+ config;
15
+ listeners = /* @__PURE__ */ new Set();
16
+ name;
17
+ totalRequests = 0;
18
+ totalFailures = 0;
19
+ constructor(name = "default", config) {
20
+ this.name = name;
21
+ this.config = {
22
+ failureThreshold: config?.failureThreshold ?? 5,
23
+ successThreshold: config?.successThreshold ?? 2,
24
+ openTimeout: config?.openTimeout ?? 3e4,
25
+ halfOpenTimeout: config?.halfOpenTimeout ?? 3e4
26
+ };
27
+ }
28
+ /**
29
+ * Execute an operation through the circuit breaker
30
+ * @throws {CircuitOpenError} If circuit is open
31
+ * @throws {Error} If operation fails
32
+ */
33
+ async execute(operation) {
34
+ this.totalRequests++;
35
+ if (this.state === "open") {
36
+ const cooldownRemaining = this.getRemainingCooldown();
37
+ if (cooldownRemaining <= 0) {
38
+ this.transition("half-open");
39
+ } else {
40
+ const error = new CircuitOpenError(cooldownRemaining, this.name);
41
+ this.notifyListeners("failure", { error });
42
+ throw error;
43
+ }
44
+ }
45
+ try {
46
+ const result = await operation();
47
+ this.onSuccess();
48
+ return result;
49
+ } catch (error) {
50
+ this.onFailure(error instanceof Error ? error : new Error(String(error)));
51
+ throw error;
52
+ }
53
+ }
54
+ /**
55
+ * Execute with timeout protection
56
+ * @throws {CircuitOpenError} If circuit is open
57
+ * @throws {Error} If operation times out or fails
58
+ */
59
+ async executeWithTimeout(operation, timeoutMs) {
60
+ const timeout = timeoutMs ?? this.config.halfOpenTimeout;
61
+ if (!timeout) {
62
+ return this.execute(operation);
63
+ }
64
+ return Promise.race([
65
+ this.execute(operation),
66
+ new Promise(
67
+ (_, reject) => setTimeout(
68
+ () => reject(new Error(`Operation timeout after ${timeout}ms`)),
69
+ timeout
70
+ )
71
+ )
72
+ ]);
73
+ }
74
+ /**
75
+ * Get current circuit state
76
+ */
77
+ getState() {
78
+ return this.state;
79
+ }
80
+ /**
81
+ * Get detailed statistics
82
+ */
83
+ getStats() {
84
+ return {
85
+ state: this.state,
86
+ failureCount: this.failureCount,
87
+ successCount: this.successCount,
88
+ lastFailureTime: this.lastFailureTime,
89
+ totalRequests: this.totalRequests,
90
+ totalFailures: this.totalFailures
91
+ };
92
+ }
93
+ /**
94
+ * Get remaining cooldown time in milliseconds
95
+ */
96
+ getRemainingCooldown() {
97
+ if (!this.lastFailureTime) {
98
+ return 0;
99
+ }
100
+ return Math.max(
101
+ 0,
102
+ this.config.openTimeout - (Date.now() - this.lastFailureTime)
103
+ );
104
+ }
105
+ /**
106
+ * Register a listener for state changes and events
107
+ */
108
+ on(listener) {
109
+ this.listeners.add(listener);
110
+ return () => {
111
+ this.listeners.delete(listener);
112
+ };
113
+ }
114
+ /**
115
+ * Reset the circuit breaker to CLOSED state
116
+ */
117
+ reset() {
118
+ const previousState = this.state;
119
+ this.state = "closed";
120
+ this.failureCount = 0;
121
+ this.successCount = 0;
122
+ this.lastFailureTime = null;
123
+ this.totalRequests = 0;
124
+ this.totalFailures = 0;
125
+ this.notifyListeners("state-change", {
126
+ state: this.state,
127
+ previousState
128
+ });
129
+ }
130
+ /**
131
+ * Manually open the circuit (for testing or explicit fault injection)
132
+ */
133
+ forceOpen() {
134
+ this.transition("open");
135
+ }
136
+ /**
137
+ * Manually close the circuit (for recovery testing)
138
+ */
139
+ forceClosed() {
140
+ this.transition("closed");
141
+ }
142
+ /**
143
+ * Manually transition to half-open state (for testing)
144
+ */
145
+ forceHalfOpen() {
146
+ this.transition("half-open");
147
+ }
148
+ // Private methods
149
+ transition(newState) {
150
+ if (newState === this.state) {
151
+ return;
152
+ }
153
+ const previousState = this.state;
154
+ this.state = newState;
155
+ if (newState === "closed") {
156
+ this.failureCount = 0;
157
+ this.successCount = 0;
158
+ } else if (newState === "half-open") {
159
+ this.successCount = 0;
160
+ this.failureCount = 0;
161
+ }
162
+ this.notifyListeners("state-change", {
163
+ state: newState,
164
+ previousState
165
+ });
166
+ }
167
+ onSuccess() {
168
+ if (this.state === "half-open") {
169
+ this.successCount++;
170
+ if (this.successCount >= this.config.successThreshold) {
171
+ this.transition("closed");
172
+ }
173
+ } else if (this.state === "closed") {
174
+ this.failureCount = 0;
175
+ }
176
+ this.notifyListeners("success", {});
177
+ }
178
+ onFailure(error) {
179
+ this.failureCount++;
180
+ this.totalFailures++;
181
+ this.lastFailureTime = Date.now();
182
+ if (this.state === "closed") {
183
+ if (this.failureCount >= this.config.failureThreshold) {
184
+ this.transition("open");
185
+ }
186
+ } else if (this.state === "half-open") {
187
+ this.transition("open");
188
+ }
189
+ this.notifyListeners("failure", { error });
190
+ }
191
+ notifyListeners(event, details) {
192
+ for (const listener of this.listeners) {
193
+ try {
194
+ listener(event, details);
195
+ } catch {
196
+ }
197
+ }
198
+ }
199
+ }
200
+ class CircuitBreakerRegistry {
201
+ breakers = /* @__PURE__ */ new Map();
202
+ defaultConfig;
203
+ constructor(defaultConfig) {
204
+ this.defaultConfig = defaultConfig;
205
+ }
206
+ /**
207
+ * Get or create a circuit breaker by name
208
+ */
209
+ getOrCreate(name, config) {
210
+ if (!this.breakers.has(name)) {
211
+ const mergedConfig = { ...this.defaultConfig, ...config };
212
+ this.breakers.set(name, new CircuitBreaker(name, mergedConfig));
213
+ }
214
+ return this.breakers.get(name);
215
+ }
216
+ /**
217
+ * Get all registered circuit breakers
218
+ */
219
+ getAll() {
220
+ return new Map(this.breakers);
221
+ }
222
+ /**
223
+ * Reset all circuit breakers
224
+ */
225
+ resetAll() {
226
+ for (const breaker of this.breakers.values()) {
227
+ breaker.reset();
228
+ }
229
+ }
230
+ /**
231
+ * Clear all circuit breakers
232
+ */
233
+ clear() {
234
+ this.breakers.clear();
235
+ }
236
+ }
237
+ export {
238
+ CircuitBreaker,
239
+ CircuitBreakerRegistry,
240
+ CircuitOpenError
241
+ };
242
+ //# sourceMappingURL=CircuitBreaker.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/CircuitBreaker.ts"],
4
+ "sourcesContent": ["/**\n * Circuit Breaker pattern implementation for handling failing services gracefully\n *\n * States:\n * - CLOSED: Service is healthy, requests pass through normally\n * - OPEN: Service is unhealthy, requests fail immediately\n * - HALF_OPEN: Testing if service has recovered, limited requests allowed\n *\n * Transitions:\n * - CLOSED \u2192 OPEN: When failure count exceeds threshold\n * - OPEN \u2192 HALF_OPEN: After cooldown timeout\n * - HALF_OPEN \u2192 CLOSED: When success count exceeds threshold\n * - HALF_OPEN \u2192 OPEN: When a request fails\n */\n\nexport interface CircuitBreakerConfig {\n /** Number of failures that trigger opening the circuit (default: 5) */\n failureThreshold: number\n /** Number of successful requests needed to close circuit from HALF_OPEN (default: 2) */\n successThreshold: number\n /** Time in ms before transitioning from OPEN to HALF_OPEN (default: 30000) */\n openTimeout: number\n /** Optional timeout in ms for operations (default: none) */\n halfOpenTimeout?: number\n}\n\nexport class CircuitOpenError extends Error {\n constructor(\n public readonly remainingCooldown: number,\n serverName?: string,\n ) {\n const message = serverName\n ? `Circuit breaker for \"${serverName}\" is open. Retry in ${Math.ceil(remainingCooldown / 1000)}s`\n : `Circuit breaker is open. Retry in ${Math.ceil(remainingCooldown / 1000)}s`\n super(message)\n this.name = 'CircuitOpenError'\n }\n}\n\nexport type CircuitBreakerState = 'closed' | 'open' | 'half-open'\n\nexport interface CircuitBreakerStats {\n state: CircuitBreakerState\n failureCount: number\n successCount: number\n lastFailureTime: number | null\n totalRequests: number\n totalFailures: number\n}\n\nexport type CircuitBreakerListener = (\n event: 'state-change' | 'failure' | 'success',\n details: {\n state?: CircuitBreakerState\n previousState?: CircuitBreakerState\n error?: Error\n },\n) => void\n\n/**\n * Circuit Breaker implementation for fault tolerance\n *\n * Prevents cascading failures by failing fast when a service is unhealthy,\n * while automatically attempting recovery when the service potentially recovers.\n */\nexport class CircuitBreaker<T = unknown> {\n private state: CircuitBreakerState = 'closed'\n private failureCount: number = 0\n private successCount: number = 0\n private lastFailureTime: number | null = null\n private readonly config: Required<CircuitBreakerConfig>\n private readonly listeners: Set<CircuitBreakerListener> = new Set()\n private readonly name: string\n private totalRequests: number = 0\n private totalFailures: number = 0\n\n constructor(\n name: string = 'default',\n config?: Partial<CircuitBreakerConfig>,\n ) {\n this.name = name\n this.config = {\n failureThreshold: config?.failureThreshold ?? 5,\n successThreshold: config?.successThreshold ?? 2,\n openTimeout: config?.openTimeout ?? 30000,\n halfOpenTimeout: config?.halfOpenTimeout ?? 30000,\n }\n }\n\n /**\n * Execute an operation through the circuit breaker\n * @throws {CircuitOpenError} If circuit is open\n * @throws {Error} If operation fails\n */\n async execute<R>(operation: () => Promise<R>): Promise<R> {\n this.totalRequests++\n\n // Check if we need to transition from OPEN to HALF_OPEN\n if (this.state === 'open') {\n const cooldownRemaining = this.getRemainingCooldown()\n if (cooldownRemaining <= 0) {\n this.transition('half-open')\n } else {\n const error = new CircuitOpenError(cooldownRemaining, this.name)\n this.notifyListeners('failure', { error })\n throw error\n }\n }\n\n // Execute the operation\n try {\n const result = await operation()\n this.onSuccess()\n return result\n } catch (error) {\n this.onFailure(error instanceof Error ? error : new Error(String(error)))\n throw error\n }\n }\n\n /**\n * Execute with timeout protection\n * @throws {CircuitOpenError} If circuit is open\n * @throws {Error} If operation times out or fails\n */\n async executeWithTimeout<R>(\n operation: () => Promise<R>,\n timeoutMs?: number,\n ): Promise<R> {\n const timeout = timeoutMs ?? this.config.halfOpenTimeout\n if (!timeout) {\n return this.execute(operation)\n }\n\n return Promise.race([\n this.execute(operation),\n new Promise<R>((_, reject) =>\n setTimeout(\n () => reject(new Error(`Operation timeout after ${timeout}ms`)),\n timeout,\n ),\n ),\n ])\n }\n\n /**\n * Get current circuit state\n */\n getState(): CircuitBreakerState {\n return this.state\n }\n\n /**\n * Get detailed statistics\n */\n getStats(): CircuitBreakerStats {\n return {\n state: this.state,\n failureCount: this.failureCount,\n successCount: this.successCount,\n lastFailureTime: this.lastFailureTime,\n totalRequests: this.totalRequests,\n totalFailures: this.totalFailures,\n }\n }\n\n /**\n * Get remaining cooldown time in milliseconds\n */\n getRemainingCooldown(): number {\n if (!this.lastFailureTime) {\n return 0\n }\n return Math.max(\n 0,\n this.config.openTimeout - (Date.now() - this.lastFailureTime),\n )\n }\n\n /**\n * Register a listener for state changes and events\n */\n on(listener: CircuitBreakerListener): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n /**\n * Reset the circuit breaker to CLOSED state\n */\n reset(): void {\n const previousState = this.state\n this.state = 'closed'\n this.failureCount = 0\n this.successCount = 0\n this.lastFailureTime = null\n this.totalRequests = 0\n this.totalFailures = 0\n\n this.notifyListeners('state-change', {\n state: this.state,\n previousState,\n })\n }\n\n /**\n * Manually open the circuit (for testing or explicit fault injection)\n */\n forceOpen(): void {\n this.transition('open')\n }\n\n /**\n * Manually close the circuit (for recovery testing)\n */\n forceClosed(): void {\n this.transition('closed')\n }\n\n /**\n * Manually transition to half-open state (for testing)\n */\n forceHalfOpen(): void {\n this.transition('half-open')\n }\n\n // Private methods\n\n private transition(newState: CircuitBreakerState): void {\n if (newState === this.state) {\n return\n }\n\n const previousState = this.state\n this.state = newState\n\n // Reset counters on state transition\n if (newState === 'closed') {\n this.failureCount = 0\n this.successCount = 0\n } else if (newState === 'half-open') {\n this.successCount = 0\n this.failureCount = 0\n }\n\n this.notifyListeners('state-change', {\n state: newState,\n previousState,\n })\n }\n\n private onSuccess(): void {\n if (this.state === 'half-open') {\n this.successCount++\n if (this.successCount >= this.config.successThreshold) {\n this.transition('closed')\n }\n } else if (this.state === 'closed') {\n this.failureCount = 0\n }\n\n this.notifyListeners('success', {})\n }\n\n private onFailure(error: Error): void {\n this.failureCount++\n this.totalFailures++\n this.lastFailureTime = Date.now()\n\n if (this.state === 'closed') {\n if (this.failureCount >= this.config.failureThreshold) {\n this.transition('open')\n }\n } else if (this.state === 'half-open') {\n // Any failure in half-open state goes back to open\n this.transition('open')\n }\n\n this.notifyListeners('failure', { error })\n }\n\n private notifyListeners(\n event: 'state-change' | 'failure' | 'success',\n details: {\n state?: CircuitBreakerState\n previousState?: CircuitBreakerState\n error?: Error\n },\n ): void {\n for (const listener of this.listeners) {\n try {\n listener(event, details)\n } catch {\n // Ignore listener errors to prevent them from affecting the circuit\n }\n }\n }\n}\n\n/**\n * Create a simple factory for managing multiple circuit breakers\n */\nexport class CircuitBreakerRegistry {\n private readonly breakers = new Map<string, CircuitBreaker>()\n private readonly defaultConfig?: Partial<CircuitBreakerConfig>\n\n constructor(defaultConfig?: Partial<CircuitBreakerConfig>) {\n this.defaultConfig = defaultConfig\n }\n\n /**\n * Get or create a circuit breaker by name\n */\n getOrCreate(\n name: string,\n config?: Partial<CircuitBreakerConfig>,\n ): CircuitBreaker {\n if (!this.breakers.has(name)) {\n const mergedConfig = { ...this.defaultConfig, ...config }\n this.breakers.set(name, new CircuitBreaker(name, mergedConfig))\n }\n return this.breakers.get(name)!\n }\n\n /**\n * Get all registered circuit breakers\n */\n getAll(): Map<string, CircuitBreaker> {\n return new Map(this.breakers)\n }\n\n /**\n * Reset all circuit breakers\n */\n resetAll(): void {\n for (const breaker of this.breakers.values()) {\n breaker.reset()\n }\n }\n\n /**\n * Clear all circuit breakers\n */\n clear(): void {\n this.breakers.clear()\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/utils/ask.js CHANGED
@@ -10,6 +10,7 @@ import { createUserMessage } from "./messages.js";
10
10
  async function ask({
11
11
  commands,
12
12
  safeMode,
13
+ safetyMode = "yolo",
13
14
  hasPermissionsToUseTool,
14
15
  messageLogName,
15
16
  prompt,
@@ -36,6 +37,7 @@ async function ask({
36
37
  tools,
37
38
  verbose,
38
39
  safeMode,
40
+ safetyMode,
39
41
  forkNumber: 0,
40
42
  messageLogName: "unused",
41
43
  maxThinkingTokens: 0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/ask.tsx"],
4
- "sourcesContent": ["import { last } from 'lodash-es'\nimport { Command } from '@commands'\nimport { getSystemPrompt } from '@constants/prompts'\nimport { getContext } from '@context'\nimport { getTotalCost } from '@costTracker'\nimport { Message, query } from '@query'\nimport { CanUseToolFn } from '@hooks/useCanUseTool'\nimport { Tool } from '@tool'\nimport { getModelManager } from '@utils/model'\nimport { setCwd } from './state'\nimport { getMessagesPath, overwriteLog } from './log'\nimport { createUserMessage } from './messages'\n\ntype Props = {\n commands: Command[]\n safeMode?: boolean\n hasPermissionsToUseTool: CanUseToolFn\n messageLogName: string\n prompt: string\n cwd: string\n tools: Tool[]\n verbose?: boolean\n}\n\n// Sends a single prompt to the Anthropic Messages API and returns the response.\n// Assumes that claude is being used non-interactively -- will not\n// ask the user for permissions or further input.\nexport async function ask({\n commands,\n safeMode,\n hasPermissionsToUseTool,\n messageLogName,\n prompt,\n cwd,\n tools,\n verbose = false,\n}: Props): Promise<{\n resultText: string\n totalCost: number\n messageHistoryFile: string\n}> {\n await setCwd(cwd)\n const message = createUserMessage(prompt)\n const messages: Message[] = [message]\n\n const [systemPrompt, context, model] = await Promise.all([\n getSystemPrompt(),\n getContext(),\n getModelManager().getModelName('main'),\n ])\n\n for await (const m of query(\n messages,\n systemPrompt,\n context,\n hasPermissionsToUseTool,\n {\n options: {\n commands,\n tools,\n verbose,\n safeMode,\n forkNumber: 0,\n messageLogName: 'unused',\n maxThinkingTokens: 0,\n },\n abortController: new AbortController(),\n messageId: undefined,\n readFileTimestamps: {},\n setToolJSX: () => {}, // No-op function for non-interactive use\n },\n )) {\n messages.push(m)\n }\n\n const result = last(messages)\n if (!result || result.type !== 'assistant') {\n throw new Error('Expected content to be an assistant message')\n }\n if (result.message.content[0]?.type !== 'text') {\n throw new Error(\n `Expected first content item to be text, but got ${JSON.stringify(\n result.message.content[0],\n null,\n 2,\n )}`,\n )\n }\n\n // Write log that can be retrieved with `claude log`\n const messageHistoryFile = getMessagesPath(messageLogName, 0, 0)\n overwriteLog(messageHistoryFile, messages)\n\n return {\n resultText: result.message.content[0].text,\n totalCost: getTotalCost(),\n messageHistoryFile,\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,YAAY;AAErB,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAkB,aAAa;AAG/B,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,SAAS,iBAAiB,oBAAoB;AAC9C,SAAS,yBAAyB;AAgBlC,eAAsB,IAAI;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AACZ,GAIG;AACD,QAAM,OAAO,GAAG;AAChB,QAAM,UAAU,kBAAkB,MAAM;AACxC,QAAM,WAAsB,CAAC,OAAO;AAEpC,QAAM,CAAC,cAAc,SAAS,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IACvD,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,gBAAgB,EAAE,aAAa,MAAM;AAAA,EACvC,CAAC;AAED,mBAAiB,KAAK;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,mBAAmB;AAAA,MACrB;AAAA,MACA,iBAAiB,IAAI,gBAAgB;AAAA,MACrC,WAAW;AAAA,MACX,oBAAoB,CAAC;AAAA,MACrB,YAAY,MAAM;AAAA,MAAC;AAAA;AAAA,IACrB;AAAA,EACF,GAAG;AACD,aAAS,KAAK,CAAC;AAAA,EACjB;AAEA,QAAM,SAAS,KAAK,QAAQ;AAC5B,MAAI,CAAC,UAAU,OAAO,SAAS,aAAa;AAC1C,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,MAAI,OAAO,QAAQ,QAAQ,CAAC,GAAG,SAAS,QAAQ;AAC9C,UAAM,IAAI;AAAA,MACR,mDAAmD,KAAK;AAAA,QACtD,OAAO,QAAQ,QAAQ,CAAC;AAAA,QACxB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,qBAAqB,gBAAgB,gBAAgB,GAAG,CAAC;AAC/D,eAAa,oBAAoB,QAAQ;AAEzC,SAAO;AAAA,IACL,YAAY,OAAO,QAAQ,QAAQ,CAAC,EAAE;AAAA,IACtC,WAAW,aAAa;AAAA,IACxB;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { last } from 'lodash-es'\nimport { Command } from '@commands'\nimport { getSystemPrompt } from '@constants/prompts'\nimport { getContext } from '@context'\nimport { getTotalCost } from '@costTracker'\nimport { Message, query } from '@query'\nimport { CanUseToolFn } from '@hooks/useCanUseTool'\nimport { Tool } from '@tool'\nimport { getModelManager } from '@utils/model'\nimport { setCwd } from './state'\nimport { getMessagesPath, overwriteLog } from './log'\nimport { createUserMessage } from './messages'\nimport type { SafetyMode } from './config'\n\ntype Props = {\n commands: Command[]\n /** @deprecated Use safetyMode instead */\n safeMode?: boolean\n /** Safety mode: 'yolo' | 'smart' | 'strict' */\n safetyMode?: SafetyMode\n hasPermissionsToUseTool: CanUseToolFn\n messageLogName: string\n prompt: string\n cwd: string\n tools: Tool[]\n verbose?: boolean\n}\n\n// Sends a single prompt to the Anthropic Messages API and returns the response.\n// Assumes that claude is being used non-interactively -- will not\n// ask the user for permissions or further input.\nexport async function ask({\n commands,\n safeMode,\n safetyMode = 'yolo',\n hasPermissionsToUseTool,\n messageLogName,\n prompt,\n cwd,\n tools,\n verbose = false,\n}: Props): Promise<{\n resultText: string\n totalCost: number\n messageHistoryFile: string\n}> {\n await setCwd(cwd)\n const message = createUserMessage(prompt)\n const messages: Message[] = [message]\n\n const [systemPrompt, context, model] = await Promise.all([\n getSystemPrompt(),\n getContext(),\n getModelManager().getModelName('main'),\n ])\n\n for await (const m of query(\n messages,\n systemPrompt,\n context,\n hasPermissionsToUseTool,\n {\n options: {\n commands,\n tools,\n verbose,\n safeMode,\n safetyMode,\n forkNumber: 0,\n messageLogName: 'unused',\n maxThinkingTokens: 0,\n },\n abortController: new AbortController(),\n messageId: undefined,\n readFileTimestamps: {},\n setToolJSX: () => {}, // No-op function for non-interactive use\n },\n )) {\n messages.push(m)\n }\n\n const result = last(messages)\n if (!result || result.type !== 'assistant') {\n throw new Error('Expected content to be an assistant message')\n }\n if (result.message.content[0]?.type !== 'text') {\n throw new Error(\n `Expected first content item to be text, but got ${JSON.stringify(\n result.message.content[0],\n null,\n 2,\n )}`,\n )\n }\n\n // Write log that can be retrieved with `claude log`\n const messageHistoryFile = getMessagesPath(messageLogName, 0, 0)\n overwriteLog(messageHistoryFile, messages)\n\n return {\n resultText: result.message.content[0].text,\n totalCost: getTotalCost(),\n messageHistoryFile,\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,YAAY;AAErB,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAkB,aAAa;AAG/B,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,SAAS,iBAAiB,oBAAoB;AAC9C,SAAS,yBAAyB;AAoBlC,eAAsB,IAAI;AAAA,EACxB;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AACZ,GAIG;AACD,QAAM,OAAO,GAAG;AAChB,QAAM,UAAU,kBAAkB,MAAM;AACxC,QAAM,WAAsB,CAAC,OAAO;AAEpC,QAAM,CAAC,cAAc,SAAS,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IACvD,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,gBAAgB,EAAE,aAAa,MAAM;AAAA,EACvC,CAAC;AAED,mBAAiB,KAAK;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,mBAAmB;AAAA,MACrB;AAAA,MACA,iBAAiB,IAAI,gBAAgB;AAAA,MACrC,WAAW;AAAA,MACX,oBAAoB,CAAC;AAAA,MACrB,YAAY,MAAM;AAAA,MAAC;AAAA;AAAA,IACrB;AAAA,EACF,GAAG;AACD,aAAS,KAAK,CAAC;AAAA,EACjB;AAEA,QAAM,SAAS,KAAK,QAAQ;AAC5B,MAAI,CAAC,UAAU,OAAO,SAAS,aAAa;AAC1C,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,MAAI,OAAO,QAAQ,QAAQ,CAAC,GAAG,SAAS,QAAQ;AAC9C,UAAM,IAAI;AAAA,MACR,mDAAmD,KAAK;AAAA,QACtD,OAAO,QAAQ,QAAQ,CAAC;AAAA,QACxB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,qBAAqB,gBAAgB,gBAAgB,GAAG,CAAC;AAC/D,eAAa,oBAAoB,QAAQ;AAEzC,SAAO;AAAA,IACL,YAAY,OAAO,QAAQ,QAAQ,CAAC,EAAE;AAAA,IACtC,WAAW,aAAa;AAAA,IACxB;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
- import { existsSync, readFileSync, writeFileSync } from "fs";
2
- import { resolve, join } from "path";
1
+ import { existsSync, readFileSync, writeFileSync, renameSync, rmSync } from "fs";
2
+ import { resolve, join, dirname } from "path";
3
3
  import { cloneDeep, memoize, pick } from "lodash-es";
4
4
  import { homedir } from "os";
5
5
  import { GLOBAL_CONFIG_FILE } from "./env.js";
@@ -83,8 +83,12 @@ const DEFAULT_GLOBAL_CONFIG = {
83
83
  // Default to business consulting compression
84
84
  thinking: false,
85
85
  // Default to thinking mode off (Phase 4.2)
86
- language: "en"
86
+ language: "en",
87
87
  // Default to English
88
+ safeMode: false,
89
+ // Deprecated: use safetyMode instead
90
+ safetyMode: "yolo"
91
+ // Default to YOLO mode for non-technical users (zero interruption)
88
92
  };
89
93
  const GLOBAL_CONFIG_KEYS = [
90
94
  "autoUpdaterStatus",
@@ -99,7 +103,8 @@ const GLOBAL_CONFIG_KEYS = [
99
103
  "shiftEnterKeyBindingInstalled",
100
104
  "maxTokens",
101
105
  "compressionMode",
102
- "language"
106
+ "language",
107
+ "safetyMode"
103
108
  ];
104
109
  function isGlobalConfigKey(key) {
105
110
  return GLOBAL_CONFIG_KEYS.includes(key);
@@ -194,6 +199,23 @@ function getCustomApiKeyStatus(truncatedApiKey) {
194
199
  }
195
200
  return "new";
196
201
  }
202
+ function atomicWriteFileSync(filePath, content) {
203
+ const dir = dirname(filePath);
204
+ const tempPath = join(
205
+ dir,
206
+ `.${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`
207
+ );
208
+ try {
209
+ writeFileSync(tempPath, content, "utf-8");
210
+ renameSync(tempPath, filePath);
211
+ } catch (error) {
212
+ try {
213
+ rmSync(tempPath, { force: true });
214
+ } catch {
215
+ }
216
+ throw error;
217
+ }
218
+ }
197
219
  function saveConfig(file, config, defaultConfig) {
198
220
  const filteredConfig = Object.fromEntries(
199
221
  Object.entries(config).filter(
@@ -201,7 +223,7 @@ function saveConfig(file, config, defaultConfig) {
201
223
  )
202
224
  );
203
225
  try {
204
- writeFileSync(file, JSON.stringify(filteredConfig, null, 2), "utf-8");
226
+ atomicWriteFileSync(file, JSON.stringify(filteredConfig, null, 2));
205
227
  } catch (error) {
206
228
  const err = error;
207
229
  if (err?.code === "EACCES" || err?.code === "EPERM" || err?.code === "EROFS") {
@@ -554,6 +576,25 @@ function setModelPointer(pointer, modelName) {
554
576
  reloadModelManager();
555
577
  });
556
578
  }
579
+ async function resolveApiKey(profile) {
580
+ if (!profile.apiKey) {
581
+ return null;
582
+ }
583
+ if (profile.apiKey.startsWith("encrypted:")) {
584
+ const modelName = profile.apiKey.slice("encrypted:".length);
585
+ try {
586
+ const { getApiKey } = await import("./credentials/index.js");
587
+ return getApiKey(modelName);
588
+ } catch (error) {
589
+ const errorMsg = error instanceof Error ? error.message : String(error);
590
+ console.warn(
591
+ `Failed to resolve encrypted API key for ${modelName}: ${errorMsg}`
592
+ );
593
+ return null;
594
+ }
595
+ }
596
+ return profile.apiKey;
597
+ }
557
598
  function isGPT5ModelName(modelName) {
558
599
  if (!modelName || typeof modelName !== "string") return false;
559
600
  const lowerName = modelName.toLowerCase();
@@ -711,6 +752,7 @@ export {
711
752
  listConfigForCLI,
712
753
  normalizeApiKeyForConfig,
713
754
  removeMcprcServerForTesting,
755
+ resolveApiKey,
714
756
  saveCurrentProjectConfig,
715
757
  saveGlobalConfig,
716
758
  setAllPointersToModel,