ummaya 0.2.13 → 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 (566) 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-github-app/OAuthFlowStep.tsx +1 -2
  32. package/tui/src/commands/install-github-app/install-github-app.tsx +4 -4
  33. package/tui/src/commands/install.tsx +0 -1
  34. package/tui/src/commands/login/login.tsx +1 -2
  35. package/tui/src/commands/logout/logout.tsx +0 -1
  36. package/tui/src/commands/mcp/mcp.tsx +1 -2
  37. package/tui/src/commands/memory/memory.tsx +0 -1
  38. package/tui/src/commands/mobile/mobile.tsx +0 -1
  39. package/tui/src/commands/model/model.tsx +0 -1
  40. package/tui/src/commands/output-style/output-style.tsx +1 -2
  41. package/tui/src/commands/passes/passes.tsx +1 -2
  42. package/tui/src/commands/permissions/permissions.tsx +1 -2
  43. package/tui/src/commands/plan/plan.tsx +1 -2
  44. package/tui/src/commands/plugin/AddMarketplace.tsx +1 -2
  45. package/tui/src/commands/plugin/BrowseMarketplace.tsx +1 -2
  46. package/tui/src/commands/plugin/DiscoverPlugins.tsx +0 -1
  47. package/tui/src/commands/plugin/ManageMarketplaces.tsx +0 -1
  48. package/tui/src/commands/plugin/ManagePlugins.tsx +1 -2
  49. package/tui/src/commands/plugin/PluginErrors.tsx +1 -2
  50. package/tui/src/commands/plugin/PluginOptionsDialog.tsx +1 -2
  51. package/tui/src/commands/plugin/PluginOptionsFlow.tsx +1 -2
  52. package/tui/src/commands/plugin/PluginSettings.tsx +1 -2
  53. package/tui/src/commands/plugin/PluginTrustWarning.tsx +0 -1
  54. package/tui/src/commands/plugin/UnifiedInstalledCell.tsx +1 -2
  55. package/tui/src/commands/plugin/ValidatePlugin.tsx +1 -2
  56. package/tui/src/commands/plugin/index.tsx +0 -1
  57. package/tui/src/commands/plugin/plugin.tsx +1 -2
  58. package/tui/src/commands/plugin/pluginDetailsHelpers.tsx +1 -2
  59. package/tui/src/commands/privacy-settings/privacy-settings.tsx +0 -1
  60. package/tui/src/commands/rate-limit-options/rate-limit-options.tsx +1 -2
  61. package/tui/src/commands/remote-env/remote-env.tsx +1 -2
  62. package/tui/src/commands/remote-setup/remote-setup.tsx +0 -1
  63. package/tui/src/commands/resume/resume.tsx +1 -2
  64. package/tui/src/commands/review/UltrareviewOverageDialog.tsx +1 -2
  65. package/tui/src/commands/review/ultrareviewCommand.tsx +0 -1
  66. package/tui/src/commands/sandbox-toggle/sandbox-toggle.tsx +1 -2
  67. package/tui/src/commands/session/session.tsx +0 -1
  68. package/tui/src/commands/skills/skills.tsx +1 -2
  69. package/tui/src/commands/stats/stats.tsx +1 -2
  70. package/tui/src/commands/status/status.tsx +1 -2
  71. package/tui/src/commands/statusline.tsx +0 -1
  72. package/tui/src/commands/tag/tag.tsx +1 -2
  73. package/tui/src/commands/tasks/tasks.tsx +1 -2
  74. package/tui/src/commands/terminalSetup/terminalSetup.tsx +1 -2
  75. package/tui/src/commands/theme/theme.tsx +1 -2
  76. package/tui/src/commands/thinkback/thinkback.tsx +0 -1
  77. package/tui/src/commands/ultraplan.tsx +0 -1
  78. package/tui/src/commands/usage/usage.tsx +1 -2
  79. package/tui/src/commands/voice/voice.ts +2 -2
  80. package/tui/src/components/AgentProgressLine.tsx +1 -2
  81. package/tui/src/components/App.tsx +1 -2
  82. package/tui/src/components/ApproveApiKey.tsx +1 -2
  83. package/tui/src/components/AutoModeOptInDialog.tsx +0 -1
  84. package/tui/src/components/AutoUpdater.tsx +1 -2
  85. package/tui/src/components/AutoUpdaterWrapper.tsx +1 -2
  86. package/tui/src/components/AwsAuthStatusBox.tsx +1 -2
  87. package/tui/src/components/BaseTextInput.tsx +0 -1
  88. package/tui/src/components/BashModeProgress.tsx +1 -2
  89. package/tui/src/components/BridgeDialog.tsx +1 -2
  90. package/tui/src/components/BypassPermissionsModeDialog.tsx +0 -1
  91. package/tui/src/components/ChannelDowngradeDialog.tsx +1 -2
  92. package/tui/src/components/ClaudeCodeHint/PluginHintMenu.tsx +1 -2
  93. package/tui/src/components/ClaudeMdExternalIncludesDialog.tsx +0 -1
  94. package/tui/src/components/ClickableImageRef.tsx +1 -2
  95. package/tui/src/components/CompactSummary.tsx +1 -2
  96. package/tui/src/components/ConfigurableShortcutHint.tsx +1 -2
  97. package/tui/src/components/ContextSuggestions.tsx +1 -2
  98. package/tui/src/components/ContextVisualization.tsx +1 -2
  99. package/tui/src/components/CoordinatorAgentStatus.tsx +1 -2
  100. package/tui/src/components/CostThresholdDialog.tsx +0 -1
  101. package/tui/src/components/CtrlOToExpand.tsx +1 -2
  102. package/tui/src/components/CustomSelect/SelectMulti.tsx +1 -2
  103. package/tui/src/components/CustomSelect/select-input-option.tsx +1 -2
  104. package/tui/src/components/CustomSelect/select-option.tsx +1 -2
  105. package/tui/src/components/CustomSelect/select.tsx +1 -2
  106. package/tui/src/components/DesktopHandoff.tsx +0 -1
  107. package/tui/src/components/DesktopUpsell/DesktopUpsellStartup.tsx +0 -1
  108. package/tui/src/components/DevBar.tsx +1 -2
  109. package/tui/src/components/DevChannelsDialog.tsx +1 -2
  110. package/tui/src/components/DiagnosticsDisplay.tsx +1 -2
  111. package/tui/src/components/EffortCallout.tsx +1 -2
  112. package/tui/src/components/ExitFlow.tsx +1 -2
  113. package/tui/src/components/ExportDialog.tsx +1 -2
  114. package/tui/src/components/FallbackToolUseErrorMessage.tsx +0 -1
  115. package/tui/src/components/FallbackToolUseRejectedMessage.tsx +1 -2
  116. package/tui/src/components/FastIcon.tsx +1 -2
  117. package/tui/src/components/Feedback.tsx +0 -1
  118. package/tui/src/components/FeedbackSurvey/FeedbackSurvey.tsx +1 -2
  119. package/tui/src/components/FeedbackSurvey/FeedbackSurveyView.tsx +0 -1
  120. package/tui/src/components/FeedbackSurvey/TranscriptSharePrompt.tsx +0 -1
  121. package/tui/src/components/FeedbackSurvey/useFeedbackSurvey.tsx +0 -1
  122. package/tui/src/components/FeedbackSurvey/useMemorySurvey.tsx +0 -1
  123. package/tui/src/components/FeedbackSurvey/usePostCompactSurvey.tsx +0 -1
  124. package/tui/src/components/FeedbackSurvey/useSurveyState.tsx +1 -2
  125. package/tui/src/components/FileEditToolDiff.tsx +1 -2
  126. package/tui/src/components/FileEditToolUpdatedMessage.tsx +1 -2
  127. package/tui/src/components/FileEditToolUseRejectedMessage.tsx +1 -2
  128. package/tui/src/components/FilePathLink.tsx +1 -2
  129. package/tui/src/components/FullscreenLayout.tsx +1 -2
  130. package/tui/src/components/GlobalSearchDialog.tsx +1 -2
  131. package/tui/src/components/HelpV2/Commands.tsx +1 -2
  132. package/tui/src/components/HelpV2/General.tsx +0 -1
  133. package/tui/src/components/HelpV2/HelpV2.tsx +0 -1
  134. package/tui/src/components/HighlightedCode/Fallback.tsx +1 -2
  135. package/tui/src/components/HighlightedCode.tsx +1 -2
  136. package/tui/src/components/HistorySearchDialog.tsx +1 -2
  137. package/tui/src/components/IdeAutoConnectDialog.tsx +1 -2
  138. package/tui/src/components/IdeOnboardingDialog.tsx +0 -1
  139. package/tui/src/components/IdeStatusIndicator.tsx +1 -2
  140. package/tui/src/components/IdleReturnDialog.tsx +1 -2
  141. package/tui/src/components/InterruptedByUser.tsx +0 -1
  142. package/tui/src/components/InvalidConfigDialog.tsx +1 -2
  143. package/tui/src/components/InvalidSettingsDialog.tsx +1 -2
  144. package/tui/src/components/KeybindingWarnings.tsx +1 -2
  145. package/tui/src/components/LanguagePicker.tsx +1 -2
  146. package/tui/src/components/LogSelector.tsx +0 -1
  147. package/tui/src/components/LogoV2/AnimatedAsterisk.tsx +1 -2
  148. package/tui/src/components/LogoV2/AnimatedClawd.tsx +1 -2
  149. package/tui/src/components/LogoV2/ChannelsNotice.tsx +0 -1
  150. package/tui/src/components/LogoV2/Clawd.tsx +1 -2
  151. package/tui/src/components/LogoV2/CondensedLogo.tsx +0 -1
  152. package/tui/src/components/LogoV2/EmergencyTip.tsx +0 -1
  153. package/tui/src/components/LogoV2/Feed.tsx +1 -2
  154. package/tui/src/components/LogoV2/FeedColumn.tsx +1 -2
  155. package/tui/src/components/LogoV2/GuestPassesUpsell.tsx +0 -1
  156. package/tui/src/components/LogoV2/LogoV2.tsx +0 -1
  157. package/tui/src/components/LogoV2/Opus1mMergeNotice.tsx +1 -2
  158. package/tui/src/components/LogoV2/OverageCreditUpsell.tsx +1 -2
  159. package/tui/src/components/LogoV2/VoiceModeNotice.tsx +1 -2
  160. package/tui/src/components/LogoV2/feedConfigs.tsx +0 -1
  161. package/tui/src/components/LspRecommendation/LspRecommendationMenu.tsx +1 -2
  162. package/tui/src/components/MCPServerApprovalDialog.tsx +1 -2
  163. package/tui/src/components/MCPServerDesktopImportDialog.tsx +0 -1
  164. package/tui/src/components/MCPServerDialogCopy.tsx +0 -1
  165. package/tui/src/components/MCPServerMultiselectDialog.tsx +1 -2
  166. package/tui/src/components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.tsx +0 -1
  167. package/tui/src/components/Markdown.tsx +1 -2
  168. package/tui/src/components/MarkdownTable.tsx +1 -2
  169. package/tui/src/components/MemoryUsageIndicator.tsx +1 -2
  170. package/tui/src/components/Message.tsx +1 -2
  171. package/tui/src/components/MessageModel.tsx +1 -2
  172. package/tui/src/components/MessageResponse.tsx +1 -2
  173. package/tui/src/components/MessageRow.tsx +1 -2
  174. package/tui/src/components/MessageSelector.tsx +1 -2
  175. package/tui/src/components/MessageTimestamp.tsx +1 -2
  176. package/tui/src/components/Messages.tsx +0 -1
  177. package/tui/src/components/ModelPicker.tsx +0 -1
  178. package/tui/src/components/NativeAutoUpdater.tsx +1 -2
  179. package/tui/src/components/NotebookEditToolUseRejectedMessage.tsx +1 -2
  180. package/tui/src/components/OffscreenFreeze.tsx +1 -2
  181. package/tui/src/components/Onboarding.tsx +2 -3
  182. package/tui/src/components/OutputStylePicker.tsx +0 -1
  183. package/tui/src/components/PackageManagerAutoUpdater.tsx +0 -1
  184. package/tui/src/components/Passes/Passes.tsx +0 -1
  185. package/tui/src/components/PrBadge.tsx +1 -2
  186. package/tui/src/components/PressEnterToContinue.tsx +1 -2
  187. package/tui/src/components/PromptInput/HistorySearchInput.tsx +1 -2
  188. package/tui/src/components/PromptInput/IssueFlagBanner.tsx +1 -2
  189. package/tui/src/components/PromptInput/Notifications.tsx +1 -2
  190. package/tui/src/components/PromptInput/PromptInput.tsx +0 -1
  191. package/tui/src/components/PromptInput/PromptInputFooter.tsx +1 -2
  192. package/tui/src/components/PromptInput/PromptInputFooterLeftSide.tsx +1 -2
  193. package/tui/src/components/PromptInput/PromptInputFooterSuggestions.tsx +1 -2
  194. package/tui/src/components/PromptInput/PromptInputHelpMenu.tsx +1 -2
  195. package/tui/src/components/PromptInput/PromptInputModeIndicator.tsx +1 -2
  196. package/tui/src/components/PromptInput/PromptInputQueuedCommands.tsx +1 -2
  197. package/tui/src/components/PromptInput/PromptInputStashNotice.tsx +1 -2
  198. package/tui/src/components/PromptInput/SandboxPromptFooterHint.tsx +1 -2
  199. package/tui/src/components/PromptInput/ShimmeredInput.tsx +1 -2
  200. package/tui/src/components/PromptInput/VoiceIndicator.tsx +1 -2
  201. package/tui/src/components/QuickOpenDialog.tsx +1 -2
  202. package/tui/src/components/RemoteCallout.tsx +0 -1
  203. package/tui/src/components/RemoteEnvironmentDialog.tsx +0 -1
  204. package/tui/src/components/ResumeTask.tsx +0 -1
  205. package/tui/src/components/SandboxViolationExpandedView.tsx +1 -2
  206. package/tui/src/components/ScrollKeybindingHandler.tsx +0 -1
  207. package/tui/src/components/SearchBox.tsx +1 -2
  208. package/tui/src/components/SessionBackgroundHint.tsx +1 -2
  209. package/tui/src/components/SessionPreview.tsx +1 -2
  210. package/tui/src/components/Settings/Config.tsx +0 -1
  211. package/tui/src/components/Settings/Settings.tsx +1 -2
  212. package/tui/src/components/Settings/Status.tsx +1 -2
  213. package/tui/src/components/Settings/Usage.tsx +1 -2
  214. package/tui/src/components/ShowInIDEPrompt.tsx +1 -2
  215. package/tui/src/components/SkillImprovementSurvey.tsx +1 -2
  216. package/tui/src/components/Spinner/FlashingChar.tsx +1 -2
  217. package/tui/src/components/Spinner/GlimmerMessage.tsx +1 -2
  218. package/tui/src/components/Spinner/ShimmerChar.tsx +1 -2
  219. package/tui/src/components/Spinner/SpinnerAnimationRow.tsx +1 -2
  220. package/tui/src/components/Spinner/SpinnerGlyph.tsx +1 -2
  221. package/tui/src/components/Spinner/TeammateSpinnerLine.tsx +1 -2
  222. package/tui/src/components/Spinner/TeammateSpinnerTree.tsx +1 -2
  223. package/tui/src/components/Spinner.tsx +0 -1
  224. package/tui/src/components/Stats.tsx +0 -1
  225. package/tui/src/components/StatusLine.tsx +1 -2
  226. package/tui/src/components/StatusNotices.tsx +1 -2
  227. package/tui/src/components/StructuredDiff/Fallback.tsx +1 -2
  228. package/tui/src/components/StructuredDiff.tsx +1 -2
  229. package/tui/src/components/StructuredDiffList.tsx +1 -2
  230. package/tui/src/components/TagTabs.tsx +1 -2
  231. package/tui/src/components/TaskListV2.tsx +1 -2
  232. package/tui/src/components/TeammateViewHeader.tsx +1 -2
  233. package/tui/src/components/TeleportError.tsx +0 -1
  234. package/tui/src/components/TeleportProgress.tsx +1 -2
  235. package/tui/src/components/TeleportRepoMismatchDialog.tsx +0 -1
  236. package/tui/src/components/TeleportStash.tsx +1 -2
  237. package/tui/src/components/TextInput.tsx +1 -2
  238. package/tui/src/components/ThemePicker.tsx +0 -1
  239. package/tui/src/components/ThinkingToggle.tsx +0 -1
  240. package/tui/src/components/TokenWarning.tsx +1 -2
  241. package/tui/src/components/ToolUseLoader.tsx +1 -2
  242. package/tui/src/components/TrustDialog/TrustDialog.tsx +0 -1
  243. package/tui/src/components/ValidationErrorsList.tsx +1 -2
  244. package/tui/src/components/VimTextInput.tsx +1 -2
  245. package/tui/src/components/VirtualMessageList.tsx +1 -2
  246. package/tui/src/components/WorktreeExitDialog.tsx +1 -2
  247. package/tui/src/components/agents/AgentDetail.tsx +0 -1
  248. package/tui/src/components/agents/AgentEditor.tsx +1 -2
  249. package/tui/src/components/agents/AgentNavigationFooter.tsx +1 -2
  250. package/tui/src/components/agents/AgentsList.tsx +0 -1
  251. package/tui/src/components/agents/AgentsMenu.tsx +1 -2
  252. package/tui/src/components/agents/ColorPicker.tsx +1 -2
  253. package/tui/src/components/agents/ModelSelector.tsx +1 -2
  254. package/tui/src/components/agents/ToolSelector.tsx +1 -2
  255. package/tui/src/components/agents/new-agent-creation/CreateAgentWizard.tsx +1 -2
  256. package/tui/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx +1 -2
  257. package/tui/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx +0 -1
  258. package/tui/src/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx +1 -2
  259. package/tui/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx +0 -1
  260. package/tui/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx +1 -2
  261. package/tui/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx +1 -2
  262. package/tui/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx +1 -2
  263. package/tui/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx +0 -1
  264. package/tui/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx +1 -2
  265. package/tui/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx +1 -2
  266. package/tui/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx +1 -2
  267. package/tui/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx +1 -2
  268. package/tui/src/components/design-system/Byline.tsx +1 -2
  269. package/tui/src/components/design-system/Dialog.tsx +1 -2
  270. package/tui/src/components/design-system/Divider.tsx +1 -2
  271. package/tui/src/components/design-system/FuzzyPicker.tsx +1 -2
  272. package/tui/src/components/design-system/KeyboardShortcutHint.tsx +1 -2
  273. package/tui/src/components/design-system/ListItem.tsx +1 -2
  274. package/tui/src/components/design-system/LoadingState.tsx +0 -1
  275. package/tui/src/components/design-system/Pane.tsx +1 -2
  276. package/tui/src/components/design-system/ProgressBar.tsx +1 -2
  277. package/tui/src/components/design-system/Ratchet.tsx +1 -2
  278. package/tui/src/components/design-system/StatusIcon.tsx +1 -2
  279. package/tui/src/components/design-system/Tabs.tsx +1 -2
  280. package/tui/src/components/design-system/ThemeProvider.tsx +1 -2
  281. package/tui/src/components/design-system/ThemedBox.tsx +1 -2
  282. package/tui/src/components/design-system/ThemedText.tsx +1 -2
  283. package/tui/src/components/diff/DiffDetailView.tsx +1 -2
  284. package/tui/src/components/diff/DiffDialog.tsx +1 -2
  285. package/tui/src/components/diff/DiffFileList.tsx +1 -2
  286. package/tui/src/components/grove/Grove.tsx +0 -1
  287. package/tui/src/components/hooks/HooksConfigMenu.tsx +0 -1
  288. package/tui/src/components/hooks/PromptDialog.tsx +1 -2
  289. package/tui/src/components/hooks/SelectEventMode.tsx +0 -1
  290. package/tui/src/components/hooks/SelectHookMode.tsx +0 -1
  291. package/tui/src/components/hooks/SelectMatcherMode.tsx +0 -1
  292. package/tui/src/components/hooks/ViewHookMode.tsx +0 -1
  293. package/tui/src/components/mcp/CapabilitiesSection.tsx +1 -2
  294. package/tui/src/components/mcp/ElicitationDialog.tsx +0 -1
  295. package/tui/src/components/mcp/MCPAgentServerMenu.tsx +1 -2
  296. package/tui/src/components/mcp/MCPListPanel.tsx +0 -1
  297. package/tui/src/components/mcp/MCPReconnect.tsx +1 -2
  298. package/tui/src/components/mcp/MCPRemoteServerMenu.tsx +2 -3
  299. package/tui/src/components/mcp/MCPSettings.tsx +0 -1
  300. package/tui/src/components/mcp/MCPStdioServerMenu.tsx +1 -2
  301. package/tui/src/components/mcp/MCPToolDetailView.tsx +1 -2
  302. package/tui/src/components/mcp/MCPToolListView.tsx +1 -2
  303. package/tui/src/components/mcp/McpParsingWarnings.tsx +0 -1
  304. package/tui/src/components/mcp/utils/reconnectHelpers.tsx +1 -2
  305. package/tui/src/components/memory/MemoryFileSelector.tsx +1 -2
  306. package/tui/src/components/memory/MemoryUpdateNotification.tsx +1 -2
  307. package/tui/src/components/messageActions.tsx +1 -2
  308. package/tui/src/components/messages/AdvisorMessage.tsx +1 -2
  309. package/tui/src/components/messages/AssistantRedactedThinkingMessage.tsx +1 -2
  310. package/tui/src/components/messages/AssistantThinkingMessage.tsx +1 -2
  311. package/tui/src/components/messages/AssistantToolUseMessage.tsx +0 -1
  312. package/tui/src/components/messages/AttachmentMessage.tsx +1 -2
  313. package/tui/src/components/messages/CollapsedReadSearchContent.tsx +1 -2
  314. package/tui/src/components/messages/CompactBoundaryMessage.tsx +1 -2
  315. package/tui/src/components/messages/GroupedToolUseContent.tsx +1 -2
  316. package/tui/src/components/messages/HighlightedThinkingText.tsx +1 -2
  317. package/tui/src/components/messages/HookProgressMessage.tsx +1 -2
  318. package/tui/src/components/messages/PlanApprovalMessage.tsx +1 -2
  319. package/tui/src/components/messages/RateLimitMessage.tsx +1 -2
  320. package/tui/src/components/messages/ShutdownMessage.tsx +1 -2
  321. package/tui/src/components/messages/SystemAPIErrorMessage.tsx +1 -2
  322. package/tui/src/components/messages/SystemTextMessage.tsx +1 -2
  323. package/tui/src/components/messages/TaskAssignmentMessage.tsx +1 -2
  324. package/tui/src/components/messages/UserAgentNotificationMessage.tsx +1 -2
  325. package/tui/src/components/messages/UserBashInputMessage.tsx +1 -2
  326. package/tui/src/components/messages/UserBashOutputMessage.tsx +1 -2
  327. package/tui/src/components/messages/UserChannelMessage.tsx +1 -2
  328. package/tui/src/components/messages/UserCommandMessage.tsx +1 -2
  329. package/tui/src/components/messages/UserImageMessage.tsx +1 -2
  330. package/tui/src/components/messages/UserLocalCommandOutputMessage.tsx +1 -2
  331. package/tui/src/components/messages/UserMemoryInputMessage.tsx +1 -2
  332. package/tui/src/components/messages/UserPlanMessage.tsx +1 -2
  333. package/tui/src/components/messages/UserPromptMessage.tsx +1 -2
  334. package/tui/src/components/messages/UserResourceUpdateMessage.tsx +1 -2
  335. package/tui/src/components/messages/UserTeammateMessage.tsx +1 -2
  336. package/tui/src/components/messages/UserTextMessage.tsx +1 -2
  337. package/tui/src/components/messages/UserToolResultMessage/RejectedPlanMessage.tsx +0 -1
  338. package/tui/src/components/messages/UserToolResultMessage/RejectedToolUseMessage.tsx +1 -2
  339. package/tui/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +1 -2
  340. package/tui/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +1 -2
  341. package/tui/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +1 -2
  342. package/tui/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +1 -2
  343. package/tui/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +1 -2
  344. package/tui/src/components/messages/UserToolResultMessage/utils.tsx +0 -1
  345. package/tui/src/components/messages/teamMemCollapsed.tsx +1 -2
  346. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx +1 -2
  347. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/PreviewBox.tsx +1 -2
  348. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/PreviewQuestionView.tsx +1 -2
  349. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/QuestionNavigationBar.tsx +1 -2
  350. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/QuestionView.tsx +1 -2
  351. package/tui/src/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx +1 -2
  352. package/tui/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +1 -2
  353. package/tui/src/components/permissions/BashPermissionRequest/bashToolUseOptions.tsx +0 -1
  354. package/tui/src/components/permissions/ComputerUseApproval/ComputerUseApproval.tsx +0 -1
  355. package/tui/src/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx +0 -1
  356. package/tui/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx +0 -1
  357. package/tui/src/components/permissions/FallbackPermissionRequest.tsx +1 -2
  358. package/tui/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +1 -2
  359. package/tui/src/components/permissions/FilePermissionDialog/FilePermissionDialog.tsx +1 -2
  360. package/tui/src/components/permissions/FilePermissionDialog/permissionOptions.tsx +0 -1
  361. package/tui/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +1 -2
  362. package/tui/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +1 -2
  363. package/tui/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +1 -2
  364. package/tui/src/components/permissions/NotebookEditPermissionRequest/NotebookEditPermissionRequest.tsx +1 -2
  365. package/tui/src/components/permissions/NotebookEditPermissionRequest/NotebookEditToolDiff.tsx +1 -2
  366. package/tui/src/components/permissions/PermissionDecisionDebugInfo.tsx +1 -2
  367. package/tui/src/components/permissions/PermissionDialog.tsx +1 -2
  368. package/tui/src/components/permissions/PermissionExplanation.tsx +1 -2
  369. package/tui/src/components/permissions/PermissionPrompt.tsx +0 -1
  370. package/tui/src/components/permissions/PermissionRequest.tsx +0 -1
  371. package/tui/src/components/permissions/PermissionRequestTitle.tsx +1 -2
  372. package/tui/src/components/permissions/PermissionRuleExplanation.tsx +1 -2
  373. package/tui/src/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx +1 -2
  374. package/tui/src/components/permissions/PowerShellPermissionRequest/powershellToolUseOptions.tsx +0 -1
  375. package/tui/src/components/permissions/SandboxPermissionRequest.tsx +0 -1
  376. package/tui/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx +1 -2
  377. package/tui/src/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx +0 -1
  378. package/tui/src/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx +0 -1
  379. package/tui/src/components/permissions/WorkerBadge.tsx +1 -2
  380. package/tui/src/components/permissions/WorkerPendingPermission.tsx +1 -2
  381. package/tui/src/components/permissions/rules/AddPermissionRules.tsx +0 -1
  382. package/tui/src/components/permissions/rules/AddWorkspaceDirectory.tsx +0 -1
  383. package/tui/src/components/permissions/rules/PermissionRuleDescription.tsx +1 -2
  384. package/tui/src/components/permissions/rules/PermissionRuleInput.tsx +1 -2
  385. package/tui/src/components/permissions/rules/PermissionRuleList.tsx +0 -1
  386. package/tui/src/components/permissions/rules/RecentDenialsTab.tsx +1 -2
  387. package/tui/src/components/permissions/rules/RemoveWorkspaceDirectory.tsx +0 -1
  388. package/tui/src/components/permissions/rules/WorkspaceTab.tsx +1 -2
  389. package/tui/src/components/permissions/shellPermissionHelpers.tsx +1 -2
  390. package/tui/src/components/sandbox/SandboxConfigTab.tsx +1 -2
  391. package/tui/src/components/sandbox/SandboxDependenciesTab.tsx +1 -2
  392. package/tui/src/components/sandbox/SandboxDoctorSection.tsx +1 -2
  393. package/tui/src/components/sandbox/SandboxOverridesTab.tsx +0 -1
  394. package/tui/src/components/sandbox/SandboxSettings.tsx +0 -1
  395. package/tui/src/components/shell/ExpandShellOutputContext.tsx +1 -2
  396. package/tui/src/components/shell/OutputLine.tsx +1 -2
  397. package/tui/src/components/shell/ShellProgressMessage.tsx +1 -2
  398. package/tui/src/components/shell/ShellTimeDisplay.tsx +1 -2
  399. package/tui/src/components/skills/SkillsMenu.tsx +0 -1
  400. package/tui/src/components/tasks/AsyncAgentDetailDialog.tsx +1 -2
  401. package/tui/src/components/tasks/BackgroundTask.tsx +1 -2
  402. package/tui/src/components/tasks/BackgroundTaskStatus.tsx +1 -2
  403. package/tui/src/components/tasks/BackgroundTasksDialog.tsx +1 -2
  404. package/tui/src/components/tasks/DreamDetailDialog.tsx +1 -2
  405. package/tui/src/components/tasks/InProcessTeammateDetailDialog.tsx +1 -2
  406. package/tui/src/components/tasks/RemoteSessionDetailDialog.tsx +0 -1
  407. package/tui/src/components/tasks/RemoteSessionProgress.tsx +1 -2
  408. package/tui/src/components/tasks/ShellDetailDialog.tsx +1 -2
  409. package/tui/src/components/tasks/ShellProgress.tsx +1 -2
  410. package/tui/src/components/tasks/renderToolActivity.tsx +1 -2
  411. package/tui/src/components/tasks/taskStatusUtils.tsx +1 -2
  412. package/tui/src/components/teams/TeamStatus.tsx +1 -2
  413. package/tui/src/components/teams/TeamsDialog.tsx +1 -2
  414. package/tui/src/components/ui/OrderedList.tsx +1 -2
  415. package/tui/src/components/ui/OrderedListItem.tsx +1 -2
  416. package/tui/src/components/ui/TreeSelect.tsx +1 -2
  417. package/tui/src/components/wizard/WizardDialogLayout.tsx +1 -2
  418. package/tui/src/components/wizard/WizardNavigationFooter.tsx +1 -2
  419. package/tui/src/components/wizard/WizardProvider.tsx +1 -2
  420. package/tui/src/context/QueuedMessageContext.tsx +1 -2
  421. package/tui/src/context/fpsMetrics.tsx +1 -2
  422. package/tui/src/context/mailbox.tsx +1 -2
  423. package/tui/src/context/modalContext.tsx +1 -2
  424. package/tui/src/context/notifications.tsx +1 -2
  425. package/tui/src/context/overlayContext.tsx +1 -2
  426. package/tui/src/context/promptOverlayContext.tsx +1 -2
  427. package/tui/src/context/stats.tsx +1 -2
  428. package/tui/src/context/voice.tsx +1 -2
  429. package/tui/src/dialogLaunchers.tsx +0 -1
  430. package/tui/src/entrypoints/cli.tsx +0 -1
  431. package/tui/src/hooks/notifs/useCanSwitchToExistingSubscription.tsx +0 -1
  432. package/tui/src/hooks/notifs/useDeprecationWarningNotification.tsx +1 -2
  433. package/tui/src/hooks/notifs/useFastModeNotification.tsx +1 -2
  434. package/tui/src/hooks/notifs/useIDEStatusIndicator.tsx +1 -2
  435. package/tui/src/hooks/notifs/useInstallMessages.tsx +1 -2
  436. package/tui/src/hooks/notifs/useLspInitializationNotification.tsx +1 -2
  437. package/tui/src/hooks/notifs/useMcpConnectivityStatus.tsx +0 -1
  438. package/tui/src/hooks/notifs/useModelMigrationNotifications.tsx +1 -2
  439. package/tui/src/hooks/notifs/useNpmDeprecationNotification.tsx +1 -2
  440. package/tui/src/hooks/notifs/usePluginAutoupdateNotification.tsx +1 -2
  441. package/tui/src/hooks/notifs/usePluginInstallationStatus.tsx +1 -2
  442. package/tui/src/hooks/notifs/useRateLimitWarningNotification.tsx +1 -2
  443. package/tui/src/hooks/notifs/useSettingsErrors.tsx +1 -2
  444. package/tui/src/hooks/useApiKeyVerification.ts +7 -7
  445. package/tui/src/hooks/useArrowKeyHistory.tsx +1 -2
  446. package/tui/src/hooks/useCanUseTool.tsx +0 -1
  447. package/tui/src/hooks/useChromeExtensionNotification.tsx +0 -1
  448. package/tui/src/hooks/useClaudeCodeHintRecommendation.tsx +1 -2
  449. package/tui/src/hooks/useCommandKeybindings.tsx +1 -2
  450. package/tui/src/hooks/useGlobalKeybindings.tsx +1 -2
  451. package/tui/src/hooks/useIDEIntegration.tsx +1 -2
  452. package/tui/src/hooks/useLspPluginRecommendation.tsx +1 -2
  453. package/tui/src/hooks/useOfficialMarketplaceNotification.tsx +0 -1
  454. package/tui/src/hooks/usePluginRecommendationBase.tsx +1 -2
  455. package/tui/src/hooks/usePromptsFromClaudeInChrome.tsx +1 -2
  456. package/tui/src/hooks/useReplBridge.tsx +1 -2
  457. package/tui/src/hooks/useTypeahead.tsx +0 -1
  458. package/tui/src/hooks/useVoiceIntegration.tsx +1 -2
  459. package/tui/src/ink/Ansi.tsx +1 -2
  460. package/tui/src/ink/components/AlternateScreen.tsx +1 -2
  461. package/tui/src/ink/components/App.tsx +0 -1
  462. package/tui/src/ink/components/Box.tsx +1 -2
  463. package/tui/src/ink/components/Button.tsx +1 -2
  464. package/tui/src/ink/components/ClockContext.tsx +1 -2
  465. package/tui/src/ink/components/ErrorOverview.tsx +1 -2
  466. package/tui/src/ink/components/Link.tsx +1 -2
  467. package/tui/src/ink/components/Newline.tsx +1 -2
  468. package/tui/src/ink/components/NoSelect.tsx +1 -2
  469. package/tui/src/ink/components/RawAnsi.tsx +1 -2
  470. package/tui/src/ink/components/ScrollBox.tsx +1 -2
  471. package/tui/src/ink/components/Spacer.tsx +1 -2
  472. package/tui/src/ink/components/TerminalFocusContext.tsx +1 -2
  473. package/tui/src/ink/components/TerminalSizeContext.tsx +1 -2
  474. package/tui/src/ink/components/Text.tsx +1 -2
  475. package/tui/src/ink/ink.tsx +0 -1
  476. package/tui/src/interactiveHelpers.tsx +0 -1
  477. package/tui/src/keybindings/KeybindingContext.tsx +1 -2
  478. package/tui/src/keybindings/KeybindingProviderSetup.tsx +1 -2
  479. package/tui/src/main.tsx +0 -1
  480. package/tui/src/moreright/useMoreRight.tsx +1 -2
  481. package/tui/src/replLauncher.tsx +1 -2
  482. package/tui/src/screens/Doctor.tsx +0 -1
  483. package/tui/src/screens/REPL.tsx +0 -1
  484. package/tui/src/screens/ResumeConversation.tsx +0 -1
  485. package/tui/src/services/mcp/MCPConnectionManager.tsx +1 -2
  486. package/tui/src/services/mcp/claudeai.ts +1 -1
  487. package/tui/src/services/mcpServerApproval.tsx +1 -2
  488. package/tui/src/services/oauth/client.ts +16 -14
  489. package/tui/src/services/oauth/getOauthProfile.ts +2 -2
  490. package/tui/src/services/oauth/index.ts +12 -16
  491. package/tui/src/services/policyLimits/index.ts +3 -3
  492. package/tui/src/services/remoteManagedSettings/index.ts +3 -3
  493. package/tui/src/services/remoteManagedSettings/securityCheck.tsx +1 -2
  494. package/tui/src/services/remoteManagedSettings/syncCache.ts +3 -3
  495. package/tui/src/services/voiceStreamSTT.ts +2 -2
  496. package/tui/src/state/AppState.tsx +1 -2
  497. package/tui/src/tasks/InProcessTeammateTask/InProcessTeammateTask.tsx +1 -2
  498. package/tui/src/tasks/LocalAgentTask/LocalAgentTask.tsx +1 -2
  499. package/tui/src/tasks/LocalShellTask/LocalShellTask.tsx +1 -2
  500. package/tui/src/tasks/RemoteAgentTask/RemoteAgentTask.tsx +0 -1
  501. package/tui/src/tools/AgentTool/UI.tsx +1 -2
  502. package/tui/src/tools/BashTool/BashToolResultMessage.tsx +0 -1
  503. package/tui/src/tools/BashTool/UI.tsx +1 -2
  504. package/tui/src/tools/BriefTool/UI.tsx +0 -1
  505. package/tui/src/tools/ConfigTool/ConfigTool.ts +2 -2
  506. package/tui/src/tools/ConfigTool/UI.tsx +1 -2
  507. package/tui/src/tools/EnterPlanModeTool/UI.tsx +0 -1
  508. package/tui/src/tools/EnterWorktreeTool/UI.tsx +1 -2
  509. package/tui/src/tools/ExitPlanModeTool/UI.tsx +0 -1
  510. package/tui/src/tools/ExitWorktreeTool/UI.tsx +1 -2
  511. package/tui/src/tools/FileEditTool/UI.tsx +1 -2
  512. package/tui/src/tools/FileReadTool/UI.tsx +1 -2
  513. package/tui/src/tools/FileWriteTool/UI.tsx +1 -2
  514. package/tui/src/tools/GlobTool/UI.tsx +1 -2
  515. package/tui/src/tools/GrepTool/UI.tsx +1 -2
  516. package/tui/src/tools/LSPTool/UI.tsx +1 -2
  517. package/tui/src/tools/ListMcpResourcesTool/UI.tsx +1 -2
  518. package/tui/src/tools/MCPTool/UI.tsx +1 -2
  519. package/tui/src/tools/NotebookEditTool/UI.tsx +1 -2
  520. package/tui/src/tools/PowerShellTool/UI.tsx +0 -1
  521. package/tui/src/tools/ReadMcpResourceTool/UI.tsx +1 -2
  522. package/tui/src/tools/RemoteTriggerTool/UI.tsx +1 -2
  523. package/tui/src/tools/ScheduleCronTool/UI.tsx +1 -2
  524. package/tui/src/tools/SendMessageTool/UI.tsx +1 -2
  525. package/tui/src/tools/SkillTool/UI.tsx +1 -2
  526. package/tui/src/tools/TaskStopTool/UI.tsx +1 -2
  527. package/tui/src/tools/TeamCreateTool/UI.tsx +1 -2
  528. package/tui/src/tools/TeamDeleteTool/UI.tsx +1 -2
  529. package/tui/src/tools/WebFetchTool/UI.tsx +1 -2
  530. package/tui/src/tools/testing/TestingPermissionTool.tsx +1 -2
  531. package/tui/src/utils/auth.ts +8 -8
  532. package/tui/src/utils/autoRunIssue.tsx +1 -2
  533. package/tui/src/utils/billing.ts +2 -2
  534. package/tui/src/utils/computerUse/toolRendering.tsx +1 -2
  535. package/tui/src/utils/computerUse/wrapper.tsx +0 -1
  536. package/tui/src/utils/exportRenderer.tsx +1 -2
  537. package/tui/src/utils/fastMode.ts +2 -2
  538. package/tui/src/utils/highlightMatch.tsx +1 -2
  539. package/tui/src/utils/http.ts +2 -2
  540. package/tui/src/utils/managedEnv.ts +1 -1
  541. package/tui/src/utils/messages/systemInit.ts +2 -2
  542. package/tui/src/utils/plugins/performStartupChecks.tsx +1 -2
  543. package/tui/src/utils/preflightChecks.tsx +0 -1
  544. package/tui/src/utils/processUserInput/processBashCommand.tsx +1 -2
  545. package/tui/src/utils/processUserInput/processSlashCommand.tsx +0 -1
  546. package/tui/src/utils/staticRender.tsx +1 -2
  547. package/tui/src/utils/status.tsx +1 -2
  548. package/tui/src/utils/statusNoticeDefinitions.tsx +5 -6
  549. package/tui/src/utils/swarm/It2SetupPrompt.tsx +1 -2
  550. package/tui/src/utils/teleport.tsx +0 -1
  551. package/tui/src/voice/voiceModeEnabled.ts +3 -3
  552. package/uv.lock +1 -1
  553. package/src/ummaya/llm/_cc_reference/README.md +0 -104
  554. package/src/ummaya/llm/_cc_reference/api.ts +0 -721
  555. package/src/ummaya/llm/_cc_reference/claude.ts +0 -3422
  556. package/src/ummaya/llm/_cc_reference/client.ts +0 -392
  557. package/src/ummaya/llm/_cc_reference/emptyUsage.ts +0 -25
  558. package/src/ummaya/llm/_cc_reference/errors.ts +0 -1210
  559. package/src/ummaya/llm/_cc_reference/messages.ts +0 -5515
  560. package/src/ummaya/llm/_cc_reference/permissions.ts +0 -1489
  561. package/src/ummaya/llm/_cc_reference/prompts.ts +0 -917
  562. package/src/ummaya/llm/_cc_reference/query.ts +0 -1732
  563. package/src/ummaya/llm/_cc_reference/toolExecution.ts +0 -1748
  564. package/src/ummaya/llm/_cc_reference/toolOrchestration.ts +0 -191
  565. package/src/ummaya/llm/_cc_reference/toolResultStorage.ts +0 -1043
  566. package/src/ummaya/llm/_cc_reference/tools.ts +0 -392
