ummaya 0.2.14 → 0.2.15

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 (564) hide show
  1. package/npm-shrinkwrap.json +2 -2
  2. package/package.json +7 -4
  3. package/pyproject.toml +2 -2
  4. package/tui/package.json +1 -1
  5. package/tui/src/buddy/CompanionSprite.tsx +1 -2
  6. package/tui/src/buddy/useBuddyNotification.tsx +1 -2
  7. package/tui/src/cli/controlAuth.ts +20 -0
  8. package/tui/src/cli/handlers/auth.ts +7 -7
  9. package/tui/src/cli/handlers/mcp.tsx +0 -1
  10. package/tui/src/cli/handlers/util.tsx +2 -3
  11. package/tui/src/cli/print.ts +7 -4
  12. package/tui/src/commands/add-dir/add-dir.tsx +1 -2
  13. package/tui/src/commands/agents/agents.tsx +1 -2
  14. package/tui/src/commands/bridge/bridge.tsx +1 -2
  15. package/tui/src/commands/btw/btw.tsx +1 -2
  16. package/tui/src/commands/config/config.tsx +1 -2
  17. package/tui/src/commands/context/context.tsx +1 -2
  18. package/tui/src/commands/copy/copy.tsx +1 -2
  19. package/tui/src/commands/desktop/desktop.tsx +1 -2
  20. package/tui/src/commands/diff/diff.tsx +1 -2
  21. package/tui/src/commands/doctor/doctor.tsx +1 -2
  22. package/tui/src/commands/effort/effort.tsx +1 -2
  23. package/tui/src/commands/exit/exit.tsx +1 -2
  24. package/tui/src/commands/export/export.tsx +1 -2
  25. package/tui/src/commands/extra-usage/extra-usage.tsx +1 -2
  26. package/tui/src/commands/fast/fast.tsx +0 -1
  27. package/tui/src/commands/feedback/feedback.tsx +1 -2
  28. package/tui/src/commands/help/help.tsx +1 -2
  29. package/tui/src/commands/hooks/hooks.tsx +1 -2
  30. package/tui/src/commands/ide/ide.tsx +0 -1
  31. package/tui/src/commands/install.tsx +0 -1
  32. package/tui/src/commands/login/login.tsx +1 -2
  33. package/tui/src/commands/logout/logout.tsx +0 -1
  34. package/tui/src/commands/mcp/mcp.tsx +1 -2
  35. package/tui/src/commands/memory/memory.tsx +0 -1
  36. package/tui/src/commands/mobile/mobile.tsx +0 -1
  37. package/tui/src/commands/model/model.tsx +0 -1
  38. package/tui/src/commands/output-style/output-style.tsx +1 -2
  39. package/tui/src/commands/passes/passes.tsx +1 -2
  40. package/tui/src/commands/permissions/permissions.tsx +1 -2
  41. package/tui/src/commands/plan/plan.tsx +1 -2
  42. package/tui/src/commands/plugin/AddMarketplace.tsx +1 -2
  43. package/tui/src/commands/plugin/BrowseMarketplace.tsx +1 -2
  44. package/tui/src/commands/plugin/DiscoverPlugins.tsx +0 -1
  45. package/tui/src/commands/plugin/ManageMarketplaces.tsx +0 -1
  46. package/tui/src/commands/plugin/ManagePlugins.tsx +1 -2
  47. package/tui/src/commands/plugin/PluginErrors.tsx +1 -2
  48. package/tui/src/commands/plugin/PluginOptionsDialog.tsx +1 -2
  49. package/tui/src/commands/plugin/PluginOptionsFlow.tsx +1 -2
  50. package/tui/src/commands/plugin/PluginSettings.tsx +1 -2
  51. package/tui/src/commands/plugin/PluginTrustWarning.tsx +0 -1
  52. package/tui/src/commands/plugin/UnifiedInstalledCell.tsx +1 -2
  53. package/tui/src/commands/plugin/ValidatePlugin.tsx +1 -2
  54. package/tui/src/commands/plugin/index.tsx +0 -1
  55. package/tui/src/commands/plugin/plugin.tsx +1 -2
  56. package/tui/src/commands/plugin/pluginDetailsHelpers.tsx +1 -2
  57. package/tui/src/commands/privacy-settings/privacy-settings.tsx +0 -1
  58. package/tui/src/commands/rate-limit-options/rate-limit-options.tsx +1 -2
  59. package/tui/src/commands/remote-env/remote-env.tsx +1 -2
  60. package/tui/src/commands/remote-setup/remote-setup.tsx +0 -1
  61. package/tui/src/commands/resume/resume.tsx +1 -2
  62. package/tui/src/commands/review/UltrareviewOverageDialog.tsx +1 -2
  63. package/tui/src/commands/review/ultrareviewCommand.tsx +0 -1
  64. package/tui/src/commands/sandbox-toggle/sandbox-toggle.tsx +1 -2
  65. package/tui/src/commands/session/session.tsx +0 -1
  66. package/tui/src/commands/skills/skills.tsx +1 -2
  67. package/tui/src/commands/stats/stats.tsx +1 -2
  68. package/tui/src/commands/status/status.tsx +1 -2
  69. package/tui/src/commands/statusline.tsx +0 -1
  70. package/tui/src/commands/tag/tag.tsx +1 -2
  71. package/tui/src/commands/tasks/tasks.tsx +1 -2
  72. package/tui/src/commands/terminalSetup/terminalSetup.tsx +1 -2
  73. package/tui/src/commands/theme/theme.tsx +1 -2
  74. package/tui/src/commands/thinkback/thinkback.tsx +0 -1
  75. package/tui/src/commands/ultraplan.tsx +0 -1
  76. package/tui/src/commands/usage/usage.tsx +1 -2
  77. package/tui/src/commands/voice/voice.ts +2 -2
  78. package/tui/src/components/AgentProgressLine.tsx +1 -2
  79. package/tui/src/components/App.tsx +1 -2
  80. package/tui/src/components/ApproveApiKey.tsx +1 -2
  81. package/tui/src/components/AutoModeOptInDialog.tsx +0 -1
  82. package/tui/src/components/AutoUpdater.tsx +1 -2
  83. package/tui/src/components/AutoUpdaterWrapper.tsx +1 -2
  84. package/tui/src/components/AwsAuthStatusBox.tsx +1 -2
  85. package/tui/src/components/BaseTextInput.tsx +0 -1
  86. package/tui/src/components/BashModeProgress.tsx +1 -2
  87. package/tui/src/components/BridgeDialog.tsx +1 -2
  88. package/tui/src/components/BypassPermissionsModeDialog.tsx +0 -1
  89. package/tui/src/components/ChannelDowngradeDialog.tsx +1 -2
  90. package/tui/src/components/ClaudeCodeHint/PluginHintMenu.tsx +1 -2
  91. package/tui/src/components/ClaudeMdExternalIncludesDialog.tsx +0 -1
  92. package/tui/src/components/ClickableImageRef.tsx +1 -2
  93. package/tui/src/components/CompactSummary.tsx +1 -2
  94. package/tui/src/components/ConfigurableShortcutHint.tsx +1 -2
  95. package/tui/src/components/ContextSuggestions.tsx +1 -2
  96. package/tui/src/components/ContextVisualization.tsx +1 -2
  97. package/tui/src/components/CoordinatorAgentStatus.tsx +1 -2
  98. package/tui/src/components/CostThresholdDialog.tsx +0 -1
  99. package/tui/src/components/CtrlOToExpand.tsx +1 -2
  100. package/tui/src/components/CustomSelect/SelectMulti.tsx +1 -2
  101. package/tui/src/components/CustomSelect/select-input-option.tsx +1 -2
  102. package/tui/src/components/CustomSelect/select-option.tsx +1 -2
  103. package/tui/src/components/CustomSelect/select.tsx +1 -2
  104. package/tui/src/components/DesktopHandoff.tsx +0 -1
  105. package/tui/src/components/DesktopUpsell/DesktopUpsellStartup.tsx +0 -1
  106. package/tui/src/components/DevBar.tsx +1 -2
  107. package/tui/src/components/DevChannelsDialog.tsx +1 -2
  108. package/tui/src/components/DiagnosticsDisplay.tsx +1 -2
  109. package/tui/src/components/EffortCallout.tsx +1 -2
  110. package/tui/src/components/ExitFlow.tsx +1 -2
  111. package/tui/src/components/ExportDialog.tsx +1 -2
  112. package/tui/src/components/FallbackToolUseErrorMessage.tsx +0 -1
  113. package/tui/src/components/FallbackToolUseRejectedMessage.tsx +1 -2
  114. package/tui/src/components/FastIcon.tsx +1 -2
  115. package/tui/src/components/Feedback.tsx +0 -1
  116. package/tui/src/components/FeedbackSurvey/FeedbackSurvey.tsx +1 -2
  117. package/tui/src/components/FeedbackSurvey/FeedbackSurveyView.tsx +0 -1
  118. package/tui/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx +0 -1
  119. package/tui/src/components/FeedbackSurvey/useFeedbackSurvey.tsx +0 -1
  120. package/tui/src/components/FeedbackSurvey/useMemorySurvey.tsx +0 -1
  121. package/tui/src/components/FeedbackSurvey/usePostCompactSurvey.tsx +0 -1
  122. package/tui/src/components/FeedbackSurvey/useSurveyState.tsx +1 -2
  123. package/tui/src/components/FileEditToolDiff.tsx +1 -2
  124. package/tui/src/components/FileEditToolUpdatedMessage.tsx +1 -2
  125. package/tui/src/components/FileEditToolUseRejectedMessage.tsx +1 -2
  126. package/tui/src/components/FilePathLink.tsx +1 -2
  127. package/tui/src/components/FullscreenLayout.tsx +1 -2
  128. package/tui/src/components/GlobalSearchDialog.tsx +1 -2
  129. package/tui/src/components/HelpV2/Commands.tsx +1 -2
  130. package/tui/src/components/HelpV2/General.tsx +0 -1
  131. package/tui/src/components/HelpV2/HelpV2.tsx +0 -1
  132. package/tui/src/components/HighlightedCode/Fallback.tsx +1 -2
  133. package/tui/src/components/HighlightedCode.tsx +1 -2
  134. package/tui/src/components/HistorySearchDialog.tsx +1 -2
  135. package/tui/src/components/IdeAutoConnectDialog.tsx +1 -2
  136. package/tui/src/components/IdeOnboardingDialog.tsx +0 -1
  137. package/tui/src/components/IdeStatusIndicator.tsx +1 -2
  138. package/tui/src/components/IdleReturnDialog.tsx +1 -2
  139. package/tui/src/components/InterruptedByUser.tsx +0 -1
  140. package/tui/src/components/InvalidConfigDialog.tsx +1 -2
  141. package/tui/src/components/InvalidSettingsDialog.tsx +1 -2
  142. package/tui/src/components/KeybindingWarnings.tsx +1 -2
  143. package/tui/src/components/LanguagePicker.tsx +1 -2
  144. package/tui/src/components/LogSelector.tsx +0 -1
  145. package/tui/src/components/LogoV2/AnimatedAsterisk.tsx +1 -2
  146. package/tui/src/components/LogoV2/AnimatedClawd.tsx +1 -2
  147. package/tui/src/components/LogoV2/ChannelsNotice.tsx +0 -1
  148. package/tui/src/components/LogoV2/Clawd.tsx +1 -2
  149. package/tui/src/components/LogoV2/CondensedLogo.tsx +0 -1
  150. package/tui/src/components/LogoV2/EmergencyTip.tsx +0 -1
  151. package/tui/src/components/LogoV2/Feed.tsx +1 -2
  152. package/tui/src/components/LogoV2/FeedColumn.tsx +1 -2
  153. package/tui/src/components/LogoV2/GuestPassesUpsell.tsx +0 -1
  154. package/tui/src/components/LogoV2/LogoV2.tsx +0 -1
  155. package/tui/src/components/LogoV2/Opus1mMergeNotice.tsx +1 -2
  156. package/tui/src/components/LogoV2/OverageCreditUpsell.tsx +1 -2
  157. package/tui/src/components/LogoV2/VoiceModeNotice.tsx +1 -2
  158. package/tui/src/components/LogoV2/feedConfigs.tsx +0 -1
  159. package/tui/src/components/LspRecommendation/LspRecommendationMenu.tsx +1 -2
  160. package/tui/src/components/MCPServerApprovalDialog.tsx +1 -2
  161. package/tui/src/components/MCPServerDesktopImportDialog.tsx +0 -1
  162. package/tui/src/components/MCPServerDialogCopy.tsx +0 -1
  163. package/tui/src/components/MCPServerMultiselectDialog.tsx +1 -2
  164. package/tui/src/components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.tsx +0 -1
  165. package/tui/src/components/Markdown.tsx +1 -2
  166. package/tui/src/components/MarkdownTable.tsx +1 -2
  167. package/tui/src/components/MemoryUsageIndicator.tsx +1 -2
  168. package/tui/src/components/Message.tsx +1 -2
  169. package/tui/src/components/MessageModel.tsx +1 -2
  170. package/tui/src/components/MessageResponse.tsx +1 -2
  171. package/tui/src/components/MessageRow.tsx +1 -2
  172. package/tui/src/components/MessageSelector.tsx +1 -2
  173. package/tui/src/components/MessageTimestamp.tsx +1 -2
  174. package/tui/src/components/Messages.tsx +0 -1
  175. package/tui/src/components/ModelPicker.tsx +0 -1
  176. package/tui/src/components/NativeAutoUpdater.tsx +1 -2
  177. package/tui/src/components/NotebookEditToolUseRejectedMessage.tsx +1 -2
  178. package/tui/src/components/OffscreenFreeze.tsx +1 -2
  179. package/tui/src/components/Onboarding.tsx +2 -3
  180. package/tui/src/components/OutputStylePicker.tsx +0 -1
  181. package/tui/src/components/PackageManagerAutoUpdater.tsx +0 -1
  182. package/tui/src/components/Passes/Passes.tsx +0 -1
  183. package/tui/src/components/PrBadge.tsx +1 -2
  184. package/tui/src/components/PressEnterToContinue.tsx +1 -2
  185. package/tui/src/components/PromptInput/HistorySearchInput.tsx +1 -2
  186. package/tui/src/components/PromptInput/IssueFlagBanner.tsx +1 -2
  187. package/tui/src/components/PromptInput/Notifications.tsx +1 -2
  188. package/tui/src/components/PromptInput/PromptInput.tsx +0 -1
  189. package/tui/src/components/PromptInput/PromptInputFooter.tsx +1 -2
  190. package/tui/src/components/PromptInput/PromptInputFooterLeftSide.tsx +1 -2
  191. package/tui/src/components/PromptInput/PromptInputFooterSuggestions.tsx +1 -2
  192. package/tui/src/components/PromptInput/PromptInputHelpMenu.tsx +1 -2
  193. package/tui/src/components/PromptInput/PromptInputModeIndicator.tsx +1 -2
  194. package/tui/src/components/PromptInput/PromptInputQueuedCommands.tsx +1 -2
  195. package/tui/src/components/PromptInput/PromptInputStashNotice.tsx +1 -2
  196. package/tui/src/components/PromptInput/SandboxPromptFooterHint.tsx +1 -2
  197. package/tui/src/components/PromptInput/ShimmeredInput.tsx +1 -2
  198. package/tui/src/components/PromptInput/VoiceIndicator.tsx +1 -2
  199. package/tui/src/components/QuickOpenDialog.tsx +1 -2
  200. package/tui/src/components/RemoteCallout.tsx +0 -1
  201. package/tui/src/components/RemoteEnvironmentDialog.tsx +0 -1
  202. package/tui/src/components/ResumeTask.tsx +0 -1
  203. package/tui/src/components/SandboxViolationExpandedView.tsx +1 -2
  204. package/tui/src/components/ScrollKeybindingHandler.tsx +0 -1
  205. package/tui/src/components/SearchBox.tsx +1 -2
  206. package/tui/src/components/SessionBackgroundHint.tsx +1 -2
  207. package/tui/src/components/SessionPreview.tsx +1 -2
  208. package/tui/src/components/Settings/Config.tsx +0 -1
  209. package/tui/src/components/Settings/Settings.tsx +1 -2
  210. package/tui/src/components/Settings/Status.tsx +1 -2
  211. package/tui/src/components/Settings/Usage.tsx +1 -2
  212. package/tui/src/components/ShowInIDEPrompt.tsx +1 -2
  213. package/tui/src/components/SkillImprovementSurvey.tsx +1 -2
  214. package/tui/src/components/Spinner/FlashingChar.tsx +1 -2
  215. package/tui/src/components/Spinner/GlimmerMessage.tsx +1 -2
  216. package/tui/src/components/Spinner/ShimmerChar.tsx +1 -2
  217. package/tui/src/components/Spinner/SpinnerAnimationRow.tsx +1 -2
  218. package/tui/src/components/Spinner/SpinnerGlyph.tsx +1 -2
  219. package/tui/src/components/Spinner/TeammateSpinnerLine.tsx +1 -2
  220. package/tui/src/components/Spinner/TeammateSpinnerTree.tsx +1 -2
  221. package/tui/src/components/Spinner.tsx +0 -1
  222. package/tui/src/components/Stats.tsx +0 -1
  223. package/tui/src/components/StatusLine.tsx +1 -2
  224. package/tui/src/components/StatusNotices.tsx +1 -2
  225. package/tui/src/components/StructuredDiff/Fallback.tsx +1 -2
  226. package/tui/src/components/StructuredDiff.tsx +1 -2
  227. package/tui/src/components/StructuredDiffList.tsx +1 -2
  228. package/tui/src/components/TagTabs.tsx +1 -2
  229. package/tui/src/components/TaskListV2.tsx +1 -2
  230. package/tui/src/components/TeammateViewHeader.tsx +1 -2
  231. package/tui/src/components/TeleportError.tsx +0 -1
  232. package/tui/src/components/TeleportProgress.tsx +1 -2
  233. package/tui/src/components/TeleportRepoMismatchDialog.tsx +0 -1
  234. package/tui/src/components/TeleportStash.tsx +1 -2
  235. package/tui/src/components/TextInput.tsx +1 -2
  236. package/tui/src/components/ThemePicker.tsx +0 -1
  237. package/tui/src/components/ThinkingToggle.tsx +0 -1
  238. package/tui/src/components/TokenWarning.tsx +1 -2
  239. package/tui/src/components/ToolUseLoader.tsx +1 -2
  240. package/tui/src/components/TrustDialog/TrustDialog.tsx +0 -1
  241. package/tui/src/components/ValidationErrorsList.tsx +1 -2
  242. package/tui/src/components/VimTextInput.tsx +1 -2
  243. package/tui/src/components/VirtualMessageList.tsx +1 -2
  244. package/tui/src/components/WorktreeExitDialog.tsx +1 -2
  245. package/tui/src/components/agents/AgentDetail.tsx +0 -1
  246. package/tui/src/components/agents/AgentEditor.tsx +1 -2
  247. package/tui/src/components/agents/AgentNavigationFooter.tsx +1 -2
  248. package/tui/src/components/agents/AgentsList.tsx +0 -1
  249. package/tui/src/components/agents/AgentsMenu.tsx +1 -2
  250. package/tui/src/components/agents/ColorPicker.tsx +1 -2
  251. package/tui/src/components/agents/ModelSelector.tsx +1 -2
  252. package/tui/src/components/agents/ToolSelector.tsx +1 -2
  253. package/tui/src/components/agents/new-agent-creation/CreateAgentWizard.tsx +1 -2
  254. package/tui/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx +1 -2
  255. package/tui/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx +0 -1
  256. package/tui/src/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx +1 -2
  257. package/tui/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx +0 -1
  258. package/tui/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx +1 -2
  259. package/tui/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx +1 -2
  260. package/tui/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx +1 -2
  261. package/tui/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx +0 -1
  262. package/tui/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx +1 -2
  263. package/tui/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx +1 -2
  264. package/tui/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx +1 -2
  265. package/tui/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx +1 -2
  266. package/tui/src/components/design-system/Byline.tsx +1 -2
  267. package/tui/src/components/design-system/Dialog.tsx +1 -2
  268. package/tui/src/components/design-system/Divider.tsx +1 -2
  269. package/tui/src/components/design-system/FuzzyPicker.tsx +1 -2
  270. package/tui/src/components/design-system/KeyboardShortcutHint.tsx +1 -2
  271. package/tui/src/components/design-system/ListItem.tsx +1 -2
  272. package/tui/src/components/design-system/LoadingState.tsx +0 -1
  273. package/tui/src/components/design-system/Pane.tsx +1 -2
  274. package/tui/src/components/design-system/ProgressBar.tsx +1 -2
  275. package/tui/src/components/design-system/Ratchet.tsx +1 -2
  276. package/tui/src/components/design-system/StatusIcon.tsx +1 -2
  277. package/tui/src/components/design-system/Tabs.tsx +1 -2
  278. package/tui/src/components/design-system/ThemeProvider.tsx +1 -2
  279. package/tui/src/components/design-system/ThemedBox.tsx +1 -2
  280. package/tui/src/components/design-system/ThemedText.tsx +1 -2
  281. package/tui/src/components/diff/DiffDetailView.tsx +1 -2
  282. package/tui/src/components/diff/DiffDialog.tsx +1 -2
  283. package/tui/src/components/diff/DiffFileList.tsx +1 -2
  284. package/tui/src/components/grove/Grove.tsx +0 -1
  285. package/tui/src/components/hooks/HooksConfigMenu.tsx +0 -1
  286. package/tui/src/components/hooks/PromptDialog.tsx +1 -2
  287. package/tui/src/components/hooks/SelectEventMode.tsx +0 -1
  288. package/tui/src/components/hooks/SelectHookMode.tsx +0 -1
  289. package/tui/src/components/hooks/SelectMatcherMode.tsx +0 -1
  290. package/tui/src/components/hooks/ViewHookMode.tsx +0 -1
  291. package/tui/src/components/mcp/CapabilitiesSection.tsx +1 -2
  292. package/tui/src/components/mcp/ElicitationDialog.tsx +0 -1
  293. package/tui/src/components/mcp/MCPAgentServerMenu.tsx +1 -2
  294. package/tui/src/components/mcp/MCPListPanel.tsx +0 -1
  295. package/tui/src/components/mcp/MCPReconnect.tsx +1 -2
  296. package/tui/src/components/mcp/MCPRemoteServerMenu.tsx +2 -3
  297. package/tui/src/components/mcp/MCPSettings.tsx +0 -1
  298. package/tui/src/components/mcp/MCPStdioServerMenu.tsx +1 -2
  299. package/tui/src/components/mcp/MCPToolDetailView.tsx +1 -2
  300. package/tui/src/components/mcp/MCPToolListView.tsx +1 -2
  301. package/tui/src/components/mcp/McpParsingWarnings.tsx +0 -1
  302. package/tui/src/components/mcp/utils/reconnectHelpers.tsx +1 -2
  303. package/tui/src/components/memory/MemoryFileSelector.tsx +1 -2
  304. package/tui/src/components/memory/MemoryUpdateNotification.tsx +1 -2
  305. package/tui/src/components/messageActions.tsx +1 -2
  306. package/tui/src/components/messages/AdvisorMessage.tsx +1 -2
  307. package/tui/src/components/messages/AssistantRedactedThinkingMessage.tsx +1 -2
  308. package/tui/src/components/messages/AssistantThinkingMessage.tsx +1 -2
  309. package/tui/src/components/messages/AssistantToolUseMessage.tsx +0 -1
  310. package/tui/src/components/messages/AttachmentMessage.tsx +1 -2
  311. package/tui/src/components/messages/CollapsedReadSearchContent.tsx +1 -2
  312. package/tui/src/components/messages/CompactBoundaryMessage.tsx +1 -2
  313. package/tui/src/components/messages/GroupedToolUseContent.tsx +1 -2
  314. package/tui/src/components/messages/HighlightedThinkingText.tsx +1 -2
  315. package/tui/src/components/messages/HookProgressMessage.tsx +1 -2
  316. package/tui/src/components/messages/PlanApprovalMessage.tsx +1 -2
  317. package/tui/src/components/messages/RateLimitMessage.tsx +1 -2
  318. package/tui/src/components/messages/ShutdownMessage.tsx +1 -2
  319. package/tui/src/components/messages/SystemAPIErrorMessage.tsx +1 -2
  320. package/tui/src/components/messages/SystemTextMessage.tsx +1 -2
  321. package/tui/src/components/messages/TaskAssignmentMessage.tsx +1 -2
  322. package/tui/src/components/messages/UserAgentNotificationMessage.tsx +1 -2
  323. package/tui/src/components/messages/UserBashInputMessage.tsx +1 -2
  324. package/tui/src/components/messages/UserBashOutputMessage.tsx +1 -2
  325. package/tui/src/components/messages/UserChannelMessage.tsx +1 -2
  326. package/tui/src/components/messages/UserCommandMessage.tsx +1 -2
  327. package/tui/src/components/messages/UserImageMessage.tsx +1 -2
  328. package/tui/src/components/messages/UserLocalCommandOutputMessage.tsx +1 -2
  329. package/tui/src/components/messages/UserMemoryInputMessage.tsx +1 -2
  330. package/tui/src/components/messages/UserPlanMessage.tsx +1 -2
  331. package/tui/src/components/messages/UserPromptMessage.tsx +1 -2
  332. package/tui/src/components/messages/UserResourceUpdateMessage.tsx +1 -2
  333. package/tui/src/components/messages/UserTeammateMessage.tsx +1 -2
  334. package/tui/src/components/messages/UserTextMessage.tsx +1 -2
  335. package/tui/src/components/messages/UserToolResultMessage/RejectedPlanMessage.tsx +0 -1
  336. package/tui/src/components/messages/UserToolResultMessage/RejectedToolUseMessage.tsx +1 -2
  337. package/tui/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +1 -2
  338. package/tui/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +1 -2
  339. package/tui/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +1 -2
  340. package/tui/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +1 -2
  341. package/tui/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +1 -2
  342. package/tui/src/components/messages/UserToolResultMessage/utils.tsx +0 -1
  343. package/tui/src/components/messages/teamMemCollapsed.tsx +1 -2
  344. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx +1 -2
  345. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/PreviewBox.tsx +1 -2
  346. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/PreviewQuestionView.tsx +1 -2
  347. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/QuestionNavigationBar.tsx +1 -2
  348. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/QuestionView.tsx +1 -2
  349. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx +1 -2
  350. package/tui/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +1 -2
  351. package/tui/src/components/permissions/BashPermissionRequest/bashToolUseOptions.tsx +0 -1
  352. package/tui/src/components/permissions/ComputerUseApproval/ComputerUseApproval.tsx +0 -1
  353. package/tui/src/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx +0 -1
  354. package/tui/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx +0 -1
  355. package/tui/src/components/permissions/FallbackPermissionRequest.tsx +1 -2
  356. package/tui/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +1 -2
  357. package/tui/src/components/permissions/FilePermissionDialog/FilePermissionDialog.tsx +1 -2
  358. package/tui/src/components/permissions/FilePermissionDialog/permissionOptions.tsx +0 -1
  359. package/tui/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +1 -2
  360. package/tui/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +1 -2
  361. package/tui/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +1 -2
  362. package/tui/src/components/permissions/NotebookEditPermissionRequest/NotebookEditPermissionRequest.tsx +1 -2
  363. package/tui/src/components/permissions/NotebookEditPermissionRequest/NotebookEditToolDiff.tsx +1 -2
  364. package/tui/src/components/permissions/PermissionDecisionDebugInfo.tsx +1 -2
  365. package/tui/src/components/permissions/PermissionDialog.tsx +1 -2
  366. package/tui/src/components/permissions/PermissionExplanation.tsx +1 -2
  367. package/tui/src/components/permissions/PermissionPrompt.tsx +0 -1
  368. package/tui/src/components/permissions/PermissionRequest.tsx +0 -1
  369. package/tui/src/components/permissions/PermissionRequestTitle.tsx +1 -2
  370. package/tui/src/components/permissions/PermissionRuleExplanation.tsx +1 -2
  371. package/tui/src/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx +1 -2
  372. package/tui/src/components/permissions/PowerShellPermissionRequest/powershellToolUseOptions.tsx +0 -1
  373. package/tui/src/components/permissions/SandboxPermissionRequest.tsx +0 -1
  374. package/tui/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx +1 -2
  375. package/tui/src/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx +0 -1
  376. package/tui/src/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx +0 -1
  377. package/tui/src/components/permissions/WorkerBadge.tsx +1 -2
  378. package/tui/src/components/permissions/WorkerPendingPermission.tsx +1 -2
  379. package/tui/src/components/permissions/rules/AddPermissionRules.tsx +0 -1
  380. package/tui/src/components/permissions/rules/AddWorkspaceDirectory.tsx +0 -1
  381. package/tui/src/components/permissions/rules/PermissionRuleDescription.tsx +1 -2
  382. package/tui/src/components/permissions/rules/PermissionRuleInput.tsx +1 -2
  383. package/tui/src/components/permissions/rules/PermissionRuleList.tsx +0 -1
  384. package/tui/src/components/permissions/rules/RecentDenialsTab.tsx +1 -2
  385. package/tui/src/components/permissions/rules/RemoveWorkspaceDirectory.tsx +0 -1
  386. package/tui/src/components/permissions/rules/WorkspaceTab.tsx +1 -2
  387. package/tui/src/components/permissions/shellPermissionHelpers.tsx +1 -2
  388. package/tui/src/components/sandbox/SandboxConfigTab.tsx +1 -2
  389. package/tui/src/components/sandbox/SandboxDependenciesTab.tsx +1 -2
  390. package/tui/src/components/sandbox/SandboxDoctorSection.tsx +1 -2
  391. package/tui/src/components/sandbox/SandboxOverridesTab.tsx +0 -1
  392. package/tui/src/components/sandbox/SandboxSettings.tsx +0 -1
  393. package/tui/src/components/shell/ExpandShellOutputContext.tsx +1 -2
  394. package/tui/src/components/shell/OutputLine.tsx +1 -2
  395. package/tui/src/components/shell/ShellProgressMessage.tsx +1 -2
  396. package/tui/src/components/shell/ShellTimeDisplay.tsx +1 -2
  397. package/tui/src/components/skills/SkillsMenu.tsx +0 -1
  398. package/tui/src/components/tasks/AsyncAgentDetailDialog.tsx +1 -2
  399. package/tui/src/components/tasks/BackgroundTask.tsx +1 -2
  400. package/tui/src/components/tasks/BackgroundTaskStatus.tsx +1 -2
  401. package/tui/src/components/tasks/BackgroundTasksDialog.tsx +1 -2
  402. package/tui/src/components/tasks/DreamDetailDialog.tsx +1 -2
  403. package/tui/src/components/tasks/InProcessTeammateDetailDialog.tsx +1 -2
  404. package/tui/src/components/tasks/RemoteSessionDetailDialog.tsx +0 -1
  405. package/tui/src/components/tasks/RemoteSessionProgress.tsx +1 -2
  406. package/tui/src/components/tasks/ShellDetailDialog.tsx +1 -2
  407. package/tui/src/components/tasks/ShellProgress.tsx +1 -2
  408. package/tui/src/components/tasks/renderToolActivity.tsx +1 -2
  409. package/tui/src/components/tasks/taskStatusUtils.tsx +1 -2
  410. package/tui/src/components/teams/TeamStatus.tsx +1 -2
  411. package/tui/src/components/teams/TeamsDialog.tsx +1 -2
  412. package/tui/src/components/ui/OrderedList.tsx +1 -2
  413. package/tui/src/components/ui/OrderedListItem.tsx +1 -2
  414. package/tui/src/components/ui/TreeSelect.tsx +1 -2
  415. package/tui/src/components/wizard/WizardDialogLayout.tsx +1 -2
  416. package/tui/src/components/wizard/WizardNavigationFooter.tsx +1 -2
  417. package/tui/src/components/wizard/WizardProvider.tsx +1 -2
  418. package/tui/src/context/QueuedMessageContext.tsx +1 -2
  419. package/tui/src/context/fpsMetrics.tsx +1 -2
  420. package/tui/src/context/mailbox.tsx +1 -2
  421. package/tui/src/context/modalContext.tsx +1 -2
  422. package/tui/src/context/notifications.tsx +1 -2
  423. package/tui/src/context/overlayContext.tsx +1 -2
  424. package/tui/src/context/promptOverlayContext.tsx +1 -2
  425. package/tui/src/context/stats.tsx +1 -2
  426. package/tui/src/context/voice.tsx +1 -2
  427. package/tui/src/dialogLaunchers.tsx +0 -1
  428. package/tui/src/entrypoints/cli.tsx +0 -1
  429. package/tui/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx +0 -1
  430. package/tui/src/hooks/notifs/useDeprecationWarningNotification.tsx +1 -2
  431. package/tui/src/hooks/notifs/useFastModeNotification.tsx +1 -2
  432. package/tui/src/hooks/notifs/useIDEStatusIndicator.tsx +1 -2
  433. package/tui/src/hooks/notifs/useInstallMessages.tsx +1 -2
  434. package/tui/src/hooks/notifs/useLspInitializationNotification.tsx +1 -2
  435. package/tui/src/hooks/notifs/useMcpConnectivityStatus.tsx +0 -1
  436. package/tui/src/hooks/notifs/useModelMigrationNotifications.tsx +1 -2
  437. package/tui/src/hooks/notifs/useNpmDeprecationNotification.tsx +1 -2
  438. package/tui/src/hooks/notifs/usePluginAutoupdateNotification.tsx +1 -2
  439. package/tui/src/hooks/notifs/usePluginInstallationStatus.tsx +1 -2
  440. package/tui/src/hooks/notifs/useRateLimitWarningNotification.tsx +1 -2
  441. package/tui/src/hooks/notifs/useSettingsErrors.tsx +1 -2
  442. package/tui/src/hooks/useApiKeyVerification.ts +7 -7
  443. package/tui/src/hooks/useArrowKeyHistory.tsx +1 -2
  444. package/tui/src/hooks/useCanUseTool.tsx +0 -1
  445. package/tui/src/hooks/useChromeExtensionNotification.tsx +0 -1
  446. package/tui/src/hooks/useClaudeCodeHintRecommendation.tsx +1 -2
  447. package/tui/src/hooks/useCommandKeybindings.tsx +1 -2
  448. package/tui/src/hooks/useGlobalKeybindings.tsx +1 -2
  449. package/tui/src/hooks/useIDEIntegration.tsx +1 -2
  450. package/tui/src/hooks/useLspPluginRecommendation.tsx +1 -2
  451. package/tui/src/hooks/useOfficialMarketplaceNotification.tsx +0 -1
  452. package/tui/src/hooks/usePluginRecommendationBase.tsx +1 -2
  453. package/tui/src/hooks/usePromptsFromClaudeInChrome.tsx +1 -2
  454. package/tui/src/hooks/useReplBridge.tsx +1 -2
  455. package/tui/src/hooks/useTypeahead.tsx +0 -1
  456. package/tui/src/hooks/useVoiceIntegration.tsx +1 -2
  457. package/tui/src/ink/Ansi.tsx +1 -2
  458. package/tui/src/ink/components/AlternateScreen.tsx +1 -2
  459. package/tui/src/ink/components/App.tsx +0 -1
  460. package/tui/src/ink/components/Box.tsx +1 -2
  461. package/tui/src/ink/components/Button.tsx +1 -2
  462. package/tui/src/ink/components/ClockContext.tsx +1 -2
  463. package/tui/src/ink/components/ErrorOverview.tsx +1 -2
  464. package/tui/src/ink/components/Link.tsx +1 -2
  465. package/tui/src/ink/components/Newline.tsx +1 -2
  466. package/tui/src/ink/components/NoSelect.tsx +1 -2
  467. package/tui/src/ink/components/RawAnsi.tsx +1 -2
  468. package/tui/src/ink/components/ScrollBox.tsx +1 -2
  469. package/tui/src/ink/components/Spacer.tsx +1 -2
  470. package/tui/src/ink/components/TerminalFocusContext.tsx +1 -2
  471. package/tui/src/ink/components/TerminalSizeContext.tsx +1 -2
  472. package/tui/src/ink/components/Text.tsx +1 -2
  473. package/tui/src/ink/ink.tsx +0 -1
  474. package/tui/src/interactiveHelpers.tsx +0 -1
  475. package/tui/src/keybindings/KeybindingContext.tsx +1 -2
  476. package/tui/src/keybindings/KeybindingProviderSetup.tsx +1 -2
  477. package/tui/src/main.tsx +0 -1
  478. package/tui/src/moreright/useMoreRight.tsx +1 -2
  479. package/tui/src/replLauncher.tsx +1 -2
  480. package/tui/src/screens/Doctor.tsx +0 -1
  481. package/tui/src/screens/REPL.tsx +0 -1
  482. package/tui/src/screens/ResumeConversation.tsx +0 -1
  483. package/tui/src/services/mcp/MCPConnectionManager.tsx +1 -2
  484. package/tui/src/services/mcp/claudeai.ts +1 -1
  485. package/tui/src/services/mcpServerApproval.tsx +1 -2
  486. package/tui/src/services/oauth/client.ts +15 -15
  487. package/tui/src/services/oauth/getOauthProfile.ts +2 -2
  488. package/tui/src/services/oauth/index.ts +12 -18
  489. package/tui/src/services/policyLimits/index.ts +3 -3
  490. package/tui/src/services/remoteManagedSettings/index.ts +3 -3
  491. package/tui/src/services/remoteManagedSettings/securityCheck.tsx +1 -2
  492. package/tui/src/services/remoteManagedSettings/syncCache.ts +3 -3
  493. package/tui/src/services/voiceStreamSTT.ts +2 -2
  494. package/tui/src/state/AppState.tsx +1 -2
  495. package/tui/src/tasks/InProcessTeammateTask/InProcessTeammateTask.tsx +1 -2
  496. package/tui/src/tasks/LocalAgentTask/LocalAgentTask.tsx +1 -2
  497. package/tui/src/tasks/LocalShellTask/LocalShellTask.tsx +1 -2
  498. package/tui/src/tasks/RemoteAgentTask/RemoteAgentTask.tsx +0 -1
  499. package/tui/src/tools/AgentTool/UI.tsx +1 -2
  500. package/tui/src/tools/BashTool/BashToolResultMessage.tsx +0 -1
  501. package/tui/src/tools/BashTool/UI.tsx +1 -2
  502. package/tui/src/tools/BriefTool/UI.tsx +0 -1
  503. package/tui/src/tools/ConfigTool/ConfigTool.ts +2 -2
  504. package/tui/src/tools/ConfigTool/UI.tsx +1 -2
  505. package/tui/src/tools/EnterPlanModeTool/UI.tsx +0 -1
  506. package/tui/src/tools/EnterWorktreeTool/UI.tsx +1 -2
  507. package/tui/src/tools/ExitPlanModeTool/UI.tsx +0 -1
  508. package/tui/src/tools/ExitWorktreeTool/UI.tsx +1 -2
  509. package/tui/src/tools/FileEditTool/UI.tsx +1 -2
  510. package/tui/src/tools/FileReadTool/UI.tsx +1 -2
  511. package/tui/src/tools/FileWriteTool/UI.tsx +1 -2
  512. package/tui/src/tools/GlobTool/UI.tsx +1 -2
  513. package/tui/src/tools/GrepTool/UI.tsx +1 -2
  514. package/tui/src/tools/LSPTool/UI.tsx +1 -2
  515. package/tui/src/tools/ListMcpResourcesTool/UI.tsx +1 -2
  516. package/tui/src/tools/MCPTool/UI.tsx +1 -2
  517. package/tui/src/tools/NotebookEditTool/UI.tsx +1 -2
  518. package/tui/src/tools/PowerShellTool/UI.tsx +0 -1
  519. package/tui/src/tools/ReadMcpResourceTool/UI.tsx +1 -2
  520. package/tui/src/tools/RemoteTriggerTool/UI.tsx +1 -2
  521. package/tui/src/tools/ScheduleCronTool/UI.tsx +1 -2
  522. package/tui/src/tools/SendMessageTool/UI.tsx +1 -2
  523. package/tui/src/tools/SkillTool/UI.tsx +1 -2
  524. package/tui/src/tools/TaskStopTool/UI.tsx +1 -2
  525. package/tui/src/tools/TeamCreateTool/UI.tsx +1 -2
  526. package/tui/src/tools/TeamDeleteTool/UI.tsx +1 -2
  527. package/tui/src/tools/WebFetchTool/UI.tsx +1 -2
  528. package/tui/src/tools/testing/TestingPermissionTool.tsx +1 -2
  529. package/tui/src/utils/auth.ts +6 -12
  530. package/tui/src/utils/autoRunIssue.tsx +1 -2
  531. package/tui/src/utils/billing.ts +2 -2
  532. package/tui/src/utils/computerUse/toolRendering.tsx +1 -2
  533. package/tui/src/utils/computerUse/wrapper.tsx +0 -1
  534. package/tui/src/utils/exportRenderer.tsx +1 -2
  535. package/tui/src/utils/fastMode.ts +2 -2
  536. package/tui/src/utils/highlightMatch.tsx +1 -2
  537. package/tui/src/utils/http.ts +2 -2
  538. package/tui/src/utils/managedEnv.ts +1 -1
  539. package/tui/src/utils/messages/systemInit.ts +2 -2
  540. package/tui/src/utils/plugins/performStartupChecks.tsx +1 -2
  541. package/tui/src/utils/preflightChecks.tsx +0 -1
  542. package/tui/src/utils/processUserInput/processBashCommand.tsx +1 -2
  543. package/tui/src/utils/processUserInput/processSlashCommand.tsx +0 -1
  544. package/tui/src/utils/staticRender.tsx +1 -2
  545. package/tui/src/utils/status.tsx +1 -2
  546. package/tui/src/utils/statusNoticeDefinitions.tsx +5 -6
  547. package/tui/src/utils/swarm/It2SetupPrompt.tsx +1 -2
  548. package/tui/src/utils/teleport.tsx +0 -1
  549. package/tui/src/voice/voiceModeEnabled.ts +3 -3
  550. package/uv.lock +1 -1
  551. package/src/ummaya/llm/_cc_reference/README.md +0 -104
  552. package/src/ummaya/llm/_cc_reference/api.ts +0 -721
  553. package/src/ummaya/llm/_cc_reference/claude.ts +0 -3422
  554. package/src/ummaya/llm/_cc_reference/client.ts +0 -392
  555. package/src/ummaya/llm/_cc_reference/emptyUsage.ts +0 -25
  556. package/src/ummaya/llm/_cc_reference/errors.ts +0 -1210
  557. package/src/ummaya/llm/_cc_reference/messages.ts +0 -5515
  558. package/src/ummaya/llm/_cc_reference/permissions.ts +0 -1489
  559. package/src/ummaya/llm/_cc_reference/prompts.ts +0 -917
  560. package/src/ummaya/llm/_cc_reference/query.ts +0 -1732
  561. package/src/ummaya/llm/_cc_reference/toolExecution.ts +0 -1748
  562. package/src/ummaya/llm/_cc_reference/toolOrchestration.ts +0 -191
  563. package/src/ummaya/llm/_cc_reference/toolResultStorage.ts +0 -1043
  564. package/src/ummaya/llm/_cc_reference/tools.ts +0 -392