@@ -1,1489 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0 (Anthropic upstream) — research-use mirror
2
- // Source: .references/claude-code-sourcemap/restored-src/src/utils/permissions/permissions.ts (CC 2.1.88)
3
-
4
- import { feature } from 'bun:bundle'
5
- import { APIUserAbortError } from '@anthropic-ai/sdk'
6
- import type { CanUseToolFn } from '../../hooks/useCanUseTool.js'
7
- import {
8
- getToolNameForPermissionCheck,
9
- mcpInfoFromString,
10
- } from '../../services/mcp/mcpStringUtils.js'
11
- import type { Tool, ToolPermissionContext, ToolUseContext } from '../../Tool.js'
12
- import { AGENT_TOOL_NAME } from '../../tools/AgentTool/constants.js'
13
- import { shouldUseSandbox } from '../../tools/BashTool/shouldUseSandbox.js'
14
- import { BASH_TOOL_NAME } from '../../tools/BashTool/toolName.js'
15
- import { POWERSHELL_TOOL_NAME } from '../../tools/PowerShellTool/toolName.js'
16
- import { REPL_TOOL_NAME } from '../../tools/REPLTool/constants.js'
17
- import type { AssistantMessage } from '../../types/message.js'
18
- import { extractOutputRedirections } from '../bash/commands.js'
19
- import { logForDebugging } from '../debug.js'
20
- import { AbortError, toError } from '../errors.js'
21
- import { logError } from '../log.js'
22
- import { SandboxManager } from '../sandbox/sandbox-adapter.js'
23
- import {
24
- getSettingSourceDisplayNameLowercase,
25
- SETTING_SOURCES,
26
- } from '../settings/constants.js'
27
- import { plural } from '../stringUtils.js'
28
- import { permissionModeTitle } from './PermissionMode.js'
29
- import type {
30
- PermissionAskDecision,
31
- PermissionDecision,
32
- PermissionDecisionReason,
33
- PermissionDenyDecision,
34
- PermissionResult,
35
- } from './PermissionResult.js'
36
- import type {
37
- PermissionBehavior,
38
- PermissionRule,
39
- PermissionRuleSource,
40
- PermissionRuleValue,
41
- } from './PermissionRule.js'
42
- import {
43
- applyPermissionUpdate,
44
- applyPermissionUpdates,
45
- persistPermissionUpdates,
46
- } from './PermissionUpdate.js'
47
- import type {
48
- PermissionUpdate,
49
- PermissionUpdateDestination,
50
- } from './PermissionUpdateSchema.js'
51
- import {
52
- permissionRuleValueFromString,
53
- permissionRuleValueToString,
54
- } from './permissionRuleParser.js'
55
- import {
56
- deletePermissionRuleFromSettings,
57
- type PermissionRuleFromEditableSettings,
58
- shouldAllowManagedPermissionRulesOnly,
59
- } from './permissionsLoader.js'
60
-
61
- /* eslint-disable @typescript-eslint/no-require-imports */
62
- const classifierDecisionModule = feature('TRANSCRIPT_CLASSIFIER')
63
- ? (require('./classifierDecision.js') as typeof import('./classifierDecision.js'))
64
- : null
65
- const autoModeStateModule = feature('TRANSCRIPT_CLASSIFIER')
66
- ? (require('./autoModeState.js') as typeof import('./autoModeState.js'))
67
- : null
68
-
69
- import {
70
- addToTurnClassifierDuration,
71
- getTotalCacheCreationInputTokens,
72
- getTotalCacheReadInputTokens,
73
- getTotalInputTokens,
74
- getTotalOutputTokens,
75
- } from '../../bootstrap/state.js'
76
- import { getFeatureValue_CACHED_WITH_REFRESH } from '../../services/analytics/growthbook.js'
77
- import {
78
- type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
79
- logEvent,
80
- } from '../../services/analytics/index.js'
81
- import { sanitizeToolNameForAnalytics } from '../../services/analytics/metadata.js'
82
- import {
83
- clearClassifierChecking,
84
- setClassifierChecking,
85
- } from '../classifierApprovals.js'
86
- import { isInProtectedNamespace } from '../envUtils.js'
87
- import { executePermissionRequestHooks } from '../hooks.js'
88
- import {
89
- AUTO_REJECT_MESSAGE,
90
- buildClassifierUnavailableMessage,
91
- buildYoloRejectionMessage,
92
- DONT_ASK_REJECT_MESSAGE,
93
- } from '../messages.js'
94
- import { calculateCostFromTokens } from '../modelCost.js'
95
- /* eslint-enable @typescript-eslint/no-require-imports */
96
- import { jsonStringify } from '../slowOperations.js'
97
- import {
98
- createDenialTrackingState,
99
- DENIAL_LIMITS,
100
- type DenialTrackingState,
101
- recordDenial,
102
- recordSuccess,
103
- shouldFallbackToPrompting,
104
- } from './denialTracking.js'
105
- import {
106
- classifyYoloAction,
107
- formatActionForClassifier,
108
- } from './yoloClassifier.js'
109
-
110
- const CLASSIFIER_FAIL_CLOSED_REFRESH_MS = 30 * 60 * 1000 // 30 minutes
111
-
112
- const PERMISSION_RULE_SOURCES = [
113
- ...SETTING_SOURCES,
114
- 'cliArg',
115
- 'command',
116
- 'session',
117
- ] as const satisfies readonly PermissionRuleSource[]
118
-
119
- export function permissionRuleSourceDisplayString(
120
- source: PermissionRuleSource,
121
- ): string {
122
- return getSettingSourceDisplayNameLowercase(source)
123
- }
124
-
125
- export function getAllowRules(
126
- context: ToolPermissionContext,
127
- ): PermissionRule[] {
128
- return PERMISSION_RULE_SOURCES.flatMap(source =>
129
- (context.alwaysAllowRules[source] || []).map(ruleString => ({
130
- source,
131
- ruleBehavior: 'allow',
132
- ruleValue: permissionRuleValueFromString(ruleString),
133
- })),
134
- )
135
- }
136
-
137
- /**
138
- * Creates a permission request message that explain the permission request
139
- */
140
- export function createPermissionRequestMessage(
141
- toolName: string,
142
- decisionReason?: PermissionDecisionReason,
143
- ): string {
144
- // Handle different decision reason types
145
- if (decisionReason) {
146
- if (
147
- (feature('BASH_CLASSIFIER') || feature('TRANSCRIPT_CLASSIFIER')) &&
148
- decisionReason.type === 'classifier'
149
- ) {
150
- return `Classifier '${decisionReason.classifier}' requires approval for this ${toolName} command: ${decisionReason.reason}`
151
- }
152
- switch (decisionReason.type) {
153
- case 'hook': {
154
- const hookMessage = decisionReason.reason
155
- ? `Hook '${decisionReason.hookName}' blocked this action: ${decisionReason.reason}`
156
- : `Hook '${decisionReason.hookName}' requires approval for this ${toolName} command`
157
- return hookMessage
158
- }
159
- case 'rule': {
160
- const ruleString = permissionRuleValueToString(
161
- decisionReason.rule.ruleValue,
162
- )
163
- const sourceString = permissionRuleSourceDisplayString(
164
- decisionReason.rule.source,
165
- )
166
- return `Permission rule '${ruleString}' from ${sourceString} requires approval for this ${toolName} command`
167
- }
168
- case 'subcommandResults': {
169
- const needsApproval: string[] = []
170
- for (const [cmd, result] of decisionReason.reasons) {
171
- if (result.behavior === 'ask' || result.behavior === 'passthrough') {
172
- // Strip output redirections for display to avoid showing filenames as commands
173
- // Only do this for Bash tool to avoid affecting other tools
174
- if (toolName === 'Bash') {
175
- const { commandWithoutRedirections, redirections } =
176
- extractOutputRedirections(cmd)
177
- // Only use stripped version if there were actual redirections
178
- const displayCmd =
179
- redirections.length > 0 ? commandWithoutRedirections : cmd
180
- needsApproval.push(displayCmd)
181
- } else {
182
- needsApproval.push(cmd)
183
- }
184
- }
185
- }
186
- if (needsApproval.length > 0) {
187
- const n = needsApproval.length
188
- return `This ${toolName} command contains multiple operations. The following ${plural(n, 'part')} ${plural(n, 'requires', 'require')} approval: ${needsApproval.join(', ')}`
189
- }
190
- return `This ${toolName} command contains multiple operations that require approval`
191
- }
192
- case 'permissionPromptTool':
193
- return `Tool '${decisionReason.permissionPromptToolName}' requires approval for this ${toolName} command`
194
- case 'sandboxOverride':
195
- return 'Run outside of the sandbox'
196
- case 'workingDir':
197
- return decisionReason.reason
198
- case 'safetyCheck':
199
- case 'other':
200
- return decisionReason.reason
201
- case 'mode': {
202
- const modeTitle = permissionModeTitle(decisionReason.mode)
203
- return `Current permission mode (${modeTitle}) requires approval for this ${toolName} command`
204
- }
205
- case 'asyncAgent':
206
- return decisionReason.reason
207
- }
208
- }
209
-
210
- // Default message without listing allowed commands
211
- const message = `Claude requested permissions to use ${toolName}, but you haven't granted it yet.`
212
-
213
- return message
214
- }
215
-
216
- export function getDenyRules(context: ToolPermissionContext): PermissionRule[] {
217
- return PERMISSION_RULE_SOURCES.flatMap(source =>
218
- (context.alwaysDenyRules[source] || []).map(ruleString => ({
219
- source,
220
- ruleBehavior: 'deny',
221
- ruleValue: permissionRuleValueFromString(ruleString),
222
- })),
223
- )
224
- }
225
-
226
- export function getAskRules(context: ToolPermissionContext): PermissionRule[] {
227
- return PERMISSION_RULE_SOURCES.flatMap(source =>
228
- (context.alwaysAskRules[source] || []).map(ruleString => ({
229
- source,
230
- ruleBehavior: 'ask',
231
- ruleValue: permissionRuleValueFromString(ruleString),
232
- })),
233
- )
234
- }
235
-
236
- /**
237
- * Check if the entire tool matches a rule
238
- * For example, this matches "Bash" but not "Bash(prefix:*)" for BashTool
239
- * This also matches MCP tools with a server name, e.g. the rule "mcp__server1"
240
- */
241
- function toolMatchesRule(
242
- tool: Pick<Tool, 'name' | 'mcpInfo'>,
243
- rule: PermissionRule,
244
- ): boolean {
245
- // Rule must not have content to match the entire tool
246
- if (rule.ruleValue.ruleContent !== undefined) {
247
- return false
248
- }
249
-
250
- // MCP tools are matched by their fully qualified mcp__server__tool name. In
251
- // skip-prefix mode (CLAUDE_AGENT_SDK_MCP_NO_PREFIX), MCP tools have unprefixed
252
- // display names (e.g., "Write") that collide with builtin names; rules targeting
253
- // builtins should not match their MCP replacements.
254
- const nameForRuleMatch = getToolNameForPermissionCheck(tool)
255
-
256
- // Direct tool name match
257
- if (rule.ruleValue.toolName === nameForRuleMatch) {
258
- return true
259
- }
260
-
261
- // MCP server-level permission: rule "mcp__server1" matches tool "mcp__server1__tool1"
262
- // Also supports wildcard: rule "mcp__server1__*" matches all tools from server1
263
- const ruleInfo = mcpInfoFromString(rule.ruleValue.toolName)
264
- const toolInfo = mcpInfoFromString(nameForRuleMatch)
265
-
266
- return (
267
- ruleInfo !== null &&
268
- toolInfo !== null &&
269
- (ruleInfo.toolName === undefined || ruleInfo.toolName === '*') &&
270
- ruleInfo.serverName === toolInfo.serverName
271
- )
272
- }
273
-
274
- /**
275
- * Check if the entire tool is listed in the always allow rules
276
- * For example, this finds "Bash" but not "Bash(prefix:*)" for BashTool
277
- */
278
- export function toolAlwaysAllowedRule(
279
- context: ToolPermissionContext,
280
- tool: Pick<Tool, 'name' | 'mcpInfo'>,
281
- ): PermissionRule | null {
282
- return (
283
- getAllowRules(context).find(rule => toolMatchesRule(tool, rule)) || null
284
- )
285
- }
286
-
287
- /**
288
- * Check if the tool is listed in the always deny rules
289
- */
290
- export function getDenyRuleForTool(
291
- context: ToolPermissionContext,
292
- tool: Pick<Tool, 'name' | 'mcpInfo'>,
293
- ): PermissionRule | null {
294
- return getDenyRules(context).find(rule => toolMatchesRule(tool, rule)) || null
295
- }
296
-
297
- /**
298
- * Check if the tool is listed in the always ask rules
299
- */
300
- export function getAskRuleForTool(
301
- context: ToolPermissionContext,
302
- tool: Pick<Tool, 'name' | 'mcpInfo'>,
303
- ): PermissionRule | null {
304
- return getAskRules(context).find(rule => toolMatchesRule(tool, rule)) || null
305
- }
306
-
307
- /**
308
- * Check if a specific agent is denied via Agent(agentType) syntax.
309
- * For example, Agent(Explore) would deny the Explore agent.
310
- */
311
- export function getDenyRuleForAgent(
312
- context: ToolPermissionContext,
313
- agentToolName: string,
314
- agentType: string,
315
- ): PermissionRule | null {
316
- return (
317
- getDenyRules(context).find(
318
- rule =>
319
- rule.ruleValue.toolName === agentToolName &&
320
- rule.ruleValue.ruleContent === agentType,
321
- ) || null
322
- )
323
- }
324
-
325
- /**
326
- * Filter agents to exclude those that are denied via Agent(agentType) syntax.
327
- */
328
- export function filterDeniedAgents<T extends { agentType: string }>(
329
- agents: T[],
330
- context: ToolPermissionContext,
331
- agentToolName: string,
332
- ): T[] {
333
- // Parse deny rules once and collect Agent(x) contents into a Set.
334
- // Previously this called getDenyRuleForAgent per agent, which re-parsed
335
- // every deny rule for every agent (O(agents×rules) parse calls).
336
- const deniedAgentTypes = new Set<string>()
337
- for (const rule of getDenyRules(context)) {
338
- if (
339
- rule.ruleValue.toolName === agentToolName &&
340
- rule.ruleValue.ruleContent !== undefined
341
- ) {
342
- deniedAgentTypes.add(rule.ruleValue.ruleContent)
343
- }
344
- }
345
- return agents.filter(agent => !deniedAgentTypes.has(agent.agentType))
346
- }
347
-
348
- /**
349
- * Map of rule contents to the associated rule for a given tool.
350
- * e.g. the string key is "prefix:*" from "Bash(prefix:*)" for BashTool
351
- */
352
- export function getRuleByContentsForTool(
353
- context: ToolPermissionContext,
354
- tool: Tool,
355
- behavior: PermissionBehavior,
356
- ): Map<string, PermissionRule> {
357
- return getRuleByContentsForToolName(
358
- context,
359
- getToolNameForPermissionCheck(tool),
360
- behavior,
361
- )
362
- }
363
-
364
- // Used to break circular dependency where a Tool calls this function
365
- export function getRuleByContentsForToolName(
366
- context: ToolPermissionContext,
367
- toolName: string,
368
- behavior: PermissionBehavior,
369
- ): Map<string, PermissionRule> {
370
- const ruleByContents = new Map<string, PermissionRule>()
371
- let rules: PermissionRule[] = []
372
- switch (behavior) {
373
- case 'allow':
374
- rules = getAllowRules(context)
375
- break
376
- case 'deny':
377
- rules = getDenyRules(context)
378
- break
379
- case 'ask':
380
- rules = getAskRules(context)
381
- break
382
- }
383
- for (const rule of rules) {
384
- if (
385
- rule.ruleValue.toolName === toolName &&
386
- rule.ruleValue.ruleContent !== undefined &&
387
- rule.ruleBehavior === behavior
388
- ) {
389
- ruleByContents.set(rule.ruleValue.ruleContent, rule)
390
- }
391
- }
392
- return ruleByContents
393
- }
394
-
395
- /**
396
- * Runs PermissionRequest hooks for headless/async agents that cannot show
397
- * permission prompts. This gives hooks an opportunity to allow or deny
398
- * tool use before the fallback auto-deny kicks in.
399
- *
400
- * Returns a PermissionDecision if a hook made a decision, or null if no
401
- * hook provided a decision (caller should proceed to auto-deny).
402
- */
403
- async function runPermissionRequestHooksForHeadlessAgent(
404
- tool: Tool,
405
- input: { [key: string]: unknown },
406
- toolUseID: string,
407
- context: ToolUseContext,
408
- permissionMode: string | undefined,
409
- suggestions: PermissionUpdate[] | undefined,
410
- ): Promise<PermissionDecision | null> {
411
- try {
412
- for await (const hookResult of executePermissionRequestHooks(
413
- tool.name,
414
- toolUseID,
415
- input,
416
- context,
417
- permissionMode,
418
- suggestions,
419
- context.abortController.signal,
420
- )) {
421
- if (!hookResult.permissionRequestResult) {
422
- continue
423
- }
424
- const decision = hookResult.permissionRequestResult
425
- if (decision.behavior === 'allow') {
426
- const finalInput = decision.updatedInput ?? input
427
- // Persist permission updates if provided
428
- if (decision.updatedPermissions?.length) {
429
- persistPermissionUpdates(decision.updatedPermissions)
430
- context.setAppState(prev => ({
431
- ...prev,
432
- toolPermissionContext: applyPermissionUpdates(
433
- prev.toolPermissionContext,
434
- decision.updatedPermissions!,
435
- ),
436
- }))
437
- }
438
- return {
439
- behavior: 'allow',
440
- updatedInput: finalInput,
441
- decisionReason: {
442
- type: 'hook',
443
- hookName: 'PermissionRequest',
444
- },
445
- }
446
- }
447
- if (decision.behavior === 'deny') {
448
- if (decision.interrupt) {
449
- logForDebugging(
450
- `Hook interrupt: tool=${tool.name} hookMessage=${decision.message}`,
451
- )
452
- context.abortController.abort()
453
- }
454
- return {
455
- behavior: 'deny',
456
- message: decision.message || 'Permission denied by hook',
457
- decisionReason: {
458
- type: 'hook',
459
- hookName: 'PermissionRequest',
460
- reason: decision.message,
461
- },
462
- }
463
- }
464
- }
465
- } catch (error) {
466
- // If hooks fail, fall through to auto-deny rather than crashing
467
- logError(
468
- new Error('PermissionRequest hook failed for headless agent', {
469
- cause: toError(error),
470
- }),
471
- )
472
- }
473
- return null
474
- }
475
-
476
- export const hasPermissionsToUseTool: CanUseToolFn = async (
477
- tool,
478
- input,
479
- context,
480
- assistantMessage,
481
- toolUseID,
482
- ): Promise<PermissionDecision> => {
483
- const result = await hasPermissionsToUseToolInner(tool, input, context)
484
-
485
-
486
- // Reset consecutive denials on any allowed tool use in auto mode.
487
- // This ensures that a successful tool use (even one auto-allowed by rules)
488
- // breaks the consecutive denial streak.
489
- if (result.behavior === 'allow') {
490
- const appState = context.getAppState()
491
- if (feature('TRANSCRIPT_CLASSIFIER')) {
492
- const currentDenialState =
493
- context.localDenialTracking ?? appState.denialTracking
494
- if (
495
- appState.toolPermissionContext.mode === 'auto' &&
496
- currentDenialState &&
497
- currentDenialState.consecutiveDenials > 0
498
- ) {
499
- const newDenialState = recordSuccess(currentDenialState)
500
- persistDenialState(context, newDenialState)
501
- }
502
- }
503
- return result
504
- }
505
-
506
- // Apply dontAsk mode transformation: convert 'ask' to 'deny'
507
- // This is done at the end so it can't be bypassed by early returns
508
- if (result.behavior === 'ask') {
509
- const appState = context.getAppState()
510
-
511
- if (appState.toolPermissionContext.mode === 'dontAsk') {
512
- return {
513
- behavior: 'deny',
514
- decisionReason: {
515
- type: 'mode',
516
- mode: 'dontAsk',
517
- },
518
- message: DONT_ASK_REJECT_MESSAGE(tool.name),
519
- }
520
- }
521
- // Apply auto mode: use AI classifier instead of prompting user
522
- // Check this BEFORE shouldAvoidPermissionPrompts so classifiers work in headless mode
523
- if (
524
- feature('TRANSCRIPT_CLASSIFIER') &&
525
- (appState.toolPermissionContext.mode === 'auto' ||
526
- (appState.toolPermissionContext.mode === 'plan' &&
527
- (autoModeStateModule?.isAutoModeActive() ?? false)))
528
- ) {
529
- // Non-classifier-approvable safetyCheck decisions stay immune to ALL
530
- // auto-approve paths: the acceptEdits fast-path, the safe-tool allowlist,
531
- // and the classifier. Step 1g only guards bypassPermissions; this guards
532
- // auto. classifierApprovable safetyChecks (sensitive-file paths) fall
533
- // through to the classifier — the fast-paths below naturally don't fire
534
- // because the tool's own checkPermissions still returns 'ask'.
535
- if (
536
- result.decisionReason?.type === 'safetyCheck' &&
537
- !result.decisionReason.classifierApprovable
538
- ) {
539
- if (appState.toolPermissionContext.shouldAvoidPermissionPrompts) {
540
- return {
541
- behavior: 'deny',
542
- message: result.message,
543
- decisionReason: {
544
- type: 'asyncAgent',
545
- reason:
546
- 'Safety check requires interactive approval and permission prompts are not available in this context',
547
- },
548
- }
549
- }
550
- return result
551
- }
552
- if (tool.requiresUserInteraction?.() && result.behavior === 'ask') {
553
- return result
554
- }
555
-
556
- // Use local denial tracking for async subagents (whose setAppState
557
- // is a no-op), otherwise read from appState as before.
558
- const denialState =
559
- context.localDenialTracking ??
560
- appState.denialTracking ??
561
- createDenialTrackingState()
562
-
563
- // PowerShell requires explicit user permission in auto mode unless
564
- // POWERSHELL_AUTO_MODE (ant-only build flag) is on. When disabled, this
565
- // guard keeps PS out of the classifier and skips the acceptEdits
566
- // fast-path below. When enabled, PS flows through to the classifier like
567
- // Bash — the classifier prompt gets POWERSHELL_DENY_GUIDANCE appended so
568
- // it recognizes `iex (iwr ...)` as download-and-execute, etc.
569
- // Note: this runs inside the behavior === 'ask' branch, so allow rules
570
- // that fire earlier (step 2b toolAlwaysAllowedRule, PS prefix allow)
571
- // return before reaching here. Allow-rule protection is handled by
572
- // permissionSetup.ts: isOverlyBroadPowerShellAllowRule strips PowerShell(*)
573
- // and isDangerousPowerShellPermission strips iex/pwsh/Start-Process
574
- // prefix rules for ant users and auto mode entry.
575
- if (
576
- tool.name === POWERSHELL_TOOL_NAME &&
577
- !feature('POWERSHELL_AUTO_MODE')
578
- ) {
579
- if (appState.toolPermissionContext.shouldAvoidPermissionPrompts) {
580
- return {
581
- behavior: 'deny',
582
- message: 'PowerShell tool requires interactive approval',
583
- decisionReason: {
584
- type: 'asyncAgent',
585
- reason:
586
- 'PowerShell tool requires interactive approval and permission prompts are not available in this context',
587
- },
588
- }
589
- }
590
- logForDebugging(
591
- `Skipping auto mode classifier for ${tool.name}: tool requires explicit user permission`,
592
- )
593
- return result
594
- }
595
-
596
- // Before running the auto mode classifier, check if acceptEdits mode would
597
- // allow this action. This avoids expensive classifier API calls for safe
598
- // operations like file edits in the working directory.
599
- // Skip for Agent and REPL — their checkPermissions returns 'allow' for
600
- // acceptEdits mode, which would silently bypass the classifier. REPL
601
- // code can contain VM escapes between inner tool calls; the classifier
602
- // must see the glue JavaScript, not just the inner tool calls.
603
- if (
604
- result.behavior === 'ask' &&
605
- tool.name !== AGENT_TOOL_NAME &&
606
- tool.name !== REPL_TOOL_NAME
607
- ) {
608
- try {
609
- const parsedInput = tool.inputSchema.parse(input)
610
- const acceptEditsResult = await tool.checkPermissions(parsedInput, {
611
- ...context,
612
- getAppState: () => {
613
- const state = context.getAppState()
614
- return {
615
- ...state,
616
- toolPermissionContext: {
617
- ...state.toolPermissionContext,
618
- mode: 'acceptEdits' as const,
619
- },
620
- }
621
- },
622
- })
623
- if (acceptEditsResult.behavior === 'allow') {
624
- const newDenialState = recordSuccess(denialState)
625
- persistDenialState(context, newDenialState)
626
- logForDebugging(
627
- `Skipping auto mode classifier for ${tool.name}: would be allowed in acceptEdits mode`,
628
- )
629
- logEvent('tengu_auto_mode_decision', {
630
- decision:
631
- 'allowed' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
632
- toolName: sanitizeToolNameForAnalytics(tool.name),
633
- inProtectedNamespace: isInProtectedNamespace(),
634
- // msg_id of the agent completion that produced this tool_use —
635
- // the action at the bottom of the classifier transcript. Joins
636
- // the decision back to the main agent's API response.
637
- agentMsgId: assistantMessage.message
638
- .id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
639
- confidence:
640
- 'high' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
641
- fastPath:
642
- 'acceptEdits' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
643
- })
644
- return {
645
- behavior: 'allow',
646
- updatedInput: acceptEditsResult.updatedInput ?? input,
647
- decisionReason: {
648
- type: 'mode',
649
- mode: 'auto',
650
- },
651
- }
652
- }
653
- } catch (e) {
654
- if (e instanceof AbortError || e instanceof APIUserAbortError) {
655
- throw e
656
- }
657
- // If the acceptEdits check fails, fall through to the classifier
658
- }
659
- }
660
-
661
- // Allowlisted tools are safe and don't need YOLO classification.
662
- // This uses the safe-tool allowlist to skip unnecessary classifier API calls.
663
- if (classifierDecisionModule!.isAutoModeAllowlistedTool(tool.name)) {
664
- const newDenialState = recordSuccess(denialState)
665
- persistDenialState(context, newDenialState)
666
- logForDebugging(
667
- `Skipping auto mode classifier for ${tool.name}: tool is on the safe allowlist`,
668
- )
669
- logEvent('tengu_auto_mode_decision', {
670
- decision:
671
- 'allowed' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
672
- toolName: sanitizeToolNameForAnalytics(tool.name),
673
- inProtectedNamespace: isInProtectedNamespace(),
674
- agentMsgId: assistantMessage.message
675
- .id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
676
- confidence:
677
- 'high' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
678
- fastPath:
679
- 'allowlist' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
680
- })
681
- return {
682
- behavior: 'allow',
683
- updatedInput: input,
684
- decisionReason: {
685
- type: 'mode',
686
- mode: 'auto',
687
- },
688
- }
689
- }
690
-
691
- // Run the auto mode classifier
692
- const action = formatActionForClassifier(tool.name, input)
693
- setClassifierChecking(toolUseID)
694
- let classifierResult
695
- try {
696
- classifierResult = await classifyYoloAction(
697
- context.messages,
698
- action,
699
- context.options.tools,
700
- appState.toolPermissionContext,
701
- context.abortController.signal,
702
- )
703
- } finally {
704
- clearClassifierChecking(toolUseID)
705
- }
706
-
707
- // Notify ants when classifier error dumped prompts (will be in /share)
708
- if (
709
- process.env.USER_TYPE === 'ant' &&
710
- classifierResult.errorDumpPath &&
711
- context.addNotification
712
- ) {
713
- context.addNotification({
714
- key: 'auto-mode-error-dump',
715
- text: `Auto mode classifier error — prompts dumped to ${classifierResult.errorDumpPath} (included in /share)`,
716
- priority: 'immediate',
717
- color: 'error',
718
- })
719
- }
720
-
721
- // Log classifier decision for metrics (including overhead telemetry)
722
- const yoloDecision = classifierResult.unavailable
723
- ? 'unavailable'
724
- : classifierResult.shouldBlock
725
- ? 'blocked'
726
- : 'allowed'
727
-
728
- // Compute classifier cost in USD for overhead analysis
729
- const classifierCostUSD =
730
- classifierResult.usage && classifierResult.model
731
- ? calculateCostFromTokens(
732
- classifierResult.model,
733
- classifierResult.usage,
734
- )
735
- : undefined
736
- logEvent('tengu_auto_mode_decision', {
737
- decision:
738
- yoloDecision as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
739
- toolName: sanitizeToolNameForAnalytics(tool.name),
740
- inProtectedNamespace: isInProtectedNamespace(),
741
- // msg_id of the agent completion that produced this tool_use —
742
- // the action at the bottom of the classifier transcript.
743
- agentMsgId: assistantMessage.message
744
- .id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
745
- classifierModel:
746
- classifierResult.model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
747
- consecutiveDenials: classifierResult.shouldBlock
748
- ? denialState.consecutiveDenials + 1
749
- : 0,
750
- totalDenials: classifierResult.shouldBlock
751
- ? denialState.totalDenials + 1
752
- : denialState.totalDenials,
753
- // Overhead telemetry: token usage and latency for the classifier API call
754
- classifierInputTokens: classifierResult.usage?.inputTokens,
755
- classifierOutputTokens: classifierResult.usage?.outputTokens,
756
- classifierCacheReadInputTokens:
757
- classifierResult.usage?.cacheReadInputTokens,
758
- classifierCacheCreationInputTokens:
759
- classifierResult.usage?.cacheCreationInputTokens,
760
- classifierDurationMs: classifierResult.durationMs,
761
- // Character lengths of the prompt components sent to the classifier
762
- classifierSystemPromptLength:
763
- classifierResult.promptLengths?.systemPrompt,
764
- classifierToolCallsLength: classifierResult.promptLengths?.toolCalls,
765
- classifierUserPromptsLength:
766
- classifierResult.promptLengths?.userPrompts,
767
- // Session totals at time of classifier call (for computing overhead %).
768
- // These are main-transcript-only — sideQuery (used by the classifier)
769
- // does NOT call addToTotalSessionCost, so classifier tokens are excluded.
770
- sessionInputTokens: getTotalInputTokens(),
771
- sessionOutputTokens: getTotalOutputTokens(),
772
- sessionCacheReadInputTokens: getTotalCacheReadInputTokens(),
773
- sessionCacheCreationInputTokens: getTotalCacheCreationInputTokens(),
774
- classifierCostUSD,
775
- classifierStage:
776
- classifierResult.stage as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
777
- classifierStage1InputTokens: classifierResult.stage1Usage?.inputTokens,
778
- classifierStage1OutputTokens:
779
- classifierResult.stage1Usage?.outputTokens,
780
- classifierStage1CacheReadInputTokens:
781
- classifierResult.stage1Usage?.cacheReadInputTokens,
782
- classifierStage1CacheCreationInputTokens:
783
- classifierResult.stage1Usage?.cacheCreationInputTokens,
784
- classifierStage1DurationMs: classifierResult.stage1DurationMs,
785
- classifierStage1RequestId:
786
- classifierResult.stage1RequestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
787
- classifierStage1MsgId:
788
- classifierResult.stage1MsgId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
789
- classifierStage1CostUSD:
790
- classifierResult.stage1Usage && classifierResult.model
791
- ? calculateCostFromTokens(
792
- classifierResult.model,
793
- classifierResult.stage1Usage,
794
- )
795
- : undefined,
796
- classifierStage2InputTokens: classifierResult.stage2Usage?.inputTokens,
797
- classifierStage2OutputTokens:
798
- classifierResult.stage2Usage?.outputTokens,
799
- classifierStage2CacheReadInputTokens:
800
- classifierResult.stage2Usage?.cacheReadInputTokens,
801
- classifierStage2CacheCreationInputTokens:
802
- classifierResult.stage2Usage?.cacheCreationInputTokens,
803
- classifierStage2DurationMs: classifierResult.stage2DurationMs,
804
- classifierStage2RequestId:
805
- classifierResult.stage2RequestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
806
- classifierStage2MsgId:
807
- classifierResult.stage2MsgId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
808
- classifierStage2CostUSD:
809
- classifierResult.stage2Usage && classifierResult.model
810
- ? calculateCostFromTokens(
811
- classifierResult.model,
812
- classifierResult.stage2Usage,
813
- )
814
- : undefined,
815
- })
816
-
817
- if (classifierResult.durationMs !== undefined) {
818
- addToTurnClassifierDuration(classifierResult.durationMs)
819
- }
820
-
821
- if (classifierResult.shouldBlock) {
822
- // Transcript exceeded the classifier's context window — deterministic
823
- // error, won't recover on retry. Skip iron_gate and fall back to
824
- // normal prompting so the user can approve/deny manually.
825
- if (classifierResult.transcriptTooLong) {
826
- if (appState.toolPermissionContext.shouldAvoidPermissionPrompts) {
827
- // Permanent condition (transcript only grows) — deny-retry-deny
828
- // wastes tokens without ever hitting the denial-limit abort.
829
- throw new AbortError(
830
- 'Agent aborted: auto mode classifier transcript exceeded context window in headless mode',
831
- )
832
- }
833
- logForDebugging(
834
- 'Auto mode classifier transcript too long, falling back to normal permission handling',
835
- { level: 'warn' },
836
- )
837
- return {
838
- ...result,
839
- decisionReason: {
840
- type: 'other',
841
- reason:
842
- 'Auto mode classifier transcript exceeded context window — falling back to manual approval',
843
- },
844
- }
845
- }
846
- // When classifier is unavailable (API error), behavior depends on
847
- // the tengu_iron_gate_closed gate.
848
- if (classifierResult.unavailable) {
849
- if (
850
- getFeatureValue_CACHED_WITH_REFRESH(
851
- 'tengu_iron_gate_closed',
852
- true,
853
- CLASSIFIER_FAIL_CLOSED_REFRESH_MS,
854
- )
855
- ) {
856
- logForDebugging(
857
- 'Auto mode classifier unavailable, denying with retry guidance (fail closed)',
858
- { level: 'warn' },
859
- )
860
- return {
861
- behavior: 'deny',
862
- decisionReason: {
863
- type: 'classifier',
864
- classifier: 'auto-mode',
865
- reason: 'Classifier unavailable',
866
- },
867
- message: buildClassifierUnavailableMessage(
868
- tool.name,
869
- classifierResult.model,
870
- ),
871
- }
872
- }
873
- // Fail open: fall back to normal permission handling
874
- logForDebugging(
875
- 'Auto mode classifier unavailable, falling back to normal permission handling (fail open)',
876
- { level: 'warn' },
877
- )
878
- return result
879
- }
880
-
881
- // Update denial tracking and check limits
882
- const newDenialState = recordDenial(denialState)
883
- persistDenialState(context, newDenialState)
884
-
885
- logForDebugging(
886
- `Auto mode classifier blocked action: ${classifierResult.reason}`,
887
- { level: 'warn' },
888
- )
889
-
890
- // If denial limit hit, fall back to prompting so the user
891
- // can review. We check after the classifier so we can include
892
- // its reason in the prompt.
893
- const denialLimitResult = handleDenialLimitExceeded(
894
- newDenialState,
895
- appState,
896
- classifierResult.reason,
897
- assistantMessage,
898
- tool,
899
- result,
900
- context,
901
- )
902
- if (denialLimitResult) {
903
- return denialLimitResult
904
- }
905
-
906
- return {
907
- behavior: 'deny',
908
- decisionReason: {
909
- type: 'classifier',
910
- classifier: 'auto-mode',
911
- reason: classifierResult.reason,
912
- },
913
- message: buildYoloRejectionMessage(classifierResult.reason),
914
- }
915
- }
916
-
917
- // Reset consecutive denials on success
918
- const newDenialState = recordSuccess(denialState)
919
- persistDenialState(context, newDenialState)
920
-
921
- return {
922
- behavior: 'allow',
923
- updatedInput: input,
924
- decisionReason: {
925
- type: 'classifier',
926
- classifier: 'auto-mode',
927
- reason: classifierResult.reason,
928
- },
929
- }
930
- }
931
-
932
- // When permission prompts should be avoided (e.g., background/headless agents),
933
- // run PermissionRequest hooks first to give them a chance to allow/deny.
934
- // Only auto-deny if no hook provides a decision.
935
- if (appState.toolPermissionContext.shouldAvoidPermissionPrompts) {
936
- const hookDecision = await runPermissionRequestHooksForHeadlessAgent(
937
- tool,
938
- input,
939
- toolUseID,
940
- context,
941
- appState.toolPermissionContext.mode,
942
- result.suggestions,
943
- )
944
- if (hookDecision) {
945
- return hookDecision
946
- }
947
- return {
948
- behavior: 'deny',
949
- decisionReason: {
950
- type: 'asyncAgent',
951
- reason: 'Permission prompts are not available in this context',
952
- },
953
- message: AUTO_REJECT_MESSAGE(tool.name),
954
- }
955
- }
956
- }
957
-
958
- return result
959
- }
960
-
961
- /**
962
- * Persist denial tracking state. For async subagents with localDenialTracking,
963
- * mutate the local state in place (since setAppState is a no-op). Otherwise,
964
- * write to appState as usual.
965
- */
966
- function persistDenialState(
967
- context: ToolUseContext,
968
- newState: DenialTrackingState,
969
- ): void {
970
- if (context.localDenialTracking) {
971
- Object.assign(context.localDenialTracking, newState)
972
- } else {
973
- context.setAppState(prev => {
974
- // recordSuccess returns the same reference when state is
975
- // unchanged. Returning prev here lets store.setState's Object.is check
976
- // skip the listener loop entirely.
977
- if (prev.denialTracking === newState) return prev
978
- return { ...prev, denialTracking: newState }
979
- })
980
- }
981
- }
982
-
983
- /**
984
- * Check if a denial limit was exceeded and return an 'ask' result
985
- * so the user can review. Returns null if no limit was hit.
986
- */
987
- function handleDenialLimitExceeded(
988
- denialState: DenialTrackingState,
989
- appState: {
990
- toolPermissionContext: { shouldAvoidPermissionPrompts?: boolean }
991
- },
992
- classifierReason: string,
993
- assistantMessage: AssistantMessage,
994
- tool: Tool,
995
- result: PermissionDecision,
996
- context: ToolUseContext,
997
- ): PermissionDecision | null {
998
- if (!shouldFallbackToPrompting(denialState)) {
999
- return null
1000
- }
1001
-
1002
- const hitTotalLimit = denialState.totalDenials >= DENIAL_LIMITS.maxTotal
1003
- const isHeadless = appState.toolPermissionContext.shouldAvoidPermissionPrompts
1004
- // Capture counts before persistDenialState, which may mutate denialState
1005
- // in-place via Object.assign for subagents with localDenialTracking.
1006
- const totalCount = denialState.totalDenials
1007
- const consecutiveCount = denialState.consecutiveDenials
1008
- const warning = hitTotalLimit
1009
- ? `${totalCount} actions were blocked this session. Please review the transcript before continuing.`
1010
- : `${consecutiveCount} consecutive actions were blocked. Please review the transcript before continuing.`
1011
-
1012
- logEvent('tengu_auto_mode_denial_limit_exceeded', {
1013
- limit: (hitTotalLimit
1014
- ? 'total'
1015
- : 'consecutive') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1016
- mode: (isHeadless
1017
- ? 'headless'
1018
- : 'cli') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1019
- messageID: assistantMessage.message
1020
- .id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1021
- consecutiveDenials: consecutiveCount,
1022
- totalDenials: totalCount,
1023
- toolName: sanitizeToolNameForAnalytics(tool.name),
1024
- })
1025
-
1026
- if (isHeadless) {
1027
- throw new AbortError(
1028
- 'Agent aborted: too many classifier denials in headless mode',
1029
- )
1030
- }
1031
-
1032
- logForDebugging(
1033
- `Classifier denial limit exceeded, falling back to prompting: ${warning}`,
1034
- { level: 'warn' },
1035
- )
1036
-
1037
- if (hitTotalLimit) {
1038
- persistDenialState(context, {
1039
- ...denialState,
1040
- totalDenials: 0,
1041
- consecutiveDenials: 0,
1042
- })
1043
- }
1044
-
1045
- // Preserve the original classifier value (e.g. 'dangerous-agent-action')
1046
- // so downstream analytics in interactiveHandler can log the correct
1047
- // user override event.
1048
- const originalClassifier =
1049
- result.decisionReason?.type === 'classifier'
1050
- ? result.decisionReason.classifier
1051
- : 'auto-mode'
1052
-
1053
- return {
1054
- ...result,
1055
- decisionReason: {
1056
- type: 'classifier',
1057
- classifier: originalClassifier,
1058
- reason: `${warning}\n\nLatest blocked action: ${classifierReason}`,
1059
- },
1060
- }
1061
- }
1062
-
1063
- /**
1064
- * Check only the rule-based steps of the permission pipeline — the subset
1065
- * that bypassPermissions mode respects (everything that fires before step 2a).
1066
- *
1067
- * Returns a deny/ask decision if a rule blocks the tool, or null if no rule
1068
- * objects. Unlike hasPermissionsToUseTool, this does NOT run the auto mode classifier,
1069
- * mode-based transformations (dontAsk/auto/asyncAgent), PermissionRequest hooks,
1070
- * or bypassPermissions / always-allowed checks.
1071
- *
1072
- * Caller must pre-check tool.requiresUserInteraction() — step 1e is not replicated.
1073
- */
1074
- export async function checkRuleBasedPermissions(
1075
- tool: Tool,
1076
- input: { [key: string]: unknown },
1077
- context: ToolUseContext,
1078
- ): Promise<PermissionAskDecision | PermissionDenyDecision | null> {
1079
- const appState = context.getAppState()
1080
-
1081
- // 1a. Entire tool is denied by rule
1082
- const denyRule = getDenyRuleForTool(appState.toolPermissionContext, tool)
1083
- if (denyRule) {
1084
- return {
1085
- behavior: 'deny',
1086
- decisionReason: {
1087
- type: 'rule',
1088
- rule: denyRule,
1089
- },
1090
- message: `Permission to use ${tool.name} has been denied.`,
1091
- }
1092
- }
1093
-
1094
- // 1b. Entire tool has an ask rule
1095
- const askRule = getAskRuleForTool(appState.toolPermissionContext, tool)
1096
- if (askRule) {
1097
- const canSandboxAutoAllow =
1098
- tool.name === BASH_TOOL_NAME &&
1099
- SandboxManager.isSandboxingEnabled() &&
1100
- SandboxManager.isAutoAllowBashIfSandboxedEnabled() &&
1101
- shouldUseSandbox(input)
1102
-
1103
- if (!canSandboxAutoAllow) {
1104
- return {
1105
- behavior: 'ask',
1106
- decisionReason: {
1107
- type: 'rule',
1108
- rule: askRule,
1109
- },
1110
- message: createPermissionRequestMessage(tool.name),
1111
- }
1112
- }
1113
- // Fall through to let tool.checkPermissions handle command-specific rules
1114
- }
1115
-
1116
- // 1c. Tool-specific permission check (e.g. bash subcommand rules)
1117
- let toolPermissionResult: PermissionResult = {
1118
- behavior: 'passthrough',
1119
- message: createPermissionRequestMessage(tool.name),
1120
- }
1121
- try {
1122
- const parsedInput = tool.inputSchema.parse(input)
1123
- toolPermissionResult = await tool.checkPermissions(parsedInput, context)
1124
- } catch (e) {
1125
- if (e instanceof AbortError || e instanceof APIUserAbortError) {
1126
- throw e
1127
- }
1128
- logError(e)
1129
- }
1130
-
1131
- // 1d. Tool implementation denied (catches bash subcommand denies wrapped
1132
- // in subcommandResults — no need to inspect decisionReason.type)
1133
- if (toolPermissionResult?.behavior === 'deny') {
1134
- return toolPermissionResult
1135
- }
1136
-
1137
- // 1f. Content-specific ask rules from tool.checkPermissions
1138
- // (e.g. Bash(npm publish:*) → {ask, type:'rule', ruleBehavior:'ask'})
1139
- if (
1140
- toolPermissionResult?.behavior === 'ask' &&
1141
- toolPermissionResult.decisionReason?.type === 'rule' &&
1142
- toolPermissionResult.decisionReason.rule.ruleBehavior === 'ask'
1143
- ) {
1144
- return toolPermissionResult
1145
- }
1146
-
1147
- // 1g. Safety checks (e.g. .git/, .claude/, .vscode/, shell configs) are
1148
- // bypass-immune — they must prompt even when a PreToolUse hook returned
1149
- // allow. checkPathSafetyForAutoEdit returns {type:'safetyCheck'} for these.
1150
- if (
1151
- toolPermissionResult?.behavior === 'ask' &&
1152
- toolPermissionResult.decisionReason?.type === 'safetyCheck'
1153
- ) {
1154
- return toolPermissionResult
1155
- }
1156
-
1157
- // No rule-based objection
1158
- return null
1159
- }
1160
-
1161
- async function hasPermissionsToUseToolInner(
1162
- tool: Tool,
1163
- input: { [key: string]: unknown },
1164
- context: ToolUseContext,
1165
- ): Promise<PermissionDecision> {
1166
- if (context.abortController.signal.aborted) {
1167
- throw new AbortError()
1168
- }
1169
-
1170
- let appState = context.getAppState()
1171
-
1172
- // 1. Check if the tool is denied
1173
- // 1a. Entire tool is denied
1174
- const denyRule = getDenyRuleForTool(appState.toolPermissionContext, tool)
1175
- if (denyRule) {
1176
- return {
1177
- behavior: 'deny',
1178
- decisionReason: {
1179
- type: 'rule',
1180
- rule: denyRule,
1181
- },
1182
- message: `Permission to use ${tool.name} has been denied.`,
1183
- }
1184
- }
1185
-
1186
- // 1b. Check if the entire tool should always ask for permission
1187
- const askRule = getAskRuleForTool(appState.toolPermissionContext, tool)
1188
- if (askRule) {
1189
- // When autoAllowBashIfSandboxed is on, sandboxed commands skip the ask rule and
1190
- // auto-allow via Bash's checkPermissions. Commands that won't be sandboxed (excluded
1191
- // commands, dangerouslyDisableSandbox) still need to respect the ask rule.
1192
- const canSandboxAutoAllow =
1193
- tool.name === BASH_TOOL_NAME &&
1194
- SandboxManager.isSandboxingEnabled() &&
1195
- SandboxManager.isAutoAllowBashIfSandboxedEnabled() &&
1196
- shouldUseSandbox(input)
1197
-
1198
- if (!canSandboxAutoAllow) {
1199
- return {
1200
- behavior: 'ask',
1201
- decisionReason: {
1202
- type: 'rule',
1203
- rule: askRule,
1204
- },
1205
- message: createPermissionRequestMessage(tool.name),
1206
- }
1207
- }
1208
- // Fall through to let Bash's checkPermissions handle command-specific rules
1209
- }
1210
-
1211
- // 1c. Ask the tool implementation for a permission result
1212
- // Overridden unless tool input schema is not valid
1213
- let toolPermissionResult: PermissionResult = {
1214
- behavior: 'passthrough',
1215
- message: createPermissionRequestMessage(tool.name),
1216
- }
1217
- try {
1218
- const parsedInput = tool.inputSchema.parse(input)
1219
- toolPermissionResult = await tool.checkPermissions(parsedInput, context)
1220
- } catch (e) {
1221
- // Rethrow abort errors so they propagate properly
1222
- if (e instanceof AbortError || e instanceof APIUserAbortError) {
1223
- throw e
1224
- }
1225
- logError(e)
1226
- }
1227
-
1228
- // 1d. Tool implementation denied permission
1229
- if (toolPermissionResult?.behavior === 'deny') {
1230
- return toolPermissionResult
1231
- }
1232
-
1233
- // 1e. Tool requires user interaction even in bypass mode
1234
- if (
1235
- tool.requiresUserInteraction?.() &&
1236
- toolPermissionResult?.behavior === 'ask'
1237
- ) {
1238
- return toolPermissionResult
1239
- }
1240
-
1241
- // 1f. Content-specific ask rules from tool.checkPermissions take precedence
1242
- // over bypassPermissions mode. When a user explicitly configures a
1243
- // content-specific ask rule (e.g. Bash(npm publish:*)), the tool's
1244
- // checkPermissions returns {behavior:'ask', decisionReason:{type:'rule',
1245
- // rule:{ruleBehavior:'ask'}}}. This must be respected even in bypass mode,
1246
- // just as deny rules are respected at step 1d.
1247
- if (
1248
- toolPermissionResult?.behavior === 'ask' &&
1249
- toolPermissionResult.decisionReason?.type === 'rule' &&
1250
- toolPermissionResult.decisionReason.rule.ruleBehavior === 'ask'
1251
- ) {
1252
- return toolPermissionResult
1253
- }
1254
-
1255
- // 1g. Safety checks (e.g. .git/, .claude/, .vscode/, shell configs) are
1256
- // bypass-immune — they must prompt even in bypassPermissions mode.
1257
- // checkPathSafetyForAutoEdit returns {type:'safetyCheck'} for these paths.
1258
- if (
1259
- toolPermissionResult?.behavior === 'ask' &&
1260
- toolPermissionResult.decisionReason?.type === 'safetyCheck'
1261
- ) {
1262
- return toolPermissionResult
1263
- }
1264
-
1265
- // 2a. Check if mode allows the tool to run
1266
- // IMPORTANT: Call getAppState() to get the latest value
1267
- appState = context.getAppState()
1268
- // Check if permissions should be bypassed:
1269
- // - Direct bypassPermissions mode
1270
- // - Plan mode when the user originally started with bypass mode (isBypassPermissionsModeAvailable)
1271
- const shouldBypassPermissions =
1272
- appState.toolPermissionContext.mode === 'bypassPermissions' ||
1273
- (appState.toolPermissionContext.mode === 'plan' &&
1274
- appState.toolPermissionContext.isBypassPermissionsModeAvailable)
1275
- if (shouldBypassPermissions) {
1276
- return {
1277
- behavior: 'allow',
1278
- updatedInput: getUpdatedInputOrFallback(toolPermissionResult, input),
1279
- decisionReason: {
1280
- type: 'mode',
1281
- mode: appState.toolPermissionContext.mode,
1282
- },
1283
- }
1284
- }
1285
-
1286
- // 2b. Entire tool is allowed
1287
- const alwaysAllowedRule = toolAlwaysAllowedRule(
1288
- appState.toolPermissionContext,
1289
- tool,
1290
- )
1291
- if (alwaysAllowedRule) {
1292
- return {
1293
- behavior: 'allow',
1294
- updatedInput: getUpdatedInputOrFallback(toolPermissionResult, input),
1295
- decisionReason: {
1296
- type: 'rule',
1297
- rule: alwaysAllowedRule,
1298
- },
1299
- }
1300
- }
1301
-
1302
- // 3. Convert "passthrough" to "ask"
1303
- const result: PermissionDecision =
1304
- toolPermissionResult.behavior === 'passthrough'
1305
- ? {
1306
- ...toolPermissionResult,
1307
- behavior: 'ask' as const,
1308
- message: createPermissionRequestMessage(
1309
- tool.name,
1310
- toolPermissionResult.decisionReason,
1311
- ),
1312
- }
1313
- : toolPermissionResult
1314
-
1315
- if (result.behavior === 'ask' && result.suggestions) {
1316
- logForDebugging(
1317
- `Permission suggestions for ${tool.name}: ${jsonStringify(result.suggestions, null, 2)}`,
1318
- )
1319
- }
1320
-
1321
- return result
1322
- }
1323
-
1324
- type EditPermissionRuleArgs = {
1325
- initialContext: ToolPermissionContext
1326
- setToolPermissionContext: (updatedContext: ToolPermissionContext) => void
1327
- }
1328
-
1329
- /**
1330
- * Delete a permission rule from the appropriate destination
1331
- */
1332
- export async function deletePermissionRule({
1333
- rule,
1334
- initialContext,
1335
- setToolPermissionContext,
1336
- }: EditPermissionRuleArgs & { rule: PermissionRule }): Promise<void> {
1337
- if (
1338
- rule.source === 'policySettings' ||
1339
- rule.source === 'flagSettings' ||
1340
- rule.source === 'command'
1341
- ) {
1342
- throw new Error('Cannot delete permission rules from read-only settings')
1343
- }
1344
-
1345
- const updatedContext = applyPermissionUpdate(initialContext, {
1346
- type: 'removeRules',
1347
- rules: [rule.ruleValue],
1348
- behavior: rule.ruleBehavior,
1349
- destination: rule.source as PermissionUpdateDestination,
1350
- })
1351
-
1352
- // Per-destination logic to delete the rule from settings
1353
- const destination = rule.source
1354
- switch (destination) {
1355
- case 'localSettings':
1356
- case 'userSettings':
1357
- case 'projectSettings': {
1358
- // Note: Typescript doesn't know that rule conforms to `PermissionRuleFromEditableSettings` even when we switch on `rule.source`
1359
- deletePermissionRuleFromSettings(
1360
- rule as PermissionRuleFromEditableSettings,
1361
- )
1362
- break
1363
- }
1364
- case 'cliArg':
1365
- case 'session': {
1366
- // No action needed for in-memory sources - not persisted to disk
1367
- break
1368
- }
1369
- }
1370
-
1371
- // Update React state with updated context
1372
- setToolPermissionContext(updatedContext)
1373
- }
1374
-
1375
- /**
1376
- * Helper to convert PermissionRule array to PermissionUpdate array
1377
- */
1378
- function convertRulesToUpdates(
1379
- rules: PermissionRule[],
1380
- updateType: 'addRules' | 'replaceRules',
1381
- ): PermissionUpdate[] {
1382
- // Group rules by source and behavior
1383
- const grouped = new Map<string, PermissionRuleValue[]>()
1384
-
1385
- for (const rule of rules) {
1386
- const key = `${rule.source}:${rule.ruleBehavior}`
1387
- if (!grouped.has(key)) {
1388
- grouped.set(key, [])
1389
- }
1390
- grouped.get(key)!.push(rule.ruleValue)
1391
- }
1392
-
1393
- // Convert to PermissionUpdate array
1394
- const updates: PermissionUpdate[] = []
1395
- for (const [key, ruleValues] of grouped) {
1396
- const [source, behavior] = key.split(':')
1397
- updates.push({
1398
- type: updateType,
1399
- rules: ruleValues,
1400
- behavior: behavior as PermissionBehavior,
1401
- destination: source as PermissionUpdateDestination,
1402
- })
1403
- }
1404
-
1405
- return updates
1406
- }
1407
-
1408
- /**
1409
- * Apply permission rules to context (additive - for initial setup)
1410
- */
1411
- export function applyPermissionRulesToPermissionContext(
1412
- toolPermissionContext: ToolPermissionContext,
1413
- rules: PermissionRule[],
1414
- ): ToolPermissionContext {
1415
- const updates = convertRulesToUpdates(rules, 'addRules')
1416
- return applyPermissionUpdates(toolPermissionContext, updates)
1417
- }
1418
-
1419
- /**
1420
- * Sync permission rules from disk (replacement - for settings changes)
1421
- */
1422
- export function syncPermissionRulesFromDisk(
1423
- toolPermissionContext: ToolPermissionContext,
1424
- rules: PermissionRule[],
1425
- ): ToolPermissionContext {
1426
- let context = toolPermissionContext
1427
-
1428
- // When allowManagedPermissionRulesOnly is enabled, clear all non-policy sources
1429
- if (shouldAllowManagedPermissionRulesOnly()) {
1430
- const sourcesToClear: PermissionUpdateDestination[] = [
1431
- 'userSettings',
1432
- 'projectSettings',
1433
- 'localSettings',
1434
- 'cliArg',
1435
- 'session',
1436
- ]
1437
- const behaviors: PermissionBehavior[] = ['allow', 'deny', 'ask']
1438
-
1439
- for (const source of sourcesToClear) {
1440
- for (const behavior of behaviors) {
1441
- context = applyPermissionUpdate(context, {
1442
- type: 'replaceRules',
1443
- rules: [],
1444
- behavior,
1445
- destination: source,
1446
- })
1447
- }
1448
- }
1449
- }
1450
-
1451
- // Clear all disk-based source:behavior combos before applying new rules.
1452
- // Without this, removing a rule from settings (e.g. deleting a deny entry)
1453
- // would leave the old rule in the context because convertRulesToUpdates
1454
- // only generates replaceRules for source:behavior pairs that have rules —
1455
- // an empty group produces no update, so stale rules persist.
1456
- const diskSources: PermissionUpdateDestination[] = [
1457
- 'userSettings',
1458
- 'projectSettings',
1459
- 'localSettings',
1460
- ]
1461
- for (const diskSource of diskSources) {
1462
- for (const behavior of ['allow', 'deny', 'ask'] as PermissionBehavior[]) {
1463
- context = applyPermissionUpdate(context, {
1464
- type: 'replaceRules',
1465
- rules: [],
1466
- behavior,
1467
- destination: diskSource,
1468
- })
1469
- }
1470
- }
1471
-
1472
- const updates = convertRulesToUpdates(rules, 'replaceRules')
1473
- return applyPermissionUpdates(context, updates)
1474
- }
1475
-
1476
- /**
1477
- * Extract updatedInput from a permission result, falling back to the original input.
1478
- * Handles the case where some PermissionResult variants don't have updatedInput.
1479
- */
1480
- function getUpdatedInputOrFallback(
1481
- permissionResult: PermissionResult,
1482
- fallback: Record<string, unknown>,
1483
- ): Record<string, unknown> {
1484
- return (
1485
- ('updatedInput' in permissionResult
1486
- ? permissionResult.updatedInput
1487
- : undefined) ?? fallback
1488
- )
1489
- }