@@ -1,1748 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0 (Anthropic upstream) — research-use mirror
2
- // Source: .references/claude-code-sourcemap/restored-src/src/services/tools/toolExecution.ts (CC 2.1.88)
3
-
4
- import { feature } from 'bun:bundle'
5
- import type {
6
- ContentBlockParam,
7
- ToolResultBlockParam,
8
- ToolUseBlock,
9
- } from '@anthropic-ai/sdk/resources/index.mjs'
10
- import {
11
- type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
12
- logEvent,
13
- } from 'src/services/analytics/index.js'
14
- import {
15
- extractMcpToolDetails,
16
- extractSkillName,
17
- extractToolInputForTelemetry,
18
- getFileExtensionForAnalytics,
19
- getFileExtensionsFromBashCommand,
20
- isToolDetailsLoggingEnabled,
21
- mcpToolDetailsForAnalytics,
22
- sanitizeToolNameForAnalytics,
23
- } from 'src/services/analytics/metadata.js'
24
- import {
25
- addToToolDuration,
26
- getCodeEditToolDecisionCounter,
27
- getStatsStore,
28
- } from '../../bootstrap/state.js'
29
- import {
30
- buildCodeEditToolAttributes,
31
- isCodeEditingTool,
32
- } from '../../hooks/toolPermission/permissionLogging.js'
33
- import type { CanUseToolFn } from '../../hooks/useCanUseTool.js'
34
- import {
35
- findToolByName,
36
- type Tool,
37
- type ToolProgress,
38
- type ToolProgressData,
39
- type ToolUseContext,
40
- } from '../../Tool.js'
41
- import type { BashToolInput } from '../../tools/BashTool/BashTool.js'
42
- import { startSpeculativeClassifierCheck } from '../../tools/BashTool/bashPermissions.js'
43
- import { BASH_TOOL_NAME } from '../../tools/BashTool/toolName.js'
44
- import { FILE_EDIT_TOOL_NAME } from '../../tools/FileEditTool/constants.js'
45
- import { FILE_READ_TOOL_NAME } from '../../tools/FileReadTool/prompt.js'
46
- import { FILE_WRITE_TOOL_NAME } from '../../tools/FileWriteTool/prompt.js'
47
- import { NOTEBOOK_EDIT_TOOL_NAME } from '../../tools/NotebookEditTool/constants.js'
48
- import { POWERSHELL_TOOL_NAME } from '../../tools/PowerShellTool/toolName.js'
49
- import { parseGitCommitId } from '../../tools/shared/gitOperationTracking.js'
50
- import {
51
- isDeferredTool,
52
- TOOL_SEARCH_TOOL_NAME,
53
- } from '../../tools/ToolSearchTool/prompt.js'
54
- import { getAllBaseTools } from '../../tools.js'
55
- import type { HookProgress } from '../../types/hooks.js'
56
- import type {
57
- AssistantMessage,
58
- AttachmentMessage,
59
- Message,
60
- ProgressMessage,
61
- StopHookInfo,
62
- } from '../../types/message.js'
63
- import { count } from '../../utils/array.js'
64
- import { createAttachmentMessage } from '../../utils/attachments.js'
65
- import { logForDebugging } from '../../utils/debug.js'
66
- import {
67
- AbortError,
68
- errorMessage,
69
- getErrnoCode,
70
- ShellError,
71
- TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
72
- } from '../../utils/errors.js'
73
- import { executePermissionDeniedHooks } from '../../utils/hooks.js'
74
- import { logError } from '../../utils/log.js'
75
- import {
76
- CANCEL_MESSAGE,
77
- createProgressMessage,
78
- createStopHookSummaryMessage,
79
- createToolResultStopMessage,
80
- createUserMessage,
81
- withMemoryCorrectionHint,
82
- } from '../../utils/messages.js'
83
- import type {
84
- PermissionDecisionReason,
85
- PermissionResult,
86
- } from '../../utils/permissions/PermissionResult.js'
87
- import {
88
- startSessionActivity,
89
- stopSessionActivity,
90
- } from '../../utils/sessionActivity.js'
91
- import { jsonStringify } from '../../utils/slowOperations.js'
92
- import { Stream } from '../../utils/stream.js'
93
- import { logOTelEvent } from '../../utils/telemetry/events.js'
94
- import {
95
- addToolContentEvent,
96
- endToolBlockedOnUserSpan,
97
- endToolExecutionSpan,
98
- endToolSpan,
99
- isBetaTracingEnabled,
100
- startToolBlockedOnUserSpan,
101
- startToolExecutionSpan,
102
- startToolSpan,
103
- } from '../../utils/telemetry/sessionTracing.js'
104
- import {
105
- formatError,
106
- formatZodValidationError,
107
- } from '../../utils/toolErrors.js'
108
- import {
109
- processPreMappedToolResultBlock,
110
- processToolResultBlock,
111
- } from '../../utils/toolResultStorage.js'
112
- import {
113
- extractDiscoveredToolNames,
114
- isToolSearchEnabledOptimistic,
115
- isToolSearchToolAvailable,
116
- } from '../../utils/toolSearch.js'
117
- import {
118
- McpAuthError,
119
- McpToolCallError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
120
- } from '../mcp/client.js'
121
- import { mcpInfoFromString } from '../mcp/mcpStringUtils.js'
122
- import { normalizeNameForMCP } from '../mcp/normalization.js'
123
- import type { MCPServerConnection } from '../mcp/types.js'
124
- import {
125
- getLoggingSafeMcpBaseUrl,
126
- getMcpServerScopeFromToolName,
127
- isMcpTool,
128
- } from '../mcp/utils.js'
129
- import {
130
- resolveHookPermissionDecision,
131
- runPostToolUseFailureHooks,
132
- runPostToolUseHooks,
133
- runPreToolUseHooks,
134
- } from './toolHooks.js'
135
-
136
- /** Minimum total hook duration (ms) to show inline timing summary */
137
- export const HOOK_TIMING_DISPLAY_THRESHOLD_MS = 500
138
- /** Log a debug warning when hooks/permission-decision block for this long. Matches
139
- * BashTool's PROGRESS_THRESHOLD_MS — the collapsed view feels stuck past this. */
140
- const SLOW_PHASE_LOG_THRESHOLD_MS = 2000
141
-
142
- /**
143
- * Classify a tool execution error into a telemetry-safe string.
144
- *
145
- * In minified/external builds, `error.constructor.name` is mangled into
146
- * short identifiers like "nJT" or "Chq" — useless for diagnostics.
147
- * This function extracts structured, telemetry-safe information instead:
148
- * - TelemetrySafeError: use its telemetryMessage (already vetted)
149
- * - Node.js fs errors: log the error code (ENOENT, EACCES, etc.)
150
- * - Known error types: use their unminified name
151
- * - Fallback: "Error" (better than a mangled 3-char identifier)
152
- */
153
- export function classifyToolError(error: unknown): string {
154
- if (
155
- error instanceof TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
156
- ) {
157
- return error.telemetryMessage.slice(0, 200)
158
- }
159
- if (error instanceof Error) {
160
- // Node.js filesystem errors have a `code` property (ENOENT, EACCES, etc.)
161
- // These are safe to log and much more useful than the constructor name.
162
- const errnoCode = getErrnoCode(error)
163
- if (typeof errnoCode === 'string') {
164
- return `Error:${errnoCode}`
165
- }
166
- // ShellError, ImageSizeError, etc. have stable `.name` properties
167
- // that survive minification (they're set in the constructor).
168
- if (error.name && error.name !== 'Error' && error.name.length > 3) {
169
- return error.name.slice(0, 60)
170
- }
171
- return 'Error'
172
- }
173
- return 'UnknownError'
174
- }
175
-
176
- /**
177
- * Map a rule's origin to the documented OTel `source` vocabulary, matching
178
- * the interactive path's semantics (permissionLogging.ts:81): session-scoped
179
- * grants are temporary, on-disk grants are permanent, and user-authored
180
- * denies are user_reject regardless of persistence. Everything the user
181
- * didn't write (cliArg, policySettings, projectSettings, flagSettings) is
182
- * config.
183
- */
184
- function ruleSourceToOTelSource(
185
- ruleSource: string,
186
- behavior: 'allow' | 'deny',
187
- ): string {
188
- switch (ruleSource) {
189
- case 'session':
190
- return behavior === 'allow' ? 'user_temporary' : 'user_reject'
191
- case 'localSettings':
192
- case 'userSettings':
193
- return behavior === 'allow' ? 'user_permanent' : 'user_reject'
194
- default:
195
- return 'config'
196
- }
197
- }
198
-
199
- /**
200
- * Map a PermissionDecisionReason to the OTel `source` label for the
201
- * non-interactive tool_decision path, staying within the documented
202
- * vocabulary (config, hook, user_permanent, user_temporary, user_reject).
203
- *
204
- * For permissionPromptTool, the SDK host may set decisionClassification on
205
- * the PermissionResult to tell us exactly what happened (once vs always vs
206
- * cache hit — the host knows, we can't tell from {behavior:'allow'} alone).
207
- * Without it, we fall back conservatively: allow → user_temporary,
208
- * deny → user_reject.
209
- */
210
- function decisionReasonToOTelSource(
211
- reason: PermissionDecisionReason | undefined,
212
- behavior: 'allow' | 'deny',
213
- ): string {
214
- if (!reason) {
215
- return 'config'
216
- }
217
- switch (reason.type) {
218
- case 'permissionPromptTool': {
219
- // toolResult is typed `unknown` on PermissionDecisionReason but carries
220
- // the parsed Output from PermissionPromptToolResultSchema. Narrow at
221
- // runtime rather than widen the cross-file type.
222
- const toolResult = reason.toolResult as
223
- | { decisionClassification?: string }
224
- | undefined
225
- const classified = toolResult?.decisionClassification
226
- if (
227
- classified === 'user_temporary' ||
228
- classified === 'user_permanent' ||
229
- classified === 'user_reject'
230
- ) {
231
- return classified
232
- }
233
- return behavior === 'allow' ? 'user_temporary' : 'user_reject'
234
- }
235
- case 'rule':
236
- return ruleSourceToOTelSource(reason.rule.source, behavior)
237
- case 'hook':
238
- return 'hook'
239
- case 'mode':
240
- case 'classifier':
241
- case 'subcommandResults':
242
- case 'asyncAgent':
243
- case 'sandboxOverride':
244
- case 'workingDir':
245
- case 'safetyCheck':
246
- case 'other':
247
- return 'config'
248
- default: {
249
- const _exhaustive: never = reason
250
- return 'config'
251
- }
252
- }
253
- }
254
-
255
- function getNextImagePasteId(messages: Message[]): number {
256
- let maxId = 0
257
- for (const message of messages) {
258
- if (message.type === 'user' && message.imagePasteIds) {
259
- for (const id of message.imagePasteIds) {
260
- if (id > maxId) maxId = id
261
- }
262
- }
263
- }
264
- return maxId + 1
265
- }
266
-
267
- export type MessageUpdateLazy<M extends Message = Message> = {
268
- message: M
269
- contextModifier?: {
270
- toolUseID: string
271
- modifyContext: (context: ToolUseContext) => ToolUseContext
272
- }
273
- }
274
-
275
- export type McpServerType =
276
- | 'stdio'
277
- | 'sse'
278
- | 'http'
279
- | 'ws'
280
- | 'sdk'
281
- | 'sse-ide'
282
- | 'ws-ide'
283
- | 'claudeai-proxy'
284
- | undefined
285
-
286
- function findMcpServerConnection(
287
- toolName: string,
288
- mcpClients: MCPServerConnection[],
289
- ): MCPServerConnection | undefined {
290
- if (!toolName.startsWith('mcp__')) {
291
- return undefined
292
- }
293
-
294
- const mcpInfo = mcpInfoFromString(toolName)
295
- if (!mcpInfo) {
296
- return undefined
297
- }
298
-
299
- // mcpInfo.serverName is normalized (e.g., "claude_ai_Slack"), but client.name
300
- // is the original name (e.g., "claude.ai Slack"). Normalize both for comparison.
301
- return mcpClients.find(
302
- client => normalizeNameForMCP(client.name) === mcpInfo.serverName,
303
- )
304
- }
305
-
306
- /**
307
- * Extracts the MCP server transport type from a tool name.
308
- * Returns the server type (stdio, sse, http, ws, sdk, etc.) for MCP tools,
309
- * or undefined for built-in tools.
310
- */
311
- function getMcpServerType(
312
- toolName: string,
313
- mcpClients: MCPServerConnection[],
314
- ): McpServerType {
315
- const serverConnection = findMcpServerConnection(toolName, mcpClients)
316
-
317
- if (serverConnection?.type === 'connected') {
318
- // Handle stdio configs where type field is optional (defaults to 'stdio')
319
- return serverConnection.config.type ?? 'stdio'
320
- }
321
-
322
- return undefined
323
- }
324
-
325
- /**
326
- * Extracts the MCP server base URL for a tool by looking up its server connection.
327
- * Returns undefined for stdio servers, built-in tools, or if the server is not connected.
328
- */
329
- function getMcpServerBaseUrlFromToolName(
330
- toolName: string,
331
- mcpClients: MCPServerConnection[],
332
- ): string | undefined {
333
- const serverConnection = findMcpServerConnection(toolName, mcpClients)
334
- if (serverConnection?.type !== 'connected') {
335
- return undefined
336
- }
337
- return getLoggingSafeMcpBaseUrl(serverConnection.config)
338
- }
339
-
340
- export async function* runToolUse(
341
- toolUse: ToolUseBlock,
342
- assistantMessage: AssistantMessage,
343
- canUseTool: CanUseToolFn,
344
- toolUseContext: ToolUseContext,
345
- ): AsyncGenerator<MessageUpdateLazy, void> {
346
- const toolName = toolUse.name
347
- // First try to find in the available tools (what the model sees)
348
- let tool = findToolByName(toolUseContext.options.tools, toolName)
349
-
350
- // If not found, check if it's a deprecated tool being called by alias
351
- // (e.g., old transcripts calling "KillShell" which is now an alias for "TaskStop")
352
- // Only fall back for tools where the name matches an alias, not the primary name
353
- if (!tool) {
354
- const fallbackTool = findToolByName(getAllBaseTools(), toolName)
355
- // Only use fallback if the tool was found via alias (deprecated name)
356
- if (fallbackTool && fallbackTool.aliases?.includes(toolName)) {
357
- tool = fallbackTool
358
- }
359
- }
360
- const messageId = assistantMessage.message.id
361
- const requestId = assistantMessage.requestId
362
- const mcpServerType = getMcpServerType(
363
- toolName,
364
- toolUseContext.options.mcpClients,
365
- )
366
- const mcpServerBaseUrl = getMcpServerBaseUrlFromToolName(
367
- toolName,
368
- toolUseContext.options.mcpClients,
369
- )
370
-
371
- // Check if the tool exists
372
- if (!tool) {
373
- const sanitizedToolName = sanitizeToolNameForAnalytics(toolName)
374
- logForDebugging(`Unknown tool ${toolName}: ${toolUse.id}`)
375
- logEvent('tengu_tool_use_error', {
376
- error:
377
- `No such tool available: ${sanitizedToolName}` as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
378
- toolName: sanitizedToolName,
379
- toolUseID:
380
- toolUse.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
381
- isMcp: toolName.startsWith('mcp__'),
382
- queryChainId: toolUseContext.queryTracking
383
- ?.chainId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
384
- queryDepth: toolUseContext.queryTracking?.depth,
385
- ...(mcpServerType && {
386
- mcpServerType:
387
- mcpServerType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
388
- }),
389
- ...(mcpServerBaseUrl && {
390
- mcpServerBaseUrl:
391
- mcpServerBaseUrl as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
392
- }),
393
- ...(requestId && {
394
- requestId:
395
- requestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
396
- }),
397
- ...mcpToolDetailsForAnalytics(toolName, mcpServerType, mcpServerBaseUrl),
398
- })
399
- yield {
400
- message: createUserMessage({
401
- content: [
402
- {
403
- type: 'tool_result',
404
- content: `<tool_use_error>Error: No such tool available: ${toolName}</tool_use_error>`,
405
- is_error: true,
406
- tool_use_id: toolUse.id,
407
- },
408
- ],
409
- toolUseResult: `Error: No such tool available: ${toolName}`,
410
- sourceToolAssistantUUID: assistantMessage.uuid,
411
- }),
412
- }
413
- return
414
- }
415
-
416
- const toolInput = toolUse.input as { [key: string]: string }
417
- try {
418
- if (toolUseContext.abortController.signal.aborted) {
419
- logEvent('tengu_tool_use_cancelled', {
420
- toolName: sanitizeToolNameForAnalytics(tool.name),
421
- toolUseID:
422
- toolUse.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
423
- isMcp: tool.isMcp ?? false,
424
-
425
- queryChainId: toolUseContext.queryTracking
426
- ?.chainId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
427
- queryDepth: toolUseContext.queryTracking?.depth,
428
- ...(mcpServerType && {
429
- mcpServerType:
430
- mcpServerType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
431
- }),
432
- ...(mcpServerBaseUrl && {
433
- mcpServerBaseUrl:
434
- mcpServerBaseUrl as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
435
- }),
436
- ...(requestId && {
437
- requestId:
438
- requestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
439
- }),
440
- ...mcpToolDetailsForAnalytics(
441
- tool.name,
442
- mcpServerType,
443
- mcpServerBaseUrl,
444
- ),
445
- })
446
- const content = createToolResultStopMessage(toolUse.id)
447
- content.content = withMemoryCorrectionHint(CANCEL_MESSAGE)
448
- yield {
449
- message: createUserMessage({
450
- content: [content],
451
- toolUseResult: CANCEL_MESSAGE,
452
- sourceToolAssistantUUID: assistantMessage.uuid,
453
- }),
454
- }
455
- return
456
- }
457
-
458
- for await (const update of streamedCheckPermissionsAndCallTool(
459
- tool,
460
- toolUse.id,
461
- toolInput,
462
- toolUseContext,
463
- canUseTool,
464
- assistantMessage,
465
- messageId,
466
- requestId,
467
- mcpServerType,
468
- mcpServerBaseUrl,
469
- )) {
470
- yield update
471
- }
472
- } catch (error) {
473
- logError(error)
474
- const errorMessage = error instanceof Error ? error.message : String(error)
475
- const toolInfo = tool ? ` (${tool.name})` : ''
476
- const detailedError = `Error calling tool${toolInfo}: ${errorMessage}`
477
-
478
- yield {
479
- message: createUserMessage({
480
- content: [
481
- {
482
- type: 'tool_result',
483
- content: `<tool_use_error>${detailedError}</tool_use_error>`,
484
- is_error: true,
485
- tool_use_id: toolUse.id,
486
- },
487
- ],
488
- toolUseResult: detailedError,
489
- sourceToolAssistantUUID: assistantMessage.uuid,
490
- }),
491
- }
492
- }
493
- }
494
-
495
- function streamedCheckPermissionsAndCallTool(
496
- tool: Tool,
497
- toolUseID: string,
498
- input: { [key: string]: boolean | string | number },
499
- toolUseContext: ToolUseContext,
500
- canUseTool: CanUseToolFn,
501
- assistantMessage: AssistantMessage,
502
- messageId: string,
503
- requestId: string | undefined,
504
- mcpServerType: McpServerType,
505
- mcpServerBaseUrl: ReturnType<typeof getLoggingSafeMcpBaseUrl>,
506
- ): AsyncIterable<MessageUpdateLazy> {
507
- // This is a bit of a hack to get progress events and final results
508
- // into a single async iterable.
509
- //
510
- // Ideally the progress reporting and tool call reporting would
511
- // be via separate mechanisms.
512
- const stream = new Stream<MessageUpdateLazy>()
513
- checkPermissionsAndCallTool(
514
- tool,
515
- toolUseID,
516
- input,
517
- toolUseContext,
518
- canUseTool,
519
- assistantMessage,
520
- messageId,
521
- requestId,
522
- mcpServerType,
523
- mcpServerBaseUrl,
524
- progress => {
525
- logEvent('tengu_tool_use_progress', {
526
- messageID:
527
- messageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
528
- toolName: sanitizeToolNameForAnalytics(tool.name),
529
- isMcp: tool.isMcp ?? false,
530
-
531
- queryChainId: toolUseContext.queryTracking
532
- ?.chainId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
533
- queryDepth: toolUseContext.queryTracking?.depth,
534
- ...(mcpServerType && {
535
- mcpServerType:
536
- mcpServerType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
537
- }),
538
- ...(mcpServerBaseUrl && {
539
- mcpServerBaseUrl:
540
- mcpServerBaseUrl as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
541
- }),
542
- ...(requestId && {
543
- requestId:
544
- requestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
545
- }),
546
- ...mcpToolDetailsForAnalytics(
547
- tool.name,
548
- mcpServerType,
549
- mcpServerBaseUrl,
550
- ),
551
- })
552
- stream.enqueue({
553
- message: createProgressMessage({
554
- toolUseID: progress.toolUseID,
555
- parentToolUseID: toolUseID,
556
- data: progress.data,
557
- }),
558
- })
559
- },
560
- )
561
- .then(results => {
562
- for (const result of results) {
563
- stream.enqueue(result)
564
- }
565
- })
566
- .catch(error => {
567
- stream.error(error)
568
- })
569
- .finally(() => {
570
- stream.done()
571
- })
572
- return stream
573
- }
574
-
575
- /**
576
- * Appended to Zod errors when a deferred tool wasn't in the discovered-tool
577
- * set — re-runs the claude.ts schema-filter scan dispatch-time to detect the
578
- * mismatch. The raw Zod error ("expected array, got string") doesn't tell the
579
- * model to re-load the tool; this hint does. Null if the schema was sent.
580
- */
581
- export function buildSchemaNotSentHint(
582
- tool: Tool,
583
- messages: Message[],
584
- tools: readonly { name: string }[],
585
- ): string | null {
586
- // Optimistic gating — reconstructing claude.ts's full useToolSearch
587
- // computation is fragile. These two gates prevent pointing at a ToolSearch
588
- // that isn't callable; occasional misfires (Haiku, tst-auto below threshold)
589
- // cost one extra round-trip on an already-failing path.
590
- if (!isToolSearchEnabledOptimistic()) return null
591
- if (!isToolSearchToolAvailable(tools)) return null
592
- if (!isDeferredTool(tool)) return null
593
- const discovered = extractDiscoveredToolNames(messages)
594
- if (discovered.has(tool.name)) return null
595
- return (
596
- `\n\nThis tool's schema was not sent to the API — it was not in the discovered-tool set derived from message history. ` +
597
- `Without the schema in your prompt, typed parameters (arrays, numbers, booleans) get emitted as strings and the client-side parser rejects them. ` +
598
- `Load the tool first: call ${TOOL_SEARCH_TOOL_NAME} with query "select:${tool.name}", then retry this call.`
599
- )
600
- }
601
-
602
- async function checkPermissionsAndCallTool(
603
- tool: Tool,
604
- toolUseID: string,
605
- input: { [key: string]: boolean | string | number },
606
- toolUseContext: ToolUseContext,
607
- canUseTool: CanUseToolFn,
608
- assistantMessage: AssistantMessage,
609
- messageId: string,
610
- requestId: string | undefined,
611
- mcpServerType: McpServerType,
612
- mcpServerBaseUrl: ReturnType<typeof getLoggingSafeMcpBaseUrl>,
613
- onToolProgress: (
614
- progress: ToolProgress<ToolProgressData> | ProgressMessage<HookProgress>,
615
- ) => void,
616
- ): Promise<MessageUpdateLazy[]> {
617
- // Validate input types with zod (surprisingly, the model is not great at generating valid input)
618
- const parsedInput = tool.inputSchema.safeParse(input)
619
- if (!parsedInput.success) {
620
- let errorContent = formatZodValidationError(tool.name, parsedInput.error)
621
-
622
- const schemaHint = buildSchemaNotSentHint(
623
- tool,
624
- toolUseContext.messages,
625
- toolUseContext.options.tools,
626
- )
627
- if (schemaHint) {
628
- logEvent('tengu_deferred_tool_schema_not_sent', {
629
- toolName: sanitizeToolNameForAnalytics(tool.name),
630
- isMcp: tool.isMcp ?? false,
631
- })
632
- errorContent += schemaHint
633
- }
634
-
635
- logForDebugging(
636
- `${tool.name} tool input error: ${errorContent.slice(0, 200)}`,
637
- )
638
- logEvent('tengu_tool_use_error', {
639
- error:
640
- 'InputValidationError' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
641
- errorDetails: errorContent.slice(
642
- 0,
643
- 2000,
644
- ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
645
- messageID:
646
- messageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
647
- toolName: sanitizeToolNameForAnalytics(tool.name),
648
- isMcp: tool.isMcp ?? false,
649
-
650
- queryChainId: toolUseContext.queryTracking
651
- ?.chainId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
652
- queryDepth: toolUseContext.queryTracking?.depth,
653
- ...(mcpServerType && {
654
- mcpServerType:
655
- mcpServerType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
656
- }),
657
- ...(mcpServerBaseUrl && {
658
- mcpServerBaseUrl:
659
- mcpServerBaseUrl as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
660
- }),
661
- ...(requestId && {
662
- requestId:
663
- requestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
664
- }),
665
- ...mcpToolDetailsForAnalytics(tool.name, mcpServerType, mcpServerBaseUrl),
666
- })
667
- return [
668
- {
669
- message: createUserMessage({
670
- content: [
671
- {
672
- type: 'tool_result',
673
- content: `<tool_use_error>InputValidationError: ${errorContent}</tool_use_error>`,
674
- is_error: true,
675
- tool_use_id: toolUseID,
676
- },
677
- ],
678
- toolUseResult: `InputValidationError: ${parsedInput.error.message}`,
679
- sourceToolAssistantUUID: assistantMessage.uuid,
680
- }),
681
- },
682
- ]
683
- }
684
-
685
- // Validate input values. Each tool has its own validation logic
686
- const isValidCall = await tool.validateInput?.(
687
- parsedInput.data,
688
- toolUseContext,
689
- )
690
- if (isValidCall?.result === false) {
691
- logForDebugging(
692
- `${tool.name} tool validation error: ${isValidCall.message?.slice(0, 200)}`,
693
- )
694
- logEvent('tengu_tool_use_error', {
695
- messageID:
696
- messageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
697
- toolName: sanitizeToolNameForAnalytics(tool.name),
698
- error:
699
- isValidCall.message as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
700
- errorCode: isValidCall.errorCode,
701
- isMcp: tool.isMcp ?? false,
702
-
703
- queryChainId: toolUseContext.queryTracking
704
- ?.chainId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
705
- queryDepth: toolUseContext.queryTracking?.depth,
706
- ...(mcpServerType && {
707
- mcpServerType:
708
- mcpServerType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
709
- }),
710
- ...(mcpServerBaseUrl && {
711
- mcpServerBaseUrl:
712
- mcpServerBaseUrl as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
713
- }),
714
- ...(requestId && {
715
- requestId:
716
- requestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
717
- }),
718
- ...mcpToolDetailsForAnalytics(tool.name, mcpServerType, mcpServerBaseUrl),
719
- })
720
- return [
721
- {
722
- message: createUserMessage({
723
- content: [
724
- {
725
- type: 'tool_result',
726
- content: `<tool_use_error>${isValidCall.message}</tool_use_error>`,
727
- is_error: true,
728
- tool_use_id: toolUseID,
729
- },
730
- ],
731
- toolUseResult: `Error: ${isValidCall.message}`,
732
- sourceToolAssistantUUID: assistantMessage.uuid,
733
- }),
734
- },
735
- ]
736
- }
737
- // Speculatively start the bash allow classifier check early so it runs in
738
- // parallel with pre-tool hooks, deny/ask classifiers, and permission dialog
739
- // setup. The UI indicator (setClassifierChecking) is NOT set here — it's
740
- // set in interactiveHandler.ts only when the permission check returns `ask`
741
- // with a pendingClassifierCheck. This avoids flashing "classifier running"
742
- // for commands that auto-allow via prefix rules.
743
- if (
744
- tool.name === BASH_TOOL_NAME &&
745
- parsedInput.data &&
746
- 'command' in parsedInput.data
747
- ) {
748
- const appState = toolUseContext.getAppState()
749
- startSpeculativeClassifierCheck(
750
- (parsedInput.data as BashToolInput).command,
751
- appState.toolPermissionContext,
752
- toolUseContext.abortController.signal,
753
- toolUseContext.options.isNonInteractiveSession,
754
- )
755
- }
756
-
757
- const resultingMessages = []
758
-
759
- // Defense-in-depth: strip _simulatedSedEdit from model-provided Bash input.
760
- // This field is internal-only — it must only be injected by the permission
761
- // system (SedEditPermissionRequest) after user approval. If the model supplies
762
- // it, the schema's strictObject should already reject it, but we strip here
763
- // as a safeguard against future regressions.
764
- let processedInput = parsedInput.data
765
- if (
766
- tool.name === BASH_TOOL_NAME &&
767
- processedInput &&
768
- typeof processedInput === 'object' &&
769
- '_simulatedSedEdit' in processedInput
770
- ) {
771
- const { _simulatedSedEdit: _, ...rest } =
772
- processedInput as typeof processedInput & {
773
- _simulatedSedEdit: unknown
774
- }
775
- processedInput = rest as typeof processedInput
776
- }
777
-
778
- // Backfill legacy/derived fields on a shallow clone so hooks/canUseTool see
779
- // them without affecting tool.call(). SendMessageTool adds fields; file
780
- // tools overwrite file_path with expandPath — that mutation must not reach
781
- // call() because tool results embed the input path verbatim (e.g. "File
782
- // created successfully at: {path}"), and changing it alters the serialized
783
- // transcript and VCR fixture hashes. If a hook/permission later returns a
784
- // fresh updatedInput, callInput converges on it below — that replacement
785
- // is intentional and should reach call().
786
- let callInput = processedInput
787
- const backfilledClone =
788
- tool.backfillObservableInput &&
789
- typeof processedInput === 'object' &&
790
- processedInput !== null
791
- ? ({ ...processedInput } as typeof processedInput)
792
- : null
793
- if (backfilledClone) {
794
- tool.backfillObservableInput!(backfilledClone as Record<string, unknown>)
795
- processedInput = backfilledClone
796
- }
797
-
798
- let shouldPreventContinuation = false
799
- let stopReason: string | undefined
800
- let hookPermissionResult: PermissionResult | undefined
801
- const preToolHookInfos: StopHookInfo[] = []
802
- const preToolHookStart = Date.now()
803
- for await (const result of runPreToolUseHooks(
804
- toolUseContext,
805
- tool,
806
- processedInput,
807
- toolUseID,
808
- assistantMessage.message.id,
809
- requestId,
810
- mcpServerType,
811
- mcpServerBaseUrl,
812
- )) {
813
- switch (result.type) {
814
- case 'message':
815
- if (result.message.message.type === 'progress') {
816
- onToolProgress(result.message.message)
817
- } else {
818
- resultingMessages.push(result.message)
819
- const att = result.message.message.attachment
820
- if (
821
- att &&
822
- 'command' in att &&
823
- att.command !== undefined &&
824
- 'durationMs' in att &&
825
- att.durationMs !== undefined
826
- ) {
827
- preToolHookInfos.push({
828
- command: att.command,
829
- durationMs: att.durationMs,
830
- })
831
- }
832
- }
833
- break
834
- case 'hookPermissionResult':
835
- hookPermissionResult = result.hookPermissionResult
836
- break
837
- case 'hookUpdatedInput':
838
- // Hook provided updatedInput without making a permission decision (passthrough)
839
- // Update processedInput so it's used in the normal permission flow
840
- processedInput = result.updatedInput
841
- break
842
- case 'preventContinuation':
843
- shouldPreventContinuation = result.shouldPreventContinuation
844
- break
845
- case 'stopReason':
846
- stopReason = result.stopReason
847
- break
848
- case 'additionalContext':
849
- resultingMessages.push(result.message)
850
- break
851
- case 'stop':
852
- getStatsStore()?.observe(
853
- 'pre_tool_hook_duration_ms',
854
- Date.now() - preToolHookStart,
855
- )
856
- resultingMessages.push({
857
- message: createUserMessage({
858
- content: [createToolResultStopMessage(toolUseID)],
859
- toolUseResult: `Error: ${stopReason}`,
860
- sourceToolAssistantUUID: assistantMessage.uuid,
861
- }),
862
- })
863
- return resultingMessages
864
- }
865
- }
866
- const preToolHookDurationMs = Date.now() - preToolHookStart
867
- getStatsStore()?.observe('pre_tool_hook_duration_ms', preToolHookDurationMs)
868
- if (preToolHookDurationMs >= SLOW_PHASE_LOG_THRESHOLD_MS) {
869
- logForDebugging(
870
- `Slow PreToolUse hooks: ${preToolHookDurationMs}ms for ${tool.name} (${preToolHookInfos.length} hooks)`,
871
- { level: 'info' },
872
- )
873
- }
874
-
875
- // Emit PreToolUse summary immediately so it's visible while the tool executes.
876
- // Use wall-clock time (not sum of individual durations) since hooks run in parallel.
877
- if (process.env.USER_TYPE === 'ant' && preToolHookInfos.length > 0) {
878
- if (preToolHookDurationMs > HOOK_TIMING_DISPLAY_THRESHOLD_MS) {
879
- resultingMessages.push({
880
- message: createStopHookSummaryMessage(
881
- preToolHookInfos.length,
882
- preToolHookInfos,
883
- [],
884
- false,
885
- undefined,
886
- false,
887
- 'suggestion',
888
- undefined,
889
- 'PreToolUse',
890
- preToolHookDurationMs,
891
- ),
892
- })
893
- }
894
- }
895
-
896
- const toolAttributes: Record<string, string | number | boolean> = {}
897
- if (processedInput && typeof processedInput === 'object') {
898
- if (tool.name === FILE_READ_TOOL_NAME && 'file_path' in processedInput) {
899
- toolAttributes.file_path = String(processedInput.file_path)
900
- } else if (
901
- (tool.name === FILE_EDIT_TOOL_NAME ||
902
- tool.name === FILE_WRITE_TOOL_NAME) &&
903
- 'file_path' in processedInput
904
- ) {
905
- toolAttributes.file_path = String(processedInput.file_path)
906
- } else if (tool.name === BASH_TOOL_NAME && 'command' in processedInput) {
907
- const bashInput = processedInput as BashToolInput
908
- toolAttributes.full_command = bashInput.command
909
- }
910
- }
911
-
912
- startToolSpan(
913
- tool.name,
914
- toolAttributes,
915
- isBetaTracingEnabled() ? jsonStringify(processedInput) : undefined,
916
- )
917
- startToolBlockedOnUserSpan()
918
-
919
- // Check whether we have permission to use the tool,
920
- // and ask the user for permission if we don't
921
- const permissionMode = toolUseContext.getAppState().toolPermissionContext.mode
922
- const permissionStart = Date.now()
923
-
924
- const resolved = await resolveHookPermissionDecision(
925
- hookPermissionResult,
926
- tool,
927
- processedInput,
928
- toolUseContext,
929
- canUseTool,
930
- assistantMessage,
931
- toolUseID,
932
- )
933
- const permissionDecision = resolved.decision
934
- processedInput = resolved.input
935
- const permissionDurationMs = Date.now() - permissionStart
936
- // In auto mode, canUseTool awaits the classifier (side_query) — if that's
937
- // slow the collapsed view shows "Running…" with no (Ns) tick since
938
- // bash_progress hasn't started yet. Auto-only: in default mode this timer
939
- // includes interactive-dialog wait (user think time), which is just noise.
940
- if (
941
- permissionDurationMs >= SLOW_PHASE_LOG_THRESHOLD_MS &&
942
- permissionMode === 'auto'
943
- ) {
944
- logForDebugging(
945
- `Slow permission decision: ${permissionDurationMs}ms for ${tool.name} ` +
946
- `(mode=${permissionMode}, behavior=${permissionDecision.behavior})`,
947
- { level: 'info' },
948
- )
949
- }
950
-
951
- // Emit tool_decision OTel event and code-edit counter if the interactive
952
- // permission path didn't already log it (headless mode bypasses permission
953
- // logging, so we need to emit both the generic event and the code-edit
954
- // counter here)
955
- if (
956
- permissionDecision.behavior !== 'ask' &&
957
- !toolUseContext.toolDecisions?.has(toolUseID)
958
- ) {
959
- const decision =
960
- permissionDecision.behavior === 'allow' ? 'accept' : 'reject'
961
- const source = decisionReasonToOTelSource(
962
- permissionDecision.decisionReason,
963
- permissionDecision.behavior,
964
- )
965
- void logOTelEvent('tool_decision', {
966
- decision,
967
- source,
968
- tool_name: sanitizeToolNameForAnalytics(tool.name),
969
- })
970
-
971
- // Increment code-edit tool decision counter for headless mode
972
- if (isCodeEditingTool(tool.name)) {
973
- void buildCodeEditToolAttributes(
974
- tool,
975
- processedInput,
976
- decision,
977
- source,
978
- ).then(attributes => getCodeEditToolDecisionCounter()?.add(1, attributes))
979
- }
980
- }
981
-
982
- // Add message if permission was granted/denied by PermissionRequest hook
983
- if (
984
- permissionDecision.decisionReason?.type === 'hook' &&
985
- permissionDecision.decisionReason.hookName === 'PermissionRequest' &&
986
- permissionDecision.behavior !== 'ask'
987
- ) {
988
- resultingMessages.push({
989
- message: createAttachmentMessage({
990
- type: 'hook_permission_decision',
991
- decision: permissionDecision.behavior,
992
- toolUseID,
993
- hookEvent: 'PermissionRequest',
994
- }),
995
- })
996
- }
997
-
998
- if (permissionDecision.behavior !== 'allow') {
999
- logForDebugging(`${tool.name} tool permission denied`)
1000
- const decisionInfo = toolUseContext.toolDecisions?.get(toolUseID)
1001
- endToolBlockedOnUserSpan('reject', decisionInfo?.source || 'unknown')
1002
- endToolSpan()
1003
-
1004
- logEvent('tengu_tool_use_can_use_tool_rejected', {
1005
- messageID:
1006
- messageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1007
- toolName: sanitizeToolNameForAnalytics(tool.name),
1008
-
1009
- queryChainId: toolUseContext.queryTracking
1010
- ?.chainId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1011
- queryDepth: toolUseContext.queryTracking?.depth,
1012
- ...(mcpServerType && {
1013
- mcpServerType:
1014
- mcpServerType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1015
- }),
1016
- ...(mcpServerBaseUrl && {
1017
- mcpServerBaseUrl:
1018
- mcpServerBaseUrl as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1019
- }),
1020
- ...(requestId && {
1021
- requestId:
1022
- requestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1023
- }),
1024
- ...mcpToolDetailsForAnalytics(tool.name, mcpServerType, mcpServerBaseUrl),
1025
- })
1026
- let errorMessage = permissionDecision.message
1027
- // Only use generic "Execution stopped" message if we don't have a detailed hook message
1028
- if (shouldPreventContinuation && !errorMessage) {
1029
- errorMessage = `Execution stopped by PreToolUse hook${stopReason ? `: ${stopReason}` : ''}`
1030
- }
1031
-
1032
- // Build top-level content: tool_result (text-only for is_error compatibility) + images alongside
1033
- const messageContent: ContentBlockParam[] = [
1034
- {
1035
- type: 'tool_result',
1036
- content: errorMessage,
1037
- is_error: true,
1038
- tool_use_id: toolUseID,
1039
- },
1040
- ]
1041
-
1042
- // Add image blocks at top level (not inside tool_result, which rejects non-text with is_error)
1043
- const rejectContentBlocks =
1044
- permissionDecision.behavior === 'ask'
1045
- ? permissionDecision.contentBlocks
1046
- : undefined
1047
- if (rejectContentBlocks?.length) {
1048
- messageContent.push(...rejectContentBlocks)
1049
- }
1050
-
1051
- // Generate sequential imagePasteIds so each image renders with a distinct label
1052
- let rejectImageIds: number[] | undefined
1053
- if (rejectContentBlocks?.length) {
1054
- const imageCount = count(
1055
- rejectContentBlocks,
1056
- (b: ContentBlockParam) => b.type === 'image',
1057
- )
1058
- if (imageCount > 0) {
1059
- const startId = getNextImagePasteId(toolUseContext.messages)
1060
- rejectImageIds = Array.from(
1061
- { length: imageCount },
1062
- (_, i) => startId + i,
1063
- )
1064
- }
1065
- }
1066
-
1067
- resultingMessages.push({
1068
- message: createUserMessage({
1069
- content: messageContent,
1070
- imagePasteIds: rejectImageIds,
1071
- toolUseResult: `Error: ${errorMessage}`,
1072
- sourceToolAssistantUUID: assistantMessage.uuid,
1073
- }),
1074
- })
1075
-
1076
- // Run PermissionDenied hooks for auto mode classifier denials.
1077
- // If a hook returns {retry: true}, tell the model it may retry.
1078
- if (
1079
- feature('TRANSCRIPT_CLASSIFIER') &&
1080
- permissionDecision.decisionReason?.type === 'classifier' &&
1081
- permissionDecision.decisionReason.classifier === 'auto-mode'
1082
- ) {
1083
- let hookSaysRetry = false
1084
- for await (const result of executePermissionDeniedHooks(
1085
- tool.name,
1086
- toolUseID,
1087
- processedInput,
1088
- permissionDecision.decisionReason.reason ?? 'Permission denied',
1089
- toolUseContext,
1090
- permissionMode,
1091
- toolUseContext.abortController.signal,
1092
- )) {
1093
- if (result.retry) hookSaysRetry = true
1094
- }
1095
- if (hookSaysRetry) {
1096
- resultingMessages.push({
1097
- message: createUserMessage({
1098
- content:
1099
- 'The PermissionDenied hook indicated this command is now approved. You may retry it if you would like.',
1100
- isMeta: true,
1101
- }),
1102
- })
1103
- }
1104
- }
1105
-
1106
- return resultingMessages
1107
- }
1108
- logEvent('tengu_tool_use_can_use_tool_allowed', {
1109
- messageID:
1110
- messageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1111
- toolName: sanitizeToolNameForAnalytics(tool.name),
1112
-
1113
- queryChainId: toolUseContext.queryTracking
1114
- ?.chainId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1115
- queryDepth: toolUseContext.queryTracking?.depth,
1116
- ...(mcpServerType && {
1117
- mcpServerType:
1118
- mcpServerType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1119
- }),
1120
- ...(mcpServerBaseUrl && {
1121
- mcpServerBaseUrl:
1122
- mcpServerBaseUrl as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1123
- }),
1124
- ...(requestId && {
1125
- requestId:
1126
- requestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1127
- }),
1128
- ...mcpToolDetailsForAnalytics(tool.name, mcpServerType, mcpServerBaseUrl),
1129
- })
1130
-
1131
- // Use the updated input from permissions if provided
1132
- // (Don't overwrite if undefined - processedInput may have been modified by passthrough hooks)
1133
- if (permissionDecision.updatedInput !== undefined) {
1134
- processedInput = permissionDecision.updatedInput
1135
- }
1136
-
1137
- // Prepare tool parameters for logging in tool_result event.
1138
- // Gated by OTEL_LOG_TOOL_DETAILS — tool parameters can contain sensitive
1139
- // content (bash commands, MCP server names, etc.) so they're opt-in only.
1140
- const telemetryToolInput = extractToolInputForTelemetry(processedInput)
1141
- let toolParameters: Record<string, unknown> = {}
1142
- if (isToolDetailsLoggingEnabled()) {
1143
- if (tool.name === BASH_TOOL_NAME && 'command' in processedInput) {
1144
- const bashInput = processedInput as BashToolInput
1145
- const commandParts = bashInput.command.trim().split(/\s+/)
1146
- const bashCommand = commandParts[0] || ''
1147
-
1148
- toolParameters = {
1149
- bash_command: bashCommand,
1150
- full_command: bashInput.command,
1151
- ...(bashInput.timeout !== undefined && {
1152
- timeout: bashInput.timeout,
1153
- }),
1154
- ...(bashInput.description !== undefined && {
1155
- description: bashInput.description,
1156
- }),
1157
- ...('dangerouslyDisableSandbox' in bashInput && {
1158
- dangerouslyDisableSandbox: bashInput.dangerouslyDisableSandbox,
1159
- }),
1160
- }
1161
- }
1162
-
1163
- const mcpDetails = extractMcpToolDetails(tool.name)
1164
- if (mcpDetails) {
1165
- toolParameters.mcp_server_name = mcpDetails.serverName
1166
- toolParameters.mcp_tool_name = mcpDetails.mcpToolName
1167
- }
1168
- const skillName = extractSkillName(tool.name, processedInput)
1169
- if (skillName) {
1170
- toolParameters.skill_name = skillName
1171
- }
1172
- }
1173
-
1174
- const decisionInfo = toolUseContext.toolDecisions?.get(toolUseID)
1175
- endToolBlockedOnUserSpan(
1176
- decisionInfo?.decision || 'unknown',
1177
- decisionInfo?.source || 'unknown',
1178
- )
1179
- startToolExecutionSpan()
1180
-
1181
- const startTime = Date.now()
1182
-
1183
- startSessionActivity('tool_exec')
1184
- // If processedInput still points at the backfill clone, no hook/permission
1185
- // replaced it — pass the pre-backfill callInput so call() sees the model's
1186
- // original field values. Otherwise converge on the hook-supplied input.
1187
- // Permission/hook flows may return a fresh object derived from the
1188
- // backfilled clone (e.g. via inputSchema.parse). If its file_path matches
1189
- // the backfill-expanded value, restore the model's original so the tool
1190
- // result string embeds the path the model emitted — keeps transcript/VCR
1191
- // hashes stable. Other hook modifications flow through unchanged.
1192
- if (
1193
- backfilledClone &&
1194
- processedInput !== callInput &&
1195
- typeof processedInput === 'object' &&
1196
- processedInput !== null &&
1197
- 'file_path' in processedInput &&
1198
- 'file_path' in (callInput as Record<string, unknown>) &&
1199
- (processedInput as Record<string, unknown>).file_path ===
1200
- (backfilledClone as Record<string, unknown>).file_path
1201
- ) {
1202
- callInput = {
1203
- ...processedInput,
1204
- file_path: (callInput as Record<string, unknown>).file_path,
1205
- } as typeof processedInput
1206
- } else if (processedInput !== backfilledClone) {
1207
- callInput = processedInput
1208
- }
1209
- try {
1210
- const result = await tool.call(
1211
- callInput,
1212
- {
1213
- ...toolUseContext,
1214
- toolUseId: toolUseID,
1215
- userModified: permissionDecision.userModified ?? false,
1216
- },
1217
- canUseTool,
1218
- assistantMessage,
1219
- progress => {
1220
- onToolProgress({
1221
- toolUseID: progress.toolUseID,
1222
- data: progress.data,
1223
- })
1224
- },
1225
- )
1226
- const durationMs = Date.now() - startTime
1227
- addToToolDuration(durationMs)
1228
-
1229
- // Log tool content/output as span event if enabled
1230
- if (result.data && typeof result.data === 'object') {
1231
- const contentAttributes: Record<string, string | number | boolean> = {}
1232
-
1233
- // Read tool: capture file_path and content
1234
- if (tool.name === FILE_READ_TOOL_NAME && 'content' in result.data) {
1235
- if ('file_path' in processedInput) {
1236
- contentAttributes.file_path = String(processedInput.file_path)
1237
- }
1238
- contentAttributes.content = String(result.data.content)
1239
- }
1240
-
1241
- // Edit/Write tools: capture file_path and diff
1242
- if (
1243
- (tool.name === FILE_EDIT_TOOL_NAME ||
1244
- tool.name === FILE_WRITE_TOOL_NAME) &&
1245
- 'file_path' in processedInput
1246
- ) {
1247
- contentAttributes.file_path = String(processedInput.file_path)
1248
-
1249
- // For Edit, capture the actual changes made
1250
- if (tool.name === FILE_EDIT_TOOL_NAME && 'diff' in result.data) {
1251
- contentAttributes.diff = String(result.data.diff)
1252
- }
1253
- // For Write, capture the written content
1254
- if (tool.name === FILE_WRITE_TOOL_NAME && 'content' in processedInput) {
1255
- contentAttributes.content = String(processedInput.content)
1256
- }
1257
- }
1258
-
1259
- // Bash tool: capture command
1260
- if (tool.name === BASH_TOOL_NAME && 'command' in processedInput) {
1261
- const bashInput = processedInput as BashToolInput
1262
- contentAttributes.bash_command = bashInput.command
1263
- // Also capture output if available
1264
- if ('output' in result.data) {
1265
- contentAttributes.output = String(result.data.output)
1266
- }
1267
- }
1268
-
1269
- if (Object.keys(contentAttributes).length > 0) {
1270
- addToolContentEvent('tool.output', contentAttributes)
1271
- }
1272
- }
1273
-
1274
- // Capture structured output from tool result if present
1275
- if (typeof result === 'object' && 'structured_output' in result) {
1276
- // Store the structured output in an attachment message
1277
- resultingMessages.push({
1278
- message: createAttachmentMessage({
1279
- type: 'structured_output',
1280
- data: result.structured_output,
1281
- }),
1282
- })
1283
- }
1284
-
1285
- endToolExecutionSpan({ success: true })
1286
- // Pass tool result for new_context logging
1287
- const toolResultStr =
1288
- result.data && typeof result.data === 'object'
1289
- ? jsonStringify(result.data)
1290
- : String(result.data ?? '')
1291
- endToolSpan(toolResultStr)
1292
-
1293
- // Map the tool result to API format once and cache it. This block is reused
1294
- // by addToolResult (skipping the remap) and measured here for analytics.
1295
- const mappedToolResultBlock = tool.mapToolResultToToolResultBlockParam(
1296
- result.data,
1297
- toolUseID,
1298
- )
1299
- const mappedContent = mappedToolResultBlock.content
1300
- const toolResultSizeBytes = !mappedContent
1301
- ? 0
1302
- : typeof mappedContent === 'string'
1303
- ? mappedContent.length
1304
- : jsonStringify(mappedContent).length
1305
-
1306
- // Extract file extension for file-related tools
1307
- let fileExtension: ReturnType<typeof getFileExtensionForAnalytics>
1308
- if (processedInput && typeof processedInput === 'object') {
1309
- if (
1310
- (tool.name === FILE_READ_TOOL_NAME ||
1311
- tool.name === FILE_EDIT_TOOL_NAME ||
1312
- tool.name === FILE_WRITE_TOOL_NAME) &&
1313
- 'file_path' in processedInput
1314
- ) {
1315
- fileExtension = getFileExtensionForAnalytics(
1316
- String(processedInput.file_path),
1317
- )
1318
- } else if (
1319
- tool.name === NOTEBOOK_EDIT_TOOL_NAME &&
1320
- 'notebook_path' in processedInput
1321
- ) {
1322
- fileExtension = getFileExtensionForAnalytics(
1323
- String(processedInput.notebook_path),
1324
- )
1325
- } else if (tool.name === BASH_TOOL_NAME && 'command' in processedInput) {
1326
- const bashInput = processedInput as BashToolInput
1327
- fileExtension = getFileExtensionsFromBashCommand(
1328
- bashInput.command,
1329
- bashInput._simulatedSedEdit?.filePath,
1330
- )
1331
- }
1332
- }
1333
-
1334
- logEvent('tengu_tool_use_success', {
1335
- messageID:
1336
- messageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1337
- toolName: sanitizeToolNameForAnalytics(tool.name),
1338
- isMcp: tool.isMcp ?? false,
1339
- durationMs,
1340
- preToolHookDurationMs,
1341
- toolResultSizeBytes,
1342
- ...(fileExtension !== undefined && { fileExtension }),
1343
-
1344
- queryChainId: toolUseContext.queryTracking
1345
- ?.chainId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1346
- queryDepth: toolUseContext.queryTracking?.depth,
1347
- ...(mcpServerType && {
1348
- mcpServerType:
1349
- mcpServerType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1350
- }),
1351
- ...(mcpServerBaseUrl && {
1352
- mcpServerBaseUrl:
1353
- mcpServerBaseUrl as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1354
- }),
1355
- ...(requestId && {
1356
- requestId:
1357
- requestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1358
- }),
1359
- ...mcpToolDetailsForAnalytics(tool.name, mcpServerType, mcpServerBaseUrl),
1360
- })
1361
-
1362
- // Enrich tool parameters with git commit ID from successful git commit output
1363
- if (
1364
- isToolDetailsLoggingEnabled() &&
1365
- (tool.name === BASH_TOOL_NAME || tool.name === POWERSHELL_TOOL_NAME) &&
1366
- 'command' in processedInput &&
1367
- typeof processedInput.command === 'string' &&
1368
- processedInput.command.match(/\bgit\s+commit\b/) &&
1369
- result.data &&
1370
- typeof result.data === 'object' &&
1371
- 'stdout' in result.data
1372
- ) {
1373
- const gitCommitId = parseGitCommitId(String(result.data.stdout))
1374
- if (gitCommitId) {
1375
- toolParameters.git_commit_id = gitCommitId
1376
- }
1377
- }
1378
-
1379
- // Log tool result event for OTLP with tool parameters and decision context
1380
- const mcpServerScope = isMcpTool(tool)
1381
- ? getMcpServerScopeFromToolName(tool.name)
1382
- : null
1383
-
1384
- void logOTelEvent('tool_result', {
1385
- tool_name: sanitizeToolNameForAnalytics(tool.name),
1386
- success: 'true',
1387
- duration_ms: String(durationMs),
1388
- ...(Object.keys(toolParameters).length > 0 && {
1389
- tool_parameters: jsonStringify(toolParameters),
1390
- }),
1391
- ...(telemetryToolInput && { tool_input: telemetryToolInput }),
1392
- tool_result_size_bytes: String(toolResultSizeBytes),
1393
- ...(decisionInfo && {
1394
- decision_source: decisionInfo.source,
1395
- decision_type: decisionInfo.decision,
1396
- }),
1397
- ...(mcpServerScope && { mcp_server_scope: mcpServerScope }),
1398
- })
1399
-
1400
- // Run PostToolUse hooks
1401
- let toolOutput = result.data
1402
- const hookResults = []
1403
- const toolContextModifier = result.contextModifier
1404
- const mcpMeta = result.mcpMeta
1405
-
1406
- async function addToolResult(
1407
- toolUseResult: unknown,
1408
- preMappedBlock?: ToolResultBlockParam,
1409
- ) {
1410
- // Use the pre-mapped block when available (non-MCP tools where hooks
1411
- // don't modify the output), otherwise map from scratch.
1412
- const toolResultBlock = preMappedBlock
1413
- ? await processPreMappedToolResultBlock(
1414
- preMappedBlock,
1415
- tool.name,
1416
- tool.maxResultSizeChars,
1417
- )
1418
- : await processToolResultBlock(tool, toolUseResult, toolUseID)
1419
-
1420
- // Build content blocks - tool result first, then optional feedback
1421
- const contentBlocks: ContentBlockParam[] = [toolResultBlock]
1422
- // Add accept feedback if user provided feedback when approving
1423
- // (acceptFeedback only exists on PermissionAllowDecision, which is guaranteed here)
1424
- if (
1425
- 'acceptFeedback' in permissionDecision &&
1426
- permissionDecision.acceptFeedback
1427
- ) {
1428
- contentBlocks.push({
1429
- type: 'text',
1430
- text: permissionDecision.acceptFeedback,
1431
- })
1432
- }
1433
-
1434
- // Add content blocks (e.g., pasted images) from the permission decision
1435
- const allowContentBlocks =
1436
- 'contentBlocks' in permissionDecision
1437
- ? permissionDecision.contentBlocks
1438
- : undefined
1439
- if (allowContentBlocks?.length) {
1440
- contentBlocks.push(...allowContentBlocks)
1441
- }
1442
-
1443
- // Generate sequential imagePasteIds so each image renders with a distinct label
1444
- let allowImageIds: number[] | undefined
1445
- if (allowContentBlocks?.length) {
1446
- const imageCount = count(
1447
- allowContentBlocks,
1448
- (b: ContentBlockParam) => b.type === 'image',
1449
- )
1450
- if (imageCount > 0) {
1451
- const startId = getNextImagePasteId(toolUseContext.messages)
1452
- allowImageIds = Array.from(
1453
- { length: imageCount },
1454
- (_, i) => startId + i,
1455
- )
1456
- }
1457
- }
1458
-
1459
- resultingMessages.push({
1460
- message: createUserMessage({
1461
- content: contentBlocks,
1462
- imagePasteIds: allowImageIds,
1463
- toolUseResult:
1464
- toolUseContext.agentId && !toolUseContext.preserveToolUseResults
1465
- ? undefined
1466
- : toolUseResult,
1467
- mcpMeta: toolUseContext.agentId ? undefined : mcpMeta,
1468
- sourceToolAssistantUUID: assistantMessage.uuid,
1469
- }),
1470
- contextModifier: toolContextModifier
1471
- ? {
1472
- toolUseID: toolUseID,
1473
- modifyContext: toolContextModifier,
1474
- }
1475
- : undefined,
1476
- })
1477
- }
1478
-
1479
- // TOOD(hackyon): refactor so we don't have different experiences for MCP tools
1480
- if (!isMcpTool(tool)) {
1481
- await addToolResult(toolOutput, mappedToolResultBlock)
1482
- }
1483
-
1484
- const postToolHookInfos: StopHookInfo[] = []
1485
- const postToolHookStart = Date.now()
1486
- for await (const hookResult of runPostToolUseHooks(
1487
- toolUseContext,
1488
- tool,
1489
- toolUseID,
1490
- assistantMessage.message.id,
1491
- processedInput,
1492
- toolOutput,
1493
- requestId,
1494
- mcpServerType,
1495
- mcpServerBaseUrl,
1496
- )) {
1497
- if ('updatedMCPToolOutput' in hookResult) {
1498
- if (isMcpTool(tool)) {
1499
- toolOutput = hookResult.updatedMCPToolOutput
1500
- }
1501
- } else if (isMcpTool(tool)) {
1502
- hookResults.push(hookResult)
1503
- if (hookResult.message.type === 'attachment') {
1504
- const att = hookResult.message.attachment
1505
- if (
1506
- 'command' in att &&
1507
- att.command !== undefined &&
1508
- 'durationMs' in att &&
1509
- att.durationMs !== undefined
1510
- ) {
1511
- postToolHookInfos.push({
1512
- command: att.command,
1513
- durationMs: att.durationMs,
1514
- })
1515
- }
1516
- }
1517
- } else {
1518
- resultingMessages.push(hookResult)
1519
- if (hookResult.message.type === 'attachment') {
1520
- const att = hookResult.message.attachment
1521
- if (
1522
- 'command' in att &&
1523
- att.command !== undefined &&
1524
- 'durationMs' in att &&
1525
- att.durationMs !== undefined
1526
- ) {
1527
- postToolHookInfos.push({
1528
- command: att.command,
1529
- durationMs: att.durationMs,
1530
- })
1531
- }
1532
- }
1533
- }
1534
- }
1535
- const postToolHookDurationMs = Date.now() - postToolHookStart
1536
- if (postToolHookDurationMs >= SLOW_PHASE_LOG_THRESHOLD_MS) {
1537
- logForDebugging(
1538
- `Slow PostToolUse hooks: ${postToolHookDurationMs}ms for ${tool.name} (${postToolHookInfos.length} hooks)`,
1539
- { level: 'info' },
1540
- )
1541
- }
1542
-
1543
- if (isMcpTool(tool)) {
1544
- await addToolResult(toolOutput)
1545
- }
1546
-
1547
- // Show PostToolUse hook timing inline below tool result when > 500ms.
1548
- // Use wall-clock time (not sum of individual durations) since hooks run in parallel.
1549
- if (process.env.USER_TYPE === 'ant' && postToolHookInfos.length > 0) {
1550
- if (postToolHookDurationMs > HOOK_TIMING_DISPLAY_THRESHOLD_MS) {
1551
- resultingMessages.push({
1552
- message: createStopHookSummaryMessage(
1553
- postToolHookInfos.length,
1554
- postToolHookInfos,
1555
- [],
1556
- false,
1557
- undefined,
1558
- false,
1559
- 'suggestion',
1560
- undefined,
1561
- 'PostToolUse',
1562
- postToolHookDurationMs,
1563
- ),
1564
- })
1565
- }
1566
- }
1567
-
1568
- // If the tool provided new messages, add them to the list to return.
1569
- if (result.newMessages && result.newMessages.length > 0) {
1570
- for (const message of result.newMessages) {
1571
- resultingMessages.push({ message })
1572
- }
1573
- }
1574
- // If hook indicated to prevent continuation after successful execution, yield a stop reason message
1575
- if (shouldPreventContinuation) {
1576
- resultingMessages.push({
1577
- message: createAttachmentMessage({
1578
- type: 'hook_stopped_continuation',
1579
- message: stopReason || 'Execution stopped by hook',
1580
- hookName: `PreToolUse:${tool.name}`,
1581
- toolUseID: toolUseID,
1582
- hookEvent: 'PreToolUse',
1583
- }),
1584
- })
1585
- }
1586
-
1587
- // Yield the remaining hook results after the other messages are sent
1588
- for (const hookResult of hookResults) {
1589
- resultingMessages.push(hookResult)
1590
- }
1591
- return resultingMessages
1592
- } catch (error) {
1593
- const durationMs = Date.now() - startTime
1594
- addToToolDuration(durationMs)
1595
-
1596
- endToolExecutionSpan({
1597
- success: false,
1598
- error: errorMessage(error),
1599
- })
1600
- endToolSpan()
1601
-
1602
- // Handle MCP auth errors by updating the client status to 'needs-auth'
1603
- // This updates the /mcp display to show the server needs re-authorization
1604
- if (error instanceof McpAuthError) {
1605
- toolUseContext.setAppState(prevState => {
1606
- const serverName = error.serverName
1607
- const existingClientIndex = prevState.mcp.clients.findIndex(
1608
- c => c.name === serverName,
1609
- )
1610
- if (existingClientIndex === -1) {
1611
- return prevState
1612
- }
1613
- const existingClient = prevState.mcp.clients[existingClientIndex]
1614
- // Only update if client was connected (don't overwrite other states)
1615
- if (!existingClient || existingClient.type !== 'connected') {
1616
- return prevState
1617
- }
1618
- const updatedClients = [...prevState.mcp.clients]
1619
- updatedClients[existingClientIndex] = {
1620
- name: serverName,
1621
- type: 'needs-auth' as const,
1622
- config: existingClient.config,
1623
- }
1624
- return {
1625
- ...prevState,
1626
- mcp: {
1627
- ...prevState.mcp,
1628
- clients: updatedClients,
1629
- },
1630
- }
1631
- })
1632
- }
1633
-
1634
- if (!(error instanceof AbortError)) {
1635
- const errorMsg = errorMessage(error)
1636
- logForDebugging(
1637
- `${tool.name} tool error (${durationMs}ms): ${errorMsg.slice(0, 200)}`,
1638
- )
1639
- if (!(error instanceof ShellError)) {
1640
- logError(error)
1641
- }
1642
- logEvent('tengu_tool_use_error', {
1643
- messageID:
1644
- messageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1645
- toolName: sanitizeToolNameForAnalytics(tool.name),
1646
- error: classifyToolError(
1647
- error,
1648
- ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1649
- isMcp: tool.isMcp ?? false,
1650
-
1651
- queryChainId: toolUseContext.queryTracking
1652
- ?.chainId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1653
- queryDepth: toolUseContext.queryTracking?.depth,
1654
- ...(mcpServerType && {
1655
- mcpServerType:
1656
- mcpServerType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1657
- }),
1658
- ...(mcpServerBaseUrl && {
1659
- mcpServerBaseUrl:
1660
- mcpServerBaseUrl as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1661
- }),
1662
- ...(requestId && {
1663
- requestId:
1664
- requestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1665
- }),
1666
- ...mcpToolDetailsForAnalytics(
1667
- tool.name,
1668
- mcpServerType,
1669
- mcpServerBaseUrl,
1670
- ),
1671
- })
1672
- // Log tool result error event for OTLP with tool parameters and decision context
1673
- const mcpServerScope = isMcpTool(tool)
1674
- ? getMcpServerScopeFromToolName(tool.name)
1675
- : null
1676
-
1677
- void logOTelEvent('tool_result', {
1678
- tool_name: sanitizeToolNameForAnalytics(tool.name),
1679
- use_id: toolUseID,
1680
- success: 'false',
1681
- duration_ms: String(durationMs),
1682
- error: errorMessage(error),
1683
- ...(Object.keys(toolParameters).length > 0 && {
1684
- tool_parameters: jsonStringify(toolParameters),
1685
- }),
1686
- ...(telemetryToolInput && { tool_input: telemetryToolInput }),
1687
- ...(decisionInfo && {
1688
- decision_source: decisionInfo.source,
1689
- decision_type: decisionInfo.decision,
1690
- }),
1691
- ...(mcpServerScope && { mcp_server_scope: mcpServerScope }),
1692
- })
1693
- }
1694
- const content = formatError(error)
1695
-
1696
- // Determine if this was a user interrupt
1697
- const isInterrupt = error instanceof AbortError
1698
-
1699
- // Run PostToolUseFailure hooks
1700
- const hookMessages: MessageUpdateLazy<
1701
- AttachmentMessage | ProgressMessage<HookProgress>
1702
- >[] = []
1703
- for await (const hookResult of runPostToolUseFailureHooks(
1704
- toolUseContext,
1705
- tool,
1706
- toolUseID,
1707
- messageId,
1708
- processedInput,
1709
- content,
1710
- isInterrupt,
1711
- requestId,
1712
- mcpServerType,
1713
- mcpServerBaseUrl,
1714
- )) {
1715
- hookMessages.push(hookResult)
1716
- }
1717
-
1718
- return [
1719
- {
1720
- message: createUserMessage({
1721
- content: [
1722
- {
1723
- type: 'tool_result',
1724
- content,
1725
- is_error: true,
1726
- tool_use_id: toolUseID,
1727
- },
1728
- ],
1729
- toolUseResult: `Error: ${content}`,
1730
- mcpMeta: toolUseContext.agentId
1731
- ? undefined
1732
- : error instanceof
1733
- McpToolCallError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
1734
- ? error.mcpMeta
1735
- : undefined,
1736
- sourceToolAssistantUUID: assistantMessage.uuid,
1737
- }),
1738
- },
1739
- ...hookMessages,
1740
- ]
1741
- } finally {
1742
- stopSessionActivity('tool_exec')
1743
- // Clean up decision info after logging
1744
- if (decisionInfo) {
1745
- toolUseContext.toolDecisions?.delete(toolUseID)
1746
- }
1747
- }
1748
- }