@iaforged/context-code 1.0.72 → 1.0.74

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 (2050) hide show
  1. package/context-bootstrap.js +7 -8
  2. package/dist/src/QueryEngine.js +928 -0
  3. package/dist/src/Task.js +49 -0
  4. package/dist/src/Tool.js +56 -0
  5. package/dist/src/assistant/AssistantSessionChooser.js +16 -0
  6. package/dist/src/assistant/index.js +16 -0
  7. package/dist/src/assistant/sessionDiscovery.js +16 -0
  8. package/dist/src/assistant/sessionHistory.js +47 -0
  9. package/dist/src/bootstrap/state.js +1165 -0
  10. package/dist/src/bridge/bridgeApi.js +304 -0
  11. package/dist/src/bridge/bridgeConfig.js +39 -0
  12. package/dist/src/bridge/bridgeDebug.js +73 -0
  13. package/dist/src/bridge/bridgeEnabled.js +187 -0
  14. package/dist/src/bridge/bridgeMain.js +2289 -0
  15. package/dist/src/bridge/bridgeMessaging.js +353 -0
  16. package/dist/src/bridge/bridgePermissionCallbacks.js +10 -0
  17. package/dist/src/bridge/bridgePointer.js +175 -0
  18. package/dist/src/bridge/bridgeStatusUtil.js +105 -0
  19. package/dist/src/bridge/bridgeUI.js +412 -0
  20. package/dist/src/bridge/capacityWake.js +35 -0
  21. package/dist/src/bridge/codeSessionApi.js +111 -0
  22. package/dist/src/bridge/createSession.js +273 -0
  23. package/dist/src/bridge/debugUtils.js +115 -0
  24. package/dist/src/bridge/envLessBridgeConfig.js +121 -0
  25. package/dist/src/bridge/flushGate.js +65 -0
  26. package/dist/src/bridge/inboundAttachments.js +152 -0
  27. package/dist/src/bridge/inboundMessages.js +63 -0
  28. package/dist/src/bridge/initReplBridge.js +428 -0
  29. package/dist/src/bridge/jwtUtils.js +185 -0
  30. package/dist/src/bridge/pollConfig.js +85 -0
  31. package/dist/src/bridge/pollConfigDefaults.js +62 -0
  32. package/dist/src/bridge/remoteBridgeCore.js +712 -0
  33. package/dist/src/bridge/replBridge.js +1720 -0
  34. package/dist/src/bridge/replBridgeHandle.js +30 -0
  35. package/dist/src/bridge/replBridgeTransport.js +236 -0
  36. package/dist/src/bridge/sessionIdCompat.js +56 -0
  37. package/dist/src/bridge/sessionRunner.js +421 -0
  38. package/dist/src/bridge/trustedDevice.js +172 -0
  39. package/dist/src/bridge/types.js +9 -0
  40. package/dist/src/bridge/workSecret.js +99 -0
  41. package/dist/src/buddy/CompanionSprite.js +349 -0
  42. package/dist/src/buddy/companion.js +107 -0
  43. package/dist/src/buddy/prompt.js +33 -0
  44. package/dist/src/buddy/sprites.js +488 -0
  45. package/dist/src/buddy/types.js +90 -0
  46. package/dist/src/buddy/useBuddyNotification.js +90 -0
  47. package/dist/src/cli/bg.js +16 -0
  48. package/dist/src/cli/exit.js +30 -0
  49. package/dist/src/cli/handlers/agents.js +55 -0
  50. package/dist/src/cli/handlers/ant.js +16 -0
  51. package/dist/src/cli/handlers/auth.js +315 -0
  52. package/dist/src/cli/handlers/autoMode.js +128 -0
  53. package/dist/src/cli/handlers/mcp.js +334 -0
  54. package/dist/src/cli/handlers/plugins.js +634 -0
  55. package/dist/src/cli/handlers/projects_bridge.js +40 -0
  56. package/dist/src/cli/handlers/provider.js +42 -0
  57. package/dist/src/cli/handlers/templateJobs.js +16 -0
  58. package/dist/src/cli/handlers/util.js +76 -0
  59. package/dist/src/cli/ndjsonSafeStringify.js +27 -0
  60. package/dist/src/cli/print.js +4225 -0
  61. package/dist/src/cli/remoteIO.js +208 -0
  62. package/dist/src/cli/rollback.js +16 -0
  63. package/dist/src/cli/structuredIO.js +644 -0
  64. package/dist/src/cli/transports/HybridTransport.js +233 -0
  65. package/dist/src/cli/transports/SSETransport.js +538 -0
  66. package/dist/src/cli/transports/SerialBatchEventUploader.js +224 -0
  67. package/dist/src/cli/transports/Transport.js +1 -0
  68. package/dist/src/cli/transports/WebSocketTransport.js +613 -0
  69. package/dist/src/cli/transports/WorkerStateUploader.js +88 -0
  70. package/dist/src/cli/transports/ccrClient.js +711 -0
  71. package/dist/src/cli/transports/transportUtils.js +39 -0
  72. package/dist/src/cli/up.js +16 -0
  73. package/dist/src/cli/update.js +315 -0
  74. package/dist/src/commands/add-dir/add-dir.js +121 -0
  75. package/dist/src/commands/add-dir/index.js +8 -0
  76. package/dist/src/commands/add-dir/validation.js +76 -0
  77. package/dist/src/commands/advisor.js +88 -0
  78. package/dist/src/commands/agent/agent.js +283 -0
  79. package/dist/src/commands/agent/agentStore.js +120 -0
  80. package/dist/src/commands/agent/index.js +9 -0
  81. package/dist/src/commands/agents/agents.js +9 -0
  82. package/dist/src/commands/agents/index.js +7 -0
  83. package/dist/src/commands/ant-trace/index.js +1 -0
  84. package/dist/src/commands/assistant/assistant.js +16 -0
  85. package/dist/src/commands/autofix-pr/index.js +1 -0
  86. package/dist/src/commands/backfill-sessions/index.js +1 -0
  87. package/dist/src/commands/branch/branch.js +205 -0
  88. package/dist/src/commands/branch/index.js +11 -0
  89. package/dist/src/commands/break-cache/index.js +1 -0
  90. package/dist/src/commands/bridge/bridge.js +512 -0
  91. package/dist/src/commands/bridge/index.js +22 -0
  92. package/dist/src/commands/bridge-kick.js +179 -0
  93. package/dist/src/commands/brief.js +94 -0
  94. package/dist/src/commands/btw/btw.js +234 -0
  95. package/dist/src/commands/btw/index.js +9 -0
  96. package/dist/src/commands/bughunter/index.js +1 -0
  97. package/dist/src/commands/chrome/chrome.js +291 -0
  98. package/dist/src/commands/chrome/index.js +11 -0
  99. package/dist/src/commands/clear/caches.js +116 -0
  100. package/dist/src/commands/clear/clear.js +5 -0
  101. package/dist/src/commands/clear/conversation.js +191 -0
  102. package/dist/src/commands/clear/index.js +9 -0
  103. package/dist/src/commands/color/color.js +58 -0
  104. package/dist/src/commands/color/index.js +9 -0
  105. package/dist/src/commands/commit-push-pr.js +137 -0
  106. package/dist/src/commands/commit.js +80 -0
  107. package/dist/src/commands/compact/compact.js +196 -0
  108. package/dist/src/commands/compact/index.js +11 -0
  109. package/dist/src/commands/config/config.js +5 -0
  110. package/dist/src/commands/config/index.js +8 -0
  111. package/dist/src/commands/context/context-noninteractive.js +221 -0
  112. package/dist/src/commands/context/context.js +46 -0
  113. package/dist/src/commands/context/index.js +21 -0
  114. package/dist/src/commands/copy/copy.js +366 -0
  115. package/dist/src/commands/copy/index.js +7 -0
  116. package/dist/src/commands/cost/cost.js +21 -0
  117. package/dist/src/commands/cost/index.js +16 -0
  118. package/dist/src/commands/createMovedToPluginCommand.js +33 -0
  119. package/dist/src/commands/ctx_viz/index.js +1 -0
  120. package/dist/src/commands/debug-tool-call/index.js +1 -0
  121. package/dist/src/commands/desktop/desktop.js +5 -0
  122. package/dist/src/commands/desktop/index.js +22 -0
  123. package/dist/src/commands/diff/diff.js +5 -0
  124. package/dist/src/commands/diff/index.js +6 -0
  125. package/dist/src/commands/doctor/doctor.js +5 -0
  126. package/dist/src/commands/doctor/index.js +9 -0
  127. package/dist/src/commands/effort/effort.js +166 -0
  128. package/dist/src/commands/effort/index.js +11 -0
  129. package/dist/src/commands/env/index.js +1 -0
  130. package/dist/src/commands/exit/exit.js +31 -0
  131. package/dist/src/commands/exit/index.js +9 -0
  132. package/dist/src/commands/export/export.js +86 -0
  133. package/dist/src/commands/export/index.js +8 -0
  134. package/dist/src/commands/extra-usage/extra-usage-core.js +99 -0
  135. package/dist/src/commands/extra-usage/extra-usage-noninteractive.js +13 -0
  136. package/dist/src/commands/extra-usage/extra-usage.js +14 -0
  137. package/dist/src/commands/extra-usage/index.js +27 -0
  138. package/dist/src/commands/fast/fast.js +275 -0
  139. package/dist/src/commands/fast/index.js +20 -0
  140. package/dist/src/commands/feedback/feedback.js +10 -0
  141. package/dist/src/commands/feedback/index.js +21 -0
  142. package/dist/src/commands/files/files.js +11 -0
  143. package/dist/src/commands/files/index.js +9 -0
  144. package/dist/src/commands/good-claude/index.js +1 -0
  145. package/dist/src/commands/heapdump/heapdump.js +14 -0
  146. package/dist/src/commands/heapdump/index.js +9 -0
  147. package/dist/src/commands/help/help.js +5 -0
  148. package/dist/src/commands/help/index.js +7 -0
  149. package/dist/src/commands/hooks/hooks.js +11 -0
  150. package/dist/src/commands/hooks/index.js +8 -0
  151. package/dist/src/commands/ide/ide.js +615 -0
  152. package/dist/src/commands/ide/index.js +8 -0
  153. package/dist/src/commands/init-verifiers.js +258 -0
  154. package/dist/src/commands/init.js +249 -0
  155. package/dist/src/commands/insights.js +2555 -0
  156. package/dist/src/commands/install-github-app/ApiKeyStep.js +230 -0
  157. package/dist/src/commands/install-github-app/CheckExistingSecretStep.js +194 -0
  158. package/dist/src/commands/install-github-app/CheckGitHubStep.js +15 -0
  159. package/dist/src/commands/install-github-app/ChooseRepoStep.js +211 -0
  160. package/dist/src/commands/install-github-app/CreatingStep.js +52 -0
  161. package/dist/src/commands/install-github-app/ErrorStep.js +83 -0
  162. package/dist/src/commands/install-github-app/ExistingWorkflowStep.js +104 -0
  163. package/dist/src/commands/install-github-app/InstallAppStep.js +96 -0
  164. package/dist/src/commands/install-github-app/OAuthFlowStep.js +190 -0
  165. package/dist/src/commands/install-github-app/SuccessStep.js +93 -0
  166. package/dist/src/commands/install-github-app/WarningsStep.js +70 -0
  167. package/dist/src/commands/install-github-app/index.js +10 -0
  168. package/dist/src/commands/install-github-app/install-github-app.js +593 -0
  169. package/dist/src/commands/install-github-app/setupGitHubActions.js +227 -0
  170. package/dist/src/commands/install-slack-app/index.js +9 -0
  171. package/dist/src/commands/install-slack-app/install-slack-app.js +25 -0
  172. package/dist/src/commands/install.js +118 -0
  173. package/dist/src/commands/issue/index.js +1 -0
  174. package/dist/src/commands/keybindings/index.js +10 -0
  175. package/dist/src/commands/keybindings/keybindings.js +47 -0
  176. package/dist/src/commands/limites/index.js +8 -0
  177. package/dist/src/commands/limites/limites.js +214 -0
  178. package/dist/src/commands/login/index.js +11 -0
  179. package/dist/src/commands/login/login.js +120 -0
  180. package/dist/src/commands/login-openai/index.js +7 -0
  181. package/dist/src/commands/login-openai/login-openai.js +54 -0
  182. package/dist/src/commands/logout/index.js +8 -0
  183. package/dist/src/commands/logout/logout.js +72 -0
  184. package/dist/src/commands/mcp/addCommand.js +183 -0
  185. package/dist/src/commands/mcp/index.js +9 -0
  186. package/dist/src/commands/mcp/mcp.js +78 -0
  187. package/dist/src/commands/mcp/xaaIdpCommand.js +193 -0
  188. package/dist/src/commands/memory/index.js +7 -0
  189. package/dist/src/commands/memory/memory.js +71 -0
  190. package/dist/src/commands/mobile/index.js +9 -0
  191. package/dist/src/commands/mobile/mobile.js +278 -0
  192. package/dist/src/commands/mock-limits/index.js +1 -0
  193. package/dist/src/commands/model/index.js +14 -0
  194. package/dist/src/commands/model/model.js +297 -0
  195. package/dist/src/commands/oauth-refresh/index.js +1 -0
  196. package/dist/src/commands/onboarding/index.js +1 -0
  197. package/dist/src/commands/orchestrate/index.js +8 -0
  198. package/dist/src/commands/orchestrate/orchestrate.js +542 -0
  199. package/dist/src/commands/output-style/index.js +8 -0
  200. package/dist/src/commands/output-style/output-style.js +6 -0
  201. package/dist/src/commands/passes/index.js +17 -0
  202. package/dist/src/commands/passes/passes.js +22 -0
  203. package/dist/src/commands/perf-issue/index.js +1 -0
  204. package/dist/src/commands/permissions/index.js +8 -0
  205. package/dist/src/commands/permissions/permissions.js +8 -0
  206. package/dist/src/commands/plan/index.js +8 -0
  207. package/dist/src/commands/plan/plan.js +115 -0
  208. package/dist/src/commands/plugin/AddMarketplace.js +95 -0
  209. package/dist/src/commands/plugin/BrowseMarketplace.js +576 -0
  210. package/dist/src/commands/plugin/DiscoverPlugins.js +613 -0
  211. package/dist/src/commands/plugin/ManageMarketplaces.js +582 -0
  212. package/dist/src/commands/plugin/ManagePlugins.js +1783 -0
  213. package/dist/src/commands/plugin/PluginErrors.js +124 -0
  214. package/dist/src/commands/plugin/PluginOptionsDialog.js +367 -0
  215. package/dist/src/commands/plugin/PluginOptionsFlow.js +97 -0
  216. package/dist/src/commands/plugin/PluginSettings.js +1042 -0
  217. package/dist/src/commands/plugin/PluginTrustWarning.js +34 -0
  218. package/dist/src/commands/plugin/UnifiedInstalledCell.js +615 -0
  219. package/dist/src/commands/plugin/ValidatePlugin.js +95 -0
  220. package/dist/src/commands/plugin/index.js +10 -0
  221. package/dist/src/commands/plugin/parseArgs.js +71 -0
  222. package/dist/src/commands/plugin/plugin.js +5 -0
  223. package/dist/src/commands/plugin/pluginDetailsHelpers.js +89 -0
  224. package/dist/src/commands/plugin/usePagination.js +89 -0
  225. package/dist/src/commands/policy/index.js +9 -0
  226. package/dist/src/commands/policy/policy.js +84 -0
  227. package/dist/src/commands/pr_comments/index.js +49 -0
  228. package/dist/src/commands/privacy-settings/index.js +11 -0
  229. package/dist/src/commands/privacy-settings/privacy-settings.js +54 -0
  230. package/dist/src/commands/profile/index.js +9 -0
  231. package/dist/src/commands/profile/profile.js +482 -0
  232. package/dist/src/commands/provider/index.js +8 -0
  233. package/dist/src/commands/provider/provider.js +457 -0
  234. package/dist/src/commands/rate-limit-options/index.js +15 -0
  235. package/dist/src/commands/rate-limit-options/rate-limit-options.js +213 -0
  236. package/dist/src/commands/release-notes/index.js +9 -0
  237. package/dist/src/commands/release-notes/release-notes.js +38 -0
  238. package/dist/src/commands/reload-plugins/index.js +11 -0
  239. package/dist/src/commands/reload-plugins/reload-plugins.js +52 -0
  240. package/dist/src/commands/remote-env/index.js +12 -0
  241. package/dist/src/commands/remote-env/remote-env.js +5 -0
  242. package/dist/src/commands/remote-setup/api.js +155 -0
  243. package/dist/src/commands/remote-setup/index.js +15 -0
  244. package/dist/src/commands/remote-setup/remote-setup.js +149 -0
  245. package/dist/src/commands/rename/generateSessionName.js +58 -0
  246. package/dist/src/commands/rename/index.js +9 -0
  247. package/dist/src/commands/rename/rename.js +52 -0
  248. package/dist/src/commands/reset-limits/index.js +4 -0
  249. package/dist/src/commands/resume/index.js +9 -0
  250. package/dist/src/commands/resume/resume.js +239 -0
  251. package/dist/src/commands/review/UltrareviewOverageDialog.js +97 -0
  252. package/dist/src/commands/review/reviewRemote.js +259 -0
  253. package/dist/src/commands/review/ultrareviewCommand.js +57 -0
  254. package/dist/src/commands/review/ultrareviewEnabled.js +10 -0
  255. package/dist/src/commands/review.js +50 -0
  256. package/dist/src/commands/rewind/index.js +10 -0
  257. package/dist/src/commands/rewind/rewind.js +7 -0
  258. package/dist/src/commands/run/index.js +9 -0
  259. package/dist/src/commands/run/run.js +1126 -0
  260. package/dist/src/commands/sandbox-toggle/index.js +41 -0
  261. package/dist/src/commands/sandbox-toggle/sandbox-toggle.js +72 -0
  262. package/dist/src/commands/security-review.js +231 -0
  263. package/dist/src/commands/session/index.js +13 -0
  264. package/dist/src/commands/session/session.js +142 -0
  265. package/dist/src/commands/share/index.js +1 -0
  266. package/dist/src/commands/skills/index.js +8 -0
  267. package/dist/src/commands/skills/skills.js +97 -0
  268. package/dist/src/commands/stats/index.js +7 -0
  269. package/dist/src/commands/stats/stats.js +5 -0
  270. package/dist/src/commands/status/index.js +8 -0
  271. package/dist/src/commands/status/status.js +5 -0
  272. package/dist/src/commands/statusline.js +22 -0
  273. package/dist/src/commands/stickers/index.js +9 -0
  274. package/dist/src/commands/stickers/stickers.js +14 -0
  275. package/dist/src/commands/summary/index.js +1 -0
  276. package/dist/src/commands/tag/index.js +9 -0
  277. package/dist/src/commands/tag/tag.js +215 -0
  278. package/dist/src/commands/tasks/index.js +8 -0
  279. package/dist/src/commands/tasks/tasks.js +5 -0
  280. package/dist/src/commands/team/index.js +9 -0
  281. package/dist/src/commands/team/team.js +582 -0
  282. package/dist/src/commands/team-auto/index.js +9 -0
  283. package/dist/src/commands/team-auto/teamAuto.js +340 -0
  284. package/dist/src/commands/telegram/index.js +9 -0
  285. package/dist/src/commands/telegram/telegram.js +118 -0
  286. package/dist/src/commands/teleport/index.js +1 -0
  287. package/dist/src/commands/terminalSetup/index.js +18 -0
  288. package/dist/src/commands/terminalSetup/terminalSetup.js +491 -0
  289. package/dist/src/commands/theme/index.js +7 -0
  290. package/dist/src/commands/theme/theme.js +50 -0
  291. package/dist/src/commands/thinkback/index.js +9 -0
  292. package/dist/src/commands/thinkback/thinkback.js +527 -0
  293. package/dist/src/commands/thinkback-play/index.js +13 -0
  294. package/dist/src/commands/thinkback-play/thinkback-play.js +34 -0
  295. package/dist/src/commands/ultraplan.js +418 -0
  296. package/dist/src/commands/upgrade/index.js +12 -0
  297. package/dist/src/commands/upgrade/upgrade.js +37 -0
  298. package/dist/src/commands/version.js +18 -0
  299. package/dist/src/commands/vim/index.js +8 -0
  300. package/dist/src/commands/vim/vim.js +25 -0
  301. package/dist/src/commands/voice/index.js +14 -0
  302. package/dist/src/commands/voice/voice.js +130 -0
  303. package/dist/src/commands/whatsapp/index.js +9 -0
  304. package/dist/src/commands/whatsapp/whatsapp.js +326 -0
  305. package/dist/src/commands/workspace/index.js +9 -0
  306. package/dist/src/commands/workspace/workspace.js +701 -0
  307. package/dist/src/commands.js +635 -0
  308. package/dist/src/components/AgentProgressLine.js +111 -0
  309. package/dist/src/components/App.js +45 -0
  310. package/dist/src/components/ApproveApiKey.js +124 -0
  311. package/dist/src/components/AutoModeOptInDialog.js +140 -0
  312. package/dist/src/components/AutoUpdater.js +157 -0
  313. package/dist/src/components/AutoUpdaterWrapper.js +78 -0
  314. package/dist/src/components/AwsAuthStatusBox.js +88 -0
  315. package/dist/src/components/BaseTextInput.js +105 -0
  316. package/dist/src/components/BashModeProgress.js +48 -0
  317. package/dist/src/components/BridgeDialog.js +414 -0
  318. package/dist/src/components/BypassPermissionsModeDialog.js +87 -0
  319. package/dist/src/components/ChannelDowngradeDialog.js +100 -0
  320. package/dist/src/components/ClaudeCodeHint/PluginHintMenu.js +37 -0
  321. package/dist/src/components/ClaudeInChromeOnboarding.js +126 -0
  322. package/dist/src/components/ClaudeMdExternalIncludesDialog.js +137 -0
  323. package/dist/src/components/ClickableImageRef.js +64 -0
  324. package/dist/src/components/CompactSummary.js +119 -0
  325. package/dist/src/components/ConfigurableShortcutHint.js +34 -0
  326. package/dist/src/components/ConsoleOAuthFlow.js +758 -0
  327. package/dist/src/components/ContextSuggestions.js +43 -0
  328. package/dist/src/components/ContextVisualization.js +483 -0
  329. package/dist/src/components/CoordinatorAgentStatus.js +261 -0
  330. package/dist/src/components/CostThresholdDialog.js +48 -0
  331. package/dist/src/components/CtrlOToExpand.js +50 -0
  332. package/dist/src/components/CustomSelect/SelectMulti.js +149 -0
  333. package/dist/src/components/CustomSelect/index.js +2 -0
  334. package/dist/src/components/CustomSelect/option-map.js +32 -0
  335. package/dist/src/components/CustomSelect/select-input-option.js +426 -0
  336. package/dist/src/components/CustomSelect/select-option.js +23 -0
  337. package/dist/src/components/CustomSelect/select.js +518 -0
  338. package/dist/src/components/CustomSelect/use-multi-select-state.js +214 -0
  339. package/dist/src/components/CustomSelect/use-select-input.js +170 -0
  340. package/dist/src/components/CustomSelect/use-select-navigation.js +366 -0
  341. package/dist/src/components/CustomSelect/use-select-state.js +22 -0
  342. package/dist/src/components/DesktopHandoff.js +195 -0
  343. package/dist/src/components/DesktopUpsell/DesktopUpsellStartup.js +173 -0
  344. package/dist/src/components/DevBar.js +50 -0
  345. package/dist/src/components/DevChannelsDialog.js +103 -0
  346. package/dist/src/components/DiagnosticsDisplay.js +91 -0
  347. package/dist/src/components/EffortCallout.js +264 -0
  348. package/dist/src/components/EffortIndicator.js +29 -0
  349. package/dist/src/components/ExitFlow.js +40 -0
  350. package/dist/src/components/ExportDialog.js +101 -0
  351. package/dist/src/components/FallbackToolUseErrorMessage.js +115 -0
  352. package/dist/src/components/FallbackToolUseRejectedMessage.js +16 -0
  353. package/dist/src/components/FastIcon.js +42 -0
  354. package/dist/src/components/Feedback.js +439 -0
  355. package/dist/src/components/FeedbackSurvey/FeedbackSurvey.js +150 -0
  356. package/dist/src/components/FeedbackSurvey/FeedbackSurveyView.js +103 -0
  357. package/dist/src/components/FeedbackSurvey/TranscriptSharePrompt.js +83 -0
  358. package/dist/src/components/FeedbackSurvey/submitTranscriptShare.js +81 -0
  359. package/dist/src/components/FeedbackSurvey/useDebouncedDigitInput.js +51 -0
  360. package/dist/src/components/FeedbackSurvey/useFeedbackSurvey.js +258 -0
  361. package/dist/src/components/FeedbackSurvey/useMemorySurvey.js +191 -0
  362. package/dist/src/components/FeedbackSurvey/usePostCompactSurvey.js +202 -0
  363. package/dist/src/components/FeedbackSurvey/useSurveyState.js +80 -0
  364. package/dist/src/components/FileEditToolDiff.js +166 -0
  365. package/dist/src/components/FileEditToolUpdatedMessage.js +111 -0
  366. package/dist/src/components/FileEditToolUseRejectedMessage.js +157 -0
  367. package/dist/src/components/FilePathLink.js +34 -0
  368. package/dist/src/components/FullscreenLayout.js +578 -0
  369. package/dist/src/components/GlobalSearchDialog.js +339 -0
  370. package/dist/src/components/HelpV2/Commands.js +65 -0
  371. package/dist/src/components/HelpV2/General.js +24 -0
  372. package/dist/src/components/HelpV2/HelpV2.js +186 -0
  373. package/dist/src/components/HighlightedCode/Fallback.js +193 -0
  374. package/dist/src/components/HighlightedCode.js +184 -0
  375. package/dist/src/components/HistorySearchDialog.js +92 -0
  376. package/dist/src/components/IdeAutoConnectDialog.js +153 -0
  377. package/dist/src/components/IdeOnboardingDialog.js +174 -0
  378. package/dist/src/components/IdeStatusIndicator.js +49 -0
  379. package/dist/src/components/IdleReturnDialog.js +116 -0
  380. package/dist/src/components/InterruptedByUser.js +15 -0
  381. package/dist/src/components/InvalidConfigDialog.js +134 -0
  382. package/dist/src/components/InvalidSettingsDialog.js +84 -0
  383. package/dist/src/components/KeybindingWarnings.js +54 -0
  384. package/dist/src/components/LanguagePicker.js +84 -0
  385. package/dist/src/components/LogSelector.js +1579 -0
  386. package/dist/src/components/LogoV2/AnimatedAsterisk.js +42 -0
  387. package/dist/src/components/LogoV2/AnimatedClawd.js +111 -0
  388. package/dist/src/components/LogoV2/ChannelsNotice.js +258 -0
  389. package/dist/src/components/LogoV2/Clawd.js +12 -0
  390. package/dist/src/components/LogoV2/CondensedLogo.js +144 -0
  391. package/dist/src/components/LogoV2/EmergencyTip.js +47 -0
  392. package/dist/src/components/LogoV2/Feed.js +84 -0
  393. package/dist/src/components/LogoV2/FeedColumn.js +55 -0
  394. package/dist/src/components/LogoV2/GuestPassesUpsell.js +71 -0
  395. package/dist/src/components/LogoV2/LogoV2.js +564 -0
  396. package/dist/src/components/LogoV2/Opus1mMergeNotice.js +56 -0
  397. package/dist/src/components/LogoV2/OverageCreditUpsell.js +160 -0
  398. package/dist/src/components/LogoV2/VoiceModeNotice.js +70 -0
  399. package/dist/src/components/LogoV2/WelcomeV2.js +8 -0
  400. package/dist/src/components/LogoV2/feedConfigs.js +78 -0
  401. package/dist/src/components/LspRecommendation/LspRecommendationMenu.js +46 -0
  402. package/dist/src/components/MCPServerApprovalDialog.js +113 -0
  403. package/dist/src/components/MCPServerDesktopImportDialog.js +206 -0
  404. package/dist/src/components/MCPServerDialogCopy.js +15 -0
  405. package/dist/src/components/MCPServerMultiselectDialog.js +133 -0
  406. package/dist/src/components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.js +149 -0
  407. package/dist/src/components/ManagedSettingsSecurityDialog/utils.js +105 -0
  408. package/dist/src/components/Markdown.js +217 -0
  409. package/dist/src/components/MarkdownTable.js +279 -0
  410. package/dist/src/components/MemoryUsageIndicator.js +27 -0
  411. package/dist/src/components/Message.js +566 -0
  412. package/dist/src/components/MessageModel.js +36 -0
  413. package/dist/src/components/MessageResponse.js +73 -0
  414. package/dist/src/components/MessageRow.js +346 -0
  415. package/dist/src/components/MessageSelector.js +743 -0
  416. package/dist/src/components/MessageTimestamp.js +57 -0
  417. package/dist/src/components/Messages.js +637 -0
  418. package/dist/src/components/ModelPicker.js +493 -0
  419. package/dist/src/components/NativeAutoUpdater.js +153 -0
  420. package/dist/src/components/NotebookEditToolUseRejectedMessage.js +83 -0
  421. package/dist/src/components/OffscreenFreeze.js +35 -0
  422. package/dist/src/components/Onboarding.js +174 -0
  423. package/dist/src/components/OutputStylePicker.js +102 -0
  424. package/dist/src/components/PackageManagerAutoUpdater.js +100 -0
  425. package/dist/src/components/Passes/Passes.js +113 -0
  426. package/dist/src/components/PrBadge.js +90 -0
  427. package/dist/src/components/PressEnterToContinue.js +15 -0
  428. package/dist/src/components/PromptInput/HistorySearchInput.js +44 -0
  429. package/dist/src/components/PromptInput/IssueFlagBanner.js +8 -0
  430. package/dist/src/components/PromptInput/Notifications.js +221 -0
  431. package/dist/src/components/PromptInput/PromptInput.js +1998 -0
  432. package/dist/src/components/PromptInput/PromptInputFooter.js +84 -0
  433. package/dist/src/components/PromptInput/PromptInputFooterLeftSide.js +409 -0
  434. package/dist/src/components/PromptInput/PromptInputFooterSuggestions.js +280 -0
  435. package/dist/src/components/PromptInput/PromptInputHelpMenu.js +379 -0
  436. package/dist/src/components/PromptInput/PromptInputModeIndicator.js +72 -0
  437. package/dist/src/components/PromptInput/PromptInputQueuedCommands.js +105 -0
  438. package/dist/src/components/PromptInput/PromptInputStashNotice.js +20 -0
  439. package/dist/src/components/PromptInput/SandboxPromptFooterHint.js +65 -0
  440. package/dist/src/components/PromptInput/ShimmeredInput.js +132 -0
  441. package/dist/src/components/PromptInput/VoiceIndicator.js +136 -0
  442. package/dist/src/components/PromptInput/inputModes.js +24 -0
  443. package/dist/src/components/PromptInput/inputPaste.js +62 -0
  444. package/dist/src/components/PromptInput/useMaybeTruncateInput.js +33 -0
  445. package/dist/src/components/PromptInput/usePromptInputPlaceholder.js +55 -0
  446. package/dist/src/components/PromptInput/useShowFastIconHint.js +23 -0
  447. package/dist/src/components/PromptInput/useSwarmBanner.js +112 -0
  448. package/dist/src/components/PromptInput/utils.js +50 -0
  449. package/dist/src/components/QuickOpenDialog.js +243 -0
  450. package/dist/src/components/RemoteCallout.js +53 -0
  451. package/dist/src/components/RemoteEnvironmentDialog.js +345 -0
  452. package/dist/src/components/ResumeTask.js +173 -0
  453. package/dist/src/components/SandboxViolationExpandedView.js +102 -0
  454. package/dist/src/components/ScrollKeybindingHandler.js +982 -0
  455. package/dist/src/components/SearchBox.js +55 -0
  456. package/dist/src/components/SentryErrorBoundary.js +16 -0
  457. package/dist/src/components/SessionBackgroundHint.js +104 -0
  458. package/dist/src/components/SessionPreview.js +200 -0
  459. package/dist/src/components/Settings/Config.js +1628 -0
  460. package/dist/src/components/Settings/Settings.js +129 -0
  461. package/dist/src/components/Settings/Status.js +239 -0
  462. package/dist/src/components/Settings/Usage.js +341 -0
  463. package/dist/src/components/ShowInIDEPrompt.js +151 -0
  464. package/dist/src/components/SkillImprovementSurvey.js +130 -0
  465. package/dist/src/components/Spinner/FlashingChar.js +51 -0
  466. package/dist/src/components/Spinner/GlimmerMessage.js +328 -0
  467. package/dist/src/components/Spinner/ShimmerChar.js +22 -0
  468. package/dist/src/components/Spinner/SpinnerAnimationRow.js +169 -0
  469. package/dist/src/components/Spinner/SpinnerGlyph.js +69 -0
  470. package/dist/src/components/Spinner/TeammateSpinnerLine.js +170 -0
  471. package/dist/src/components/Spinner/TeammateSpinnerTree.js +268 -0
  472. package/dist/src/components/Spinner/index.js +11 -0
  473. package/dist/src/components/Spinner/teammateSelectHint.js +1 -0
  474. package/dist/src/components/Spinner/useShimmerAnimation.js +22 -0
  475. package/dist/src/components/Spinner/useStalledAnimation.js +63 -0
  476. package/dist/src/components/Spinner/utils.js +77 -0
  477. package/dist/src/components/Spinner.js +470 -0
  478. package/dist/src/components/Stats.js +1000 -0
  479. package/dist/src/components/StatusLine.js +288 -0
  480. package/dist/src/components/StatusNotices.js +50 -0
  481. package/dist/src/components/StructuredDiff/Fallback.js +335 -0
  482. package/dist/src/components/StructuredDiff/colorDiff.js +26 -0
  483. package/dist/src/components/StructuredDiff.js +152 -0
  484. package/dist/src/components/StructuredDiffList.js +8 -0
  485. package/dist/src/components/TagTabs.js +100 -0
  486. package/dist/src/components/TaskListV2.js +333 -0
  487. package/dist/src/components/TeammateViewHeader.js +87 -0
  488. package/dist/src/components/TeleportError.js +191 -0
  489. package/dist/src/components/TeleportProgress.js +130 -0
  490. package/dist/src/components/TeleportRepoMismatchDialog.js +98 -0
  491. package/dist/src/components/TeleportResumeWrapper.js +158 -0
  492. package/dist/src/components/TeleportStash.js +82 -0
  493. package/dist/src/components/TextInput.js +108 -0
  494. package/dist/src/components/ThemePicker.js +330 -0
  495. package/dist/src/components/ThinkingToggle.js +153 -0
  496. package/dist/src/components/TokenWarning.js +172 -0
  497. package/dist/src/components/ToolUseLoader.js +34 -0
  498. package/dist/src/components/TrustDialog/TrustDialog.js +113 -0
  499. package/dist/src/components/TrustDialog/utils.js +199 -0
  500. package/dist/src/components/ValidationErrorsList.js +146 -0
  501. package/dist/src/components/VimTextInput.js +136 -0
  502. package/dist/src/components/VirtualMessageList.js +892 -0
  503. package/dist/src/components/WorkflowMultiselectDialog.js +118 -0
  504. package/dist/src/components/WorktreeExitDialog.js +222 -0
  505. package/dist/src/components/agents/AgentDetail.js +226 -0
  506. package/dist/src/components/agents/AgentEditor.js +146 -0
  507. package/dist/src/components/agents/AgentNavigationFooter.js +21 -0
  508. package/dist/src/components/agents/AgentsList.js +436 -0
  509. package/dist/src/components/agents/AgentsMenu.js +848 -0
  510. package/dist/src/components/agents/ColorPicker.js +110 -0
  511. package/dist/src/components/agents/ModelSelector.js +62 -0
  512. package/dist/src/components/agents/SnapshotUpdateDialog.js +16 -0
  513. package/dist/src/components/agents/ToolSelector.js +557 -0
  514. package/dist/src/components/agents/agentFileUtils.js +187 -0
  515. package/dist/src/components/agents/generateAgent.js +161 -0
  516. package/dist/src/components/agents/new-agent-creation/CreateAgentWizard.js +88 -0
  517. package/dist/src/components/agents/new-agent-creation/wizard-steps/ColorStep.js +80 -0
  518. package/dist/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.js +386 -0
  519. package/dist/src/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.js +63 -0
  520. package/dist/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.js +126 -0
  521. package/dist/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.js +118 -0
  522. package/dist/src/components/agents/new-agent-creation/wizard-steps/LocationStep.js +92 -0
  523. package/dist/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.js +120 -0
  524. package/dist/src/components/agents/new-agent-creation/wizard-steps/MethodStep.js +79 -0
  525. package/dist/src/components/agents/new-agent-creation/wizard-steps/ModelStep.js +48 -0
  526. package/dist/src/components/agents/new-agent-creation/wizard-steps/PromptStep.js +131 -0
  527. package/dist/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.js +51 -0
  528. package/dist/src/components/agents/new-agent-creation/wizard-steps/TypeStep.js +100 -0
  529. package/dist/src/components/agents/types.js +4 -0
  530. package/dist/src/components/agents/utils.js +14 -0
  531. package/dist/src/components/agents/validateAgent.js +79 -0
  532. package/dist/src/components/design-system/Byline.js +72 -0
  533. package/dist/src/components/design-system/Dialog.js +116 -0
  534. package/dist/src/components/design-system/Divider.js +109 -0
  535. package/dist/src/components/design-system/FuzzyPicker.js +187 -0
  536. package/dist/src/components/design-system/KeyboardShortcutHint.js +67 -0
  537. package/dist/src/components/design-system/ListItem.js +183 -0
  538. package/dist/src/components/design-system/LoadingState.js +68 -0
  539. package/dist/src/components/design-system/Pane.js +68 -0
  540. package/dist/src/components/design-system/ProgressBar.js +62 -0
  541. package/dist/src/components/design-system/Ratchet.js +71 -0
  542. package/dist/src/components/design-system/StatusIcon.js +69 -0
  543. package/dist/src/components/design-system/Tabs.js +269 -0
  544. package/dist/src/components/design-system/ThemeProvider.js +137 -0
  545. package/dist/src/components/design-system/ThemedBox.js +125 -0
  546. package/dist/src/components/design-system/ThemedText.js +60 -0
  547. package/dist/src/components/design-system/color.js +22 -0
  548. package/dist/src/components/diff/DiffDetailView.js +284 -0
  549. package/dist/src/components/diff/DiffDialog.js +387 -0
  550. package/dist/src/components/diff/DiffFileList.js +291 -0
  551. package/dist/src/components/grove/Grove.js +483 -0
  552. package/dist/src/components/hooks/HooksConfigMenu.js +570 -0
  553. package/dist/src/components/hooks/PromptDialog.js +81 -0
  554. package/dist/src/components/hooks/SelectEventMode.js +117 -0
  555. package/dist/src/components/hooks/SelectHookMode.js +93 -0
  556. package/dist/src/components/hooks/SelectMatcherMode.js +124 -0
  557. package/dist/src/components/hooks/ViewHookMode.js +197 -0
  558. package/dist/src/components/mcp/CapabilitiesSection.js +55 -0
  559. package/dist/src/components/mcp/ElicitationDialog.js +945 -0
  560. package/dist/src/components/mcp/MCPAgentServerMenu.js +95 -0
  561. package/dist/src/components/mcp/MCPListPanel.js +504 -0
  562. package/dist/src/components/mcp/MCPReconnect.js +168 -0
  563. package/dist/src/components/mcp/MCPRemoteServerMenu.js +460 -0
  564. package/dist/src/components/mcp/MCPSettings.js +414 -0
  565. package/dist/src/components/mcp/MCPStdioServerMenu.js +95 -0
  566. package/dist/src/components/mcp/MCPToolDetailView.js +219 -0
  567. package/dist/src/components/mcp/MCPToolListView.js +136 -0
  568. package/dist/src/components/mcp/McpParsingWarnings.js +211 -0
  569. package/dist/src/components/mcp/index.js +8 -0
  570. package/dist/src/components/mcp/types.js +3 -0
  571. package/dist/src/components/mcp/utils/reconnectHelpers.js +35 -0
  572. package/dist/src/components/memory/MemoryFileSelector.js +455 -0
  573. package/dist/src/components/memory/MemoryUpdateNotification.js +42 -0
  574. package/dist/src/components/messageActions.js +418 -0
  575. package/dist/src/components/messages/AdvisorMessage.js +151 -0
  576. package/dist/src/components/messages/AssistantRedactedThinkingMessage.js +27 -0
  577. package/dist/src/components/messages/AssistantTextMessage.js +274 -0
  578. package/dist/src/components/messages/AssistantThinkingMessage.js +69 -0
  579. package/dist/src/components/messages/AssistantToolUseMessage.js +323 -0
  580. package/dist/src/components/messages/AttachmentMessage.js +418 -0
  581. package/dist/src/components/messages/CollapsedReadSearchContent.js +362 -0
  582. package/dist/src/components/messages/CompactBoundaryMessage.js +18 -0
  583. package/dist/src/components/messages/GroupedToolUseContent.js +37 -0
  584. package/dist/src/components/messages/HighlightedThinkingText.js +164 -0
  585. package/dist/src/components/messages/HookProgressMessage.js +110 -0
  586. package/dist/src/components/messages/PlanApprovalMessage.js +212 -0
  587. package/dist/src/components/messages/RateLimitMessage.js +149 -0
  588. package/dist/src/components/messages/ShutdownMessage.js +123 -0
  589. package/dist/src/components/messages/SystemAPIErrorMessage.js +135 -0
  590. package/dist/src/components/messages/SystemTextMessage.js +843 -0
  591. package/dist/src/components/messages/TaskAssignmentMessage.js +71 -0
  592. package/dist/src/components/messages/UserAgentNotificationMessage.js +77 -0
  593. package/dist/src/components/messages/UserBashInputMessage.js +51 -0
  594. package/dist/src/components/messages/UserBashOutputMessage.js +54 -0
  595. package/dist/src/components/messages/UserChannelMessage.js +129 -0
  596. package/dist/src/components/messages/UserCommandMessage.js +106 -0
  597. package/dist/src/components/messages/UserImageMessage.js +53 -0
  598. package/dist/src/components/messages/UserLocalCommandOutputMessage.js +169 -0
  599. package/dist/src/components/messages/UserMemoryInputMessage.js +72 -0
  600. package/dist/src/components/messages/UserPlanMessage.js +37 -0
  601. package/dist/src/components/messages/UserPromptMessage.js +63 -0
  602. package/dist/src/components/messages/UserResourceUpdateMessage.js +101 -0
  603. package/dist/src/components/messages/UserTeammateMessage.js +156 -0
  604. package/dist/src/components/messages/UserTextMessage.js +271 -0
  605. package/dist/src/components/messages/UserToolResultMessage/RejectedPlanMessage.js +27 -0
  606. package/dist/src/components/messages/UserToolResultMessage/RejectedToolUseMessage.js +16 -0
  607. package/dist/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.js +16 -0
  608. package/dist/src/components/messages/UserToolResultMessage/UserToolErrorMessage.js +91 -0
  609. package/dist/src/components/messages/UserToolResultMessage/UserToolRejectMessage.js +73 -0
  610. package/dist/src/components/messages/UserToolResultMessage/UserToolResultMessage.js +83 -0
  611. package/dist/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.js +58 -0
  612. package/dist/src/components/messages/UserToolResultMessage/utils.js +43 -0
  613. package/dist/src/components/messages/nullRenderingAttachments.js +58 -0
  614. package/dist/src/components/messages/teamMemCollapsed.js +142 -0
  615. package/dist/src/components/messages/teamMemSaved.js +17 -0
  616. package/dist/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.js +654 -0
  617. package/dist/src/components/permissions/AskUserQuestionPermissionRequest/PreviewBox.js +219 -0
  618. package/dist/src/components/permissions/AskUserQuestionPermissionRequest/PreviewQuestionView.js +227 -0
  619. package/dist/src/components/permissions/AskUserQuestionPermissionRequest/QuestionNavigationBar.js +174 -0
  620. package/dist/src/components/permissions/AskUserQuestionPermissionRequest/QuestionView.js +444 -0
  621. package/dist/src/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.js +136 -0
  622. package/dist/src/components/permissions/AskUserQuestionPermissionRequest/use-multiple-choice-state.js +100 -0
  623. package/dist/src/components/permissions/BashPermissionRequest/BashPermissionRequest.js +404 -0
  624. package/dist/src/components/permissions/BashPermissionRequest/bashToolUseOptions.js +110 -0
  625. package/dist/src/components/permissions/ComputerUseApproval/ComputerUseApproval.js +448 -0
  626. package/dist/src/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.js +125 -0
  627. package/dist/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.js +649 -0
  628. package/dist/src/components/permissions/FallbackPermissionRequest.js +348 -0
  629. package/dist/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.js +184 -0
  630. package/dist/src/components/permissions/FilePermissionDialog/FilePermissionDialog.js +108 -0
  631. package/dist/src/components/permissions/FilePermissionDialog/ideDiffConfig.js +13 -0
  632. package/dist/src/components/permissions/FilePermissionDialog/permissionOptions.js +136 -0
  633. package/dist/src/components/permissions/FilePermissionDialog/useFilePermissionDialog.js +131 -0
  634. package/dist/src/components/permissions/FilePermissionDialog/usePermissionHandler.js +86 -0
  635. package/dist/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.js +163 -0
  636. package/dist/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.js +78 -0
  637. package/dist/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.js +112 -0
  638. package/dist/src/components/permissions/NotebookEditPermissionRequest/NotebookEditPermissionRequest.js +163 -0
  639. package/dist/src/components/permissions/NotebookEditPermissionRequest/NotebookEditToolDiff.js +217 -0
  640. package/dist/src/components/permissions/PermissionDecisionDebugInfo.js +466 -0
  641. package/dist/src/components/permissions/PermissionDialog.js +54 -0
  642. package/dist/src/components/permissions/PermissionExplanation.js +269 -0
  643. package/dist/src/components/permissions/PermissionPrompt.js +316 -0
  644. package/dist/src/components/permissions/PermissionRequest.js +160 -0
  645. package/dist/src/components/permissions/PermissionRequestTitle.js +57 -0
  646. package/dist/src/components/permissions/PermissionRuleExplanation.js +116 -0
  647. package/dist/src/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.js +178 -0
  648. package/dist/src/components/permissions/PowerShellPermissionRequest/powershellToolUseOptions.js +73 -0
  649. package/dist/src/components/permissions/SandboxPermissionRequest.js +161 -0
  650. package/dist/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.js +228 -0
  651. package/dist/src/components/permissions/SkillPermissionRequest/SkillPermissionRequest.js +384 -0
  652. package/dist/src/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.js +258 -0
  653. package/dist/src/components/permissions/WorkerBadge.js +43 -0
  654. package/dist/src/components/permissions/WorkerPendingPermission.js +106 -0
  655. package/dist/src/components/permissions/hooks.js +163 -0
  656. package/dist/src/components/permissions/rules/AddPermissionRules.js +172 -0
  657. package/dist/src/components/permissions/rules/AddWorkspaceDirectory.js +334 -0
  658. package/dist/src/components/permissions/rules/PermissionRuleDescription.js +77 -0
  659. package/dist/src/components/permissions/rules/PermissionRuleInput.js +135 -0
  660. package/dist/src/components/permissions/rules/PermissionRuleList.js +1189 -0
  661. package/dist/src/components/permissions/rules/RecentDenialsTab.js +204 -0
  662. package/dist/src/components/permissions/rules/RemoveWorkspaceDirectory.js +102 -0
  663. package/dist/src/components/permissions/rules/WorkspaceTab.js +132 -0
  664. package/dist/src/components/permissions/shellPermissionHelpers.js +111 -0
  665. package/dist/src/components/permissions/useShellPermissionFeedback.js +108 -0
  666. package/dist/src/components/permissions/utils.js +14 -0
  667. package/dist/src/components/sandbox/SandboxConfigTab.js +47 -0
  668. package/dist/src/components/sandbox/SandboxDependenciesTab.js +122 -0
  669. package/dist/src/components/sandbox/SandboxDoctorSection.js +46 -0
  670. package/dist/src/components/sandbox/SandboxOverridesTab.js +192 -0
  671. package/dist/src/components/sandbox/SandboxSettings.js +296 -0
  672. package/dist/src/components/shell/ExpandShellOutputContext.js +33 -0
  673. package/dist/src/components/shell/OutputLine.js +110 -0
  674. package/dist/src/components/shell/ShellProgressMessage.js +143 -0
  675. package/dist/src/components/shell/ShellTimeDisplay.js +71 -0
  676. package/dist/src/components/skills/SkillsMenu.js +238 -0
  677. package/dist/src/components/tasks/AsyncAgentDetailDialog.js +234 -0
  678. package/dist/src/components/tasks/BackgroundTask.js +363 -0
  679. package/dist/src/components/tasks/BackgroundTaskStatus.js +419 -0
  680. package/dist/src/components/tasks/BackgroundTasksDialog.js +496 -0
  681. package/dist/src/components/tasks/DreamDetailDialog.js +250 -0
  682. package/dist/src/components/tasks/InProcessTeammateDetailDialog.js +274 -0
  683. package/dist/src/components/tasks/RemoteSessionDetailDialog.js +868 -0
  684. package/dist/src/components/tasks/RemoteSessionProgress.js +249 -0
  685. package/dist/src/components/tasks/ShellDetailDialog.js +403 -0
  686. package/dist/src/components/tasks/ShellProgress.js +76 -0
  687. package/dist/src/components/tasks/renderToolActivity.js +28 -0
  688. package/dist/src/components/tasks/taskStatusUtils.js +94 -0
  689. package/dist/src/components/teams/TeamStatus.js +76 -0
  690. package/dist/src/components/teams/TeamsDialog.js +672 -0
  691. package/dist/src/components/ui/ContextCard.js +5 -0
  692. package/dist/src/components/ui/OrderedList.js +66 -0
  693. package/dist/src/components/ui/OrderedListItem.js +41 -0
  694. package/dist/src/components/ui/TreeSelect.js +300 -0
  695. package/dist/src/components/wizard/WizardDialogLayout.js +47 -0
  696. package/dist/src/components/wizard/WizardNavigationFooter.js +10 -0
  697. package/dist/src/components/wizard/WizardProvider.js +217 -0
  698. package/dist/src/components/wizard/index.js +4 -0
  699. package/dist/src/components/wizard/useWizard.js +9 -0
  700. package/dist/src/constants/apiLimits.js +81 -0
  701. package/dist/src/constants/betas.js +45 -0
  702. package/dist/src/constants/common.js +29 -0
  703. package/dist/src/constants/cyberRiskInstruction.js +24 -0
  704. package/dist/src/constants/errorIds.js +14 -0
  705. package/dist/src/constants/figures.js +38 -0
  706. package/dist/src/constants/files.js +150 -0
  707. package/dist/src/constants/github-app.js +139 -0
  708. package/dist/src/constants/keys.js +10 -0
  709. package/dist/src/constants/messages.js +1 -0
  710. package/dist/src/constants/oauth.js +221 -0
  711. package/dist/src/constants/outputStyles.js +162 -0
  712. package/dist/src/constants/product.js +56 -0
  713. package/dist/src/constants/prompts.js +752 -0
  714. package/dist/src/constants/spinnerVerbs.js +202 -0
  715. package/dist/src/constants/system.js +77 -0
  716. package/dist/src/constants/systemPromptSections.js +39 -0
  717. package/dist/src/constants/toolLimits.js +50 -0
  718. package/dist/src/constants/tools.js +103 -0
  719. package/dist/src/constants/turnCompletionVerbs.js +12 -0
  720. package/dist/src/constants/xml.js +73 -0
  721. package/dist/src/context/QueuedMessageContext.js +51 -0
  722. package/dist/src/context/fpsMetrics.js +22 -0
  723. package/dist/src/context/mailbox.js +35 -0
  724. package/dist/src/context/modalContext.js +34 -0
  725. package/dist/src/context/notifications.js +199 -0
  726. package/dist/src/context/overlayContext.js +149 -0
  727. package/dist/src/context/promptOverlayContext.js +118 -0
  728. package/dist/src/context/stats.js +207 -0
  729. package/dist/src/context/voice.js +74 -0
  730. package/dist/src/context.js +146 -0
  731. package/dist/src/coordinator/coordinatorMode.js +349 -0
  732. package/dist/src/cost-tracker.js +208 -0
  733. package/dist/src/costHook.js +17 -0
  734. package/dist/src/daemon/main.js +16 -0
  735. package/dist/src/daemon/workerRegistry.js +16 -0
  736. package/dist/src/dialogLaunchers.js +68 -0
  737. package/dist/src/entrypoints/agentSdkTypes.js +202 -0
  738. package/dist/src/entrypoints/cli.js +252 -0
  739. package/dist/src/entrypoints/init.js +265 -0
  740. package/dist/src/entrypoints/mcp.js +142 -0
  741. package/dist/src/entrypoints/sandboxTypes.js +112 -0
  742. package/dist/src/entrypoints/sdk/controlSchemas.js +452 -0
  743. package/dist/src/entrypoints/sdk/controlTypes.js +1 -0
  744. package/dist/src/entrypoints/sdk/coreSchemas.js +1331 -0
  745. package/dist/src/entrypoints/sdk/coreTypes.generated.js +1 -0
  746. package/dist/src/entrypoints/sdk/coreTypes.js +49 -0
  747. package/dist/src/entrypoints/sdk/runtimeTypes.js +1 -0
  748. package/dist/src/entrypoints/sdk/toolTypes.js +1 -0
  749. package/dist/src/environment-runner/main.js +16 -0
  750. package/dist/src/history.js +386 -0
  751. package/dist/src/hooks/fileSuggestions.js +635 -0
  752. package/dist/src/hooks/notifs/useAutoModeUnavailableNotification.js +47 -0
  753. package/dist/src/hooks/notifs/useCanSwitchToExistingSubscription.js +57 -0
  754. package/dist/src/hooks/notifs/useDeprecationWarningNotification.js +43 -0
  755. package/dist/src/hooks/notifs/useFastModeNotification.js +164 -0
  756. package/dist/src/hooks/notifs/useIDEStatusIndicator.js +174 -0
  757. package/dist/src/hooks/notifs/useInstallMessages.js +27 -0
  758. package/dist/src/hooks/notifs/useLspInitializationNotification.js +144 -0
  759. package/dist/src/hooks/notifs/useMcpConnectivityStatus.js +80 -0
  760. package/dist/src/hooks/notifs/useModelMigrationNotifications.js +53 -0
  761. package/dist/src/hooks/notifs/useNpmDeprecationNotification.js +24 -0
  762. package/dist/src/hooks/notifs/usePluginAutoupdateNotification.js +82 -0
  763. package/dist/src/hooks/notifs/usePluginInstallationStatus.js +127 -0
  764. package/dist/src/hooks/notifs/useRateLimitWarningNotification.js +118 -0
  765. package/dist/src/hooks/notifs/useSettingsErrors.js +64 -0
  766. package/dist/src/hooks/notifs/useStartupNotification.js +33 -0
  767. package/dist/src/hooks/notifs/useTeammateShutdownNotification.js +64 -0
  768. package/dist/src/hooks/renderPlaceholder.js +26 -0
  769. package/dist/src/hooks/toolPermission/PermissionContext.js +211 -0
  770. package/dist/src/hooks/toolPermission/handlers/coordinatorHandler.js +44 -0
  771. package/dist/src/hooks/toolPermission/handlers/interactiveHandler.js +397 -0
  772. package/dist/src/hooks/toolPermission/handlers/swarmWorkerHandler.js +108 -0
  773. package/dist/src/hooks/toolPermission/permissionLogging.js +145 -0
  774. package/dist/src/hooks/unifiedSuggestions.js +130 -0
  775. package/dist/src/hooks/useAfterFirstRender.js +12 -0
  776. package/dist/src/hooks/useApiKeyVerification.js +63 -0
  777. package/dist/src/hooks/useArrowKeyHistory.js +203 -0
  778. package/dist/src/hooks/useAssistantHistory.js +194 -0
  779. package/dist/src/hooks/useAwaySummary.js +105 -0
  780. package/dist/src/hooks/useBackgroundTaskNavigation.js +204 -0
  781. package/dist/src/hooks/useBlink.js +28 -0
  782. package/dist/src/hooks/useCanUseTool.js +192 -0
  783. package/dist/src/hooks/useCancelRequest.js +195 -0
  784. package/dist/src/hooks/useChromeExtensionNotification.js +49 -0
  785. package/dist/src/hooks/useClaudeCodeHintRecommendation.js +117 -0
  786. package/dist/src/hooks/useClipboardImageHint.js +59 -0
  787. package/dist/src/hooks/useCommandKeybindings.js +87 -0
  788. package/dist/src/hooks/useCommandQueue.js +10 -0
  789. package/dist/src/hooks/useCopyOnSelect.js +88 -0
  790. package/dist/src/hooks/useDeferredHookMessages.js +43 -0
  791. package/dist/src/hooks/useDiffData.js +69 -0
  792. package/dist/src/hooks/useDiffInIDE.js +252 -0
  793. package/dist/src/hooks/useDirectConnect.js +150 -0
  794. package/dist/src/hooks/useDoublePress.js +44 -0
  795. package/dist/src/hooks/useDynamicConfig.js +17 -0
  796. package/dist/src/hooks/useElapsedTime.js +25 -0
  797. package/dist/src/hooks/useExitOnCtrlCD.js +57 -0
  798. package/dist/src/hooks/useExitOnCtrlCDWithKeybindings.js +17 -0
  799. package/dist/src/hooks/useFileHistorySnapshotInit.js +14 -0
  800. package/dist/src/hooks/useGlobalKeybindings.js +215 -0
  801. package/dist/src/hooks/useHistorySearch.js +241 -0
  802. package/dist/src/hooks/useIDEIntegration.js +56 -0
  803. package/dist/src/hooks/useIdeAtMentioned.js +51 -0
  804. package/dist/src/hooks/useIdeConnectionStatus.js +21 -0
  805. package/dist/src/hooks/useIdeLogging.js +29 -0
  806. package/dist/src/hooks/useIdeSelection.js +106 -0
  807. package/dist/src/hooks/useInboxPoller.js +709 -0
  808. package/dist/src/hooks/useInputBuffer.js +73 -0
  809. package/dist/src/hooks/useIssueFlagBanner.js +115 -0
  810. package/dist/src/hooks/useLogMessages.js +98 -0
  811. package/dist/src/hooks/useLspPluginRecommendation.js +176 -0
  812. package/dist/src/hooks/useMailboxBridge.js +15 -0
  813. package/dist/src/hooks/useMainLoopModel.js +25 -0
  814. package/dist/src/hooks/useManagePlugins.js +261 -0
  815. package/dist/src/hooks/useMemoryUsage.js +28 -0
  816. package/dist/src/hooks/useMergedClients.js +11 -0
  817. package/dist/src/hooks/useMergedCommands.js +10 -0
  818. package/dist/src/hooks/useMergedTools.js +32 -0
  819. package/dist/src/hooks/useMinDisplayTime.js +26 -0
  820. package/dist/src/hooks/useNotifyAfterTimeout.js +51 -0
  821. package/dist/src/hooks/useOfficialMarketplaceNotification.js +46 -0
  822. package/dist/src/hooks/usePasteHandler.js +188 -0
  823. package/dist/src/hooks/usePluginRecommendationBase.js +101 -0
  824. package/dist/src/hooks/usePrStatus.js +91 -0
  825. package/dist/src/hooks/usePromptSuggestion.js +128 -0
  826. package/dist/src/hooks/usePromptsFromClaudeInChrome.js +66 -0
  827. package/dist/src/hooks/useQueueProcessor.js +46 -0
  828. package/dist/src/hooks/useRemoteSession.js +431 -0
  829. package/dist/src/hooks/useReplBridge.js +717 -0
  830. package/dist/src/hooks/useSSHSession.js +167 -0
  831. package/dist/src/hooks/useScheduledTasks.js +104 -0
  832. package/dist/src/hooks/useSearchInput.js +302 -0
  833. package/dist/src/hooks/useSessionBackgrounding.js +132 -0
  834. package/dist/src/hooks/useSettings.js +10 -0
  835. package/dist/src/hooks/useSettingsChange.js +13 -0
  836. package/dist/src/hooks/useSkillImprovementSurvey.js +69 -0
  837. package/dist/src/hooks/useSkillsChange.js +51 -0
  838. package/dist/src/hooks/useSwarmInitialization.js +67 -0
  839. package/dist/src/hooks/useSwarmPermissionPoller.js +215 -0
  840. package/dist/src/hooks/useTaskListWatcher.js +157 -0
  841. package/dist/src/hooks/useTasksV2.js +220 -0
  842. package/dist/src/hooks/useTeammateViewAutoExit.js +55 -0
  843. package/dist/src/hooks/useTelegramMirror.js +47 -0
  844. package/dist/src/hooks/useTeleportResume.js +81 -0
  845. package/dist/src/hooks/useTerminalSize.js +9 -0
  846. package/dist/src/hooks/useTextInput.js +397 -0
  847. package/dist/src/hooks/useTimeout.js +10 -0
  848. package/dist/src/hooks/useTurnDiffs.js +160 -0
  849. package/dist/src/hooks/useTypeahead.js +1617 -0
  850. package/dist/src/hooks/useUpdateNotification.js +22 -0
  851. package/dist/src/hooks/useVimInput.js +232 -0
  852. package/dist/src/hooks/useVirtualScroll.js +627 -0
  853. package/dist/src/hooks/useVoice.js +954 -0
  854. package/dist/src/hooks/useVoiceEnabled.js +21 -0
  855. package/dist/src/hooks/useVoiceIntegration.js +631 -0
  856. package/dist/src/hooks/useWhatsAppMirror.js +46 -0
  857. package/dist/src/index.js +18 -0
  858. package/dist/src/ink/Ansi.js +269 -0
  859. package/dist/src/ink/bidi.js +117 -0
  860. package/dist/src/ink/clearTerminal.js +58 -0
  861. package/dist/src/ink/colorize.js +198 -0
  862. package/dist/src/ink/components/AlternateScreen.js +74 -0
  863. package/dist/src/ink/components/App.js +558 -0
  864. package/dist/src/ink/components/AppContext.js +11 -0
  865. package/dist/src/ink/components/Box.js +153 -0
  866. package/dist/src/ink/components/Button.js +166 -0
  867. package/dist/src/ink/components/ClockContext.js +108 -0
  868. package/dist/src/ink/components/CursorDeclarationContext.js +3 -0
  869. package/dist/src/ink/components/ErrorOverview.js +49 -0
  870. package/dist/src/ink/components/Link.js +33 -0
  871. package/dist/src/ink/components/Newline.js +29 -0
  872. package/dist/src/ink/components/NoSelect.js +56 -0
  873. package/dist/src/ink/components/RawAnsi.js +45 -0
  874. package/dist/src/ink/components/ScrollBox.js +170 -0
  875. package/dist/src/ink/components/Spacer.js +19 -0
  876. package/dist/src/ink/components/StdinContext.js +16 -0
  877. package/dist/src/ink/components/TerminalFocusContext.js +45 -0
  878. package/dist/src/ink/components/TerminalSizeContext.js +3 -0
  879. package/dist/src/ink/components/Text.js +194 -0
  880. package/dist/src/ink/constants.js +2 -0
  881. package/dist/src/ink/devtools.js +16 -0
  882. package/dist/src/ink/dom.js +298 -0
  883. package/dist/src/ink/events/click-event.js +36 -0
  884. package/dist/src/ink/events/dispatcher.js +172 -0
  885. package/dist/src/ink/events/emitter.js +31 -0
  886. package/dist/src/ink/events/event-handlers.js +30 -0
  887. package/dist/src/ink/events/event.js +9 -0
  888. package/dist/src/ink/events/focus-event.js +16 -0
  889. package/dist/src/ink/events/input-event.js +161 -0
  890. package/dist/src/ink/events/keyboard-event.js +45 -0
  891. package/dist/src/ink/events/terminal-event.js +78 -0
  892. package/dist/src/ink/events/terminal-focus-event.js +15 -0
  893. package/dist/src/ink/focus.js +158 -0
  894. package/dist/src/ink/frame.js +30 -0
  895. package/dist/src/ink/get-max-width.js +23 -0
  896. package/dist/src/ink/hit-test.js +113 -0
  897. package/dist/src/ink/hooks/use-animation-frame.js +48 -0
  898. package/dist/src/ink/hooks/use-app.js +7 -0
  899. package/dist/src/ink/hooks/use-declared-cursor.js +60 -0
  900. package/dist/src/ink/hooks/use-input.js +70 -0
  901. package/dist/src/ink/hooks/use-interval.js +54 -0
  902. package/dist/src/ink/hooks/use-search-highlight.js +32 -0
  903. package/dist/src/ink/hooks/use-selection.js +60 -0
  904. package/dist/src/ink/hooks/use-stdin.js +7 -0
  905. package/dist/src/ink/hooks/use-tab-status.js +57 -0
  906. package/dist/src/ink/hooks/use-terminal-focus.js +15 -0
  907. package/dist/src/ink/hooks/use-terminal-title.js +29 -0
  908. package/dist/src/ink/hooks/use-terminal-viewport.js +77 -0
  909. package/dist/src/ink/ink.js +1644 -0
  910. package/dist/src/ink/instances.js +7 -0
  911. package/dist/src/ink/layout/engine.js +4 -0
  912. package/dist/src/ink/layout/geometry.js +61 -0
  913. package/dist/src/ink/layout/node.js +62 -0
  914. package/dist/src/ink/layout/yoga.js +237 -0
  915. package/dist/src/ink/line-width-cache.js +19 -0
  916. package/dist/src/ink/log-update.js +583 -0
  917. package/dist/src/ink/measure-element.js +8 -0
  918. package/dist/src/ink/measure-text.js +35 -0
  919. package/dist/src/ink/node-cache.js +30 -0
  920. package/dist/src/ink/optimizer.js +81 -0
  921. package/dist/src/ink/output.js +556 -0
  922. package/dist/src/ink/parse-keypress.js +695 -0
  923. package/dist/src/ink/reconciler.js +384 -0
  924. package/dist/src/ink/render-border.js +134 -0
  925. package/dist/src/ink/render-node-to-output.js +1216 -0
  926. package/dist/src/ink/render-to-screen.js +171 -0
  927. package/dist/src/ink/renderer.js +129 -0
  928. package/dist/src/ink/root.js +80 -0
  929. package/dist/src/ink/screen.js +1132 -0
  930. package/dist/src/ink/searchHighlight.js +78 -0
  931. package/dist/src/ink/selection.js +792 -0
  932. package/dist/src/ink/squash-text-nodes.js +56 -0
  933. package/dist/src/ink/stringWidth.js +200 -0
  934. package/dist/src/ink/styles.js +299 -0
  935. package/dist/src/ink/supports-hyperlinks.js +40 -0
  936. package/dist/src/ink/tabstops.js +39 -0
  937. package/dist/src/ink/terminal-focus-state.js +35 -0
  938. package/dist/src/ink/terminal-querier.js +173 -0
  939. package/dist/src/ink/terminal.js +208 -0
  940. package/dist/src/ink/termio/ansi.js +70 -0
  941. package/dist/src/ink/termio/csi.js +260 -0
  942. package/dist/src/ink/termio/dec.js +53 -0
  943. package/dist/src/ink/termio/esc.js +55 -0
  944. package/dist/src/ink/termio/osc.js +432 -0
  945. package/dist/src/ink/termio/parser.js +356 -0
  946. package/dist/src/ink/termio/sgr.js +292 -0
  947. package/dist/src/ink/termio/tokenize.js +264 -0
  948. package/dist/src/ink/termio/types.js +55 -0
  949. package/dist/src/ink/termio.js +24 -0
  950. package/dist/src/ink/useTerminalNotification.js +57 -0
  951. package/dist/src/ink/warn.js +10 -0
  952. package/dist/src/ink/widest-line.js +14 -0
  953. package/dist/src/ink/wrap-text.js +54 -0
  954. package/dist/src/ink/wrapAnsi.js +6 -0
  955. package/dist/src/ink.js +50 -0
  956. package/dist/src/interactiveHelpers.js +317 -0
  957. package/dist/src/keybindings/KeybindingContext.js +184 -0
  958. package/dist/src/keybindings/KeybindingProviderSetup.js +259 -0
  959. package/dist/src/keybindings/defaultBindings.js +333 -0
  960. package/dist/src/keybindings/loadUserBindings.js +393 -0
  961. package/dist/src/keybindings/match.js +111 -0
  962. package/dist/src/keybindings/parser.js +184 -0
  963. package/dist/src/keybindings/reservedShortcuts.js +109 -0
  964. package/dist/src/keybindings/resolver.js +182 -0
  965. package/dist/src/keybindings/schema.js +205 -0
  966. package/dist/src/keybindings/shortcutFormat.js +48 -0
  967. package/dist/src/keybindings/template.js +40 -0
  968. package/dist/src/keybindings/useKeybinding.js +161 -0
  969. package/dist/src/keybindings/useShortcutDisplay.js +43 -0
  970. package/dist/src/keybindings/validate.js +395 -0
  971. package/dist/src/main.js +4080 -0
  972. package/dist/src/memdir/findRelevantMemories.js +101 -0
  973. package/dist/src/memdir/memdir.js +408 -0
  974. package/dist/src/memdir/memoryAge.js +52 -0
  975. package/dist/src/memdir/memoryScan.js +65 -0
  976. package/dist/src/memdir/memoryTypes.js +260 -0
  977. package/dist/src/memdir/paths.js +236 -0
  978. package/dist/src/memdir/teamMemPaths.js +261 -0
  979. package/dist/src/memdir/teamMemPrompts.js +82 -0
  980. package/dist/src/migrations/migrateAutoUpdatesToSettings.js +47 -0
  981. package/dist/src/migrations/migrateBypassPermissionsAcceptedToSettings.js +32 -0
  982. package/dist/src/migrations/migrateEnableAllProjectMcpServersToSettings.js +83 -0
  983. package/dist/src/migrations/migrateFennecToOpus.js +39 -0
  984. package/dist/src/migrations/migrateLegacyOpusToCurrent.js +44 -0
  985. package/dist/src/migrations/migrateOpusToOpus1m.js +31 -0
  986. package/dist/src/migrations/migrateReplBridgeEnabledToRemoteControlAtStartup.js +23 -0
  987. package/dist/src/migrations/migrateSonnet1mToSonnet45.js +38 -0
  988. package/dist/src/migrations/migrateSonnet45ToSonnet46.js +48 -0
  989. package/dist/src/migrations/resetAutoModeOptInForDefaultOffer.js +47 -0
  990. package/dist/src/migrations/resetProToOpusDefault.js +46 -0
  991. package/dist/src/mirrors/shared.js +186 -0
  992. package/dist/src/moreright/useMoreRight.js +13 -0
  993. package/dist/src/native-ts/color-diff/index.js +821 -0
  994. package/dist/src/native-ts/file-index/index.js +328 -0
  995. package/dist/src/native-ts/yoga-layout/enums.js +101 -0
  996. package/dist/src/native-ts/yoga-layout/index.js +2113 -0
  997. package/dist/src/outputStyles/loadOutputStylesDir.js +73 -0
  998. package/dist/src/plugins/builtinPlugins.js +132 -0
  999. package/dist/src/plugins/bundled/index.js +22 -0
  1000. package/dist/src/projectOnboardingState.js +61 -0
  1001. package/dist/src/query/config.js +17 -0
  1002. package/dist/src/query/deps.js +12 -0
  1003. package/dist/src/query/stopHooks.js +334 -0
  1004. package/dist/src/query/tokenBudget.js +49 -0
  1005. package/dist/src/query.js +1330 -0
  1006. package/dist/src/recovery/bunBundleShim.js +11 -0
  1007. package/dist/src/remote/RemoteSessionManager.js +195 -0
  1008. package/dist/src/remote/SessionsWebSocket.js +305 -0
  1009. package/dist/src/remote/remotePermissionBridge.js +70 -0
  1010. package/dist/src/remote/sdkMessageAdapter.js +227 -0
  1011. package/dist/src/replLauncher.js +6 -0
  1012. package/dist/src/schemas/hooks.js +174 -0
  1013. package/dist/src/screens/Doctor.js +581 -0
  1014. package/dist/src/screens/REPL.js +4434 -0
  1015. package/dist/src/screens/ResumeConversation.js +341 -0
  1016. package/dist/src/self-hosted-runner/main.js +16 -0
  1017. package/dist/src/server/backends/dangerousBackend.js +16 -0
  1018. package/dist/src/server/connectHeadless.js +16 -0
  1019. package/dist/src/server/createDirectConnectSession.js +62 -0
  1020. package/dist/src/server/directConnectManager.js +153 -0
  1021. package/dist/src/server/lockfile.js +16 -0
  1022. package/dist/src/server/parseConnectUrl.js +16 -0
  1023. package/dist/src/server/server.js +16 -0
  1024. package/dist/src/server/serverBanner.js +16 -0
  1025. package/dist/src/server/serverLog.js +16 -0
  1026. package/dist/src/server/sessionManager.js +16 -0
  1027. package/dist/src/server/types.js +7 -0
  1028. package/dist/src/services/AgentSummary/agentSummary.js +147 -0
  1029. package/dist/src/services/MagicDocs/magicDocs.js +193 -0
  1030. package/dist/src/services/MagicDocs/prompts.js +110 -0
  1031. package/dist/src/services/PromptSuggestion/promptSuggestion.js +402 -0
  1032. package/dist/src/services/PromptSuggestion/speculation.js +643 -0
  1033. package/dist/src/services/SessionMemory/prompts.js +254 -0
  1034. package/dist/src/services/SessionMemory/sessionMemory.js +358 -0
  1035. package/dist/src/services/SessionMemory/sessionMemoryUtils.js +157 -0
  1036. package/dist/src/services/analytics/config.js +33 -0
  1037. package/dist/src/services/analytics/datadog.js +259 -0
  1038. package/dist/src/services/analytics/firstPartyEventLogger.js +342 -0
  1039. package/dist/src/services/analytics/firstPartyEventLoggingExporter.js +594 -0
  1040. package/dist/src/services/analytics/growthbook.js +952 -0
  1041. package/dist/src/services/analytics/index.js +114 -0
  1042. package/dist/src/services/analytics/metadata.js +698 -0
  1043. package/dist/src/services/analytics/sink.js +95 -0
  1044. package/dist/src/services/analytics/sinkKillswitch.js +19 -0
  1045. package/dist/src/services/api/adminRequests.js +57 -0
  1046. package/dist/src/services/api/bootstrap.js +149 -0
  1047. package/dist/src/services/api/claude.js +2461 -0
  1048. package/dist/src/services/api/client.js +325 -0
  1049. package/dist/src/services/api/dumpPrompts.js +174 -0
  1050. package/dist/src/services/api/emptyUsage.js +20 -0
  1051. package/dist/src/services/api/errorUtils.js +203 -0
  1052. package/dist/src/services/api/errors.js +934 -0
  1053. package/dist/src/services/api/filesApi.js +523 -0
  1054. package/dist/src/services/api/firstTokenDate.js +49 -0
  1055. package/dist/src/services/api/grove.js +272 -0
  1056. package/dist/src/services/api/index.js +23 -0
  1057. package/dist/src/services/api/logging.js +484 -0
  1058. package/dist/src/services/api/metricsOptOut.js +129 -0
  1059. package/dist/src/services/api/openai.js +1187 -0
  1060. package/dist/src/services/api/openrouter.js +30 -0
  1061. package/dist/src/services/api/overageCreditGrant.js +123 -0
  1062. package/dist/src/services/api/promptCacheBreakDetection.js +510 -0
  1063. package/dist/src/services/api/referral.js +219 -0
  1064. package/dist/src/services/api/sessionIngress.js +358 -0
  1065. package/dist/src/services/api/ultrareviewQuota.js +29 -0
  1066. package/dist/src/services/api/usage.js +31 -0
  1067. package/dist/src/services/api/withRetry.js +599 -0
  1068. package/dist/src/services/autoDream/autoDream.js +244 -0
  1069. package/dist/src/services/autoDream/config.js +17 -0
  1070. package/dist/src/services/autoDream/consolidationLock.js +122 -0
  1071. package/dist/src/services/autoDream/consolidationPrompt.js +56 -0
  1072. package/dist/src/services/awaySummary.js +61 -0
  1073. package/dist/src/services/claudeAiLimits.js +331 -0
  1074. package/dist/src/services/claudeAiLimitsHook.js +15 -0
  1075. package/dist/src/services/compact/apiMicrocompact.js +97 -0
  1076. package/dist/src/services/compact/autoCompact.js +236 -0
  1077. package/dist/src/services/compact/cachedMicrocompact.js +16 -0
  1078. package/dist/src/services/compact/compact.js +1258 -0
  1079. package/dist/src/services/compact/compactWarningHook.js +12 -0
  1080. package/dist/src/services/compact/compactWarningState.js +15 -0
  1081. package/dist/src/services/compact/grouping.js +58 -0
  1082. package/dist/src/services/compact/microCompact.js +414 -0
  1083. package/dist/src/services/compact/postCompactCleanup.js +72 -0
  1084. package/dist/src/services/compact/prompt.js +327 -0
  1085. package/dist/src/services/compact/sessionMemoryCompact.js +467 -0
  1086. package/dist/src/services/compact/timeBasedMCConfig.js +11 -0
  1087. package/dist/src/services/diagnosticTracking.js +282 -0
  1088. package/dist/src/services/extractMemories/extractMemories.js +444 -0
  1089. package/dist/src/services/extractMemories/prompts.js +129 -0
  1090. package/dist/src/services/internalLogging.js +68 -0
  1091. package/dist/src/services/limits/adapters/claude.js +219 -0
  1092. package/dist/src/services/limits/adapters/minimax.js +177 -0
  1093. package/dist/src/services/limits/adapters/ollama.js +189 -0
  1094. package/dist/src/services/limits/adapters/openai.js +167 -0
  1095. package/dist/src/services/limits/adapters/openrouter.js +166 -0
  1096. package/dist/src/services/limits/adapters/zai.js +154 -0
  1097. package/dist/src/services/limits/index.js +2 -0
  1098. package/dist/src/services/limits/registry.js +179 -0
  1099. package/dist/src/services/limits/sessionCounter.js +168 -0
  1100. package/dist/src/services/limits/types.js +1 -0
  1101. package/dist/src/services/lsp/LSPClient.js +306 -0
  1102. package/dist/src/services/lsp/LSPDiagnosticRegistry.js +277 -0
  1103. package/dist/src/services/lsp/LSPServerInstance.js +390 -0
  1104. package/dist/src/services/lsp/LSPServerManager.js +305 -0
  1105. package/dist/src/services/lsp/config.js +57 -0
  1106. package/dist/src/services/lsp/manager.js +246 -0
  1107. package/dist/src/services/lsp/passiveFeedback.js +226 -0
  1108. package/dist/src/services/mcp/InProcessTransport.js +54 -0
  1109. package/dist/src/services/mcp/MCPConnectionManager.js +50 -0
  1110. package/dist/src/services/mcp/SdkControlTransport.js +115 -0
  1111. package/dist/src/services/mcp/auth.js +1882 -0
  1112. package/dist/src/services/mcp/channelAllowlist.js +57 -0
  1113. package/dist/src/services/mcp/channelNotification.js +236 -0
  1114. package/dist/src/services/mcp/channelPermissions.js +192 -0
  1115. package/dist/src/services/mcp/claudeai.js +123 -0
  1116. package/dist/src/services/mcp/client.js +2480 -0
  1117. package/dist/src/services/mcp/config.js +1277 -0
  1118. package/dist/src/services/mcp/elicitationHandler.js +192 -0
  1119. package/dist/src/services/mcp/envExpansion.js +30 -0
  1120. package/dist/src/services/mcp/headersHelper.js +94 -0
  1121. package/dist/src/services/mcp/mcpStringUtils.js +85 -0
  1122. package/dist/src/services/mcp/normalization.js +21 -0
  1123. package/dist/src/services/mcp/oauthPort.js +69 -0
  1124. package/dist/src/services/mcp/officialRegistry.js +58 -0
  1125. package/dist/src/services/mcp/types.js +94 -0
  1126. package/dist/src/services/mcp/useManageMCPConnections.js +820 -0
  1127. package/dist/src/services/mcp/utils.js +433 -0
  1128. package/dist/src/services/mcp/vscodeSdkMcp.js +69 -0
  1129. package/dist/src/services/mcp/xaa.js +342 -0
  1130. package/dist/src/services/mcp/xaaIdpLogin.js +377 -0
  1131. package/dist/src/services/mcpServerApproval.js +29 -0
  1132. package/dist/src/services/mockRateLimits.js +666 -0
  1133. package/dist/src/services/notifier.js +114 -0
  1134. package/dist/src/services/oauth/auth-code-listener.js +236 -0
  1135. package/dist/src/services/oauth/client.js +545 -0
  1136. package/dist/src/services/oauth/crypto.js +19 -0
  1137. package/dist/src/services/oauth/getOauthProfile.js +48 -0
  1138. package/dist/src/services/oauth/index.js +152 -0
  1139. package/dist/src/services/oauth/types.js +1 -0
  1140. package/dist/src/services/orchestration/execution/AgentTaskExecutor.js +315 -0
  1141. package/dist/src/services/orchestration/execution/OrchestrationExecutionRuntime.js +1147 -0
  1142. package/dist/src/services/orchestration/execution/index.js +2 -0
  1143. package/dist/src/services/orchestration/execution/types.js +1 -0
  1144. package/dist/src/services/orchestration/global/GlobalOrchestratorRuntime.js +140 -0
  1145. package/dist/src/services/orchestration/global/index.js +3 -0
  1146. package/dist/src/services/orchestration/global/reporting.js +541 -0
  1147. package/dist/src/services/orchestration/global/types.js +1 -0
  1148. package/dist/src/services/orchestration/index.js +4 -0
  1149. package/dist/src/services/orchestration/policy/index.js +2 -0
  1150. package/dist/src/services/orchestration/policy/scoring.js +291 -0
  1151. package/dist/src/services/orchestration/policy/types.js +1 -0
  1152. package/dist/src/services/orchestration/squad/SquadOrchestratorRuntime.js +198 -0
  1153. package/dist/src/services/orchestration/squad/index.js +3 -0
  1154. package/dist/src/services/orchestration/squad/reporting.js +525 -0
  1155. package/dist/src/services/orchestration/squad/types.js +1 -0
  1156. package/dist/src/services/plugins/PluginInstallationManager.js +139 -0
  1157. package/dist/src/services/plugins/pluginCliCommands.js +230 -0
  1158. package/dist/src/services/plugins/pluginOperations.js +826 -0
  1159. package/dist/src/services/policyLimits/index.js +547 -0
  1160. package/dist/src/services/policyLimits/types.js +9 -0
  1161. package/dist/src/services/preventSleep.js +143 -0
  1162. package/dist/src/services/rateLimitMessages.js +271 -0
  1163. package/dist/src/services/rateLimitMocking.js +91 -0
  1164. package/dist/src/services/remoteManagedSettings/index.js +534 -0
  1165. package/dist/src/services/remoteManagedSettings/securityCheck.js +59 -0
  1166. package/dist/src/services/remoteManagedSettings/syncCache.js +90 -0
  1167. package/dist/src/services/remoteManagedSettings/syncCacheState.js +89 -0
  1168. package/dist/src/services/remoteManagedSettings/types.js +12 -0
  1169. package/dist/src/services/settingsSync/index.js +478 -0
  1170. package/dist/src/services/settingsSync/types.js +35 -0
  1171. package/dist/src/services/teamMemorySync/index.js +976 -0
  1172. package/dist/src/services/teamMemorySync/secretScanner.js +275 -0
  1173. package/dist/src/services/teamMemorySync/teamMemSecretGuard.js +35 -0
  1174. package/dist/src/services/teamMemorySync/types.js +47 -0
  1175. package/dist/src/services/teamMemorySync/watcher.js +326 -0
  1176. package/dist/src/services/tips/tipHistory.js +17 -0
  1177. package/dist/src/services/tips/tipRegistry.js +589 -0
  1178. package/dist/src/services/tips/tipScheduler.js +40 -0
  1179. package/dist/src/services/tokenEstimation.js +365 -0
  1180. package/dist/src/services/toolUseSummary/toolUseSummaryGenerator.js +87 -0
  1181. package/dist/src/services/tools/StreamingToolExecutor.js +413 -0
  1182. package/dist/src/services/tools/toolExecution.js +1218 -0
  1183. package/dist/src/services/tools/toolHooks.js +454 -0
  1184. package/dist/src/services/tools/toolOrchestration.js +110 -0
  1185. package/dist/src/services/vcr.js +291 -0
  1186. package/dist/src/services/voice.js +394 -0
  1187. package/dist/src/services/voiceKeyterms.js +94 -0
  1188. package/dist/src/services/voiceStreamSTT.js +406 -0
  1189. package/dist/src/setup.js +349 -0
  1190. package/dist/src/skills/bundled/batch.js +114 -0
  1191. package/dist/src/skills/bundled/claudeApi.js +145 -0
  1192. package/dist/src/skills/bundled/claudeApiContent.js +14 -0
  1193. package/dist/src/skills/bundled/claudeInChrome.js +27 -0
  1194. package/dist/src/skills/bundled/debug.js +99 -0
  1195. package/dist/src/skills/bundled/index.js +5 -0
  1196. package/dist/src/skills/bundled/keybindings.js +292 -0
  1197. package/dist/src/skills/bundled/loop.js +81 -0
  1198. package/dist/src/skills/bundled/loremIpsum.js +266 -0
  1199. package/dist/src/skills/bundled/remember.js +75 -0
  1200. package/dist/src/skills/bundled/scheduleRemoteAgents.js +373 -0
  1201. package/dist/src/skills/bundled/simplify.js +66 -0
  1202. package/dist/src/skills/bundled/skillify.js +184 -0
  1203. package/dist/src/skills/bundled/stuck.js +75 -0
  1204. package/dist/src/skills/bundled/updateConfig.js +463 -0
  1205. package/dist/src/skills/bundled/verify.js +25 -0
  1206. package/dist/src/skills/bundled/verifyContent.js +7 -0
  1207. package/dist/src/skills/bundledSkills.js +159 -0
  1208. package/dist/src/skills/loadSkillsDir.js +751 -0
  1209. package/dist/src/skills/mcpSkillBuilders.js +10 -0
  1210. package/dist/src/ssh/createSSHSession.js +16 -0
  1211. package/dist/src/state/AppState.js +184 -0
  1212. package/dist/src/state/AppStateStore.js +120 -0
  1213. package/dist/src/state/onChangeAppState.js +132 -0
  1214. package/dist/src/state/selectors.js +51 -0
  1215. package/dist/src/state/store.js +21 -0
  1216. package/dist/src/state/teammateViewHelpers.js +124 -0
  1217. package/dist/src/tasks/DreamTask/DreamTask.js +99 -0
  1218. package/dist/src/tasks/InProcessTeammateTask/InProcessTeammateTask.js +116 -0
  1219. package/dist/src/tasks/InProcessTeammateTask/types.js +35 -0
  1220. package/dist/src/tasks/LocalAgentTask/LocalAgentTask.js +507 -0
  1221. package/dist/src/tasks/LocalMainSessionTask.js +338 -0
  1222. package/dist/src/tasks/LocalShellTask/LocalShellTask.js +475 -0
  1223. package/dist/src/tasks/LocalShellTask/guards.js +9 -0
  1224. package/dist/src/tasks/LocalShellTask/killShellTasks.js +59 -0
  1225. package/dist/src/tasks/RemoteAgentTask/RemoteAgentTask.js +742 -0
  1226. package/dist/src/tasks/pillLabel.js +69 -0
  1227. package/dist/src/tasks/stopTask.js +67 -0
  1228. package/dist/src/tasks/types.js +18 -0
  1229. package/dist/src/tasks.js +39 -0
  1230. package/dist/src/telegram/bridge.js +329 -0
  1231. package/dist/src/telegram/config.js +89 -0
  1232. package/dist/src/telegram/mirror.js +91 -0
  1233. package/dist/src/tools/AgentTool/AgentTool.js +1222 -0
  1234. package/dist/src/tools/AgentTool/UI.js +592 -0
  1235. package/dist/src/tools/AgentTool/agentColorManager.js +43 -0
  1236. package/dist/src/tools/AgentTool/agentDisplay.js +72 -0
  1237. package/dist/src/tools/AgentTool/agentMemory.js +139 -0
  1238. package/dist/src/tools/AgentTool/agentMemorySnapshot.js +136 -0
  1239. package/dist/src/tools/AgentTool/agentToolUtils.js +456 -0
  1240. package/dist/src/tools/AgentTool/built-in/claudeCodeGuideAgent.js +175 -0
  1241. package/dist/src/tools/AgentTool/built-in/exploreAgent.js +76 -0
  1242. package/dist/src/tools/AgentTool/built-in/generalPurposeAgent.js +28 -0
  1243. package/dist/src/tools/AgentTool/built-in/planAgent.js +87 -0
  1244. package/dist/src/tools/AgentTool/built-in/statuslineSetup.js +140 -0
  1245. package/dist/src/tools/AgentTool/built-in/verificationAgent.js +146 -0
  1246. package/dist/src/tools/AgentTool/builtInAgents.js +59 -0
  1247. package/dist/src/tools/AgentTool/constants.js +11 -0
  1248. package/dist/src/tools/AgentTool/forkSubagent.js +177 -0
  1249. package/dist/src/tools/AgentTool/loadAgentsDir.js +497 -0
  1250. package/dist/src/tools/AgentTool/prompt.js +262 -0
  1251. package/dist/src/tools/AgentTool/resumeAgent.js +182 -0
  1252. package/dist/src/tools/AgentTool/runAgent.js +629 -0
  1253. package/dist/src/tools/AskUserQuestionTool/AskUserQuestionTool.js +237 -0
  1254. package/dist/src/tools/AskUserQuestionTool/prompt.js +38 -0
  1255. package/dist/src/tools/BashTool/BashTool.js +1008 -0
  1256. package/dist/src/tools/BashTool/BashToolResultMessage.js +168 -0
  1257. package/dist/src/tools/BashTool/UI.js +133 -0
  1258. package/dist/src/tools/BashTool/bashCommandHelpers.js +184 -0
  1259. package/dist/src/tools/BashTool/bashPermissions.js +2023 -0
  1260. package/dist/src/tools/BashTool/bashSecurity.js +2267 -0
  1261. package/dist/src/tools/BashTool/commandSemantics.js +105 -0
  1262. package/dist/src/tools/BashTool/commentLabel.js +14 -0
  1263. package/dist/src/tools/BashTool/destructiveCommandWarning.js +88 -0
  1264. package/dist/src/tools/BashTool/modeValidation.js +86 -0
  1265. package/dist/src/tools/BashTool/pathValidation.js +1080 -0
  1266. package/dist/src/tools/BashTool/prompt.js +334 -0
  1267. package/dist/src/tools/BashTool/readOnlyValidation.js +1794 -0
  1268. package/dist/src/tools/BashTool/sedEditParser.js +282 -0
  1269. package/dist/src/tools/BashTool/sedValidation.js +580 -0
  1270. package/dist/src/tools/BashTool/shouldUseSandbox.js +125 -0
  1271. package/dist/src/tools/BashTool/toolName.js +2 -0
  1272. package/dist/src/tools/BashTool/utils.js +180 -0
  1273. package/dist/src/tools/BriefTool/BriefTool.js +172 -0
  1274. package/dist/src/tools/BriefTool/UI.js +66 -0
  1275. package/dist/src/tools/BriefTool/attachments.js +86 -0
  1276. package/dist/src/tools/BriefTool/prompt.js +19 -0
  1277. package/dist/src/tools/BriefTool/upload.js +136 -0
  1278. package/dist/src/tools/ConfigTool/ConfigTool.js +398 -0
  1279. package/dist/src/tools/ConfigTool/UI.js +24 -0
  1280. package/dist/src/tools/ConfigTool/constants.js +1 -0
  1281. package/dist/src/tools/ConfigTool/prompt.js +82 -0
  1282. package/dist/src/tools/ConfigTool/supportedSettings.js +180 -0
  1283. package/dist/src/tools/EnterPlanModeTool/EnterPlanModeTool.js +98 -0
  1284. package/dist/src/tools/EnterPlanModeTool/UI.js +13 -0
  1285. package/dist/src/tools/EnterPlanModeTool/constants.js +1 -0
  1286. package/dist/src/tools/EnterPlanModeTool/prompt.js +164 -0
  1287. package/dist/src/tools/EnterWorktreeTool/EnterWorktreeTool.js +104 -0
  1288. package/dist/src/tools/EnterWorktreeTool/UI.js +8 -0
  1289. package/dist/src/tools/EnterWorktreeTool/constants.js +1 -0
  1290. package/dist/src/tools/EnterWorktreeTool/prompt.js +30 -0
  1291. package/dist/src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.js +385 -0
  1292. package/dist/src/tools/ExitPlanModeTool/UI.js +31 -0
  1293. package/dist/src/tools/ExitPlanModeTool/constants.js +2 -0
  1294. package/dist/src/tools/ExitPlanModeTool/prompt.js +27 -0
  1295. package/dist/src/tools/ExitWorktreeTool/ExitWorktreeTool.js +257 -0
  1296. package/dist/src/tools/ExitWorktreeTool/UI.js +9 -0
  1297. package/dist/src/tools/ExitWorktreeTool/constants.js +1 -0
  1298. package/dist/src/tools/ExitWorktreeTool/prompt.js +32 -0
  1299. package/dist/src/tools/FileEditTool/FileEditTool.js +480 -0
  1300. package/dist/src/tools/FileEditTool/UI.js +201 -0
  1301. package/dist/src/tools/FileEditTool/constants.js +7 -0
  1302. package/dist/src/tools/FileEditTool/prompt.js +24 -0
  1303. package/dist/src/tools/FileEditTool/types.js +50 -0
  1304. package/dist/src/tools/FileEditTool/utils.js +579 -0
  1305. package/dist/src/tools/FileReadTool/FileReadTool.js +889 -0
  1306. package/dist/src/tools/FileReadTool/UI.js +125 -0
  1307. package/dist/src/tools/FileReadTool/imageProcessor.js +46 -0
  1308. package/dist/src/tools/FileReadTool/limits.js +70 -0
  1309. package/dist/src/tools/FileReadTool/prompt.js +31 -0
  1310. package/dist/src/tools/FileWriteTool/FileWriteTool.js +341 -0
  1311. package/dist/src/tools/FileWriteTool/UI.js +338 -0
  1312. package/dist/src/tools/FileWriteTool/prompt.js +15 -0
  1313. package/dist/src/tools/GlobTool/GlobTool.js +161 -0
  1314. package/dist/src/tools/GlobTool/UI.js +39 -0
  1315. package/dist/src/tools/GlobTool/prompt.js +6 -0
  1316. package/dist/src/tools/GrepTool/GrepTool.js +439 -0
  1317. package/dist/src/tools/GrepTool/UI.js +154 -0
  1318. package/dist/src/tools/GrepTool/prompt.js +16 -0
  1319. package/dist/src/tools/LSPTool/LSPTool.js +660 -0
  1320. package/dist/src/tools/LSPTool/UI.js +204 -0
  1321. package/dist/src/tools/LSPTool/formatters.js +445 -0
  1322. package/dist/src/tools/LSPTool/prompt.js +20 -0
  1323. package/dist/src/tools/LSPTool/schemas.js +197 -0
  1324. package/dist/src/tools/LSPTool/symbolContext.js +75 -0
  1325. package/dist/src/tools/ListMcpResourcesTool/ListMcpResourcesTool.js +100 -0
  1326. package/dist/src/tools/ListMcpResourcesTool/UI.js +16 -0
  1327. package/dist/src/tools/ListMcpResourcesTool/prompt.js +18 -0
  1328. package/dist/src/tools/MCPTool/MCPTool.js +60 -0
  1329. package/dist/src/tools/MCPTool/UI.js +342 -0
  1330. package/dist/src/tools/MCPTool/classifyForCollapse.js +597 -0
  1331. package/dist/src/tools/MCPTool/prompt.js +3 -0
  1332. package/dist/src/tools/McpAuthTool/McpAuthTool.js +162 -0
  1333. package/dist/src/tools/NotebookEditTool/NotebookEditTool.js +421 -0
  1334. package/dist/src/tools/NotebookEditTool/UI.js +40 -0
  1335. package/dist/src/tools/NotebookEditTool/constants.js +2 -0
  1336. package/dist/src/tools/NotebookEditTool/prompt.js +2 -0
  1337. package/dist/src/tools/PowerShellTool/PowerShellTool.js +899 -0
  1338. package/dist/src/tools/PowerShellTool/UI.js +57 -0
  1339. package/dist/src/tools/PowerShellTool/clmTypes.js +207 -0
  1340. package/dist/src/tools/PowerShellTool/commandSemantics.js +115 -0
  1341. package/dist/src/tools/PowerShellTool/commonParameters.js +27 -0
  1342. package/dist/src/tools/PowerShellTool/destructiveCommandWarning.js +92 -0
  1343. package/dist/src/tools/PowerShellTool/gitSafety.js +185 -0
  1344. package/dist/src/tools/PowerShellTool/modeValidation.js +357 -0
  1345. package/dist/src/tools/PowerShellTool/pathValidation.js +1712 -0
  1346. package/dist/src/tools/PowerShellTool/powershellPermissions.js +1351 -0
  1347. package/dist/src/tools/PowerShellTool/powershellSecurity.js +942 -0
  1348. package/dist/src/tools/PowerShellTool/prompt.js +134 -0
  1349. package/dist/src/tools/PowerShellTool/readOnlyValidation.js +1633 -0
  1350. package/dist/src/tools/PowerShellTool/toolName.js +2 -0
  1351. package/dist/src/tools/REPLTool/constants.js +43 -0
  1352. package/dist/src/tools/REPLTool/primitiveTools.js +36 -0
  1353. package/dist/src/tools/ReadMcpResourceTool/ReadMcpResourceTool.js +112 -0
  1354. package/dist/src/tools/ReadMcpResourceTool/UI.js +23 -0
  1355. package/dist/src/tools/ReadMcpResourceTool/prompt.js +15 -0
  1356. package/dist/src/tools/RemoteTriggerTool/RemoteTriggerTool.js +142 -0
  1357. package/dist/src/tools/RemoteTriggerTool/UI.js +11 -0
  1358. package/dist/src/tools/RemoteTriggerTool/prompt.js +12 -0
  1359. package/dist/src/tools/ScheduleCronTool/CronCreateTool.js +120 -0
  1360. package/dist/src/tools/ScheduleCronTool/CronDeleteTool.js +74 -0
  1361. package/dist/src/tools/ScheduleCronTool/CronListTool.js +77 -0
  1362. package/dist/src/tools/ScheduleCronTool/UI.js +28 -0
  1363. package/dist/src/tools/ScheduleCronTool/prompt.js +115 -0
  1364. package/dist/src/tools/SendMessageTool/SendMessageTool.js +675 -0
  1365. package/dist/src/tools/SendMessageTool/UI.js +23 -0
  1366. package/dist/src/tools/SendMessageTool/constants.js +1 -0
  1367. package/dist/src/tools/SendMessageTool/prompt.js +47 -0
  1368. package/dist/src/tools/SkillTool/SkillTool.js +827 -0
  1369. package/dist/src/tools/SkillTool/UI.js +60 -0
  1370. package/dist/src/tools/SkillTool/constants.js +1 -0
  1371. package/dist/src/tools/SkillTool/prompt.js +184 -0
  1372. package/dist/src/tools/SleepTool/prompt.js +14 -0
  1373. package/dist/src/tools/SyntheticOutputTool/SyntheticOutputTool.js +138 -0
  1374. package/dist/src/tools/TaskCreateTool/TaskCreateTool.js +104 -0
  1375. package/dist/src/tools/TaskCreateTool/constants.js +1 -0
  1376. package/dist/src/tools/TaskCreateTool/prompt.js +52 -0
  1377. package/dist/src/tools/TaskGetTool/TaskGetTool.js +106 -0
  1378. package/dist/src/tools/TaskGetTool/constants.js +1 -0
  1379. package/dist/src/tools/TaskGetTool/prompt.js +23 -0
  1380. package/dist/src/tools/TaskListTool/TaskListTool.js +89 -0
  1381. package/dist/src/tools/TaskListTool/constants.js +1 -0
  1382. package/dist/src/tools/TaskListTool/prompt.js +44 -0
  1383. package/dist/src/tools/TaskOutputTool/TaskOutputTool.js +535 -0
  1384. package/dist/src/tools/TaskOutputTool/constants.js +1 -0
  1385. package/dist/src/tools/TaskStopTool/TaskStopTool.js +110 -0
  1386. package/dist/src/tools/TaskStopTool/UI.js +30 -0
  1387. package/dist/src/tools/TaskStopTool/prompt.js +7 -0
  1388. package/dist/src/tools/TaskUpdateTool/TaskUpdateTool.js +301 -0
  1389. package/dist/src/tools/TaskUpdateTool/constants.js +1 -0
  1390. package/dist/src/tools/TaskUpdateTool/prompt.js +76 -0
  1391. package/dist/src/tools/TeamCreateTool/TeamCreateTool.js +177 -0
  1392. package/dist/src/tools/TeamCreateTool/UI.js +4 -0
  1393. package/dist/src/tools/TeamCreateTool/constants.js +1 -0
  1394. package/dist/src/tools/TeamCreateTool/prompt.js +113 -0
  1395. package/dist/src/tools/TeamDeleteTool/TeamDeleteTool.js +102 -0
  1396. package/dist/src/tools/TeamDeleteTool/UI.js +13 -0
  1397. package/dist/src/tools/TeamDeleteTool/constants.js +1 -0
  1398. package/dist/src/tools/TeamDeleteTool/prompt.js +16 -0
  1399. package/dist/src/tools/TodoWriteTool/TodoWriteTool.js +99 -0
  1400. package/dist/src/tools/TodoWriteTool/constants.js +1 -0
  1401. package/dist/src/tools/TodoWriteTool/prompt.js +181 -0
  1402. package/dist/src/tools/ToolSearchTool/ToolSearchTool.js +357 -0
  1403. package/dist/src/tools/ToolSearchTool/constants.js +1 -0
  1404. package/dist/src/tools/ToolSearchTool/prompt.js +99 -0
  1405. package/dist/src/tools/TungstenTool/TungstenLiveMonitor.js +7 -0
  1406. package/dist/src/tools/TungstenTool/TungstenTool.js +3 -0
  1407. package/dist/src/tools/WebFetchTool/UI.js +30 -0
  1408. package/dist/src/tools/WebFetchTool/WebFetchTool.js +246 -0
  1409. package/dist/src/tools/WebFetchTool/preapproved.js +155 -0
  1410. package/dist/src/tools/WebFetchTool/prompt.js +39 -0
  1411. package/dist/src/tools/WebFetchTool/utils.js +381 -0
  1412. package/dist/src/tools/WebSearchTool/UI.js +66 -0
  1413. package/dist/src/tools/WebSearchTool/WebSearchTool.js +352 -0
  1414. package/dist/src/tools/WebSearchTool/prompt.js +32 -0
  1415. package/dist/src/tools/WorkflowTool/constants.js +2 -0
  1416. package/dist/src/tools/shared/gitOperationTracking.js +220 -0
  1417. package/dist/src/tools/shared/spawnMultiAgent.js +805 -0
  1418. package/dist/src/tools/testing/TestingPermissionTool.js +72 -0
  1419. package/dist/src/tools/utils.js +24 -0
  1420. package/dist/src/tools.js +332 -0
  1421. package/dist/src/types/command.js +10 -0
  1422. package/dist/src/types/connectorText.js +2 -0
  1423. package/dist/src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.js +673 -0
  1424. package/dist/src/types/generated/events_mono/common/v1/auth.js +49 -0
  1425. package/dist/src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.js +147 -0
  1426. package/dist/src/types/generated/google/protobuf/timestamp.js +38 -0
  1427. package/dist/src/types/hooks.js +153 -0
  1428. package/dist/src/types/ids.js +27 -0
  1429. package/dist/src/types/logs.js +11 -0
  1430. package/dist/src/types/message.js +1 -0
  1431. package/dist/src/types/permissions.js +25 -0
  1432. package/dist/src/types/plugin.js +72 -0
  1433. package/dist/src/types/textInputTypes.js +20 -0
  1434. package/dist/src/types/utils.js +5 -0
  1435. package/dist/src/upstreamproxy/relay.js +346 -0
  1436. package/dist/src/upstreamproxy/upstreamproxy.js +236 -0
  1437. package/dist/src/utils/CircularBuffer.js +75 -0
  1438. package/dist/src/utils/Cursor.js +1229 -0
  1439. package/dist/src/utils/QueryGuard.js +115 -0
  1440. package/dist/src/utils/Shell.js +374 -0
  1441. package/dist/src/utils/ShellCommand.js +338 -0
  1442. package/dist/src/utils/abortController.js +74 -0
  1443. package/dist/src/utils/activityManager.js +127 -0
  1444. package/dist/src/utils/advisor.js +77 -0
  1445. package/dist/src/utils/agentContext.js +91 -0
  1446. package/dist/src/utils/agentId.js +83 -0
  1447. package/dist/src/utils/agentSwarmsEnabled.js +37 -0
  1448. package/dist/src/utils/agenticSessionSearch.js +255 -0
  1449. package/dist/src/utils/analyzeContext.js +848 -0
  1450. package/dist/src/utils/ansiToPng.js +259 -0
  1451. package/dist/src/utils/ansiToSvg.js +207 -0
  1452. package/dist/src/utils/api.js +555 -0
  1453. package/dist/src/utils/apiPreconnect.js +62 -0
  1454. package/dist/src/utils/appleTerminalBackup.js +95 -0
  1455. package/dist/src/utils/argumentSubstitution.js +114 -0
  1456. package/dist/src/utils/array.js +12 -0
  1457. package/dist/src/utils/asciicast.js +200 -0
  1458. package/dist/src/utils/attachments.js +2514 -0
  1459. package/dist/src/utils/attribution.js +308 -0
  1460. package/dist/src/utils/attributionHooks.js +16 -0
  1461. package/dist/src/utils/attributionTrailer.js +16 -0
  1462. package/dist/src/utils/auth.js +2022 -0
  1463. package/dist/src/utils/authFileDescriptor.js +152 -0
  1464. package/dist/src/utils/authPortable.js +14 -0
  1465. package/dist/src/utils/autoModeDenials.js +15 -0
  1466. package/dist/src/utils/autoRunIssue.js +112 -0
  1467. package/dist/src/utils/autoUpdater.js +461 -0
  1468. package/dist/src/utils/aws.js +44 -0
  1469. package/dist/src/utils/awsAuthStatusManager.js +66 -0
  1470. package/dist/src/utils/background/remote/preconditions.js +175 -0
  1471. package/dist/src/utils/background/remote/remoteSession.js +53 -0
  1472. package/dist/src/utils/backgroundHousekeeping.js +66 -0
  1473. package/dist/src/utils/bash/ParsedCommand.js +241 -0
  1474. package/dist/src/utils/bash/ShellSnapshot.js +489 -0
  1475. package/dist/src/utils/bash/ast.js +2590 -0
  1476. package/dist/src/utils/bash/bashParser.js +4355 -0
  1477. package/dist/src/utils/bash/bashPipeCommand.js +249 -0
  1478. package/dist/src/utils/bash/commands.js +1131 -0
  1479. package/dist/src/utils/bash/heredoc.js +647 -0
  1480. package/dist/src/utils/bash/parser.js +195 -0
  1481. package/dist/src/utils/bash/prefix.js +154 -0
  1482. package/dist/src/utils/bash/registry.js +23 -0
  1483. package/dist/src/utils/bash/shellCompletion.js +196 -0
  1484. package/dist/src/utils/bash/shellPrefix.js +25 -0
  1485. package/dist/src/utils/bash/shellQuote.js +253 -0
  1486. package/dist/src/utils/bash/shellQuoting.js +106 -0
  1487. package/dist/src/utils/bash/specs/alias.js +11 -0
  1488. package/dist/src/utils/bash/specs/index.js +16 -0
  1489. package/dist/src/utils/bash/specs/nohup.js +10 -0
  1490. package/dist/src/utils/bash/specs/pyright.js +88 -0
  1491. package/dist/src/utils/bash/specs/sleep.js +10 -0
  1492. package/dist/src/utils/bash/specs/srun.js +28 -0
  1493. package/dist/src/utils/bash/specs/time.js +10 -0
  1494. package/dist/src/utils/bash/specs/timeout.js +17 -0
  1495. package/dist/src/utils/bash/treeSitterAnalysis.js +407 -0
  1496. package/dist/src/utils/betas.js +332 -0
  1497. package/dist/src/utils/billing.js +54 -0
  1498. package/dist/src/utils/binaryCheck.js +40 -0
  1499. package/dist/src/utils/browser.js +58 -0
  1500. package/dist/src/utils/bufferedWriter.js +77 -0
  1501. package/dist/src/utils/bundledMode.js +19 -0
  1502. package/dist/src/utils/caCerts.js +93 -0
  1503. package/dist/src/utils/caCertsConfig.js +77 -0
  1504. package/dist/src/utils/cachePaths.js +28 -0
  1505. package/dist/src/utils/ccshareResume.js +16 -0
  1506. package/dist/src/utils/classifierApprovals.js +66 -0
  1507. package/dist/src/utils/classifierApprovalsHook.js +10 -0
  1508. package/dist/src/utils/claudeCodeHints.js +142 -0
  1509. package/dist/src/utils/claudeDesktop.js +108 -0
  1510. package/dist/src/utils/claudeInChrome/chromeNativeHost.js +416 -0
  1511. package/dist/src/utils/claudeInChrome/common.js +466 -0
  1512. package/dist/src/utils/claudeInChrome/mcpServer.js +237 -0
  1513. package/dist/src/utils/claudeInChrome/prompt.js +79 -0
  1514. package/dist/src/utils/claudeInChrome/setup.js +320 -0
  1515. package/dist/src/utils/claudeInChrome/setupPortable.js +172 -0
  1516. package/dist/src/utils/claudeInChrome/toolRendering.js +234 -0
  1517. package/dist/src/utils/claudemd.js +1054 -0
  1518. package/dist/src/utils/cleanup.js +514 -0
  1519. package/dist/src/utils/cleanupRegistry.js +22 -0
  1520. package/dist/src/utils/cliArgs.js +53 -0
  1521. package/dist/src/utils/cliHighlight.js +45 -0
  1522. package/dist/src/utils/codeIndexing.js +149 -0
  1523. package/dist/src/utils/collapseBackgroundBashNotifications.js +70 -0
  1524. package/dist/src/utils/collapseHookSummaries.js +48 -0
  1525. package/dist/src/utils/collapseReadSearch.js +871 -0
  1526. package/dist/src/utils/collapseTeammateShutdowns.js +44 -0
  1527. package/dist/src/utils/combinedAbortSignal.js +40 -0
  1528. package/dist/src/utils/commandLifecycle.js +7 -0
  1529. package/dist/src/utils/commitAttribution.js +720 -0
  1530. package/dist/src/utils/completionCache.js +138 -0
  1531. package/dist/src/utils/computerUse/appNames.js +170 -0
  1532. package/dist/src/utils/computerUse/cleanup.js +66 -0
  1533. package/dist/src/utils/computerUse/common.js +56 -0
  1534. package/dist/src/utils/computerUse/computerUseLock.js +183 -0
  1535. package/dist/src/utils/computerUse/drainRunLoop.js +71 -0
  1536. package/dist/src/utils/computerUse/escHotkey.js +53 -0
  1537. package/dist/src/utils/computerUse/executor.js +480 -0
  1538. package/dist/src/utils/computerUse/gates.js +55 -0
  1539. package/dist/src/utils/computerUse/hostAdapter.js +62 -0
  1540. package/dist/src/utils/computerUse/inputLoader.js +27 -0
  1541. package/dist/src/utils/computerUse/mcpServer.js +84 -0
  1542. package/dist/src/utils/computerUse/setup.js +42 -0
  1543. package/dist/src/utils/computerUse/swiftLoader.js +20 -0
  1544. package/dist/src/utils/computerUse/toolRendering.js +100 -0
  1545. package/dist/src/utils/computerUse/wrapper.js +318 -0
  1546. package/dist/src/utils/concurrentSessions.js +179 -0
  1547. package/dist/src/utils/config.js +1084 -0
  1548. package/dist/src/utils/configConstants.js +18 -0
  1549. package/dist/src/utils/contentArray.js +45 -0
  1550. package/dist/src/utils/context.js +185 -0
  1551. package/dist/src/utils/contextAnalysis.js +171 -0
  1552. package/dist/src/utils/contextSuggestions.js +158 -0
  1553. package/dist/src/utils/controlMessageCompat.js +31 -0
  1554. package/dist/src/utils/conversationRecovery.js +436 -0
  1555. package/dist/src/utils/cron.js +260 -0
  1556. package/dist/src/utils/cronJitterConfig.js +62 -0
  1557. package/dist/src/utils/cronScheduler.js +388 -0
  1558. package/dist/src/utils/cronTasks.js +332 -0
  1559. package/dist/src/utils/cronTasksLock.js +164 -0
  1560. package/dist/src/utils/crossProjectResume.js +46 -0
  1561. package/dist/src/utils/crypto.js +13 -0
  1562. package/dist/src/utils/cwd.js +29 -0
  1563. package/dist/src/utils/databaseMcp/common.js +48 -0
  1564. package/dist/src/utils/databaseMcp/mcpServer.js +2 -0
  1565. package/dist/src/utils/databaseMcp/server/connection.js +243 -0
  1566. package/dist/src/utils/databaseMcp/server/index.js +1442 -0
  1567. package/dist/src/utils/databaseMcp/server/queries.js +683 -0
  1568. package/dist/src/utils/databaseMcp/server/types.js +7 -0
  1569. package/dist/src/utils/databaseMcp/setup.js +28 -0
  1570. package/dist/src/utils/debug.js +220 -0
  1571. package/dist/src/utils/debugFilter.js +125 -0
  1572. package/dist/src/utils/deepLink/banner.js +103 -0
  1573. package/dist/src/utils/deepLink/parseDeepLink.js +138 -0
  1574. package/dist/src/utils/deepLink/protocolHandler.js +119 -0
  1575. package/dist/src/utils/deepLink/registerProtocol.js +291 -0
  1576. package/dist/src/utils/deepLink/terminalLauncher.js +455 -0
  1577. package/dist/src/utils/deepLink/terminalPreference.js +51 -0
  1578. package/dist/src/utils/desktopDeepLink.js +208 -0
  1579. package/dist/src/utils/detectRepository.js +157 -0
  1580. package/dist/src/utils/diagLogs.js +74 -0
  1581. package/dist/src/utils/diff.js +108 -0
  1582. package/dist/src/utils/directMemberMessage.js +34 -0
  1583. package/dist/src/utils/displayTags.js +46 -0
  1584. package/dist/src/utils/doctorContextWarnings.js +179 -0
  1585. package/dist/src/utils/doctorDiagnostic.js +495 -0
  1586. package/dist/src/utils/dxt/helpers.js +64 -0
  1587. package/dist/src/utils/dxt/zip.js +167 -0
  1588. package/dist/src/utils/earlyInput.js +166 -0
  1589. package/dist/src/utils/editor.js +163 -0
  1590. package/dist/src/utils/effort.js +278 -0
  1591. package/dist/src/utils/embeddedTools.js +26 -0
  1592. package/dist/src/utils/env.js +358 -0
  1593. package/dist/src/utils/envDynamic.js +130 -0
  1594. package/dist/src/utils/envUtils.js +192 -0
  1595. package/dist/src/utils/envValidation.js +26 -0
  1596. package/dist/src/utils/errorLogSink.js +197 -0
  1597. package/dist/src/utils/errors.js +207 -0
  1598. package/dist/src/utils/eventLoopStallDetector.js +16 -0
  1599. package/dist/src/utils/exampleCommands.js +165 -0
  1600. package/dist/src/utils/execFileNoThrow.js +93 -0
  1601. package/dist/src/utils/execFileNoThrowPortable.js +111 -0
  1602. package/dist/src/utils/execSyncWrapper.js +68 -0
  1603. package/dist/src/utils/exportRenderer.js +71 -0
  1604. package/dist/src/utils/extraUsage.js +19 -0
  1605. package/dist/src/utils/fastMode.js +393 -0
  1606. package/dist/src/utils/file.js +467 -0
  1607. package/dist/src/utils/fileHistory.js +851 -0
  1608. package/dist/src/utils/fileOperationAnalytics.js +45 -0
  1609. package/dist/src/utils/filePersistence/filePersistence.js +212 -0
  1610. package/dist/src/utils/filePersistence/outputsScanner.js +104 -0
  1611. package/dist/src/utils/filePersistence/types.js +5 -0
  1612. package/dist/src/utils/fileRead.js +81 -0
  1613. package/dist/src/utils/fileReadCache.js +78 -0
  1614. package/dist/src/utils/fileStateCache.js +99 -0
  1615. package/dist/src/utils/findExecutable.js +13 -0
  1616. package/dist/src/utils/fingerprint.js +59 -0
  1617. package/dist/src/utils/forkedAgent.js +410 -0
  1618. package/dist/src/utils/format.js +238 -0
  1619. package/dist/src/utils/formatBriefTimestamp.js +72 -0
  1620. package/dist/src/utils/fpsTracker.js +34 -0
  1621. package/dist/src/utils/frontmatterParser.js +260 -0
  1622. package/dist/src/utils/fsOperations.js +834 -0
  1623. package/dist/src/utils/fullscreen.js +194 -0
  1624. package/dist/src/utils/generatedFiles.js +122 -0
  1625. package/dist/src/utils/generators.js +67 -0
  1626. package/dist/src/utils/genericProcessUtils.js +155 -0
  1627. package/dist/src/utils/getWorktreePaths.js +56 -0
  1628. package/dist/src/utils/getWorktreePathsPortable.js +23 -0
  1629. package/dist/src/utils/ghPrStatus.js +71 -0
  1630. package/dist/src/utils/git/gitConfigParser.js +226 -0
  1631. package/dist/src/utils/git/gitFilesystem.js +606 -0
  1632. package/dist/src/utils/git/gitignore.js +84 -0
  1633. package/dist/src/utils/git.js +725 -0
  1634. package/dist/src/utils/gitDiff.js +395 -0
  1635. package/dist/src/utils/gitSettings.js +18 -0
  1636. package/dist/src/utils/github/ghAuthStatus.js +23 -0
  1637. package/dist/src/utils/githubRepoPathMapping.js +135 -0
  1638. package/dist/src/utils/glob.js +90 -0
  1639. package/dist/src/utils/gracefulShutdown.js +447 -0
  1640. package/dist/src/utils/groupToolUses.js +126 -0
  1641. package/dist/src/utils/handlePromptSubmit.js +398 -0
  1642. package/dist/src/utils/hash.js +46 -0
  1643. package/dist/src/utils/headlessProfiler.js +147 -0
  1644. package/dist/src/utils/heapDumpService.js +202 -0
  1645. package/dist/src/utils/heatmap.js +151 -0
  1646. package/dist/src/utils/highlightMatch.js +28 -0
  1647. package/dist/src/utils/hooks/AsyncHookRegistry.js +187 -0
  1648. package/dist/src/utils/hooks/apiQueryHookHelper.js +77 -0
  1649. package/dist/src/utils/hooks/execAgentHook.js +257 -0
  1650. package/dist/src/utils/hooks/execHttpHook.js +184 -0
  1651. package/dist/src/utils/hooks/execPromptHook.js +171 -0
  1652. package/dist/src/utils/hooks/fileChangedWatcher.js +161 -0
  1653. package/dist/src/utils/hooks/hookEvents.js +111 -0
  1654. package/dist/src/utils/hooks/hookHelpers.js +60 -0
  1655. package/dist/src/utils/hooks/hooksConfigManager.js +323 -0
  1656. package/dist/src/utils/hooks/hooksConfigSnapshot.js +114 -0
  1657. package/dist/src/utils/hooks/hooksSettings.js +204 -0
  1658. package/dist/src/utils/hooks/postSamplingHooks.js +39 -0
  1659. package/dist/src/utils/hooks/registerFrontmatterHooks.js +47 -0
  1660. package/dist/src/utils/hooks/registerSkillHooks.js +40 -0
  1661. package/dist/src/utils/hooks/sessionHooks.js +252 -0
  1662. package/dist/src/utils/hooks/skillImprovement.js +211 -0
  1663. package/dist/src/utils/hooks/ssrfGuard.js +258 -0
  1664. package/dist/src/utils/hooks.js +3668 -0
  1665. package/dist/src/utils/horizontalScroll.js +108 -0
  1666. package/dist/src/utils/http.js +121 -0
  1667. package/dist/src/utils/hyperlink.js +28 -0
  1668. package/dist/src/utils/iTermBackup.js +48 -0
  1669. package/dist/src/utils/ide.js +1235 -0
  1670. package/dist/src/utils/idePathConversion.js +66 -0
  1671. package/dist/src/utils/idleTimeout.js +44 -0
  1672. package/dist/src/utils/imagePaste.js +329 -0
  1673. package/dist/src/utils/imageResizer.js +664 -0
  1674. package/dist/src/utils/imageStore.js +150 -0
  1675. package/dist/src/utils/imageValidation.js +92 -0
  1676. package/dist/src/utils/immediateCommand.js +12 -0
  1677. package/dist/src/utils/inProcessTeammateHelpers.js +71 -0
  1678. package/dist/src/utils/ink.js +20 -0
  1679. package/dist/src/utils/intl.js +83 -0
  1680. package/dist/src/utils/jetbrains.js +152 -0
  1681. package/dist/src/utils/json.js +295 -0
  1682. package/dist/src/utils/jsonRead.js +14 -0
  1683. package/dist/src/utils/keyboardShortcuts.js +11 -0
  1684. package/dist/src/utils/lazySchema.js +8 -0
  1685. package/dist/src/utils/listSessionsImpl.js +332 -0
  1686. package/dist/src/utils/localInstaller.js +131 -0
  1687. package/dist/src/utils/lockfile.js +22 -0
  1688. package/dist/src/utils/log.js +280 -0
  1689. package/dist/src/utils/logoV2Utils.js +290 -0
  1690. package/dist/src/utils/mailbox.js +50 -0
  1691. package/dist/src/utils/managedEnv.js +160 -0
  1692. package/dist/src/utils/managedEnvConstants.js +200 -0
  1693. package/dist/src/utils/markdown.js +300 -0
  1694. package/dist/src/utils/markdownConfigLoader.js +494 -0
  1695. package/dist/src/utils/mcp/dateTimeParser.js +102 -0
  1696. package/dist/src/utils/mcp/elicitationValidation.js +259 -0
  1697. package/dist/src/utils/mcpInstructionsDelta.js +97 -0
  1698. package/dist/src/utils/mcpOutputStorage.js +159 -0
  1699. package/dist/src/utils/mcpValidation.js +165 -0
  1700. package/dist/src/utils/mcpWebSocketTransport.js +180 -0
  1701. package/dist/src/utils/memoize.js +205 -0
  1702. package/dist/src/utils/memory/types.js +9 -0
  1703. package/dist/src/utils/memory/versions.js +7 -0
  1704. package/dist/src/utils/memoryFileDetection.js +247 -0
  1705. package/dist/src/utils/messagePredicates.js +6 -0
  1706. package/dist/src/utils/messageQueueManager.js +430 -0
  1707. package/dist/src/utils/messages/mappers.js +240 -0
  1708. package/dist/src/utils/messages/systemInit.js +74 -0
  1709. package/dist/src/utils/messages.js +4273 -0
  1710. package/dist/src/utils/model/agent.js +128 -0
  1711. package/dist/src/utils/model/aliases.js +21 -0
  1712. package/dist/src/utils/model/antModels.js +25 -0
  1713. package/dist/src/utils/model/bedrock.js +220 -0
  1714. package/dist/src/utils/model/check1mAccess.js +64 -0
  1715. package/dist/src/utils/model/configs.js +93 -0
  1716. package/dist/src/utils/model/contextWindowUpgradeCheck.js +41 -0
  1717. package/dist/src/utils/model/deprecation.js +72 -0
  1718. package/dist/src/utils/model/model.js +641 -0
  1719. package/dist/src/utils/model/modelAllowlist.js +148 -0
  1720. package/dist/src/utils/model/modelCapabilities.js +107 -0
  1721. package/dist/src/utils/model/modelOptions.js +645 -0
  1722. package/dist/src/utils/model/modelStrings.js +144 -0
  1723. package/dist/src/utils/model/modelSupportOverrides.js +40 -0
  1724. package/dist/src/utils/model/openrouter.js +51 -0
  1725. package/dist/src/utils/model/providerBaseUrls.js +77 -0
  1726. package/dist/src/utils/model/providerCatalog.js +81 -0
  1727. package/dist/src/utils/model/providerModels.js +334 -0
  1728. package/dist/src/utils/model/providerProfiles.js +392 -0
  1729. package/dist/src/utils/model/providerProfilesDb.js +886 -0
  1730. package/dist/src/utils/model/providerSwitch.js +50 -0
  1731. package/dist/src/utils/model/providerWorkspaces.js +36 -0
  1732. package/dist/src/utils/model/providers.js +199 -0
  1733. package/dist/src/utils/model/validateModel.js +257 -0
  1734. package/dist/src/utils/modelCost.js +160 -0
  1735. package/dist/src/utils/modifiers.js +35 -0
  1736. package/dist/src/utils/mtls.js +134 -0
  1737. package/dist/src/utils/nativeInstaller/download.js +370 -0
  1738. package/dist/src/utils/nativeInstaller/index.js +8 -0
  1739. package/dist/src/utils/nativeInstaller/installer.js +1396 -0
  1740. package/dist/src/utils/nativeInstaller/packageManagers.js +258 -0
  1741. package/dist/src/utils/nativeInstaller/pidLock.js +347 -0
  1742. package/dist/src/utils/notebook.js +176 -0
  1743. package/dist/src/utils/objectGroupBy.js +15 -0
  1744. package/dist/src/utils/orchestration/store/index.js +6 -0
  1745. package/dist/src/utils/orchestration/store/orchestrationDb.js +42 -0
  1746. package/dist/src/utils/orchestration/store/providerAgentStore.js +244 -0
  1747. package/dist/src/utils/orchestration/store/providerWorkspaceStore.js +125 -0
  1748. package/dist/src/utils/orchestration/store/runStore.js +486 -0
  1749. package/dist/src/utils/orchestration/store/teamStore.js +285 -0
  1750. package/dist/src/utils/orchestration/store/types.js +1 -0
  1751. package/dist/src/utils/pasteStore.js +93 -0
  1752. package/dist/src/utils/path.js +140 -0
  1753. package/dist/src/utils/pdf.js +236 -0
  1754. package/dist/src/utils/pdfUtils.js +61 -0
  1755. package/dist/src/utils/peerAddress.js +20 -0
  1756. package/dist/src/utils/permissions/PermissionMode.js +95 -0
  1757. package/dist/src/utils/permissions/PermissionPromptToolResultSchema.js +85 -0
  1758. package/dist/src/utils/permissions/PermissionResult.js +11 -0
  1759. package/dist/src/utils/permissions/PermissionRule.js +19 -0
  1760. package/dist/src/utils/permissions/PermissionUpdate.js +330 -0
  1761. package/dist/src/utils/permissions/PermissionUpdateSchema.js +61 -0
  1762. package/dist/src/utils/permissions/autoModeState.js +34 -0
  1763. package/dist/src/utils/permissions/bashClassifier.js +30 -0
  1764. package/dist/src/utils/permissions/bypassPermissionsKillswitch.js +115 -0
  1765. package/dist/src/utils/permissions/classifierDecision.js +88 -0
  1766. package/dist/src/utils/permissions/classifierShared.js +28 -0
  1767. package/dist/src/utils/permissions/dangerousPatterns.js +78 -0
  1768. package/dist/src/utils/permissions/denialTracking.js +34 -0
  1769. package/dist/src/utils/permissions/filesystem.js +1426 -0
  1770. package/dist/src/utils/permissions/getNextPermissionMode.js +74 -0
  1771. package/dist/src/utils/permissions/pathValidation.js +351 -0
  1772. package/dist/src/utils/permissions/permissionExplainer.js +188 -0
  1773. package/dist/src/utils/permissions/permissionRuleParser.js +177 -0
  1774. package/dist/src/utils/permissions/permissionSetup.js +1164 -0
  1775. package/dist/src/utils/permissions/permissions.js +1106 -0
  1776. package/dist/src/utils/permissions/permissionsDb.js +322 -0
  1777. package/dist/src/utils/permissions/permissionsLoader.js +217 -0
  1778. package/dist/src/utils/permissions/shadowedRuleDetection.js +149 -0
  1779. package/dist/src/utils/permissions/shellRuleMatching.js +174 -0
  1780. package/dist/src/utils/permissions/yoloClassifier.js +1195 -0
  1781. package/dist/src/utils/planModeV2.js +75 -0
  1782. package/dist/src/utils/plans.js +334 -0
  1783. package/dist/src/utils/platform.js +122 -0
  1784. package/dist/src/utils/plugins/addDirPluginSettings.js +53 -0
  1785. package/dist/src/utils/plugins/cacheUtils.js +174 -0
  1786. package/dist/src/utils/plugins/dependencyResolver.js +244 -0
  1787. package/dist/src/utils/plugins/fetchTelemetry.js +108 -0
  1788. package/dist/src/utils/plugins/gitAvailability.js +65 -0
  1789. package/dist/src/utils/plugins/headlessPluginInstall.js +136 -0
  1790. package/dist/src/utils/plugins/hintRecommendation.js +136 -0
  1791. package/dist/src/utils/plugins/installCounts.js +218 -0
  1792. package/dist/src/utils/plugins/installedPluginsManager.js +1003 -0
  1793. package/dist/src/utils/plugins/loadPluginAgents.js +219 -0
  1794. package/dist/src/utils/plugins/loadPluginCommands.js +595 -0
  1795. package/dist/src/utils/plugins/loadPluginHooks.js +239 -0
  1796. package/dist/src/utils/plugins/loadPluginOutputStyles.js +112 -0
  1797. package/dist/src/utils/plugins/lspPluginIntegration.js +293 -0
  1798. package/dist/src/utils/plugins/lspRecommendation.js +278 -0
  1799. package/dist/src/utils/plugins/managedPlugins.js +26 -0
  1800. package/dist/src/utils/plugins/marketplaceHelpers.js +470 -0
  1801. package/dist/src/utils/plugins/marketplaceManager.js +1939 -0
  1802. package/dist/src/utils/plugins/mcpPluginIntegration.js +465 -0
  1803. package/dist/src/utils/plugins/mcpbHandler.js +708 -0
  1804. package/dist/src/utils/plugins/officialMarketplace.js +21 -0
  1805. package/dist/src/utils/plugins/officialMarketplaceGcs.js +195 -0
  1806. package/dist/src/utils/plugins/officialMarketplaceStartupCheck.js +338 -0
  1807. package/dist/src/utils/plugins/orphanedPluginFilter.js +96 -0
  1808. package/dist/src/utils/plugins/parseMarketplaceInput.js +143 -0
  1809. package/dist/src/utils/plugins/performStartupChecks.js +66 -0
  1810. package/dist/src/utils/plugins/pluginAutoupdate.js +210 -0
  1811. package/dist/src/utils/plugins/pluginBlocklist.js +93 -0
  1812. package/dist/src/utils/plugins/pluginDirectories.js +172 -0
  1813. package/dist/src/utils/plugins/pluginFlagging.js +173 -0
  1814. package/dist/src/utils/plugins/pluginIdentifier.js +78 -0
  1815. package/dist/src/utils/plugins/pluginInstallationHelpers.js +400 -0
  1816. package/dist/src/utils/plugins/pluginLoader.js +2426 -0
  1817. package/dist/src/utils/plugins/pluginOptionsStorage.js +311 -0
  1818. package/dist/src/utils/plugins/pluginPolicy.js +18 -0
  1819. package/dist/src/utils/plugins/pluginStartupCheck.js +261 -0
  1820. package/dist/src/utils/plugins/pluginVersioning.js +128 -0
  1821. package/dist/src/utils/plugins/reconciler.js +181 -0
  1822. package/dist/src/utils/plugins/refresh.js +162 -0
  1823. package/dist/src/utils/plugins/schemas.js +1283 -0
  1824. package/dist/src/utils/plugins/validatePlugin.js +765 -0
  1825. package/dist/src/utils/plugins/walkPluginMarkdown.js +49 -0
  1826. package/dist/src/utils/plugins/zipCache.js +346 -0
  1827. package/dist/src/utils/plugins/zipCacheAdapters.js +133 -0
  1828. package/dist/src/utils/postCommitAttribution.js +16 -0
  1829. package/dist/src/utils/powershell/dangerousCmdlets.js +174 -0
  1830. package/dist/src/utils/powershell/parser.js +1357 -0
  1831. package/dist/src/utils/powershell/staticPrefix.js +277 -0
  1832. package/dist/src/utils/preflightChecks.js +147 -0
  1833. package/dist/src/utils/privacyLevel.js +49 -0
  1834. package/dist/src/utils/process.js +56 -0
  1835. package/dist/src/utils/processUserInput/processBashCommand.js +119 -0
  1836. package/dist/src/utils/processUserInput/processSlashCommand.js +859 -0
  1837. package/dist/src/utils/processUserInput/processTextPrompt.js +68 -0
  1838. package/dist/src/utils/processUserInput/processUserInput.js +326 -0
  1839. package/dist/src/utils/profilerBase.js +29 -0
  1840. package/dist/src/utils/promptCategory.js +39 -0
  1841. package/dist/src/utils/promptEditor.js +151 -0
  1842. package/dist/src/utils/promptShellExecution.js +119 -0
  1843. package/dist/src/utils/proxy.js +347 -0
  1844. package/dist/src/utils/queryContext.js +110 -0
  1845. package/dist/src/utils/queryHelpers.js +436 -0
  1846. package/dist/src/utils/queryProfiler.js +242 -0
  1847. package/dist/src/utils/queueProcessor.js +70 -0
  1848. package/dist/src/utils/readEditContext.js +176 -0
  1849. package/dist/src/utils/readFileInRange.js +278 -0
  1850. package/dist/src/utils/releaseNotes.js +304 -0
  1851. package/dist/src/utils/renderOptions.js +67 -0
  1852. package/dist/src/utils/ripgrep.js +540 -0
  1853. package/dist/src/utils/sandbox/sandbox-adapter.js +751 -0
  1854. package/dist/src/utils/sandbox/sandbox-ui-utils.js +11 -0
  1855. package/dist/src/utils/sanitization.js +72 -0
  1856. package/dist/src/utils/screenshotClipboard.js +89 -0
  1857. package/dist/src/utils/sdkEventQueue.js +49 -0
  1858. package/dist/src/utils/sdkHeapDumpMonitor.js +16 -0
  1859. package/dist/src/utils/secureStorage/fallbackStorage.js +59 -0
  1860. package/dist/src/utils/secureStorage/index.js +14 -0
  1861. package/dist/src/utils/secureStorage/keychainPrefetch.js +91 -0
  1862. package/dist/src/utils/secureStorage/macOsKeychainHelpers.js +91 -0
  1863. package/dist/src/utils/secureStorage/macOsKeychainStorage.js +192 -0
  1864. package/dist/src/utils/secureStorage/plainTextStorage.js +81 -0
  1865. package/dist/src/utils/secureStorage/secureStoreDefs.js +1 -0
  1866. package/dist/src/utils/secureStorage/sqliteStorage.js +224 -0
  1867. package/dist/src/utils/secureStorage/types.js +1 -0
  1868. package/dist/src/utils/semanticBoolean.js +23 -0
  1869. package/dist/src/utils/semanticNumber.js +34 -0
  1870. package/dist/src/utils/semver.js +53 -0
  1871. package/dist/src/utils/sequential.js +43 -0
  1872. package/dist/src/utils/sessionActivity.js +120 -0
  1873. package/dist/src/utils/sessionDataUploader.js +16 -0
  1874. package/dist/src/utils/sessionEnvVars.js +18 -0
  1875. package/dist/src/utils/sessionEnvironment.js +131 -0
  1876. package/dist/src/utils/sessionFileAccessHooks.js +207 -0
  1877. package/dist/src/utils/sessionIngressAuth.js +113 -0
  1878. package/dist/src/utils/sessionRestore.js +359 -0
  1879. package/dist/src/utils/sessionStart.js +165 -0
  1880. package/dist/src/utils/sessionState.js +76 -0
  1881. package/dist/src/utils/sessionStorage.js +4162 -0
  1882. package/dist/src/utils/sessionStoragePortable.js +665 -0
  1883. package/dist/src/utils/sessionTitle.js +120 -0
  1884. package/dist/src/utils/sessionUrl.js +50 -0
  1885. package/dist/src/utils/set.js +50 -0
  1886. package/dist/src/utils/settings/allErrors.js +29 -0
  1887. package/dist/src/utils/settings/applySettingsChange.js +65 -0
  1888. package/dist/src/utils/settings/changeDetector.js +409 -0
  1889. package/dist/src/utils/settings/constants.js +166 -0
  1890. package/dist/src/utils/settings/internalWrites.js +33 -0
  1891. package/dist/src/utils/settings/managedPath.js +29 -0
  1892. package/dist/src/utils/settings/mdm/constants.js +62 -0
  1893. package/dist/src/utils/settings/mdm/rawRead.js +97 -0
  1894. package/dist/src/utils/settings/mdm/settings.js +254 -0
  1895. package/dist/src/utils/settings/permissionValidation.js +224 -0
  1896. package/dist/src/utils/settings/pluginOnlyPolicy.js +53 -0
  1897. package/dist/src/utils/settings/schemaOutput.js +7 -0
  1898. package/dist/src/utils/settings/settings.js +844 -0
  1899. package/dist/src/utils/settings/settingsCache.js +47 -0
  1900. package/dist/src/utils/settings/toolValidationConfig.js +76 -0
  1901. package/dist/src/utils/settings/types.js +846 -0
  1902. package/dist/src/utils/settings/validateEditTool.js +34 -0
  1903. package/dist/src/utils/settings/validation.js +192 -0
  1904. package/dist/src/utils/settings/validationTips.js +111 -0
  1905. package/dist/src/utils/shell/bashProvider.js +202 -0
  1906. package/dist/src/utils/shell/outputLimits.js +7 -0
  1907. package/dist/src/utils/shell/powershellDetection.js +96 -0
  1908. package/dist/src/utils/shell/powershellProvider.js +104 -0
  1909. package/dist/src/utils/shell/prefix.js +246 -0
  1910. package/dist/src/utils/shell/readOnlyCommandValidation.js +1776 -0
  1911. package/dist/src/utils/shell/resolveDefaultShell.js +13 -0
  1912. package/dist/src/utils/shell/shellProvider.js +2 -0
  1913. package/dist/src/utils/shell/shellToolUtils.js +21 -0
  1914. package/dist/src/utils/shell/specPrefix.js +198 -0
  1915. package/dist/src/utils/shellConfig.js +136 -0
  1916. package/dist/src/utils/sideQuery.js +195 -0
  1917. package/dist/src/utils/sideQuestion.js +121 -0
  1918. package/dist/src/utils/signal.js +34 -0
  1919. package/dist/src/utils/sinks.js +15 -0
  1920. package/dist/src/utils/skills/skillChangeDetector.js +266 -0
  1921. package/dist/src/utils/slashCommandParsing.js +46 -0
  1922. package/dist/src/utils/sleep.js +72 -0
  1923. package/dist/src/utils/sliceAnsi.js +74 -0
  1924. package/dist/src/utils/slowOperations.js +323 -0
  1925. package/dist/src/utils/standaloneAgent.js +20 -0
  1926. package/dist/src/utils/startupProfiler.js +158 -0
  1927. package/dist/src/utils/staticRender.js +103 -0
  1928. package/dist/src/utils/stats.js +802 -0
  1929. package/dist/src/utils/statsCache.js +330 -0
  1930. package/dist/src/utils/status.js +552 -0
  1931. package/dist/src/utils/statusNoticeDefinitions.js +112 -0
  1932. package/dist/src/utils/statusNoticeHelpers.js +15 -0
  1933. package/dist/src/utils/stream.js +73 -0
  1934. package/dist/src/utils/streamJsonStdoutGuard.js +107 -0
  1935. package/dist/src/utils/streamlinedTransform.js +162 -0
  1936. package/dist/src/utils/stringUtils.js +202 -0
  1937. package/dist/src/utils/subprocessEnv.js +87 -0
  1938. package/dist/src/utils/suggestions/commandSuggestions.js +458 -0
  1939. package/dist/src/utils/suggestions/directoryCompletion.js +191 -0
  1940. package/dist/src/utils/suggestions/shellHistoryCompletion.js +95 -0
  1941. package/dist/src/utils/suggestions/skillUsageTracking.js +50 -0
  1942. package/dist/src/utils/suggestions/slackChannelSuggestions.js +169 -0
  1943. package/dist/src/utils/swarm/It2SetupPrompt.js +386 -0
  1944. package/dist/src/utils/swarm/backends/ITermBackend.js +276 -0
  1945. package/dist/src/utils/swarm/backends/InProcessBackend.js +237 -0
  1946. package/dist/src/utils/swarm/backends/PaneBackendExecutor.js +250 -0
  1947. package/dist/src/utils/swarm/backends/TmuxBackend.js +574 -0
  1948. package/dist/src/utils/swarm/backends/detection.js +112 -0
  1949. package/dist/src/utils/swarm/backends/it2Setup.js +185 -0
  1950. package/dist/src/utils/swarm/backends/registry.js +369 -0
  1951. package/dist/src/utils/swarm/backends/teammateModeSnapshot.js +68 -0
  1952. package/dist/src/utils/swarm/backends/types.js +9 -0
  1953. package/dist/src/utils/swarm/constants.js +29 -0
  1954. package/dist/src/utils/swarm/inProcessRunner.js +1021 -0
  1955. package/dist/src/utils/swarm/leaderPermissionBridge.js +31 -0
  1956. package/dist/src/utils/swarm/permissionSync.js +667 -0
  1957. package/dist/src/utils/swarm/reconnection.js +82 -0
  1958. package/dist/src/utils/swarm/spawnInProcess.js +218 -0
  1959. package/dist/src/utils/swarm/spawnUtils.js +123 -0
  1960. package/dist/src/utils/swarm/teamHelpers.js +484 -0
  1961. package/dist/src/utils/swarm/teammateInit.js +87 -0
  1962. package/dist/src/utils/swarm/teammateLayoutManager.js +82 -0
  1963. package/dist/src/utils/swarm/teammateModel.js +9 -0
  1964. package/dist/src/utils/swarm/teammatePromptAddendum.js +17 -0
  1965. package/dist/src/utils/systemDirectories.js +51 -0
  1966. package/dist/src/utils/systemPrompt.js +91 -0
  1967. package/dist/src/utils/systemPromptType.js +9 -0
  1968. package/dist/src/utils/systemTheme.js +108 -0
  1969. package/dist/src/utils/systemThemeWatcher.js +16 -0
  1970. package/dist/src/utils/taggedId.js +49 -0
  1971. package/dist/src/utils/task/TaskOutput.js +320 -0
  1972. package/dist/src/utils/task/diskOutput.js +387 -0
  1973. package/dist/src/utils/task/framework.js +236 -0
  1974. package/dist/src/utils/task/outputFormatting.js +24 -0
  1975. package/dist/src/utils/task/sdkProgress.js +24 -0
  1976. package/dist/src/utils/tasks.js +672 -0
  1977. package/dist/src/utils/teamDiscovery.js +48 -0
  1978. package/dist/src/utils/teamMemoryOps.js +67 -0
  1979. package/dist/src/utils/teammate.js +237 -0
  1980. package/dist/src/utils/teammateContext.js +56 -0
  1981. package/dist/src/utils/teammateMailbox.js +793 -0
  1982. package/dist/src/utils/telemetry/betaSessionTracing.js +371 -0
  1983. package/dist/src/utils/telemetry/bigqueryExporter.js +181 -0
  1984. package/dist/src/utils/telemetry/events.js +57 -0
  1985. package/dist/src/utils/telemetry/instrumentation.js +617 -0
  1986. package/dist/src/utils/telemetry/logger.js +25 -0
  1987. package/dist/src/utils/telemetry/perfettoTracing.js +882 -0
  1988. package/dist/src/utils/telemetry/pluginTelemetry.js +157 -0
  1989. package/dist/src/utils/telemetry/sessionTracing.js +693 -0
  1990. package/dist/src/utils/telemetry/skillLoadedEvent.js +26 -0
  1991. package/dist/src/utils/telemetryAttributes.js +57 -0
  1992. package/dist/src/utils/teleport/api.js +299 -0
  1993. package/dist/src/utils/teleport/environmentSelection.js +55 -0
  1994. package/dist/src/utils/teleport/environments.js +84 -0
  1995. package/dist/src/utils/teleport/gitBundle.js +192 -0
  1996. package/dist/src/utils/teleport.js +1041 -0
  1997. package/dist/src/utils/tempfile.js +26 -0
  1998. package/dist/src/utils/terminal.js +105 -0
  1999. package/dist/src/utils/terminalPanel.js +155 -0
  2000. package/dist/src/utils/textHighlighting.js +113 -0
  2001. package/dist/src/utils/theme.js +525 -0
  2002. package/dist/src/utils/thinking.js +130 -0
  2003. package/dist/src/utils/timeouts.js +35 -0
  2004. package/dist/src/utils/tmuxSocket.js +373 -0
  2005. package/dist/src/utils/todo/types.js +9 -0
  2006. package/dist/src/utils/tokenBudget.js +62 -0
  2007. package/dist/src/utils/tokens.js +223 -0
  2008. package/dist/src/utils/toolErrors.js +101 -0
  2009. package/dist/src/utils/toolPool.js +63 -0
  2010. package/dist/src/utils/toolResultStorage.js +769 -0
  2011. package/dist/src/utils/toolSchemaCache.js +7 -0
  2012. package/dist/src/utils/toolSearch.js +551 -0
  2013. package/dist/src/utils/transcriptSearch.js +200 -0
  2014. package/dist/src/utils/treeify.js +111 -0
  2015. package/dist/src/utils/truncate.js +164 -0
  2016. package/dist/src/utils/udsClient.js +16 -0
  2017. package/dist/src/utils/udsMessaging.js +16 -0
  2018. package/dist/src/utils/ultraplan/ccrSession.js +264 -0
  2019. package/dist/src/utils/ultraplan/keyword.js +122 -0
  2020. package/dist/src/utils/ultraplan/prompt.txt +1 -0
  2021. package/dist/src/utils/unaryLogging.js +16 -0
  2022. package/dist/src/utils/undercover.js +89 -0
  2023. package/dist/src/utils/user.js +138 -0
  2024. package/dist/src/utils/userAgent.js +13 -0
  2025. package/dist/src/utils/userPromptKeywords.js +21 -0
  2026. package/dist/src/utils/uuid.js +22 -0
  2027. package/dist/src/utils/warningHandler.js +97 -0
  2028. package/dist/src/utils/which.js +75 -0
  2029. package/dist/src/utils/windowsPaths.js +150 -0
  2030. package/dist/src/utils/withResolvers.js +13 -0
  2031. package/dist/src/utils/words.js +793 -0
  2032. package/dist/src/utils/workloadContext.js +42 -0
  2033. package/dist/src/utils/worktree.js +1145 -0
  2034. package/dist/src/utils/worktreeModeEnabled.js +11 -0
  2035. package/dist/src/utils/xdg.js +52 -0
  2036. package/dist/src/utils/xml.js +15 -0
  2037. package/dist/src/utils/yaml.js +16 -0
  2038. package/dist/src/utils/zodToJsonSchema.js +19 -0
  2039. package/dist/src/vim/motions.js +73 -0
  2040. package/dist/src/vim/operators.js +401 -0
  2041. package/dist/src/vim/textObjects.js +153 -0
  2042. package/dist/src/vim/transitions.js +340 -0
  2043. package/dist/src/vim/types.js +93 -0
  2044. package/dist/src/voice/voiceModeEnabled.js +48 -0
  2045. package/dist/src/whatsapp/bridge.js +267 -0
  2046. package/dist/src/whatsapp/config.js +153 -0
  2047. package/dist/src/whatsapp/markdown.js +37 -0
  2048. package/dist/src/whatsapp/mirror.js +74 -0
  2049. package/dist/src/whatsapp/session.js +142 -0
  2050. package/package.json +2 -1
@@ -0,0 +1,4434 @@
1
+ import { feature } from '../recovery/bunBundleShim.js';
2
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { createRequire } from 'module';
4
+ const require = createRequire(import.meta.url);
5
+ import { c as _c } from "react/compiler-runtime";
6
+ // biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered
7
+ import { spawnSync } from 'child_process';
8
+ import { snapshotOutputTokensForTurn, getCurrentTurnTokenBudget, getTurnOutputTokens, getBudgetContinuationCount, getTotalInputTokens } from '../bootstrap/state.js';
9
+ import { parseTokenBudget } from '../utils/tokenBudget.js';
10
+ import { count } from '../utils/array.js';
11
+ import { dirname, join } from 'path';
12
+ import { tmpdir } from 'os';
13
+ import figures from 'figures';
14
+ // eslint-disable-next-line custom-rules/prefer-use-keybindings -- / n N Esc [ v are bare letters in transcript modal context, same class as g/G/j/k in ScrollKeybindingHandler
15
+ import { useInput } from '../ink.js';
16
+ import { useSearchInput } from '../hooks/useSearchInput.js';
17
+ import { useTerminalSize } from '../hooks/useTerminalSize.js';
18
+ import { useSearchHighlight } from '../ink/hooks/use-search-highlight.js';
19
+ import { renderMessagesToPlainText } from '../utils/exportRenderer.js';
20
+ import { openFileInExternalEditor } from '../utils/editor.js';
21
+ import { writeFile } from 'fs/promises';
22
+ import { Box, Text, useStdin, useTheme, useTerminalFocus, useTerminalTitle, useTabStatus } from '../ink.js';
23
+ import { CostThresholdDialog } from '../components/CostThresholdDialog.js';
24
+ import { IdleReturnDialog } from '../components/IdleReturnDialog.js';
25
+ import * as React from 'react';
26
+ import { useEffect, useMemo, useRef, useState, useCallback, useDeferredValue, useLayoutEffect } from 'react';
27
+ import { useNotifications } from '../context/notifications.js';
28
+ import { sendNotification } from '../services/notifier.js';
29
+ import { startPreventSleep, stopPreventSleep } from '../services/preventSleep.js';
30
+ import { useTerminalNotification } from '../ink/useTerminalNotification.js';
31
+ import { hasCursorUpViewportYankBug } from '../ink/terminal.js';
32
+ import { createFileStateCacheWithSizeLimit, mergeFileStateCaches, READ_FILE_STATE_CACHE_SIZE } from '../utils/fileStateCache.js';
33
+ import { updateLastInteractionTime, getLastInteractionTime, getOriginalCwd, getProjectRoot, getSessionId, switchSession, setCostStateForRestore, getTurnHookDurationMs, getTurnHookCount, resetTurnHookDuration, getTurnToolDurationMs, getTurnToolCount, resetTurnToolDuration, getTurnClassifierDurationMs, getTurnClassifierCount, resetTurnClassifierDuration } from '../bootstrap/state.js';
34
+ import { asSessionId, asAgentId } from '../types/ids.js';
35
+ import { logForDebugging } from '../utils/debug.js';
36
+ import { QueryGuard } from '../utils/QueryGuard.js';
37
+ import { isEnvTruthy } from '../utils/envUtils.js';
38
+ import { formatTokens, truncateToWidth } from '../utils/format.js';
39
+ import { consumeEarlyInput } from '../utils/earlyInput.js';
40
+ import { setMemberActive } from '../utils/swarm/teamHelpers.js';
41
+ import { isSwarmWorker, generateSandboxRequestId, sendSandboxPermissionRequestViaMailbox, sendSandboxPermissionResponseViaMailbox } from '../utils/swarm/permissionSync.js';
42
+ import { registerSandboxPermissionCallback } from '../hooks/useSwarmPermissionPoller.js';
43
+ import { getTeamName, getAgentName } from '../utils/teammate.js';
44
+ import { WorkerPendingPermission } from '../components/permissions/WorkerPendingPermission.js';
45
+ import { injectUserMessageToTeammate, getAllInProcessTeammateTasks } from '../tasks/InProcessTeammateTask/InProcessTeammateTask.js';
46
+ import { isLocalAgentTask, queuePendingMessage, appendMessageToLocalAgent } from '../tasks/LocalAgentTask/LocalAgentTask.js';
47
+ import { registerLeaderToolUseConfirmQueue, unregisterLeaderToolUseConfirmQueue, registerLeaderSetToolPermissionContext, unregisterLeaderSetToolPermissionContext } from '../utils/swarm/leaderPermissionBridge.js';
48
+ import { endInteractionSpan } from '../utils/telemetry/sessionTracing.js';
49
+ import { useLogMessages } from '../hooks/useLogMessages.js';
50
+ import { useReplBridge } from '../hooks/useReplBridge.js';
51
+ import { useTelegramMirror } from '../hooks/useTelegramMirror.js';
52
+ import { useWhatsAppMirror } from '../hooks/useWhatsAppMirror.js';
53
+ import { getCommandName, isCommandEnabled } from '../commands.js';
54
+ import { MessageSelector, selectableUserMessagesFilter, messagesAfterAreOnlySynthetic } from '../components/MessageSelector.js';
55
+ import { useIdeLogging } from '../hooks/useIdeLogging.js';
56
+ import { PermissionRequest } from '../components/permissions/PermissionRequest.js';
57
+ import { ElicitationDialog } from '../components/mcp/ElicitationDialog.js';
58
+ import { PromptDialog } from '../components/hooks/PromptDialog.js';
59
+ import PromptInput from '../components/PromptInput/PromptInput.js';
60
+ import { PromptInputQueuedCommands } from '../components/PromptInput/PromptInputQueuedCommands.js';
61
+ import { useRemoteSession } from '../hooks/useRemoteSession.js';
62
+ import { useDirectConnect } from '../hooks/useDirectConnect.js';
63
+ import { useSSHSession } from '../hooks/useSSHSession.js';
64
+ import { useAssistantHistory } from '../hooks/useAssistantHistory.js';
65
+ import { SkillImprovementSurvey } from '../components/SkillImprovementSurvey.js';
66
+ import { useSkillImprovementSurvey } from '../hooks/useSkillImprovementSurvey.js';
67
+ import { useMoreRight } from '../moreright/useMoreRight.js';
68
+ import { SpinnerWithVerb, BriefIdleStatus } from '../components/Spinner.js';
69
+ import { getSystemPrompt } from '../constants/prompts.js';
70
+ import { buildEffectiveSystemPrompt } from '../utils/systemPrompt.js';
71
+ import { getSystemContext, getUserContext } from '../context.js';
72
+ import { getMemoryFiles } from '../utils/claudemd.js';
73
+ import { startBackgroundHousekeeping } from '../utils/backgroundHousekeeping.js';
74
+ import { getTotalCost, saveCurrentSessionCosts, resetCostState, getStoredSessionCosts } from '../cost-tracker.js';
75
+ import { useCostSummary } from '../costHook.js';
76
+ import { useFpsMetrics } from '../context/fpsMetrics.js';
77
+ import { useAfterFirstRender } from '../hooks/useAfterFirstRender.js';
78
+ import { useDeferredHookMessages } from '../hooks/useDeferredHookMessages.js';
79
+ import { addToHistory, removeLastFromHistory, expandPastedTextRefs, parseReferences } from '../history.js';
80
+ import { prependModeCharacterToInput } from '../components/PromptInput/inputModes.js';
81
+ import { prependToShellHistoryCache } from '../utils/suggestions/shellHistoryCompletion.js';
82
+ import { useApiKeyVerification } from '../hooks/useApiKeyVerification.js';
83
+ import { GlobalKeybindingHandlers } from '../hooks/useGlobalKeybindings.js';
84
+ import { CommandKeybindingHandlers } from '../hooks/useCommandKeybindings.js';
85
+ import { KeybindingSetup } from '../keybindings/KeybindingProviderSetup.js';
86
+ import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js';
87
+ import { getShortcutDisplay } from '../keybindings/shortcutFormat.js';
88
+ import { CancelRequestHandler } from '../hooks/useCancelRequest.js';
89
+ import { useBackgroundTaskNavigation } from '../hooks/useBackgroundTaskNavigation.js';
90
+ import { useSwarmInitialization } from '../hooks/useSwarmInitialization.js';
91
+ import { useTeammateViewAutoExit } from '../hooks/useTeammateViewAutoExit.js';
92
+ import { errorMessage } from '../utils/errors.js';
93
+ import { isHumanTurn } from '../utils/messagePredicates.js';
94
+ import { logError } from '../utils/log.js';
95
+ // Dead code elimination: conditional imports
96
+ /* eslint-disable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
97
+ const useVoiceIntegration = feature('VOICE_MODE') ? require('../hooks/useVoiceIntegration.js').useVoiceIntegration : () => ({
98
+ stripTrailing: () => 0,
99
+ handleKeyEvent: () => { },
100
+ resetAnchor: () => { }
101
+ });
102
+ const VoiceKeybindingHandler = feature('VOICE_MODE') ? require('../hooks/useVoiceIntegration.js').VoiceKeybindingHandler : () => null;
103
+ // Frustration detection is ant-only (dogfooding). Conditional require so external
104
+ // builds eliminate the module entirely (including its two O(n) useMemos that run
105
+ // on every messages change, plus the GrowthBook fetch).
106
+ const useFrustrationDetection = "external" === 'ant' ? require('../components/FeedbackSurvey/useFrustrationDetection.js').useFrustrationDetection : () => ({
107
+ state: 'closed',
108
+ handleTranscriptSelect: () => { }
109
+ });
110
+ // Ant-only org warning. Conditional require so the org UUID list is
111
+ // eliminated from external builds (one UUID is on excluded-strings).
112
+ const useAntOrgWarningNotification = "external" === 'ant' ? require('../hooks/notifs/useAntOrgWarningNotification.js').useAntOrgWarningNotification : () => { };
113
+ // Dead code elimination: conditional import for coordinator mode
114
+ const getCoordinatorUserContext = feature('COORDINATOR_MODE') ? require('../coordinator/coordinatorMode.js').getCoordinatorUserContext : () => ({});
115
+ /* eslint-enable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
116
+ import useCanUseTool from '../hooks/useCanUseTool.js';
117
+ import { applyPermissionUpdate, applyPermissionUpdates, persistPermissionUpdate } from '../utils/permissions/PermissionUpdate.js';
118
+ import { buildPermissionUpdates } from '../components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.js';
119
+ import { stripDangerousPermissionsForAutoMode } from '../utils/permissions/permissionSetup.js';
120
+ import { getScratchpadDir, isScratchpadEnabled } from '../utils/permissions/filesystem.js';
121
+ import { WEB_FETCH_TOOL_NAME } from '../tools/WebFetchTool/prompt.js';
122
+ import { SLEEP_TOOL_NAME } from '../tools/SleepTool/prompt.js';
123
+ import { clearSpeculativeChecks } from '../tools/BashTool/bashPermissions.js';
124
+ import { getGlobalConfig, saveGlobalConfig, getGlobalConfigWriteCount } from '../utils/config.js';
125
+ import { hasConsoleBillingAccess } from '../utils/billing.js';
126
+ import { logEvent } from '../services/analytics/index.js';
127
+ import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js';
128
+ import { textForResubmit, handleMessageFromStream, isCompactBoundaryMessage, getMessagesAfterCompactBoundary, getContentText, createUserMessage, createAssistantMessage, createTurnDurationMessage, createAgentsKilledMessage, createApiMetricsMessage, createSystemMessage, createCommandInputMessage, formatCommandInputTags } from '../utils/messages.js';
129
+ import { generateSessionTitle } from '../utils/sessionTitle.js';
130
+ import { BASH_INPUT_TAG, COMMAND_MESSAGE_TAG, COMMAND_NAME_TAG, LOCAL_COMMAND_STDOUT_TAG } from '../constants/xml.js';
131
+ import { escapeXml } from '../utils/xml.js';
132
+ import { gracefulShutdownSync } from '../utils/gracefulShutdown.js';
133
+ import { handlePromptSubmit } from '../utils/handlePromptSubmit.js';
134
+ import { useQueueProcessor } from '../hooks/useQueueProcessor.js';
135
+ import { useMailboxBridge } from '../hooks/useMailboxBridge.js';
136
+ import { queryCheckpoint, logQueryProfileReport } from '../utils/queryProfiler.js';
137
+ import { query } from '../query.js';
138
+ import { mergeClients, useMergedClients } from '../hooks/useMergedClients.js';
139
+ import { getQuerySourceForREPL } from '../utils/promptCategory.js';
140
+ import { useMergedTools } from '../hooks/useMergedTools.js';
141
+ import { mergeAndFilterTools } from '../utils/toolPool.js';
142
+ import { useMergedCommands } from '../hooks/useMergedCommands.js';
143
+ import { useSkillsChange } from '../hooks/useSkillsChange.js';
144
+ import { useManagePlugins } from '../hooks/useManagePlugins.js';
145
+ import { Messages } from '../components/Messages.js';
146
+ import { TaskListV2 } from '../components/TaskListV2.js';
147
+ import { TeammateViewHeader } from '../components/TeammateViewHeader.js';
148
+ import { useTasksV2WithCollapseEffect } from '../hooks/useTasksV2.js';
149
+ import { maybeMarkProjectOnboardingComplete } from '../projectOnboardingState.js';
150
+ import { randomUUID } from 'crypto';
151
+ import { processSessionStartHooks } from '../utils/sessionStart.js';
152
+ import { executeSessionEndHooks, getSessionEndHookTimeoutMs } from '../utils/hooks.js';
153
+ import { useIdeSelection } from '../hooks/useIdeSelection.js';
154
+ import { getTools, assembleToolPool } from '../tools.js';
155
+ import { resolveAgentTools } from '../tools/AgentTool/agentToolUtils.js';
156
+ import { resumeAgentBackground } from '../tools/AgentTool/resumeAgent.js';
157
+ import { useMainLoopModel } from '../hooks/useMainLoopModel.js';
158
+ import { useAppState, useSetAppState, useAppStateStore } from '../state/AppState.js';
159
+ import { copyPlanForFork, copyPlanForResume, getPlanSlug, setPlanSlug } from '../utils/plans.js';
160
+ import { clearSessionMetadata, resetSessionFilePointer, adoptResumedSessionFile, removeTranscriptMessage, restoreSessionMetadata, getCurrentSessionTitle, isEphemeralToolProgress, isLoggableMessage, saveWorktreeState, getAgentTranscript } from '../utils/sessionStorage.js';
161
+ import { deserializeMessages } from '../utils/conversationRecovery.js';
162
+ import { extractReadFilesFromMessages, extractBashToolsFromMessages } from '../utils/queryHelpers.js';
163
+ import { resetMicrocompactState } from '../services/compact/microCompact.js';
164
+ import { runPostCompactCleanup } from '../services/compact/postCompactCleanup.js';
165
+ import { provisionContentReplacementState, reconstructContentReplacementState } from '../utils/toolResultStorage.js';
166
+ import { partialCompactConversation } from '../services/compact/compact.js';
167
+ import { fileHistoryMakeSnapshot, fileHistoryRewind, copyFileHistoryForResume, fileHistoryEnabled, fileHistoryHasAnyChanges } from '../utils/fileHistory.js';
168
+ import { incrementPromptCount } from '../utils/commitAttribution.js';
169
+ import { recordAttributionSnapshot } from '../utils/sessionStorage.js';
170
+ import { computeStandaloneAgentContext, restoreAgentFromSession, restoreSessionStateFromLog, restoreWorktreeForResume, exitRestoredWorktree } from '../utils/sessionRestore.js';
171
+ import { isBgSession, updateSessionName, updateSessionActivity } from '../utils/concurrentSessions.js';
172
+ import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js';
173
+ import { restoreRemoteAgentTasks } from '../tasks/RemoteAgentTask/RemoteAgentTask.js';
174
+ import { useInboxPoller } from '../hooks/useInboxPoller.js';
175
+ // Dead code elimination: conditional import for loop mode
176
+ /* eslint-disable @typescript-eslint/no-require-imports */
177
+ const proactiveModule = feature('PROACTIVE') || feature('KAIROS') ? require('../proactive/index.js') : null;
178
+ const PROACTIVE_NO_OP_SUBSCRIBE = (_cb) => () => { };
179
+ const PROACTIVE_FALSE = () => false;
180
+ const SUGGEST_BG_PR_NOOP = (_p, _n) => false;
181
+ const useProactive = feature('PROACTIVE') || feature('KAIROS') ? require('../proactive/useProactive.js').useProactive : null;
182
+ const useScheduledTasks = feature('AGENT_TRIGGERS') ? require('../hooks/useScheduledTasks.js').useScheduledTasks : null;
183
+ /* eslint-enable @typescript-eslint/no-require-imports */
184
+ import { isAgentSwarmsEnabled } from '../utils/agentSwarmsEnabled.js';
185
+ import { useTaskListWatcher } from '../hooks/useTaskListWatcher.js';
186
+ import { closeOpenDiffs, getConnectedIdeClient } from '../utils/ide.js';
187
+ import { useIDEIntegration } from '../hooks/useIDEIntegration.js';
188
+ import exit from '../commands/exit/index.js';
189
+ import { ExitFlow } from '../components/ExitFlow.js';
190
+ import { getCurrentWorktreeSession } from '../utils/worktree.js';
191
+ import { popAllEditable, enqueue, getCommandQueue, getCommandQueueLength, removeByFilter } from '../utils/messageQueueManager.js';
192
+ import { useCommandQueue } from '../hooks/useCommandQueue.js';
193
+ import { SessionBackgroundHint } from '../components/SessionBackgroundHint.js';
194
+ import { startBackgroundSession } from '../tasks/LocalMainSessionTask.js';
195
+ import { useSessionBackgrounding } from '../hooks/useSessionBackgrounding.js';
196
+ import { diagnosticTracker } from '../services/diagnosticTracking.js';
197
+ import { handleSpeculationAccept } from '../services/PromptSuggestion/speculation.js';
198
+ import { IdeOnboardingDialog } from '../components/IdeOnboardingDialog.js';
199
+ import { EffortCallout, shouldShowEffortCallout } from '../components/EffortCallout.js';
200
+ import { RemoteCallout } from '../components/RemoteCallout.js';
201
+ /* eslint-disable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
202
+ const AntModelSwitchCallout = "external" === 'ant' ? require('../components/AntModelSwitchCallout.js').AntModelSwitchCallout : null;
203
+ const shouldShowAntModelSwitch = "external" === 'ant' ? require('../components/AntModelSwitchCallout.js').shouldShowModelSwitchCallout : () => false;
204
+ const UndercoverAutoCallout = "external" === 'ant' ? require('../components/UndercoverAutoCallout.js').UndercoverAutoCallout : null;
205
+ /* eslint-enable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
206
+ import { activityManager } from '../utils/activityManager.js';
207
+ import { createAbortController } from '../utils/abortController.js';
208
+ import { MCPConnectionManager } from '../services/mcp/MCPConnectionManager.js';
209
+ import { useFeedbackSurvey } from '../components/FeedbackSurvey/useFeedbackSurvey.js';
210
+ import { useMemorySurvey } from '../components/FeedbackSurvey/useMemorySurvey.js';
211
+ import { usePostCompactSurvey } from '../components/FeedbackSurvey/usePostCompactSurvey.js';
212
+ import { FeedbackSurvey } from '../components/FeedbackSurvey/FeedbackSurvey.js';
213
+ import { useInstallMessages } from '../hooks/notifs/useInstallMessages.js';
214
+ import { useAwaySummary } from '../hooks/useAwaySummary.js';
215
+ import { useChromeExtensionNotification } from '../hooks/useChromeExtensionNotification.js';
216
+ import { useOfficialMarketplaceNotification } from '../hooks/useOfficialMarketplaceNotification.js';
217
+ import { usePromptsFromClaudeInChrome } from '../hooks/usePromptsFromClaudeInChrome.js';
218
+ import { getTipToShowOnSpinner, recordShownTip } from '../services/tips/tipScheduler.js';
219
+ import { checkAndDisableBypassPermissionsIfNeeded, checkAndDisableAutoModeIfNeeded, useKickOffCheckAndDisableBypassPermissionsIfNeeded, useKickOffCheckAndDisableAutoModeIfNeeded } from '../utils/permissions/bypassPermissionsKillswitch.js';
220
+ import { SandboxManager } from '../utils/sandbox/sandbox-adapter.js';
221
+ import { SANDBOX_NETWORK_ACCESS_TOOL_NAME } from '../cli/structuredIO.js';
222
+ import { useFileHistorySnapshotInit } from '../hooks/useFileHistorySnapshotInit.js';
223
+ import { SandboxPermissionRequest } from '../components/permissions/SandboxPermissionRequest.js';
224
+ import { SandboxViolationExpandedView } from '../components/SandboxViolationExpandedView.js';
225
+ import { useSettingsErrors } from '../hooks/notifs/useSettingsErrors.js';
226
+ import { useMcpConnectivityStatus } from '../hooks/notifs/useMcpConnectivityStatus.js';
227
+ import { useAutoModeUnavailableNotification } from '../hooks/notifs/useAutoModeUnavailableNotification.js';
228
+ import { AUTO_MODE_DESCRIPTION } from '../components/AutoModeOptInDialog.js';
229
+ import { useLspInitializationNotification } from '../hooks/notifs/useLspInitializationNotification.js';
230
+ import { useLspPluginRecommendation } from '../hooks/useLspPluginRecommendation.js';
231
+ import { LspRecommendationMenu } from '../components/LspRecommendation/LspRecommendationMenu.js';
232
+ import { useClaudeCodeHintRecommendation } from '../hooks/useClaudeCodeHintRecommendation.js';
233
+ import { PluginHintMenu } from '../components/ClaudeCodeHint/PluginHintMenu.js';
234
+ import { DesktopUpsellStartup, shouldShowDesktopUpsellStartup } from '../components/DesktopUpsell/DesktopUpsellStartup.js';
235
+ import { usePluginInstallationStatus } from '../hooks/notifs/usePluginInstallationStatus.js';
236
+ import { usePluginAutoupdateNotification } from '../hooks/notifs/usePluginAutoupdateNotification.js';
237
+ import { performStartupChecks } from '../utils/plugins/performStartupChecks.js';
238
+ import { UserTextMessage } from '../components/messages/UserTextMessage.js';
239
+ import { AwsAuthStatusBox } from '../components/AwsAuthStatusBox.js';
240
+ import { useRateLimitWarningNotification } from '../hooks/notifs/useRateLimitWarningNotification.js';
241
+ import { useDeprecationWarningNotification } from '../hooks/notifs/useDeprecationWarningNotification.js';
242
+ import { useNpmDeprecationNotification } from '../hooks/notifs/useNpmDeprecationNotification.js';
243
+ import { useIDEStatusIndicator } from '../hooks/notifs/useIDEStatusIndicator.js';
244
+ import { useModelMigrationNotifications } from '../hooks/notifs/useModelMigrationNotifications.js';
245
+ import { useCanSwitchToExistingSubscription } from '../hooks/notifs/useCanSwitchToExistingSubscription.js';
246
+ import { useTeammateLifecycleNotification } from '../hooks/notifs/useTeammateShutdownNotification.js';
247
+ import { useFastModeNotification } from '../hooks/notifs/useFastModeNotification.js';
248
+ import { AutoRunIssueNotification, shouldAutoRunIssue, getAutoRunIssueReasonText, getAutoRunCommand } from '../utils/autoRunIssue.js';
249
+ import { TungstenLiveMonitor } from '../tools/TungstenTool/TungstenLiveMonitor.js';
250
+ /* eslint-disable @typescript-eslint/no-require-imports */
251
+ const WebBrowserPanelModule = feature('WEB_BROWSER_TOOL') ? require('../tools/WebBrowserTool/WebBrowserPanel.js') : null;
252
+ /* eslint-enable @typescript-eslint/no-require-imports */
253
+ import { IssueFlagBanner } from '../components/PromptInput/IssueFlagBanner.js';
254
+ import { useIssueFlagBanner } from '../hooks/useIssueFlagBanner.js';
255
+ import { CompanionSprite, CompanionFloatingBubble, MIN_COLS_FOR_FULL_SPRITE } from '../buddy/CompanionSprite.js';
256
+ import { DevBar } from '../components/DevBar.js';
257
+ import { REMOTE_SAFE_COMMANDS } from '../commands.js';
258
+ import { FullscreenLayout, useUnseenDivider, computeUnseenDivider } from '../components/FullscreenLayout.js';
259
+ import { isFullscreenEnvEnabled, maybeGetTmuxMouseHint, isMouseTrackingEnabled } from '../utils/fullscreen.js';
260
+ import { AlternateScreen } from '../ink/components/AlternateScreen.js';
261
+ import { ScrollKeybindingHandler } from '../components/ScrollKeybindingHandler.js';
262
+ import { useMessageActions, MessageActionsKeybindings, MessageActionsBar } from '../components/messageActions.js';
263
+ import { setClipboard } from '../ink/termio/osc.js';
264
+ import { createAttachmentMessage, getQueuedCommandAttachments } from '../utils/attachments.js';
265
+ // Stable empty array for hooks that accept MCPServerConnection[] — avoids
266
+ // creating a new [] literal on every render in remote mode, which would
267
+ // cause useEffect dependency changes and infinite re-render loops.
268
+ const EMPTY_MCP_CLIENTS = [];
269
+ // Stable stub for useAssistantHistory's non-KAIROS branch — avoids a new
270
+ // function identity each render, which would break composedOnScroll's memo.
271
+ const HISTORY_STUB = {
272
+ maybeLoadOlder: (_) => { }
273
+ };
274
+ // Window after a user-initiated scroll during which type-into-empty does NOT
275
+ // repin to bottom. Josh Rosen's workflow: Claude emits long output → scroll
276
+ // up to read the start → start typing → before this fix, snapped to bottom.
277
+ // https://anthropic.slack.com/archives/C07VBSHV7EV/p1773545449871739
278
+ const RECENT_SCROLL_REPIN_WINDOW_MS = 3000;
279
+ // Use LRU cache to prevent unbounded memory growth
280
+ // 100 files should be sufficient for most coding sessions while preventing
281
+ // memory issues when working across many files in large projects
282
+ function median(values) {
283
+ const sorted = [...values].sort((a, b) => a - b);
284
+ const mid = Math.floor(sorted.length / 2);
285
+ return sorted.length % 2 === 0 ? Math.round((sorted[mid - 1] + sorted[mid]) / 2) : sorted[mid];
286
+ }
287
+ /**
288
+ * Small component to display transcript mode footer with dynamic keybinding.
289
+ * Must be rendered inside KeybindingSetup to access keybinding context.
290
+ */
291
+ function TranscriptModeFooter(t0) {
292
+ const $ = _c(9);
293
+ const { showAllInTranscript, virtualScroll, searchBadge, suppressShowAll: t1, status } = t0;
294
+ const suppressShowAll = t1 === undefined ? false : t1;
295
+ const toggleShortcut = useShortcutDisplay("app:toggleTranscript", "Global", "ctrl+o");
296
+ const showAllShortcut = useShortcutDisplay("transcript:toggleShowAll", "Transcript", "ctrl+e");
297
+ const t2 = searchBadge ? " \xB7 n/N para navegar" : virtualScroll ? ` · ${figures.arrowUp}${figures.arrowDown} desplazar · inicio/fin extremo` : suppressShowAll ? "" : ` · ${showAllShortcut} para ${showAllInTranscript ? "contraer" : "mostrar todo"}`;
298
+ let t3;
299
+ if ($[0] !== t2 || $[1] !== toggleShortcut) {
300
+ t3 = _jsxs(Text, { dimColor: true, children: ["Mostrando transcripci\u00F3n detallada \u00B7 ", toggleShortcut, " para alternar", t2] });
301
+ $[0] = t2;
302
+ $[1] = toggleShortcut;
303
+ $[2] = t3;
304
+ }
305
+ else {
306
+ t3 = $[2];
307
+ }
308
+ let t4;
309
+ if ($[3] !== searchBadge || $[4] !== status) {
310
+ t4 = status ? _jsxs(_Fragment, { children: [_jsx(Box, { flexGrow: 1 }), _jsxs(Text, { children: [status, " "] })] }) : searchBadge ? _jsxs(_Fragment, { children: [_jsx(Box, { flexGrow: 1 }), _jsxs(Text, { dimColor: true, children: [searchBadge.current, "/", searchBadge.count, " "] })] }) : null;
311
+ $[3] = searchBadge;
312
+ $[4] = status;
313
+ $[5] = t4;
314
+ }
315
+ else {
316
+ t4 = $[5];
317
+ }
318
+ let t5;
319
+ if ($[6] !== t3 || $[7] !== t4) {
320
+ t5 = _jsxs(Box, { noSelect: true, alignItems: "center", alignSelf: "center", borderTopDimColor: true, borderBottom: false, borderLeft: false, borderRight: false, borderStyle: "single", marginTop: 1, paddingLeft: 2, width: "100%", children: [t3, t4] });
321
+ $[6] = t3;
322
+ $[7] = t4;
323
+ $[8] = t5;
324
+ }
325
+ else {
326
+ t5 = $[8];
327
+ }
328
+ return t5;
329
+ }
330
+ /** less-style / bar. 1-row, same border-top styling as TranscriptModeFooter
331
+ * so swapping them in the bottom slot doesn't shift ScrollBox height.
332
+ * useSearchInput handles readline editing; we report query changes and
333
+ * render the counter. Incremental — re-search + highlight per keystroke. */
334
+ function TranscriptSearchBar({ jumpRef, count, current, onClose, onCancel, setHighlight, initialQuery }) {
335
+ const { query, cursorOffset } = useSearchInput({
336
+ isActive: true,
337
+ initialQuery,
338
+ onExit: () => onClose(query),
339
+ onCancel
340
+ });
341
+ // Index warm-up runs before the query effect so it measures the real
342
+ // cost — otherwise setSearchQuery fills the cache first and warm
343
+ // reports ~0ms while the user felt the actual lag.
344
+ // First / in a transcript session pays the extractSearchText cost.
345
+ // Subsequent / return 0 immediately (indexWarmed ref in VML).
346
+ // Transcript is frozen at ctrl+o so the cache stays valid.
347
+ // Initial 'building' so warmDone is false on mount — the [query] effect
348
+ // waits for the warm effect's first resolve instead of racing it. With
349
+ // null initial, warmDone would be true on mount → [query] fires →
350
+ // setSearchQuery fills cache → warm reports ~0ms while the user felt
351
+ // the real lag.
352
+ const [indexStatus, setIndexStatus] = React.useState('building');
353
+ React.useEffect(() => {
354
+ let alive = true;
355
+ const warm = jumpRef.current?.warmSearchIndex;
356
+ if (!warm) {
357
+ setIndexStatus(null); // VML not mounted yet — rare, skip indicator
358
+ return;
359
+ }
360
+ setIndexStatus('building');
361
+ warm().then(ms => {
362
+ if (!alive)
363
+ return;
364
+ // <20ms = imperceptible. No point showing "indexed in 3ms".
365
+ if (ms < 20) {
366
+ setIndexStatus(null);
367
+ }
368
+ else {
369
+ setIndexStatus({
370
+ ms
371
+ });
372
+ setTimeout(() => alive && setIndexStatus(null), 2000);
373
+ }
374
+ });
375
+ return () => {
376
+ alive = false;
377
+ };
378
+ // eslint-disable-next-line react-hooks/exhaustive-deps
379
+ }, []); // mount-only: bar opens once per /
380
+ // Gate the query effect on warm completion. setHighlight stays instant
381
+ // (screen-space overlay, no indexing). setSearchQuery (the scan) waits.
382
+ const warmDone = indexStatus !== 'building';
383
+ useEffect(() => {
384
+ if (!warmDone)
385
+ return;
386
+ jumpRef.current?.setSearchQuery(query);
387
+ setHighlight(query);
388
+ // eslint-disable-next-line react-hooks/exhaustive-deps
389
+ }, [query, warmDone]);
390
+ const off = cursorOffset;
391
+ const cursorChar = off < query.length ? query[off] : ' ';
392
+ return _jsxs(Box, { borderTopDimColor: true, borderBottom: false, borderLeft: false, borderRight: false, borderStyle: "single", marginTop: 1, paddingLeft: 2, width: "100%",
393
+ // applySearchHighlight scans the whole screen buffer. The query
394
+ // text rendered here IS on screen — /foo matches its own 'foo' in
395
+ // the bar. With no content matches that's the ONLY visible match →
396
+ // gets CURRENT → underlined. noSelect makes searchHighlight.ts:76
397
+ // skip these cells (same exclusion as gutters). You can't text-
398
+ // select the bar either; it's transient chrome, fine.
399
+ noSelect: true, children: [_jsx(Text, { children: "/" }), _jsx(Text, { children: query.slice(0, off) }), _jsx(Text, { inverse: true, children: cursorChar }), off < query.length && _jsx(Text, { children: query.slice(off + 1) }), _jsx(Box, { flexGrow: 1 }), indexStatus === 'building' ? _jsx(Text, { dimColor: true, children: "indexando\u2026 " }) : indexStatus ? _jsxs(Text, { dimColor: true, children: ["indexado en ", indexStatus.ms, "ms "] }) : count === 0 && query ? _jsx(Text, { color: "error", children: "sin coincidencias " }) : count > 0 ?
400
+ _jsxs(Text, { dimColor: true, children: [current, "/", count, ' '] }) : null] });
401
+ }
402
+ const TITLE_ANIMATION_FRAMES = ['⠂', '⠐'];
403
+ const TITLE_STATIC_PREFIX = '✳';
404
+ const TITLE_ANIMATION_INTERVAL_MS = 960;
405
+ /**
406
+ * Sets the terminal tab title, with an animated prefix glyph while a query
407
+ * is running. Isolated from REPL so the 960ms animation tick re-renders only
408
+ * this leaf component (which returns null — pure side-effect) instead of the
409
+ * entire REPL tree. Before extraction, the tick was ~1 REPL render/sec for
410
+ * the duration of every turn, dragging PromptInput and friends along.
411
+ */
412
+ function AnimatedTerminalTitle(t0) {
413
+ const $ = _c(6);
414
+ const { isAnimating, title, disabled, noPrefix } = t0;
415
+ const terminalFocused = useTerminalFocus();
416
+ const [frame, setFrame] = useState(0);
417
+ let t1;
418
+ let t2;
419
+ if ($[0] !== disabled || $[1] !== isAnimating || $[2] !== noPrefix || $[3] !== terminalFocused) {
420
+ t1 = () => {
421
+ if (disabled || noPrefix || !isAnimating || !terminalFocused) {
422
+ return;
423
+ }
424
+ const interval = setInterval(_temp2, TITLE_ANIMATION_INTERVAL_MS, setFrame);
425
+ return () => clearInterval(interval);
426
+ };
427
+ t2 = [disabled, noPrefix, isAnimating, terminalFocused];
428
+ $[0] = disabled;
429
+ $[1] = isAnimating;
430
+ $[2] = noPrefix;
431
+ $[3] = terminalFocused;
432
+ $[4] = t1;
433
+ $[5] = t2;
434
+ }
435
+ else {
436
+ t1 = $[4];
437
+ t2 = $[5];
438
+ }
439
+ useEffect(t1, t2);
440
+ const prefix = isAnimating ? TITLE_ANIMATION_FRAMES[frame] ?? TITLE_STATIC_PREFIX : TITLE_STATIC_PREFIX;
441
+ useTerminalTitle(disabled ? null : noPrefix ? title : `${prefix} ${title}`);
442
+ return null;
443
+ }
444
+ function _temp2(setFrame_0) {
445
+ return setFrame_0(_temp);
446
+ }
447
+ function _temp(f) {
448
+ return (f + 1) % TITLE_ANIMATION_FRAMES.length;
449
+ }
450
+ export function REPL({ commands: initialCommands, debug, initialTools, initialMessages, pendingHookMessages, initialFileHistorySnapshots, initialContentReplacements, initialAgentName, initialAgentColor, mcpClients: initialMcpClients, dynamicMcpConfig: initialDynamicMcpConfig, autoConnectIdeFlag, strictMcpConfig = false, systemPrompt: customSystemPrompt, appendSystemPrompt, onBeforeQuery, onTurnComplete, disabled = false, mainThreadAgentDefinition: initialMainThreadAgentDefinition, disableSlashCommands = false, taskListId, remoteSessionConfig, directConnectConfig, sshSession, thinkingConfig }) {
451
+ const isRemoteSession = !!remoteSessionConfig;
452
+ // Env-var gates hoisted to mount-time — isEnvTruthy does toLowerCase+trim+
453
+ // includes, and these were on the render path (hot during PageUp spam).
454
+ const titleDisabled = useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_TERMINAL_TITLE), []);
455
+ const moreRightEnabled = useMemo(() => "external" === 'ant' && isEnvTruthy(process.env.CLAUDE_MORERIGHT), []);
456
+ const disableVirtualScroll = useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_VIRTUAL_SCROLL), []);
457
+ const disableMessageActions = feature('MESSAGE_ACTIONS') ?
458
+ // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
459
+ useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_MESSAGE_ACTIONS), []) : false;
460
+ // Log REPL mount/unmount lifecycle
461
+ useEffect(() => {
462
+ logForDebugging(`[REPL:mount] REPL mounted, disabled=${disabled}`);
463
+ return () => logForDebugging(`[REPL:unmount] REPL unmounting`);
464
+ }, [disabled]);
465
+ // Agent definition is state so /resume can update it mid-session
466
+ const [mainThreadAgentDefinition, setMainThreadAgentDefinition] = useState(initialMainThreadAgentDefinition);
467
+ const toolPermissionContext = useAppState(s => s.toolPermissionContext);
468
+ const verbose = useAppState(s => s.verbose);
469
+ const mcp = useAppState(s => s.mcp);
470
+ const plugins = useAppState(s => s.plugins);
471
+ const agentDefinitions = useAppState(s => s.agentDefinitions);
472
+ const fileHistory = useAppState(s => s.fileHistory);
473
+ const initialMessage = useAppState(s => s.initialMessage);
474
+ const queuedCommands = useCommandQueue();
475
+ // feature() is a build-time constant — dead code elimination removes the hook
476
+ // call entirely in external builds, so this is safe despite looking conditional.
477
+ // These fields contain excluded strings that must not appear in external builds.
478
+ const spinnerTip = useAppState(s => s.spinnerTip);
479
+ const showExpandedTodos = useAppState(s => s.expandedView) === 'tasks';
480
+ const pendingWorkerRequest = useAppState(s => s.pendingWorkerRequest);
481
+ const pendingSandboxRequest = useAppState(s => s.pendingSandboxRequest);
482
+ const teamContext = useAppState(s => s.teamContext);
483
+ const tasks = useAppState(s => s.tasks);
484
+ const workerSandboxPermissions = useAppState(s => s.workerSandboxPermissions);
485
+ const elicitation = useAppState(s => s.elicitation);
486
+ const ultraplanPendingChoice = useAppState(s => s.ultraplanPendingChoice);
487
+ const ultraplanLaunchPending = useAppState(s => s.ultraplanLaunchPending);
488
+ const viewingAgentTaskId = useAppState(s => s.viewingAgentTaskId);
489
+ const setAppState = useSetAppState();
490
+ // Bootstrap: retained local_agent that hasn't loaded disk yet → read
491
+ // sidechain JSONL and UUID-merge with whatever stream has appended so far.
492
+ // Stream appends immediately on retain (no defer); bootstrap fills the
493
+ // prefix. Disk-write-before-yield means live is always a suffix of disk.
494
+ const viewedLocalAgent = viewingAgentTaskId ? tasks[viewingAgentTaskId] : undefined;
495
+ const needsBootstrap = isLocalAgentTask(viewedLocalAgent) && viewedLocalAgent.retain && !viewedLocalAgent.diskLoaded;
496
+ useEffect(() => {
497
+ if (!viewingAgentTaskId || !needsBootstrap)
498
+ return;
499
+ const taskId = viewingAgentTaskId;
500
+ void getAgentTranscript(asAgentId(taskId)).then(result => {
501
+ setAppState(prev => {
502
+ const t = prev.tasks[taskId];
503
+ if (!isLocalAgentTask(t) || t.diskLoaded || !t.retain)
504
+ return prev;
505
+ const live = t.messages ?? [];
506
+ const liveUuids = new Set(live.map(m => m.uuid));
507
+ const diskOnly = result ? result.messages.filter(m => !liveUuids.has(m.uuid)) : [];
508
+ return {
509
+ ...prev,
510
+ tasks: {
511
+ ...prev.tasks,
512
+ [taskId]: {
513
+ ...t,
514
+ messages: [...diskOnly, ...live],
515
+ diskLoaded: true
516
+ }
517
+ }
518
+ };
519
+ });
520
+ });
521
+ }, [viewingAgentTaskId, needsBootstrap, setAppState]);
522
+ const store = useAppStateStore();
523
+ const terminal = useTerminalNotification();
524
+ const mainLoopModel = useMainLoopModel();
525
+ // Note: standaloneAgentContext is initialized in main.tsx (via initialState) or
526
+ // ResumeConversation.tsx (via setAppState before rendering REPL) to avoid
527
+ // useEffect-based state initialization on mount (per CLAUDE.md guidelines)
528
+ // Local state for commands (hot-reloadable when skill files change)
529
+ const [localCommands, setLocalCommands] = useState(initialCommands);
530
+ // Watch for skill file changes and reload all commands
531
+ useSkillsChange(isRemoteSession ? undefined : getProjectRoot(), setLocalCommands);
532
+ // Track proactive mode for tools dependency - SleepTool filters by proactive state
533
+ const proactiveActive = React.useSyncExternalStore(proactiveModule?.subscribeToProactiveChanges ?? PROACTIVE_NO_OP_SUBSCRIBE, proactiveModule?.isProactiveActive ?? PROACTIVE_FALSE);
534
+ // BriefTool.isEnabled() reads getUserMsgOptIn() from bootstrap state, which
535
+ // /brief flips mid-session alongside isBriefOnly. The memo below needs a
536
+ // React-visible dep to re-run getTools() when that happens; isBriefOnly is
537
+ // the AppState mirror that triggers the re-render. Without this, toggling
538
+ // /brief mid-session leaves the stale tool list (no SendUserMessage) and
539
+ // the model emits plain text the brief filter hides.
540
+ const isBriefOnly = useAppState(s => s.isBriefOnly);
541
+ const localTools = useMemo(() => getTools(toolPermissionContext), [toolPermissionContext, proactiveActive, isBriefOnly]);
542
+ useKickOffCheckAndDisableBypassPermissionsIfNeeded();
543
+ useKickOffCheckAndDisableAutoModeIfNeeded();
544
+ const [dynamicMcpConfig, setDynamicMcpConfig] = useState(initialDynamicMcpConfig);
545
+ const onChangeDynamicMcpConfig = useCallback((config) => {
546
+ setDynamicMcpConfig(config);
547
+ }, [setDynamicMcpConfig]);
548
+ const [screen, setScreen] = useState('prompt');
549
+ const [showAllInTranscript, setShowAllInTranscript] = useState(false);
550
+ // [ forces the dump-to-scrollback path inside transcript mode. Separate
551
+ // from CLAUDE_CODE_NO_FLICKER=0 (which is process-lifetime) — this is
552
+ // ephemeral, reset on transcript exit. Diagnostic escape hatch so
553
+ // terminal/tmux native cmd-F can search the full flat render.
554
+ const [dumpMode, setDumpMode] = useState(false);
555
+ // v-for-editor render progress. Inline in the footer — notifications
556
+ // render inside PromptInput which isn't mounted in transcript.
557
+ const [editorStatus, setEditorStatus] = useState('');
558
+ // Incremented on transcript exit. Async v-render captures this at start;
559
+ // each status write no-ops if stale (user left transcript mid-render —
560
+ // the stable setState would otherwise stamp a ghost toast into the next
561
+ // session). Also clears any pending 4s auto-clear.
562
+ const editorGenRef = useRef(0);
563
+ const editorTimerRef = useRef(undefined);
564
+ const editorRenderingRef = useRef(false);
565
+ const { addNotification, removeNotification } = useNotifications();
566
+ // eslint-disable-next-line prefer-const
567
+ let trySuggestBgPRIntercept = SUGGEST_BG_PR_NOOP;
568
+ const mcpClients = useMergedClients(initialMcpClients, mcp.clients);
569
+ // IDE integration
570
+ const [ideSelection, setIDESelection] = useState(undefined);
571
+ const [ideToInstallExtension, setIDEToInstallExtension] = useState(null);
572
+ const [ideInstallationStatus, setIDEInstallationStatus] = useState(null);
573
+ const [showIdeOnboarding, setShowIdeOnboarding] = useState(false);
574
+ // Dead code elimination: model switch callout state (ant-only)
575
+ const [showModelSwitchCallout, setShowModelSwitchCallout] = useState(() => {
576
+ if ("external" === 'ant') {
577
+ return shouldShowAntModelSwitch();
578
+ }
579
+ return false;
580
+ });
581
+ const [showEffortCallout, setShowEffortCallout] = useState(() => shouldShowEffortCallout(mainLoopModel));
582
+ const showRemoteCallout = useAppState(s => s.showRemoteCallout);
583
+ const [showDesktopUpsellStartup, setShowDesktopUpsellStartup] = useState(() => shouldShowDesktopUpsellStartup());
584
+ // notifications
585
+ useModelMigrationNotifications();
586
+ useCanSwitchToExistingSubscription();
587
+ useIDEStatusIndicator({
588
+ ideSelection,
589
+ mcpClients,
590
+ ideInstallationStatus
591
+ });
592
+ useMcpConnectivityStatus({
593
+ mcpClients
594
+ });
595
+ useAutoModeUnavailableNotification();
596
+ usePluginInstallationStatus();
597
+ usePluginAutoupdateNotification();
598
+ useSettingsErrors();
599
+ useRateLimitWarningNotification(mainLoopModel);
600
+ useFastModeNotification();
601
+ useDeprecationWarningNotification(mainLoopModel);
602
+ useNpmDeprecationNotification();
603
+ useAntOrgWarningNotification();
604
+ useInstallMessages();
605
+ useChromeExtensionNotification();
606
+ useOfficialMarketplaceNotification();
607
+ useLspInitializationNotification();
608
+ useTeammateLifecycleNotification();
609
+ const { recommendation: lspRecommendation, handleResponse: handleLspResponse } = useLspPluginRecommendation();
610
+ const { recommendation: hintRecommendation, handleResponse: handleHintResponse } = useClaudeCodeHintRecommendation();
611
+ // Memoize the combined initial tools array to prevent reference changes
612
+ const combinedInitialTools = useMemo(() => {
613
+ return [...localTools, ...initialTools];
614
+ }, [localTools, initialTools]);
615
+ // Initialize plugin management
616
+ useManagePlugins({
617
+ enabled: !isRemoteSession
618
+ });
619
+ const tasksV2 = useTasksV2WithCollapseEffect();
620
+ // Start background plugin installations
621
+ // SECURITY: This code is guaranteed to run ONLY after the "trust this folder" dialog
622
+ // has been confirmed by the user. The trust dialog is shown in cli.tsx (line ~387)
623
+ // before the REPL component is rendered. The dialog blocks execution until the user
624
+ // accepts, and only then is the REPL component mounted and this effect runs.
625
+ // This ensures that plugin installations from repository and user settings only
626
+ // happen after explicit user consent to trust the current working directory.
627
+ useEffect(() => {
628
+ if (isRemoteSession)
629
+ return;
630
+ void performStartupChecks(setAppState);
631
+ }, [setAppState, isRemoteSession]);
632
+ // Allow Claude in Chrome MCP to send prompts through MCP notifications
633
+ // and sync permission mode changes to the Chrome extension
634
+ usePromptsFromClaudeInChrome(isRemoteSession ? EMPTY_MCP_CLIENTS : mcpClients, toolPermissionContext.mode);
635
+ // Initialize swarm features: teammate hooks and context
636
+ // Handles both fresh spawns and resumed teammate sessions
637
+ useSwarmInitialization(setAppState, initialMessages, {
638
+ enabled: !isRemoteSession
639
+ });
640
+ const mergedTools = useMergedTools(combinedInitialTools, mcp.tools, toolPermissionContext);
641
+ // Apply agent tool restrictions if mainThreadAgentDefinition is set
642
+ const { tools, allowedAgentTypes } = useMemo(() => {
643
+ if (!mainThreadAgentDefinition) {
644
+ return {
645
+ tools: mergedTools,
646
+ allowedAgentTypes: undefined
647
+ };
648
+ }
649
+ const resolved = resolveAgentTools(mainThreadAgentDefinition, mergedTools, false, true);
650
+ return {
651
+ tools: resolved.resolvedTools,
652
+ allowedAgentTypes: resolved.allowedAgentTypes
653
+ };
654
+ }, [mainThreadAgentDefinition, mergedTools]);
655
+ // Merge commands from local state, plugins, and MCP
656
+ const commandsWithPlugins = useMergedCommands(localCommands, plugins.commands);
657
+ const mergedCommands = useMergedCommands(commandsWithPlugins, mcp.commands);
658
+ // Filter out all commands if disableSlashCommands is true
659
+ const commands = useMemo(() => disableSlashCommands ? [] : mergedCommands, [disableSlashCommands, mergedCommands]);
660
+ useIdeLogging(isRemoteSession ? EMPTY_MCP_CLIENTS : mcp.clients);
661
+ useIdeSelection(isRemoteSession ? EMPTY_MCP_CLIENTS : mcp.clients, setIDESelection);
662
+ const [streamMode, setStreamMode] = useState('responding');
663
+ // Ref mirror so onSubmit can read the latest value without adding
664
+ // streamMode to its deps. streamMode flips between
665
+ // requesting/responding/tool-use ~10x per turn during streaming; having it
666
+ // in onSubmit's deps was recreating onSubmit on every flip, which
667
+ // cascaded into PromptInput prop churn and downstream useCallback/useMemo
668
+ // invalidation. The only consumers inside callbacks are debug logging and
669
+ // telemetry (handlePromptSubmit.ts), so a stale-by-one-render value is
670
+ // harmless — but ref mirrors sync on every render anyway so it's fresh.
671
+ const streamModeRef = useRef(streamMode);
672
+ streamModeRef.current = streamMode;
673
+ const [streamingToolUses, setStreamingToolUses] = useState([]);
674
+ const [streamingThinking, setStreamingThinking] = useState(null);
675
+ // Auto-hide streaming thinking after 30 seconds of being completed
676
+ useEffect(() => {
677
+ if (streamingThinking && !streamingThinking.isStreaming && streamingThinking.streamingEndedAt) {
678
+ const elapsed = Date.now() - streamingThinking.streamingEndedAt;
679
+ const remaining = 30000 - elapsed;
680
+ if (remaining > 0) {
681
+ const timer = setTimeout(setStreamingThinking, remaining, null);
682
+ return () => clearTimeout(timer);
683
+ }
684
+ else {
685
+ setStreamingThinking(null);
686
+ }
687
+ }
688
+ }, [streamingThinking]);
689
+ const [abortController, setAbortController] = useState(null);
690
+ // Ref that always points to the current abort controller, used by the
691
+ // REPL bridge to abort the active query when a remote interrupt arrives.
692
+ const abortControllerRef = useRef(null);
693
+ abortControllerRef.current = abortController;
694
+ // Ref for the bridge result callback — set after useReplBridge initializes,
695
+ // read in the onQuery finally block to notify mobile clients that a turn ended.
696
+ const sendBridgeResultRef = useRef(() => { });
697
+ // Ref for the synchronous restore callback — set after restoreMessageSync is
698
+ // defined, read in the onQuery finally block for auto-restore on interrupt.
699
+ const restoreMessageSyncRef = useRef(() => { });
700
+ // Ref to the fullscreen layout's scroll box for keyboard scrolling.
701
+ // Null when fullscreen mode is disabled (ref never attached).
702
+ const scrollRef = useRef(null);
703
+ // Separate ref for the modal slot's inner ScrollBox — passed through
704
+ // FullscreenLayout → ModalContext so Tabs can attach it to its own
705
+ // ScrollBox for tall content (e.g. /status's MCP-server list). NOT
706
+ // keyboard-driven — ScrollKeybindingHandler stays on the outer ref so
707
+ // PgUp/PgDn/wheel always scroll the transcript behind the modal.
708
+ // Plumbing kept for future modal-scroll wiring.
709
+ const modalScrollRef = useRef(null);
710
+ // Timestamp of the last user-initiated scroll (wheel, PgUp/PgDn, ctrl+u,
711
+ // End/Home, G, drag-to-scroll). Stamped in composedOnScroll — the single
712
+ // chokepoint ScrollKeybindingHandler calls for every user scroll action.
713
+ // Programmatic scrolls (repinScroll's scrollToBottom, sticky auto-follow)
714
+ // do NOT go through composedOnScroll, so they don't stamp this. Ref not
715
+ // state: no re-render on every wheel tick.
716
+ const lastUserScrollTsRef = useRef(0);
717
+ // Synchronous state machine for the query lifecycle. Replaces the
718
+ // error-prone dual-state pattern where isLoading (React state, async
719
+ // batched) and isQueryRunning (ref, sync) could desync. See QueryGuard.ts.
720
+ const queryGuard = React.useRef(new QueryGuard()).current;
721
+ // Subscribe to the guard — true during dispatching or running.
722
+ // This is the single source of truth for "is a local query in flight".
723
+ const isQueryActive = React.useSyncExternalStore(queryGuard.subscribe, queryGuard.getSnapshot);
724
+ // Separate loading flag for operations outside the local query guard:
725
+ // remote sessions (useRemoteSession / useDirectConnect) and foregrounded
726
+ // background tasks (useSessionBackgrounding). These don't route through
727
+ // onQuery / queryGuard, so they need their own spinner-visibility state.
728
+ // Initialize true if remote mode with initial prompt (CCR processing it).
729
+ const [isExternalLoading, setIsExternalLoadingRaw] = React.useState(remoteSessionConfig?.hasInitialPrompt ?? false);
730
+ // Derived: any loading source active. Read-only — no setter. Local query
731
+ // loading is driven by queryGuard (reserve/tryStart/end/cancelReservation),
732
+ // external loading by setIsExternalLoading.
733
+ const isLoading = isQueryActive || isExternalLoading;
734
+ // Elapsed time is computed by SpinnerWithVerb from these refs on each
735
+ // animation frame, avoiding a useInterval that re-renders the entire REPL.
736
+ const [userInputOnProcessing, setUserInputOnProcessingRaw] = React.useState(undefined);
737
+ // messagesRef.current.length at the moment userInputOnProcessing was set.
738
+ // The placeholder hides once displayedMessages grows past this — i.e. the
739
+ // real user message has landed in the visible transcript.
740
+ const userInputBaselineRef = React.useRef(0);
741
+ // True while the submitted prompt is being processed but its user message
742
+ // hasn't reached setMessages yet. setMessages uses this to keep the
743
+ // baseline in sync when unrelated async messages (bridge status, hook
744
+ // results, scheduled tasks) land during that window.
745
+ const userMessagePendingRef = React.useRef(false);
746
+ // Wall-clock time tracking refs for accurate elapsed time calculation
747
+ const loadingStartTimeRef = React.useRef(0);
748
+ const totalPausedMsRef = React.useRef(0);
749
+ const pauseStartTimeRef = React.useRef(null);
750
+ const resetTimingRefs = React.useCallback(() => {
751
+ loadingStartTimeRef.current = Date.now();
752
+ totalPausedMsRef.current = 0;
753
+ pauseStartTimeRef.current = null;
754
+ }, []);
755
+ // Reset timing refs inline when isQueryActive transitions false→true.
756
+ // queryGuard.reserve() (in executeUserInput) fires BEFORE processUserInput's
757
+ // first await, but the ref reset in onQuery's try block runs AFTER. During
758
+ // that gap, React renders the spinner with loadingStartTimeRef=0, computing
759
+ // elapsedTimeMs = Date.now() - 0 ≈ 56 years. This inline reset runs on the
760
+ // first render where isQueryActive is observed true — the same render that
761
+ // first shows the spinner — so the ref is correct by the time the spinner
762
+ // reads it. See INC-4549.
763
+ const wasQueryActiveRef = React.useRef(false);
764
+ if (isQueryActive && !wasQueryActiveRef.current) {
765
+ resetTimingRefs();
766
+ }
767
+ wasQueryActiveRef.current = isQueryActive;
768
+ // Wrapper for setIsExternalLoading that resets timing refs on transition
769
+ // to true — SpinnerWithVerb reads these for elapsed time, so they must be
770
+ // reset for remote sessions / foregrounded tasks too (not just local
771
+ // queries, which reset them in onQuery). Without this, a remote-only
772
+ // session would show ~56 years elapsed (Date.now() - 0).
773
+ const setIsExternalLoading = React.useCallback((value) => {
774
+ setIsExternalLoadingRaw(value);
775
+ if (value)
776
+ resetTimingRefs();
777
+ }, [resetTimingRefs]);
778
+ // Start time of the first turn that had swarm teammates running
779
+ // Used to compute total elapsed time (including teammate execution) for the deferred message
780
+ const swarmStartTimeRef = React.useRef(null);
781
+ const swarmBudgetInfoRef = React.useRef(undefined);
782
+ // Ref to track current focusedInputDialog for use in callbacks
783
+ // This avoids stale closures when checking dialog state in timer callbacks
784
+ const focusedInputDialogRef = React.useRef(undefined);
785
+ // How long after the last keystroke before deferred dialogs are shown
786
+ const PROMPT_SUPPRESSION_MS = 1500;
787
+ // True when user is actively typing — defers interrupt dialogs so keystrokes
788
+ // don't accidentally dismiss or answer a permission prompt the user hasn't read yet.
789
+ const [isPromptInputActive, setIsPromptInputActive] = React.useState(false);
790
+ const [autoUpdaterResult, setAutoUpdaterResult] = useState(null);
791
+ useEffect(() => {
792
+ if (autoUpdaterResult?.notifications) {
793
+ autoUpdaterResult.notifications.forEach(notification => {
794
+ addNotification({
795
+ key: 'auto-updater-notification',
796
+ text: notification,
797
+ priority: 'low'
798
+ });
799
+ });
800
+ }
801
+ }, [autoUpdaterResult, addNotification]);
802
+ // tmux + fullscreen + `mouse off`: one-time hint that wheel won't scroll.
803
+ // We no longer mutate tmux's session-scoped mouse option (it poisoned
804
+ // sibling panes); tmux users already know this tradeoff from vim/less.
805
+ useEffect(() => {
806
+ if (isFullscreenEnvEnabled()) {
807
+ void maybeGetTmuxMouseHint().then(hint => {
808
+ if (hint) {
809
+ addNotification({
810
+ key: 'tmux-mouse-hint',
811
+ text: hint,
812
+ priority: 'low'
813
+ });
814
+ }
815
+ });
816
+ }
817
+ // eslint-disable-next-line react-hooks/exhaustive-deps
818
+ }, []);
819
+ const [showUndercoverCallout, setShowUndercoverCallout] = useState(false);
820
+ useEffect(() => {
821
+ if ("external" === 'ant') {
822
+ void (async () => {
823
+ // Wait for repo classification to settle (memoized, no-op if primed).
824
+ const { isInternalModelRepo } = await import('../utils/commitAttribution.js');
825
+ await isInternalModelRepo();
826
+ const { shouldShowUndercoverAutoNotice } = await import('../utils/undercover.js');
827
+ if (shouldShowUndercoverAutoNotice()) {
828
+ setShowUndercoverCallout(true);
829
+ }
830
+ })();
831
+ }
832
+ // eslint-disable-next-line react-hooks/exhaustive-deps
833
+ }, []);
834
+ const [toolJSX, setToolJSXInternal] = useState(null);
835
+ // Track local JSX commands separately so tools can't overwrite them.
836
+ // This enables "immediate" commands (like /btw) to persist while Claude is processing.
837
+ const localJSXCommandRef = useRef(null);
838
+ // Wrapper for setToolJSX that preserves local JSX commands (like /btw).
839
+ // When a local JSX command is active, we ignore updates from tools
840
+ // unless they explicitly set clearLocalJSX: true (from onDone callbacks).
841
+ //
842
+ // TO ADD A NEW IMMEDIATE COMMAND:
843
+ // 1. Set `immediate: true` in the command definition
844
+ // 2. Set `isLocalJSXCommand: true` when calling setToolJSX in the command's JSX
845
+ // 3. In the onDone callback, use `setToolJSX({ jsx: null, shouldHidePromptInput: false, clearLocalJSX: true })`
846
+ // to explicitly clear the overlay when the user dismisses it
847
+ const setToolJSX = useCallback((args) => {
848
+ // If setting a local JSX command, store it in the ref
849
+ if (args?.isLocalJSXCommand) {
850
+ const { clearLocalJSX: _, ...rest } = args;
851
+ localJSXCommandRef.current = {
852
+ ...rest,
853
+ isLocalJSXCommand: true
854
+ };
855
+ setToolJSXInternal(rest);
856
+ return;
857
+ }
858
+ // If there's an active local JSX command in the ref
859
+ if (localJSXCommandRef.current) {
860
+ // Allow clearing only if explicitly requested (from onDone callbacks)
861
+ if (args?.clearLocalJSX) {
862
+ localJSXCommandRef.current = null;
863
+ setToolJSXInternal(null);
864
+ return;
865
+ }
866
+ // Otherwise, keep the local JSX command visible - ignore tool updates
867
+ return;
868
+ }
869
+ // No active local JSX command, allow any update
870
+ if (args?.clearLocalJSX) {
871
+ setToolJSXInternal(null);
872
+ return;
873
+ }
874
+ setToolJSXInternal(args);
875
+ }, []);
876
+ const [toolUseConfirmQueue, setToolUseConfirmQueue] = useState([]);
877
+ // Sticky footer JSX registered by permission request components (currently
878
+ // only ExitPlanModePermissionRequest). Renders in FullscreenLayout's `bottom`
879
+ // slot so response options stay visible while the user scrolls a long plan.
880
+ const [permissionStickyFooter, setPermissionStickyFooter] = useState(null);
881
+ const [sandboxPermissionRequestQueue, setSandboxPermissionRequestQueue] = useState([]);
882
+ const [promptQueue, setPromptQueue] = useState([]);
883
+ // Track bridge cleanup functions for sandbox permission requests so the
884
+ // local dialog handler can cancel the remote prompt when the local user
885
+ // responds first. Keyed by host to support concurrent same-host requests.
886
+ const sandboxBridgeCleanupRef = useRef(new Map());
887
+ // -- Terminal title management
888
+ // Session title (set via /rename or restored on resume) wins over
889
+ // the agent name, which wins over the Haiku-extracted topic;
890
+ // all fall back to the product name.
891
+ const terminalTitleFromRename = useAppState(s => s.settings.terminalTitleFromRename) !== false;
892
+ const sessionTitle = terminalTitleFromRename ? getCurrentSessionTitle(getSessionId()) : undefined;
893
+ const [haikuTitle, setHaikuTitle] = useState();
894
+ // Gates the one-shot Haiku call that generates the tab title. Seeded true
895
+ // on resume (initialMessages present) so we don't re-title a resumed
896
+ // session from mid-conversation context.
897
+ const haikuTitleAttemptedRef = useRef((initialMessages?.length ?? 0) > 0);
898
+ const agentTitle = mainThreadAgentDefinition?.agentType;
899
+ const terminalTitle = sessionTitle ?? agentTitle ?? haikuTitle ?? 'Context Code';
900
+ const isWaitingForApproval = toolUseConfirmQueue.length > 0 || promptQueue.length > 0 || pendingWorkerRequest || pendingSandboxRequest;
901
+ // Local-jsx commands (like /plugin, /config) show user-facing dialogs that
902
+ // wait for input. Require jsx != null — if the flag is stuck true but jsx
903
+ // is null, treat as not-showing so TextInput focus and queue processor
904
+ // aren't deadlocked by a phantom overlay.
905
+ const isShowingLocalJSXCommand = toolJSX?.isLocalJSXCommand === true && toolJSX?.jsx != null;
906
+ const titleIsAnimating = isLoading && !isWaitingForApproval && !isShowingLocalJSXCommand;
907
+ // Title animation state lives in <AnimatedTerminalTitle> so the 960ms tick
908
+ // doesn't re-render REPL. titleDisabled/terminalTitle are still computed
909
+ // here because onQueryImpl reads them (background session description,
910
+ // haiku title extraction gate).
911
+ // Prevent macOS from sleeping while Claude is working
912
+ useEffect(() => {
913
+ if (isLoading && !isWaitingForApproval && !isShowingLocalJSXCommand) {
914
+ startPreventSleep();
915
+ return () => stopPreventSleep();
916
+ }
917
+ }, [isLoading, isWaitingForApproval, isShowingLocalJSXCommand]);
918
+ const sessionStatus = isWaitingForApproval || isShowingLocalJSXCommand ? 'waiting' : isLoading ? 'busy' : 'idle';
919
+ const waitingFor = sessionStatus !== 'waiting' ? undefined : toolUseConfirmQueue.length > 0 ? `approve ${toolUseConfirmQueue[0].tool.name}` : pendingWorkerRequest ? 'worker request' : pendingSandboxRequest ? 'sandbox request' : isShowingLocalJSXCommand ? 'dialog open' : 'input needed';
920
+ // Push status to the PID file for `claude ps`. Fire-and-forget; ps falls
921
+ // back to transcript-tail derivation when this is missing/stale.
922
+ useEffect(() => {
923
+ if (feature('BG_SESSIONS')) {
924
+ void updateSessionActivity({
925
+ status: sessionStatus,
926
+ waitingFor
927
+ });
928
+ }
929
+ }, [sessionStatus, waitingFor]);
930
+ // 3P default: off — OSC 21337 is ant-only while the spec stabilizes.
931
+ // Gated so we can roll back if the sidebar indicator conflicts with
932
+ // the title spinner in terminals that render both. When the flag is
933
+ // on, the user-facing config setting controls whether it's active.
934
+ const tabStatusGateEnabled = getFeatureValue_CACHED_MAY_BE_STALE('tengu_terminal_sidebar', false);
935
+ const showStatusInTerminalTab = tabStatusGateEnabled && (getGlobalConfig().showStatusInTerminalTab ?? false);
936
+ useTabStatus(titleDisabled || !showStatusInTerminalTab ? null : sessionStatus);
937
+ // Register the leader's setToolUseConfirmQueue for in-process teammates
938
+ useEffect(() => {
939
+ registerLeaderToolUseConfirmQueue(setToolUseConfirmQueue);
940
+ return () => unregisterLeaderToolUseConfirmQueue();
941
+ }, [setToolUseConfirmQueue]);
942
+ const [messages, rawSetMessages] = useState(initialMessages ?? []);
943
+ const messagesRef = useRef(messages);
944
+ // Stores the willowMode variant that was shown (or false if no hint shown).
945
+ // Captured at hint_shown time so hint_converted telemetry reports the same
946
+ // variant — the GrowthBook value shouldn't change mid-session, but reading
947
+ // it once guarantees consistency between the paired events.
948
+ const idleHintShownRef = useRef(false);
949
+ // Wrap setMessages so messagesRef is always current the instant the
950
+ // call returns — not when React later processes the batch. Apply the
951
+ // updater eagerly against the ref, then hand React the computed value
952
+ // (not the function). rawSetMessages batching becomes last-write-wins,
953
+ // and the last write is correct because each call composes against the
954
+ // already-updated ref. This is the Zustand pattern: ref is source of
955
+ // truth, React state is the render projection. Without this, paths
956
+ // that queue functional updaters then synchronously read the ref
957
+ // (e.g. handleSpeculationAccept → onQuery) see stale data.
958
+ const setMessages = useCallback((action) => {
959
+ const prev = messagesRef.current;
960
+ const next = typeof action === 'function' ? action(messagesRef.current) : action;
961
+ messagesRef.current = next;
962
+ if (next.length < userInputBaselineRef.current) {
963
+ // Shrank (compact/rewind/clear) — clamp so placeholderText's length
964
+ // check can't go stale.
965
+ userInputBaselineRef.current = 0;
966
+ }
967
+ else if (next.length > prev.length && userMessagePendingRef.current) {
968
+ // Grew while the submitted user message hasn't landed yet. If the
969
+ // added messages don't include it (bridge status, hook results,
970
+ // scheduled tasks landing async during processUserInputBase), bump
971
+ // baseline so the placeholder stays visible. Once the user message
972
+ // lands, stop tracking — later additions (assistant stream) should
973
+ // not re-show the placeholder.
974
+ const delta = next.length - prev.length;
975
+ const added = prev.length === 0 || next[0] === prev[0] ? next.slice(-delta) : next.slice(0, delta);
976
+ if (added.some(isHumanTurn)) {
977
+ userMessagePendingRef.current = false;
978
+ }
979
+ else {
980
+ userInputBaselineRef.current = next.length;
981
+ }
982
+ }
983
+ rawSetMessages(next);
984
+ }, []);
985
+ // Capture the baseline message count alongside the placeholder text so
986
+ // the render can hide it once displayedMessages grows past the baseline.
987
+ const setUserInputOnProcessing = useCallback((input) => {
988
+ if (input !== undefined) {
989
+ userInputBaselineRef.current = messagesRef.current.length;
990
+ userMessagePendingRef.current = true;
991
+ }
992
+ else {
993
+ userMessagePendingRef.current = false;
994
+ }
995
+ setUserInputOnProcessingRaw(input);
996
+ }, []);
997
+ // Fullscreen: track the unseen-divider position. dividerIndex changes
998
+ // only ~twice/scroll-session (first scroll-away + repin). pillVisible
999
+ // and stickyPrompt now live in FullscreenLayout — they subscribe to
1000
+ // ScrollBox directly so per-frame scroll never re-renders REPL.
1001
+ const { dividerIndex, dividerYRef, onScrollAway, onRepin, jumpToNew, shiftDivider } = useUnseenDivider(messages.length);
1002
+ if (feature('AWAY_SUMMARY')) {
1003
+ // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
1004
+ useAwaySummary(messages, setMessages, isLoading);
1005
+ }
1006
+ const [cursor, setCursor] = useState(null);
1007
+ const cursorNavRef = useRef(null);
1008
+ // Memoized so Messages' React.memo holds.
1009
+ const unseenDivider = useMemo(() => computeUnseenDivider(messages, dividerIndex),
1010
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- length change covers appends; useUnseenDivider's count-drop guard clears dividerIndex on replace/rewind
1011
+ [dividerIndex, messages.length]);
1012
+ // Re-pin scroll to bottom and clear the unseen-messages baseline. Called
1013
+ // on any user-driven return-to-live action (submit, type-into-empty,
1014
+ // overlay appear/dismiss).
1015
+ const repinScroll = useCallback(() => {
1016
+ scrollRef.current?.scrollToBottom();
1017
+ onRepin();
1018
+ setCursor(null);
1019
+ }, [onRepin, setCursor]);
1020
+ // Backstop for the submit-handler repin at onSubmit. If a buffered stdin
1021
+ // event (wheel/drag) races between handler-fire and state-commit, the
1022
+ // handler's scrollToBottom can be undone. This effect fires on the render
1023
+ // where the user's message actually lands — tied to React's commit cycle,
1024
+ // so it can't race with stdin. Keyed on lastMsg identity (not messages.length)
1025
+ // so useAssistantHistory's prepends don't spuriously repin.
1026
+ const lastMsg = messages.at(-1);
1027
+ const lastMsgIsHuman = lastMsg != null && isHumanTurn(lastMsg);
1028
+ useEffect(() => {
1029
+ if (lastMsgIsHuman) {
1030
+ repinScroll();
1031
+ }
1032
+ }, [lastMsgIsHuman, lastMsg, repinScroll]);
1033
+ // Assistant-chat: lazy-load remote history on scroll-up. No-op unless
1034
+ // KAIROS build + config.viewerOnly. feature() is build-time constant so
1035
+ // the branch is dead-code-eliminated in non-KAIROS builds (same pattern
1036
+ // as useUnseenDivider above).
1037
+ const { maybeLoadOlder } = feature('KAIROS') ?
1038
+ // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
1039
+ useAssistantHistory({
1040
+ config: remoteSessionConfig,
1041
+ setMessages,
1042
+ scrollRef,
1043
+ onPrepend: shiftDivider
1044
+ }) : HISTORY_STUB;
1045
+ // Compose useUnseenDivider's callbacks with the lazy-load trigger.
1046
+ const composedOnScroll = useCallback((sticky, handle) => {
1047
+ lastUserScrollTsRef.current = Date.now();
1048
+ if (sticky) {
1049
+ onRepin();
1050
+ }
1051
+ else {
1052
+ onScrollAway(handle);
1053
+ if (feature('KAIROS'))
1054
+ maybeLoadOlder(handle);
1055
+ // Dismiss the companion bubble on scroll — it's absolute-positioned
1056
+ // at bottom-right and covers transcript content. Scrolling = user is
1057
+ // trying to read something under it.
1058
+ if (feature('BUDDY')) {
1059
+ setAppState(prev => prev.companionReaction === undefined ? prev : {
1060
+ ...prev,
1061
+ companionReaction: undefined
1062
+ });
1063
+ }
1064
+ }
1065
+ }, [onRepin, onScrollAway, maybeLoadOlder, setAppState]);
1066
+ // Deferred SessionStart hook messages — REPL renders immediately and
1067
+ // hook messages are injected when they resolve. awaitPendingHooks()
1068
+ // must be called before the first API call so the model sees hook context.
1069
+ const awaitPendingHooks = useDeferredHookMessages(pendingHookMessages, setMessages);
1070
+ // Deferred messages for the Messages component — renders at transition
1071
+ // priority so the reconciler yields every 5ms, keeping input responsive
1072
+ // while the expensive message processing pipeline runs.
1073
+ const deferredMessages = useDeferredValue(messages);
1074
+ const deferredBehind = messages.length - deferredMessages.length;
1075
+ if (deferredBehind > 0) {
1076
+ logForDebugging(`[useDeferredValue] Messages deferred by ${deferredBehind} (${deferredMessages.length}→${messages.length})`);
1077
+ }
1078
+ // Frozen state for transcript mode - stores lengths instead of cloning arrays for memory efficiency
1079
+ const [frozenTranscriptState, setFrozenTranscriptState] = useState(null);
1080
+ // Initialize input with any early input that was captured before REPL was ready.
1081
+ // Using lazy initialization ensures cursor offset is set correctly in PromptInput.
1082
+ const [inputValue, setInputValueRaw] = useState(() => consumeEarlyInput());
1083
+ const inputValueRef = useRef(inputValue);
1084
+ inputValueRef.current = inputValue;
1085
+ const insertTextRef = useRef(null);
1086
+ // Wrap setInputValue to co-locate suppression state updates.
1087
+ // Both setState calls happen in the same synchronous context so React
1088
+ // batches them into a single render, eliminating the extra render that
1089
+ // the previous useEffect → setState pattern caused.
1090
+ const setInputValue = useCallback((value) => {
1091
+ if (trySuggestBgPRIntercept(inputValueRef.current, value))
1092
+ return;
1093
+ // In fullscreen mode, typing into an empty prompt re-pins scroll to
1094
+ // bottom. Only fires on empty→non-empty so scrolling up to reference
1095
+ // something while composing a message doesn't yank the view back on
1096
+ // every keystroke. Restores the pre-fullscreen muscle memory of
1097
+ // typing to snap back to the end of the conversation.
1098
+ // Skipped if the user scrolled within the last 3s — they're actively
1099
+ // reading, not lost. lastUserScrollTsRef starts at 0 so the first-
1100
+ // ever keypress (no scroll yet) always repins.
1101
+ if (inputValueRef.current === '' && value !== '' && Date.now() - lastUserScrollTsRef.current >= RECENT_SCROLL_REPIN_WINDOW_MS) {
1102
+ repinScroll();
1103
+ }
1104
+ // Sync ref immediately (like setMessages) so callers that read
1105
+ // inputValueRef before React commits — e.g. the auto-restore finally
1106
+ // block's `=== ''` guard — see the fresh value, not the stale render.
1107
+ inputValueRef.current = value;
1108
+ setInputValueRaw(value);
1109
+ setIsPromptInputActive(value.trim().length > 0);
1110
+ }, [setIsPromptInputActive, repinScroll, trySuggestBgPRIntercept]);
1111
+ // Schedule a timeout to stop suppressing dialogs after the user stops typing.
1112
+ // Only manages the timeout — the immediate activation is handled by setInputValue above.
1113
+ useEffect(() => {
1114
+ if (inputValue.trim().length === 0)
1115
+ return;
1116
+ const timer = setTimeout(setIsPromptInputActive, PROMPT_SUPPRESSION_MS, false);
1117
+ return () => clearTimeout(timer);
1118
+ }, [inputValue]);
1119
+ const [inputMode, setInputMode] = useState('prompt');
1120
+ const [stashedPrompt, setStashedPrompt] = useState();
1121
+ // Callback to filter commands based on CCR's available slash commands
1122
+ const handleRemoteInit = useCallback((remoteSlashCommands) => {
1123
+ const remoteCommandSet = new Set(remoteSlashCommands);
1124
+ // Keep commands that CCR lists OR that are in the local-safe set
1125
+ setLocalCommands(prev => prev.filter(cmd => remoteCommandSet.has(cmd.name) || REMOTE_SAFE_COMMANDS.has(cmd)));
1126
+ }, [setLocalCommands]);
1127
+ const [inProgressToolUseIDs, setInProgressToolUseIDs] = useState(new Set());
1128
+ const hasInterruptibleToolInProgressRef = useRef(false);
1129
+ // Remote session hook - manages WebSocket connection and message handling for --remote mode
1130
+ const remoteSession = useRemoteSession({
1131
+ config: remoteSessionConfig,
1132
+ setMessages,
1133
+ setIsLoading: setIsExternalLoading,
1134
+ onInit: handleRemoteInit,
1135
+ setToolUseConfirmQueue,
1136
+ tools: combinedInitialTools,
1137
+ setStreamingToolUses,
1138
+ setStreamMode,
1139
+ setInProgressToolUseIDs
1140
+ });
1141
+ // Direct connect hook - manages WebSocket to a claude server for `claude connect` mode
1142
+ const directConnect = useDirectConnect({
1143
+ config: directConnectConfig,
1144
+ setMessages,
1145
+ setIsLoading: setIsExternalLoading,
1146
+ setToolUseConfirmQueue,
1147
+ tools: combinedInitialTools
1148
+ });
1149
+ // SSH session hook - manages ssh child process for `claude ssh` mode.
1150
+ // Same callback shape as useDirectConnect; only the transport under the
1151
+ // hood differs (ChildProcess stdin/stdout vs WebSocket).
1152
+ const sshRemote = useSSHSession({
1153
+ session: sshSession,
1154
+ setMessages,
1155
+ setIsLoading: setIsExternalLoading,
1156
+ setToolUseConfirmQueue,
1157
+ tools: combinedInitialTools
1158
+ });
1159
+ // Use whichever remote mode is active
1160
+ const activeRemote = sshRemote.isRemoteMode ? sshRemote : directConnect.isRemoteMode ? directConnect : remoteSession;
1161
+ const [pastedContents, setPastedContents] = useState({});
1162
+ const [submitCount, setSubmitCount] = useState(0);
1163
+ // Ref instead of state to avoid triggering React re-renders on every
1164
+ // streaming text_delta. The spinner reads this via its animation timer.
1165
+ const responseLengthRef = useRef(0);
1166
+ // API performance metrics ref for ant-only spinner display (TTFT/OTPS).
1167
+ // Accumulates metrics from all API requests in a turn for P50 aggregation.
1168
+ const apiMetricsRef = useRef([]);
1169
+ const setResponseLength = useCallback((f) => {
1170
+ const prev = responseLengthRef.current;
1171
+ responseLengthRef.current = f(prev);
1172
+ // When content is added (not a compaction reset), update the latest
1173
+ // metrics entry so OTPS reflects all content generation activity.
1174
+ // Updating lastTokenTime here ensures the denominator includes both
1175
+ // streaming time AND subagent execution time, preventing inflation.
1176
+ if (responseLengthRef.current > prev) {
1177
+ const entries = apiMetricsRef.current;
1178
+ if (entries.length > 0) {
1179
+ const lastEntry = entries.at(-1);
1180
+ lastEntry.lastTokenTime = Date.now();
1181
+ lastEntry.endResponseLength = responseLengthRef.current;
1182
+ }
1183
+ }
1184
+ }, []);
1185
+ // Streaming text display: set state directly per delta (Ink's 16ms render
1186
+ // throttle batches rapid updates). Cleared on message arrival (messages.ts)
1187
+ // so displayedMessages switches from deferredMessages to messages atomically.
1188
+ const [streamingText, setStreamingText] = useState(null);
1189
+ const reducedMotion = useAppState(s => s.settings.prefersReducedMotion) ?? false;
1190
+ const showStreamingText = !reducedMotion && !hasCursorUpViewportYankBug();
1191
+ const onStreamingText = useCallback((f) => {
1192
+ if (!showStreamingText)
1193
+ return;
1194
+ setStreamingText(f);
1195
+ }, [showStreamingText]);
1196
+ // Hide the in-progress source line so text streams line-by-line, not
1197
+ // char-by-char. lastIndexOf returns -1 when no newline, giving '' → null.
1198
+ // Guard on showStreamingText so toggling reducedMotion mid-stream
1199
+ // immediately hides the streaming preview.
1200
+ const visibleStreamingText = streamingText && showStreamingText ? streamingText.substring(0, streamingText.lastIndexOf('\n') + 1) || null : null;
1201
+ const [lastQueryCompletionTime, setLastQueryCompletionTime] = useState(0);
1202
+ const [spinnerMessage, setSpinnerMessage] = useState(null);
1203
+ const [spinnerColor, setSpinnerColor] = useState(null);
1204
+ const [spinnerShimmerColor, setSpinnerShimmerColor] = useState(null);
1205
+ const [isMessageSelectorVisible, setIsMessageSelectorVisible] = useState(false);
1206
+ const [messageSelectorPreselect, setMessageSelectorPreselect] = useState(undefined);
1207
+ const [showCostDialog, setShowCostDialog] = useState(false);
1208
+ const [conversationId, setConversationId] = useState(randomUUID());
1209
+ // Idle-return dialog: shown when user submits after a long idle gap
1210
+ const [idleReturnPending, setIdleReturnPending] = useState(null);
1211
+ const skipIdleCheckRef = useRef(false);
1212
+ const lastQueryCompletionTimeRef = useRef(lastQueryCompletionTime);
1213
+ lastQueryCompletionTimeRef.current = lastQueryCompletionTime;
1214
+ // Aggregate tool result budget: per-conversation decision tracking.
1215
+ // When the GrowthBook flag is on, query.ts enforces the budget; when
1216
+ // off (undefined), enforcement is skipped entirely. Stale entries after
1217
+ // /clear, rewind, or compact are harmless (tool_use_ids are UUIDs, stale
1218
+ // keys are never looked up). Memory is bounded by total replacement count
1219
+ // × ~2KB preview over the REPL lifetime — negligible.
1220
+ //
1221
+ // Lazy init via useState initializer — useRef(expr) evaluates expr on every
1222
+ // render (React ignores it after first, but the computation still runs).
1223
+ // For large resumed sessions, reconstruction does O(messages × blocks)
1224
+ // work; we only want that once.
1225
+ const [contentReplacementStateRef] = useState(() => ({
1226
+ current: provisionContentReplacementState(initialMessages, initialContentReplacements)
1227
+ }));
1228
+ const [haveShownCostDialog, setHaveShownCostDialog] = useState(getGlobalConfig().hasAcknowledgedCostThreshold);
1229
+ const [vimMode, setVimMode] = useState('INSERT');
1230
+ const [showBashesDialog, setShowBashesDialog] = useState(false);
1231
+ const [isSearchingHistory, setIsSearchingHistory] = useState(false);
1232
+ const [isHelpOpen, setIsHelpOpen] = useState(false);
1233
+ // showBashesDialog is REPL-level so it survives PromptInput unmounting.
1234
+ // When ultraplan approval fires while the pill dialog is open, PromptInput
1235
+ // unmounts (focusedInputDialog → 'ultraplan-choice') but this stays true;
1236
+ // after accepting, PromptInput remounts into an empty "No tasks" dialog
1237
+ // (the completed ultraplan task has been filtered out). Close it here.
1238
+ useEffect(() => {
1239
+ if (ultraplanPendingChoice && showBashesDialog) {
1240
+ setShowBashesDialog(false);
1241
+ }
1242
+ }, [ultraplanPendingChoice, showBashesDialog]);
1243
+ const isTerminalFocused = useTerminalFocus();
1244
+ const terminalFocusRef = useRef(isTerminalFocused);
1245
+ terminalFocusRef.current = isTerminalFocused;
1246
+ const [theme] = useTheme();
1247
+ // resetLoadingState runs twice per turn (onQueryImpl tail + onQuery finally).
1248
+ // Without this guard, both calls pick a tip → two recordShownTip → two
1249
+ // saveGlobalConfig writes back-to-back. Reset at submit in onSubmit.
1250
+ const tipPickedThisTurnRef = React.useRef(false);
1251
+ const pickNewSpinnerTip = useCallback(() => {
1252
+ if (tipPickedThisTurnRef.current)
1253
+ return;
1254
+ tipPickedThisTurnRef.current = true;
1255
+ const newMessages = messagesRef.current.slice(bashToolsProcessedIdx.current);
1256
+ for (const tool of extractBashToolsFromMessages(newMessages)) {
1257
+ bashTools.current.add(tool);
1258
+ }
1259
+ bashToolsProcessedIdx.current = messagesRef.current.length;
1260
+ void getTipToShowOnSpinner({
1261
+ theme,
1262
+ readFileState: readFileState.current,
1263
+ bashTools: bashTools.current
1264
+ }).then(async (tip) => {
1265
+ if (tip) {
1266
+ const content = await tip.content({
1267
+ theme
1268
+ });
1269
+ setAppState(prev => ({
1270
+ ...prev,
1271
+ spinnerTip: content
1272
+ }));
1273
+ recordShownTip(tip);
1274
+ }
1275
+ else {
1276
+ setAppState(prev => {
1277
+ if (prev.spinnerTip === undefined)
1278
+ return prev;
1279
+ return {
1280
+ ...prev,
1281
+ spinnerTip: undefined
1282
+ };
1283
+ });
1284
+ }
1285
+ });
1286
+ }, [setAppState, theme]);
1287
+ // Resets UI loading state. Does NOT call onTurnComplete - that should be
1288
+ // called explicitly only when a query turn actually completes.
1289
+ const resetLoadingState = useCallback(() => {
1290
+ // isLoading is now derived from queryGuard — no setter call needed.
1291
+ // queryGuard.end() (onQuery finally) or cancelReservation() (executeUserInput
1292
+ // finally) have already transitioned the guard to idle by the time this runs.
1293
+ // External loading (remote/backgrounding) is reset separately by those hooks.
1294
+ setIsExternalLoading(false);
1295
+ setUserInputOnProcessing(undefined);
1296
+ responseLengthRef.current = 0;
1297
+ apiMetricsRef.current = [];
1298
+ setStreamingText(null);
1299
+ setStreamingToolUses([]);
1300
+ setSpinnerMessage(null);
1301
+ setSpinnerColor(null);
1302
+ setSpinnerShimmerColor(null);
1303
+ pickNewSpinnerTip();
1304
+ endInteractionSpan();
1305
+ // Speculative bash classifier checks are only valid for the current
1306
+ // turn's commands — clear after each turn to avoid accumulating
1307
+ // Promise chains for unconsumed checks (denied/aborted paths).
1308
+ clearSpeculativeChecks();
1309
+ }, [pickNewSpinnerTip]);
1310
+ // Session backgrounding — hook is below, after getToolUseContext
1311
+ const hasRunningTeammates = useMemo(() => getAllInProcessTeammateTasks(tasks).some(t => t.status === 'running'), [tasks]);
1312
+ // Show deferred turn duration message once all swarm teammates finish
1313
+ useEffect(() => {
1314
+ if (!hasRunningTeammates && swarmStartTimeRef.current !== null) {
1315
+ const totalMs = Date.now() - swarmStartTimeRef.current;
1316
+ const deferredBudget = swarmBudgetInfoRef.current;
1317
+ swarmStartTimeRef.current = null;
1318
+ swarmBudgetInfoRef.current = undefined;
1319
+ setMessages(prev => [...prev, createTurnDurationMessage(totalMs, deferredBudget,
1320
+ // Count only what recordTranscript will persist — ephemeral
1321
+ // progress ticks and non-ant attachments are filtered by
1322
+ // isLoggableMessage and never reach disk. Using raw prev.length
1323
+ // would make checkResumeConsistency report false delta<0 for
1324
+ // every turn that ran a progress-emitting tool.
1325
+ count(prev, isLoggableMessage))]);
1326
+ }
1327
+ }, [hasRunningTeammates, setMessages]);
1328
+ // Show auto permissions warning when entering auto mode
1329
+ // (either via Shift+Tab toggle or on startup). Debounced to avoid
1330
+ // flashing when the user is cycling through modes quickly.
1331
+ // Only shown 3 times total across sessions.
1332
+ const safeYoloMessageShownRef = useRef(false);
1333
+ useEffect(() => {
1334
+ if (feature('TRANSCRIPT_CLASSIFIER')) {
1335
+ if (toolPermissionContext.mode !== 'auto') {
1336
+ safeYoloMessageShownRef.current = false;
1337
+ return;
1338
+ }
1339
+ if (safeYoloMessageShownRef.current)
1340
+ return;
1341
+ const config = getGlobalConfig();
1342
+ const count = config.autoPermissionsNotificationCount ?? 0;
1343
+ if (count >= 3)
1344
+ return;
1345
+ const timer = setTimeout((ref, setMessages) => {
1346
+ ref.current = true;
1347
+ saveGlobalConfig(prev => {
1348
+ const prevCount = prev.autoPermissionsNotificationCount ?? 0;
1349
+ if (prevCount >= 3)
1350
+ return prev;
1351
+ return {
1352
+ ...prev,
1353
+ autoPermissionsNotificationCount: prevCount + 1
1354
+ };
1355
+ });
1356
+ setMessages(prev => [...prev, createSystemMessage(AUTO_MODE_DESCRIPTION, 'warning')]);
1357
+ }, 800, safeYoloMessageShownRef, setMessages);
1358
+ return () => clearTimeout(timer);
1359
+ }
1360
+ }, [toolPermissionContext.mode, setMessages]);
1361
+ // If worktree creation was slow and sparse-checkout isn't configured,
1362
+ // nudge the user toward settings.worktree.sparsePaths.
1363
+ const worktreeTipShownRef = useRef(false);
1364
+ useEffect(() => {
1365
+ if (worktreeTipShownRef.current)
1366
+ return;
1367
+ const wt = getCurrentWorktreeSession();
1368
+ if (!wt?.creationDurationMs || wt.usedSparsePaths)
1369
+ return;
1370
+ if (wt.creationDurationMs < 15_000)
1371
+ return;
1372
+ worktreeTipShownRef.current = true;
1373
+ const secs = Math.round(wt.creationDurationMs / 1000);
1374
+ setMessages(prev => [...prev, createSystemMessage(`Worktree creation took ${secs}s. For large repos, set \`worktree.sparsePaths\` in .claude/settings.json to check out only the directories you need — e.g. \`{"worktree": {"sparsePaths": ["src", "packages/foo"]}}\`.`, 'info')]);
1375
+ }, [setMessages]);
1376
+ // Hide spinner when the only in-progress tool is Sleep
1377
+ const onlySleepToolActive = useMemo(() => {
1378
+ const lastAssistant = messages.findLast(m => m.type === 'assistant');
1379
+ if (lastAssistant?.type !== 'assistant')
1380
+ return false;
1381
+ const inProgressToolUses = lastAssistant.message.content.filter(b => b.type === 'tool_use' && inProgressToolUseIDs.has(b.id));
1382
+ return inProgressToolUses.length > 0 && inProgressToolUses.every(b => b.type === 'tool_use' && b.name === SLEEP_TOOL_NAME);
1383
+ }, [messages, inProgressToolUseIDs]);
1384
+ const { onBeforeQuery: mrOnBeforeQuery, onTurnComplete: mrOnTurnComplete, render: mrRender } = useMoreRight({
1385
+ enabled: moreRightEnabled,
1386
+ setMessages,
1387
+ inputValue,
1388
+ setInputValue,
1389
+ setToolJSX
1390
+ });
1391
+ const showSpinner = (!toolJSX || toolJSX.showSpinner === true) && toolUseConfirmQueue.length === 0 && promptQueue.length === 0 && (
1392
+ // Show spinner during input processing, API call, while teammates are running,
1393
+ // or while pending task notifications are queued (prevents spinner bounce between consecutive notifications)
1394
+ isLoading || userInputOnProcessing || hasRunningTeammates ||
1395
+ // Keep spinner visible while task notifications are queued for processing.
1396
+ // Without this, the spinner briefly disappears between consecutive notifications
1397
+ // (e.g., multiple background agents completing in rapid succession) because
1398
+ // isLoading goes false momentarily between processing each one.
1399
+ getCommandQueueLength() > 0) &&
1400
+ // Hide spinner when waiting for leader to approve permission request
1401
+ !pendingWorkerRequest && !onlySleepToolActive && (
1402
+ // Hide spinner when streaming text is visible (the text IS the feedback),
1403
+ // but keep it when isBriefOnly suppresses the streaming text display
1404
+ !visibleStreamingText || isBriefOnly);
1405
+ // Check if any permission or ask question prompt is currently visible
1406
+ // This is used to prevent the survey from opening while prompts are active
1407
+ const hasActivePrompt = toolUseConfirmQueue.length > 0 || promptQueue.length > 0 || sandboxPermissionRequestQueue.length > 0 || elicitation.queue.length > 0 || workerSandboxPermissions.queue.length > 0;
1408
+ const feedbackSurveyOriginal = useFeedbackSurvey(messages, isLoading, submitCount, 'session', hasActivePrompt);
1409
+ const skillImprovementSurvey = useSkillImprovementSurvey(setMessages);
1410
+ const showIssueFlagBanner = useIssueFlagBanner(messages, submitCount);
1411
+ // Wrap feedback survey handler to trigger auto-run /issue
1412
+ const feedbackSurvey = useMemo(() => ({
1413
+ ...feedbackSurveyOriginal,
1414
+ handleSelect: (selected) => {
1415
+ // Reset the ref when a new survey response comes in
1416
+ didAutoRunIssueRef.current = false;
1417
+ const showedTranscriptPrompt = feedbackSurveyOriginal.handleSelect(selected);
1418
+ // Auto-run /issue for "bad" if transcript prompt wasn't shown
1419
+ if (selected === 'bad' && !showedTranscriptPrompt && shouldAutoRunIssue('feedback_survey_bad')) {
1420
+ setAutoRunIssueReason('feedback_survey_bad');
1421
+ didAutoRunIssueRef.current = true;
1422
+ }
1423
+ }
1424
+ }), [feedbackSurveyOriginal]);
1425
+ // Post-compact survey: shown after compaction if feature gate is enabled
1426
+ const postCompactSurvey = usePostCompactSurvey(messages, isLoading, hasActivePrompt, {
1427
+ enabled: !isRemoteSession
1428
+ });
1429
+ // Memory survey: shown when the assistant mentions memory and a memory file
1430
+ // was read this conversation
1431
+ const memorySurvey = useMemorySurvey(messages, isLoading, hasActivePrompt, {
1432
+ enabled: !isRemoteSession
1433
+ });
1434
+ // Frustration detection: show transcript sharing prompt after detecting frustrated messages
1435
+ const frustrationDetection = useFrustrationDetection(messages, isLoading, hasActivePrompt, feedbackSurvey.state !== 'closed' || postCompactSurvey.state !== 'closed' || memorySurvey.state !== 'closed');
1436
+ // Initialize IDE integration
1437
+ useIDEIntegration({
1438
+ autoConnectIdeFlag,
1439
+ ideToInstallExtension,
1440
+ setDynamicMcpConfig,
1441
+ setShowIdeOnboarding,
1442
+ setIDEInstallationState: setIDEInstallationStatus
1443
+ });
1444
+ useFileHistorySnapshotInit(initialFileHistorySnapshots, fileHistory, fileHistoryState => setAppState(prev => ({
1445
+ ...prev,
1446
+ fileHistory: fileHistoryState
1447
+ })));
1448
+ const resume = useCallback(async (sessionId, log, entrypoint) => {
1449
+ const resumeStart = performance.now();
1450
+ try {
1451
+ // Deserialize messages to properly clean up the conversation
1452
+ // This filters unresolved tool uses and adds a synthetic assistant message if needed
1453
+ const messages = deserializeMessages(log.messages);
1454
+ // Match coordinator/normal mode to the resumed session
1455
+ if (feature('COORDINATOR_MODE')) {
1456
+ /* eslint-disable @typescript-eslint/no-require-imports */
1457
+ const coordinatorModule = require('../coordinator/coordinatorMode.js');
1458
+ /* eslint-enable @typescript-eslint/no-require-imports */
1459
+ const warning = coordinatorModule.matchSessionMode(log.mode);
1460
+ if (warning) {
1461
+ // Re-derive agent definitions after mode switch so built-in agents
1462
+ // reflect the new coordinator/normal mode
1463
+ /* eslint-disable @typescript-eslint/no-require-imports */
1464
+ const { getAgentDefinitionsWithOverrides, getActiveAgentsFromList } = require('../tools/AgentTool/loadAgentsDir.js');
1465
+ /* eslint-enable @typescript-eslint/no-require-imports */
1466
+ getAgentDefinitionsWithOverrides.cache.clear?.();
1467
+ const freshAgentDefs = await getAgentDefinitionsWithOverrides(getOriginalCwd());
1468
+ setAppState(prev => ({
1469
+ ...prev,
1470
+ agentDefinitions: {
1471
+ ...freshAgentDefs,
1472
+ allAgents: freshAgentDefs.allAgents,
1473
+ activeAgents: getActiveAgentsFromList(freshAgentDefs.allAgents)
1474
+ }
1475
+ }));
1476
+ messages.push(createSystemMessage(warning, 'warning'));
1477
+ }
1478
+ }
1479
+ // Fire SessionEnd hooks for the current session before starting the
1480
+ // resumed one, mirroring the /clear flow in conversation.ts.
1481
+ const sessionEndTimeoutMs = getSessionEndHookTimeoutMs();
1482
+ await executeSessionEndHooks('resume', {
1483
+ getAppState: () => store.getState(),
1484
+ setAppState,
1485
+ signal: AbortSignal.timeout(sessionEndTimeoutMs),
1486
+ timeoutMs: sessionEndTimeoutMs
1487
+ });
1488
+ // Process session start hooks for resume
1489
+ const hookMessages = await processSessionStartHooks('resume', {
1490
+ sessionId,
1491
+ agentType: mainThreadAgentDefinition?.agentType,
1492
+ model: mainLoopModel
1493
+ });
1494
+ // Append hook messages to the conversation
1495
+ messages.push(...hookMessages);
1496
+ // For forks, generate a new plan slug and copy the plan content so the
1497
+ // original and forked sessions don't clobber each other's plan files.
1498
+ // For regular resumes, reuse the original session's plan slug.
1499
+ if (entrypoint === 'fork') {
1500
+ void copyPlanForFork(log, asSessionId(sessionId));
1501
+ }
1502
+ else {
1503
+ void copyPlanForResume(log, asSessionId(sessionId));
1504
+ }
1505
+ // Restore file history and attribution state from the resumed conversation
1506
+ restoreSessionStateFromLog(log, setAppState);
1507
+ if (log.fileHistorySnapshots) {
1508
+ void copyFileHistoryForResume(log);
1509
+ }
1510
+ // Restore agent setting from the resumed conversation
1511
+ // Always reset to the new session's values (or clear if none),
1512
+ // matching the standaloneAgentContext pattern below
1513
+ const { agentDefinition: restoredAgent } = restoreAgentFromSession(log.agentSetting, initialMainThreadAgentDefinition, agentDefinitions);
1514
+ setMainThreadAgentDefinition(restoredAgent);
1515
+ setAppState(prev => ({
1516
+ ...prev,
1517
+ agent: restoredAgent?.agentType
1518
+ }));
1519
+ // Restore standalone agent context from the resumed conversation
1520
+ // Always reset to the new session's values (or clear if none)
1521
+ setAppState(prev => ({
1522
+ ...prev,
1523
+ standaloneAgentContext: computeStandaloneAgentContext(log.agentName, log.agentColor)
1524
+ }));
1525
+ void updateSessionName(log.agentName);
1526
+ // Restore read file state from the message history
1527
+ restoreReadFileState(messages, log.projectPath ?? getOriginalCwd());
1528
+ // Clear any active loading state (no queryId since we're not in a query)
1529
+ resetLoadingState();
1530
+ setAbortController(null);
1531
+ setConversationId(sessionId);
1532
+ // Get target session's costs BEFORE saving current session
1533
+ // (saveCurrentSessionCosts overwrites the config, so we need to read first)
1534
+ const targetSessionCosts = getStoredSessionCosts(sessionId);
1535
+ // Save current session's costs before switching to avoid losing accumulated costs
1536
+ saveCurrentSessionCosts();
1537
+ // Reset cost state for clean slate before restoring target session
1538
+ resetCostState();
1539
+ // Switch session (id + project dir atomically). fullPath may point to
1540
+ // a different project (cross-worktree, /branch); null derives from
1541
+ // current originalCwd.
1542
+ switchSession(asSessionId(sessionId), log.fullPath ? dirname(log.fullPath) : null);
1543
+ // Rename asciicast recording to match the resumed session ID
1544
+ const { renameRecordingForSession } = await import('../utils/asciicast.js');
1545
+ await renameRecordingForSession();
1546
+ await resetSessionFilePointer();
1547
+ // Clear then restore session metadata so it's re-appended on exit via
1548
+ // reAppendSessionMetadata. clearSessionMetadata must be called first:
1549
+ // restoreSessionMetadata only sets-if-truthy, so without the clear,
1550
+ // a session without an agent name would inherit the previous session's
1551
+ // cached name and write it to the wrong transcript on first message.
1552
+ clearSessionMetadata();
1553
+ restoreSessionMetadata(log);
1554
+ // Resumed sessions shouldn't re-title from mid-conversation context
1555
+ // (same reasoning as the useRef seed), and the previous session's
1556
+ // Haiku title shouldn't carry over.
1557
+ haikuTitleAttemptedRef.current = true;
1558
+ setHaikuTitle(undefined);
1559
+ // Exit any worktree a prior /resume entered, then cd into the one
1560
+ // this session was in. Without the exit, resuming from worktree B
1561
+ // to non-worktree C leaves cwd/currentWorktreeSession stale;
1562
+ // resuming B→C where C is also a worktree fails entirely
1563
+ // (getCurrentWorktreeSession guard blocks the switch).
1564
+ //
1565
+ // Skipped for /branch: forkLog doesn't carry worktreeSession, so
1566
+ // this would kick the user out of a worktree they're still working
1567
+ // in. Same fork skip as processResumedConversation for the adopt —
1568
+ // fork materializes its own file via recordTranscript on REPL mount.
1569
+ if (entrypoint !== 'fork') {
1570
+ exitRestoredWorktree();
1571
+ restoreWorktreeForResume(log.worktreeSession);
1572
+ adoptResumedSessionFile();
1573
+ void restoreRemoteAgentTasks({
1574
+ abortController: new AbortController(),
1575
+ getAppState: () => store.getState(),
1576
+ setAppState
1577
+ });
1578
+ }
1579
+ else {
1580
+ // Fork: same re-persist as /clear (conversation.ts). The clear
1581
+ // above wiped currentSessionWorktree, forkLog doesn't carry it,
1582
+ // and the process is still in the same worktree.
1583
+ const ws = getCurrentWorktreeSession();
1584
+ if (ws)
1585
+ saveWorktreeState(ws);
1586
+ }
1587
+ // Persist the current mode so future resumes know what mode this session was in
1588
+ if (feature('COORDINATOR_MODE')) {
1589
+ /* eslint-disable @typescript-eslint/no-require-imports */
1590
+ const { saveMode } = require('../utils/sessionStorage.js');
1591
+ const { isCoordinatorMode } = require('../coordinator/coordinatorMode.js');
1592
+ /* eslint-enable @typescript-eslint/no-require-imports */
1593
+ saveMode(isCoordinatorMode() ? 'coordinator' : 'normal');
1594
+ }
1595
+ // Restore target session's costs from the data we read earlier
1596
+ if (targetSessionCosts) {
1597
+ setCostStateForRestore(targetSessionCosts);
1598
+ }
1599
+ // Reconstruct replacement state for the resumed session. Runs after
1600
+ // setSessionId so any NEW replacements post-resume write to the
1601
+ // resumed session's tool-results dir. Gated on ref.current: the
1602
+ // initial mount already read the feature flag, so we don't re-read
1603
+ // it here (mid-session flag flips stay unobservable in both
1604
+ // directions).
1605
+ //
1606
+ // Skipped for in-session /branch: the existing ref is already correct
1607
+ // (branch preserves tool_use_ids), so there's no need to reconstruct.
1608
+ // createFork() does write content-replacement entries to the forked
1609
+ // JSONL with the fork's sessionId, so `claude -r {forkId}` also works.
1610
+ if (contentReplacementStateRef.current && entrypoint !== 'fork') {
1611
+ contentReplacementStateRef.current = reconstructContentReplacementState(messages, log.contentReplacements ?? []);
1612
+ }
1613
+ // Reset messages to the provided initial messages
1614
+ // Use a callback to ensure we're not dependent on stale state
1615
+ setMessages(() => messages);
1616
+ // Clear any active tool JSX
1617
+ setToolJSX(null);
1618
+ // Clear input to ensure no residual state
1619
+ setInputValue('');
1620
+ logEvent('tengu_session_resumed', {
1621
+ entrypoint: entrypoint,
1622
+ success: true,
1623
+ resume_duration_ms: Math.round(performance.now() - resumeStart)
1624
+ });
1625
+ }
1626
+ catch (error) {
1627
+ logEvent('tengu_session_resumed', {
1628
+ entrypoint: entrypoint,
1629
+ success: false
1630
+ });
1631
+ throw error;
1632
+ }
1633
+ }, [resetLoadingState, setAppState]);
1634
+ // Lazy init: useRef(createX()) would call createX on every render and
1635
+ // discard the result. LRUCache construction inside FileStateCache is
1636
+ // expensive (~170ms), so we use useState's lazy initializer to create
1637
+ // it exactly once, then feed that stable reference into useRef.
1638
+ const [initialReadFileState] = useState(() => createFileStateCacheWithSizeLimit(READ_FILE_STATE_CACHE_SIZE));
1639
+ const readFileState = useRef(initialReadFileState);
1640
+ const bashTools = useRef(new Set());
1641
+ const bashToolsProcessedIdx = useRef(0);
1642
+ // Session-scoped skill discovery tracking (feeds was_discovered on
1643
+ // tengu_skill_tool_invocation). Must persist across getToolUseContext
1644
+ // rebuilds within a session: turn-0 discovery writes via processUserInput
1645
+ // before onQuery builds its own context, and discovery on turn N must
1646
+ // still attribute a SkillTool call on turn N+k. Cleared in clearConversation.
1647
+ const discoveredSkillNamesRef = useRef(new Set());
1648
+ // Session-level dedup for nested_memory CLAUDE.md attachments.
1649
+ // readFileState is a 100-entry LRU; once it evicts a CLAUDE.md path,
1650
+ // the next discovery cycle re-injects it. Cleared in clearConversation.
1651
+ const loadedNestedMemoryPathsRef = useRef(new Set());
1652
+ // Helper to restore read file state from messages (used for resume flows)
1653
+ // This allows Claude to edit files that were read in previous sessions
1654
+ const restoreReadFileState = useCallback((messages, cwd) => {
1655
+ const extracted = extractReadFilesFromMessages(messages, cwd, READ_FILE_STATE_CACHE_SIZE);
1656
+ readFileState.current = mergeFileStateCaches(readFileState.current, extracted);
1657
+ for (const tool of extractBashToolsFromMessages(messages)) {
1658
+ bashTools.current.add(tool);
1659
+ }
1660
+ }, []);
1661
+ // Extract read file state from initialMessages on mount
1662
+ // This handles CLI flag resume (--resume-session) and ResumeConversation screen
1663
+ // where messages are passed as props rather than through the resume callback
1664
+ useEffect(() => {
1665
+ if (initialMessages && initialMessages.length > 0) {
1666
+ restoreReadFileState(initialMessages, getOriginalCwd());
1667
+ void restoreRemoteAgentTasks({
1668
+ abortController: new AbortController(),
1669
+ getAppState: () => store.getState(),
1670
+ setAppState
1671
+ });
1672
+ }
1673
+ // Only run on mount - initialMessages shouldn't change during component lifetime
1674
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1675
+ }, []);
1676
+ const { status: apiKeyStatus, reverify } = useApiKeyVerification();
1677
+ // Auto-run /issue state
1678
+ const [autoRunIssueReason, setAutoRunIssueReason] = useState(null);
1679
+ // Ref to track if autoRunIssue was triggered this survey cycle,
1680
+ // so we can suppress the [1] follow-up prompt even after
1681
+ // autoRunIssueReason is cleared.
1682
+ const didAutoRunIssueRef = useRef(false);
1683
+ // State for exit feedback flow
1684
+ const [exitFlow, setExitFlow] = useState(null);
1685
+ const [isExiting, setIsExiting] = useState(false);
1686
+ // Calculate if cost dialog should be shown
1687
+ const showingCostDialog = !isLoading && showCostDialog;
1688
+ // Determine which dialog should have focus (if any)
1689
+ // Permission and interactive dialogs can show even when toolJSX is set,
1690
+ // as long as shouldContinueAnimation is true. This prevents deadlocks when
1691
+ // agents set background hints while waiting for user interaction.
1692
+ function getFocusedInputDialog() {
1693
+ // Exit states always take precedence
1694
+ if (isExiting || exitFlow)
1695
+ return undefined;
1696
+ // High priority dialogs (always show regardless of typing)
1697
+ if (isMessageSelectorVisible)
1698
+ return 'message-selector';
1699
+ // Suppress interrupt dialogs while user is actively typing
1700
+ if (isPromptInputActive)
1701
+ return undefined;
1702
+ if (sandboxPermissionRequestQueue[0])
1703
+ return 'sandbox-permission';
1704
+ // Permission/interactive dialogs (show unless blocked by toolJSX)
1705
+ const allowDialogsWithAnimation = !toolJSX || toolJSX.shouldContinueAnimation;
1706
+ if (allowDialogsWithAnimation && toolUseConfirmQueue[0])
1707
+ return 'tool-permission';
1708
+ if (allowDialogsWithAnimation && promptQueue[0])
1709
+ return 'prompt';
1710
+ // Worker sandbox permission prompts (network access) from swarm workers
1711
+ if (allowDialogsWithAnimation && workerSandboxPermissions.queue[0])
1712
+ return 'worker-sandbox-permission';
1713
+ if (allowDialogsWithAnimation && elicitation.queue[0])
1714
+ return 'elicitation';
1715
+ if (allowDialogsWithAnimation && showingCostDialog)
1716
+ return 'cost';
1717
+ if (allowDialogsWithAnimation && idleReturnPending)
1718
+ return 'idle-return';
1719
+ if (feature('ULTRAPLAN') && allowDialogsWithAnimation && !isLoading && ultraplanPendingChoice)
1720
+ return 'ultraplan-choice';
1721
+ if (feature('ULTRAPLAN') && allowDialogsWithAnimation && !isLoading && ultraplanLaunchPending)
1722
+ return 'ultraplan-launch';
1723
+ // Onboarding dialogs (special conditions)
1724
+ if (allowDialogsWithAnimation && showIdeOnboarding)
1725
+ return 'ide-onboarding';
1726
+ // Model switch callout (ant-only, eliminated from external builds)
1727
+ if ("external" === 'ant' && allowDialogsWithAnimation && showModelSwitchCallout)
1728
+ return 'model-switch';
1729
+ // Undercover auto-enable explainer (ant-only, eliminated from external builds)
1730
+ if ("external" === 'ant' && allowDialogsWithAnimation && showUndercoverCallout)
1731
+ return 'undercover-callout';
1732
+ // Effort callout (shown once for Opus 4.6 users when effort is enabled)
1733
+ if (allowDialogsWithAnimation && showEffortCallout)
1734
+ return 'effort-callout';
1735
+ // Remote callout (shown once before first bridge enable)
1736
+ if (allowDialogsWithAnimation && showRemoteCallout)
1737
+ return 'remote-callout';
1738
+ // LSP plugin recommendation (lowest priority - non-blocking suggestion)
1739
+ if (allowDialogsWithAnimation && lspRecommendation)
1740
+ return 'lsp-recommendation';
1741
+ // Plugin hint from CLI/SDK stderr (same priority band as LSP rec)
1742
+ if (allowDialogsWithAnimation && hintRecommendation)
1743
+ return 'plugin-hint';
1744
+ // Desktop app upsell (max 3 launches, lowest priority)
1745
+ if (allowDialogsWithAnimation && showDesktopUpsellStartup)
1746
+ return 'desktop-upsell';
1747
+ return undefined;
1748
+ }
1749
+ const focusedInputDialog = getFocusedInputDialog();
1750
+ // True when permission prompts exist but are hidden because the user is typing
1751
+ const hasSuppressedDialogs = isPromptInputActive && (sandboxPermissionRequestQueue[0] || toolUseConfirmQueue[0] || promptQueue[0] || workerSandboxPermissions.queue[0] || elicitation.queue[0] || showingCostDialog);
1752
+ // Keep ref in sync so timer callbacks can read the current value
1753
+ focusedInputDialogRef.current = focusedInputDialog;
1754
+ // Immediately capture pause/resume when focusedInputDialog changes
1755
+ // This ensures accurate timing even under high system load, rather than
1756
+ // relying on the 100ms polling interval to detect state changes
1757
+ useEffect(() => {
1758
+ if (!isLoading)
1759
+ return;
1760
+ const isPaused = focusedInputDialog === 'tool-permission';
1761
+ const now = Date.now();
1762
+ if (isPaused && pauseStartTimeRef.current === null) {
1763
+ // Just entered pause state - record the exact moment
1764
+ pauseStartTimeRef.current = now;
1765
+ }
1766
+ else if (!isPaused && pauseStartTimeRef.current !== null) {
1767
+ // Just exited pause state - accumulate paused time immediately
1768
+ totalPausedMsRef.current += now - pauseStartTimeRef.current;
1769
+ pauseStartTimeRef.current = null;
1770
+ }
1771
+ }, [focusedInputDialog, isLoading]);
1772
+ // Re-pin scroll to bottom whenever the permission overlay appears or
1773
+ // dismisses. Overlay now renders below messages inside the same
1774
+ // ScrollBox (no remount), so we need an explicit scrollToBottom for:
1775
+ // - appear: user may have been scrolled up (sticky broken) — the
1776
+ // dialog is blocking and must be visible
1777
+ // - dismiss: user may have scrolled up to read context during the
1778
+ // overlay, and onScroll was suppressed so the pill state is stale
1779
+ // useLayoutEffect so the re-pin commits before the Ink frame renders —
1780
+ // no 1-frame flash of the wrong scroll position.
1781
+ const prevDialogRef = useRef(focusedInputDialog);
1782
+ useLayoutEffect(() => {
1783
+ const was = prevDialogRef.current === 'tool-permission';
1784
+ const now = focusedInputDialog === 'tool-permission';
1785
+ if (was !== now)
1786
+ repinScroll();
1787
+ prevDialogRef.current = focusedInputDialog;
1788
+ }, [focusedInputDialog, repinScroll]);
1789
+ function onCancel() {
1790
+ if (focusedInputDialog === 'elicitation') {
1791
+ // Elicitation dialog handles its own Escape, and closing it shouldn't affect any loading state.
1792
+ return;
1793
+ }
1794
+ logForDebugging(`[onCancel] focusedInputDialog=${focusedInputDialog} streamMode=${streamMode}`);
1795
+ // Pause proactive mode so the user gets control back.
1796
+ // It will resume when they submit their next input (see onSubmit).
1797
+ if (feature('PROACTIVE') || feature('KAIROS')) {
1798
+ proactiveModule?.pauseProactive();
1799
+ }
1800
+ queryGuard.forceEnd();
1801
+ skipIdleCheckRef.current = false;
1802
+ // Preserve partially-streamed text so the user can read what was
1803
+ // generated before pressing Esc. Pushed before resetLoadingState clears
1804
+ // streamingText, and before query.ts yields the async interrupt marker,
1805
+ // giving final order [user, partial-assistant, [Request interrupted by user]].
1806
+ if (streamingText?.trim()) {
1807
+ setMessages(prev => [...prev, createAssistantMessage({
1808
+ content: streamingText
1809
+ })]);
1810
+ }
1811
+ resetLoadingState();
1812
+ // Clear any active token budget so the backstop doesn't fire on
1813
+ // a stale budget if the query generator hasn't exited yet.
1814
+ if (feature('TOKEN_BUDGET')) {
1815
+ snapshotOutputTokensForTurn(null);
1816
+ }
1817
+ if (focusedInputDialog === 'tool-permission') {
1818
+ // Tool use confirm handles the abort signal itself
1819
+ toolUseConfirmQueue[0]?.onAbort();
1820
+ setToolUseConfirmQueue([]);
1821
+ }
1822
+ else if (focusedInputDialog === 'prompt') {
1823
+ // Reject all pending prompts and clear the queue
1824
+ for (const item of promptQueue) {
1825
+ item.reject(new Error('Prompt cancelled by user'));
1826
+ }
1827
+ setPromptQueue([]);
1828
+ abortController?.abort('user-cancel');
1829
+ }
1830
+ else if (activeRemote.isRemoteMode) {
1831
+ // Remote mode: send interrupt signal to CCR
1832
+ activeRemote.cancelRequest();
1833
+ }
1834
+ else {
1835
+ abortController?.abort('user-cancel');
1836
+ }
1837
+ // Clear the controller so subsequent Escape presses don't see a stale
1838
+ // aborted signal. Without this, canCancelRunningTask is false (signal
1839
+ // defined but .aborted === true), so isActive becomes false if no other
1840
+ // activating conditions hold — leaving the Escape keybinding inactive.
1841
+ setAbortController(null);
1842
+ // forceEnd() skips the finally path — fire directly (aborted=true).
1843
+ void mrOnTurnComplete(messagesRef.current, true);
1844
+ }
1845
+ // Function to handle queued command when canceling a permission request
1846
+ const handleQueuedCommandOnCancel = useCallback(() => {
1847
+ const result = popAllEditable(inputValue, 0);
1848
+ if (!result)
1849
+ return;
1850
+ setInputValue(result.text);
1851
+ setInputMode('prompt');
1852
+ // Restore images from queued commands to pastedContents
1853
+ if (result.images.length > 0) {
1854
+ setPastedContents(prev => {
1855
+ const newContents = {
1856
+ ...prev
1857
+ };
1858
+ for (const image of result.images) {
1859
+ newContents[image.id] = image;
1860
+ }
1861
+ return newContents;
1862
+ });
1863
+ }
1864
+ }, [setInputValue, setInputMode, inputValue, setPastedContents]);
1865
+ // CancelRequestHandler props - rendered inside KeybindingSetup
1866
+ const cancelRequestProps = {
1867
+ setToolUseConfirmQueue,
1868
+ onCancel,
1869
+ onAgentsKilled: () => setMessages(prev => [...prev, createAgentsKilledMessage()]),
1870
+ isMessageSelectorVisible: isMessageSelectorVisible || !!showBashesDialog,
1871
+ screen,
1872
+ abortSignal: abortController?.signal,
1873
+ popCommandFromQueue: handleQueuedCommandOnCancel,
1874
+ vimMode,
1875
+ isLocalJSXCommand: toolJSX?.isLocalJSXCommand,
1876
+ isSearchingHistory,
1877
+ isHelpOpen,
1878
+ inputMode,
1879
+ inputValue,
1880
+ streamMode
1881
+ };
1882
+ useEffect(() => {
1883
+ const totalCost = getTotalCost();
1884
+ if (totalCost >= 5 /* $5 */ && !showCostDialog && !haveShownCostDialog) {
1885
+ logEvent('tengu_cost_threshold_reached', {});
1886
+ // Mark as shown even if the dialog won't render (no console billing
1887
+ // access). Otherwise this effect re-fires on every message change for
1888
+ // the rest of the session — 200k+ spurious events observed.
1889
+ setHaveShownCostDialog(true);
1890
+ if (hasConsoleBillingAccess()) {
1891
+ setShowCostDialog(true);
1892
+ }
1893
+ }
1894
+ }, [messages, showCostDialog, haveShownCostDialog]);
1895
+ const sandboxAskCallback = useCallback(async (hostPattern) => {
1896
+ // If running as a swarm worker, forward the request to the leader via mailbox
1897
+ if (isAgentSwarmsEnabled() && isSwarmWorker()) {
1898
+ const requestId = generateSandboxRequestId();
1899
+ // Send the request to the leader via mailbox
1900
+ const sent = await sendSandboxPermissionRequestViaMailbox(hostPattern.host, requestId);
1901
+ return new Promise(resolveShouldAllowHost => {
1902
+ if (!sent) {
1903
+ // If we couldn't send via mailbox, fall back to local handling
1904
+ setSandboxPermissionRequestQueue(prev => [...prev, {
1905
+ hostPattern,
1906
+ resolvePromise: resolveShouldAllowHost
1907
+ }]);
1908
+ return;
1909
+ }
1910
+ // Register the callback for when the leader responds
1911
+ registerSandboxPermissionCallback({
1912
+ requestId,
1913
+ host: hostPattern.host,
1914
+ resolve: resolveShouldAllowHost
1915
+ });
1916
+ // Update AppState to show pending indicator
1917
+ setAppState(prev => ({
1918
+ ...prev,
1919
+ pendingSandboxRequest: {
1920
+ requestId,
1921
+ host: hostPattern.host
1922
+ }
1923
+ }));
1924
+ });
1925
+ }
1926
+ // Normal flow for non-workers: show local UI and optionally race
1927
+ // against the REPL bridge (Remote Control) if connected.
1928
+ return new Promise(resolveShouldAllowHost => {
1929
+ let resolved = false;
1930
+ function resolveOnce(allow) {
1931
+ if (resolved)
1932
+ return;
1933
+ resolved = true;
1934
+ resolveShouldAllowHost(allow);
1935
+ }
1936
+ // Queue the local sandbox permission dialog
1937
+ setSandboxPermissionRequestQueue(prev => [...prev, {
1938
+ hostPattern,
1939
+ resolvePromise: resolveOnce
1940
+ }]);
1941
+ // When the REPL bridge is connected, also forward the sandbox
1942
+ // permission request as a can_use_tool control_request so the
1943
+ // remote user (e.g. on claude.ai) can approve it too.
1944
+ if (feature('BRIDGE_MODE')) {
1945
+ const bridgeCallbacks = store.getState().replBridgePermissionCallbacks;
1946
+ if (bridgeCallbacks) {
1947
+ const bridgeRequestId = randomUUID();
1948
+ bridgeCallbacks.sendRequest(bridgeRequestId, SANDBOX_NETWORK_ACCESS_TOOL_NAME, {
1949
+ host: hostPattern.host
1950
+ }, randomUUID(), `Allow network connection to ${hostPattern.host}?`);
1951
+ const unsubscribe = bridgeCallbacks.onResponse(bridgeRequestId, response => {
1952
+ unsubscribe();
1953
+ const allow = response.behavior === 'allow';
1954
+ // Resolve ALL pending requests for the same host, not just
1955
+ // this one — mirrors the local dialog handler pattern.
1956
+ setSandboxPermissionRequestQueue(queue => {
1957
+ queue.filter(item => item.hostPattern.host === hostPattern.host).forEach(item => item.resolvePromise(allow));
1958
+ return queue.filter(item => item.hostPattern.host !== hostPattern.host);
1959
+ });
1960
+ // Clean up all sibling bridge subscriptions for this host
1961
+ // (other concurrent same-host requests) before deleting.
1962
+ const siblingCleanups = sandboxBridgeCleanupRef.current.get(hostPattern.host);
1963
+ if (siblingCleanups) {
1964
+ for (const fn of siblingCleanups) {
1965
+ fn();
1966
+ }
1967
+ sandboxBridgeCleanupRef.current.delete(hostPattern.host);
1968
+ }
1969
+ });
1970
+ // Register cleanup so the local dialog handler can cancel
1971
+ // the remote prompt and unsubscribe when the local user
1972
+ // responds first.
1973
+ const cleanup = () => {
1974
+ unsubscribe();
1975
+ bridgeCallbacks.cancelRequest(bridgeRequestId);
1976
+ };
1977
+ const existing = sandboxBridgeCleanupRef.current.get(hostPattern.host) ?? [];
1978
+ existing.push(cleanup);
1979
+ sandboxBridgeCleanupRef.current.set(hostPattern.host, existing);
1980
+ }
1981
+ }
1982
+ });
1983
+ }, [setAppState, store]);
1984
+ // #34044: if user explicitly set sandbox.enabled=true but deps are missing,
1985
+ // isSandboxingEnabled() returns false silently. Surface the reason once at
1986
+ // mount so users know their security config isn't being enforced. Full
1987
+ // reason goes to debug log; notification points to /sandbox for details.
1988
+ // addNotification is stable (useCallback) so the effect fires once.
1989
+ useEffect(() => {
1990
+ const reason = SandboxManager.getSandboxUnavailableReason();
1991
+ if (!reason)
1992
+ return;
1993
+ if (SandboxManager.isSandboxRequired()) {
1994
+ process.stderr.write(`\nError: sandbox required but unavailable: ${reason}\n` + ` sandbox.failIfUnavailable is set — refusing to start without a working sandbox.\n\n`);
1995
+ gracefulShutdownSync(1, 'other');
1996
+ return;
1997
+ }
1998
+ logForDebugging(`sandbox disabled: ${reason}`, {
1999
+ level: 'warn'
2000
+ });
2001
+ addNotification({
2002
+ key: 'sandbox-unavailable',
2003
+ jsx: _jsxs(_Fragment, { children: [_jsx(Text, { color: "warning", children: "sandbox disabled" }), _jsx(Text, { dimColor: true, children: " \u00B7 /sandbox" })] }),
2004
+ priority: 'medium'
2005
+ });
2006
+ }, [addNotification]);
2007
+ if (SandboxManager.isSandboxingEnabled()) {
2008
+ // If sandboxing is enabled (setting.sandbox is defined, initialise the manager)
2009
+ SandboxManager.initialize(sandboxAskCallback).catch(err => {
2010
+ // Initialization/validation failed - display error and exit
2011
+ process.stderr.write(`\n❌ Sandbox Error: ${errorMessage(err)}\n`);
2012
+ gracefulShutdownSync(1, 'other');
2013
+ });
2014
+ }
2015
+ const setToolPermissionContext = useCallback((context, options) => {
2016
+ setAppState(prev => ({
2017
+ ...prev,
2018
+ toolPermissionContext: {
2019
+ ...context,
2020
+ // Preserve the coordinator's mode only when explicitly requested.
2021
+ // Workers' getAppState() returns a transformed context with mode
2022
+ // 'acceptEdits' that must not leak into the coordinator's actual
2023
+ // state via permission-rule updates — those call sites pass
2024
+ // { preserveMode: true }. User-initiated mode changes (e.g.,
2025
+ // selecting "allow all edits") must NOT be overridden.
2026
+ mode: options?.preserveMode ? prev.toolPermissionContext.mode : context.mode
2027
+ }
2028
+ }));
2029
+ // When permission context changes, recheck all queued items
2030
+ // This handles the case where approving item1 with "don't ask again"
2031
+ // should auto-approve other queued items that now match the updated rules
2032
+ setImmediate(setToolUseConfirmQueue => {
2033
+ // Use setToolUseConfirmQueue callback to get current queue state
2034
+ // instead of capturing it in the closure, to avoid stale closure issues
2035
+ setToolUseConfirmQueue(currentQueue => {
2036
+ currentQueue.forEach(item => {
2037
+ void item.recheckPermission();
2038
+ });
2039
+ return currentQueue;
2040
+ });
2041
+ }, setToolUseConfirmQueue);
2042
+ }, [setAppState, setToolUseConfirmQueue]);
2043
+ // Register the leader's setToolPermissionContext for in-process teammates
2044
+ useEffect(() => {
2045
+ registerLeaderSetToolPermissionContext(setToolPermissionContext);
2046
+ return () => unregisterLeaderSetToolPermissionContext();
2047
+ }, [setToolPermissionContext]);
2048
+ const canUseTool = useCanUseTool(setToolUseConfirmQueue, setToolPermissionContext);
2049
+ const requestPrompt = useCallback((title, toolInputSummary) => (request) => new Promise((resolve, reject) => {
2050
+ setPromptQueue(prev => [...prev, {
2051
+ request,
2052
+ title,
2053
+ toolInputSummary,
2054
+ resolve,
2055
+ reject
2056
+ }]);
2057
+ }), []);
2058
+ const getToolUseContext = useCallback((messages, newMessages, abortController, mainLoopModel) => {
2059
+ // Read mutable values fresh from the store rather than closure-capturing
2060
+ // useAppState() snapshots. Same values today (closure is refreshed by the
2061
+ // render between turns); decouples freshness from React's render cycle for
2062
+ // a future headless conversation loop. Same pattern refreshTools() uses.
2063
+ const s = store.getState();
2064
+ // Compute tools fresh from store.getState() rather than the closure-
2065
+ // captured `tools`. useManageMCPConnections populates appState.mcp
2066
+ // async as servers connect — the store may have newer MCP state than
2067
+ // the closure captured at render time. Also doubles as refreshTools()
2068
+ // for mid-query tool list updates.
2069
+ const computeTools = () => {
2070
+ const state = store.getState();
2071
+ const assembled = assembleToolPool(state.toolPermissionContext, state.mcp.tools);
2072
+ const merged = mergeAndFilterTools(combinedInitialTools, assembled, state.toolPermissionContext.mode);
2073
+ if (!mainThreadAgentDefinition)
2074
+ return merged;
2075
+ return resolveAgentTools(mainThreadAgentDefinition, merged, false, true).resolvedTools;
2076
+ };
2077
+ return {
2078
+ abortController,
2079
+ options: {
2080
+ commands,
2081
+ tools: computeTools(),
2082
+ debug,
2083
+ verbose: s.verbose,
2084
+ mainLoopModel,
2085
+ thinkingConfig: s.thinkingEnabled !== false ? thinkingConfig : {
2086
+ type: 'disabled'
2087
+ },
2088
+ // Merge fresh from store rather than closing over useMergedClients'
2089
+ // memoized output. initialMcpClients is a prop (session-constant).
2090
+ mcpClients: mergeClients(initialMcpClients, s.mcp.clients),
2091
+ mcpResources: s.mcp.resources,
2092
+ ideInstallationStatus: ideInstallationStatus,
2093
+ isNonInteractiveSession: false,
2094
+ dynamicMcpConfig,
2095
+ theme,
2096
+ agentDefinitions: allowedAgentTypes ? {
2097
+ ...s.agentDefinitions,
2098
+ allowedAgentTypes
2099
+ } : s.agentDefinitions,
2100
+ customSystemPrompt,
2101
+ appendSystemPrompt,
2102
+ refreshTools: computeTools
2103
+ },
2104
+ getAppState: () => store.getState(),
2105
+ setAppState,
2106
+ messages,
2107
+ setMessages,
2108
+ updateFileHistoryState(updater) {
2109
+ // Perf: skip the setState when the updater returns the same reference
2110
+ // (e.g. fileHistoryTrackEdit returns `state` when the file is already
2111
+ // tracked). Otherwise every no-op call would notify all store listeners.
2112
+ setAppState(prev => {
2113
+ const updated = updater(prev.fileHistory);
2114
+ if (updated === prev.fileHistory)
2115
+ return prev;
2116
+ return {
2117
+ ...prev,
2118
+ fileHistory: updated
2119
+ };
2120
+ });
2121
+ },
2122
+ updateAttributionState(updater) {
2123
+ setAppState(prev => {
2124
+ const updated = updater(prev.attribution);
2125
+ if (updated === prev.attribution)
2126
+ return prev;
2127
+ return {
2128
+ ...prev,
2129
+ attribution: updated
2130
+ };
2131
+ });
2132
+ },
2133
+ openMessageSelector: () => {
2134
+ if (!disabled) {
2135
+ setIsMessageSelectorVisible(true);
2136
+ }
2137
+ },
2138
+ onChangeAPIKey: reverify,
2139
+ readFileState: readFileState.current,
2140
+ setToolJSX,
2141
+ addNotification,
2142
+ appendSystemMessage: msg => setMessages(prev => [...prev, msg]),
2143
+ sendOSNotification: opts => {
2144
+ void sendNotification(opts, terminal);
2145
+ },
2146
+ onChangeDynamicMcpConfig,
2147
+ onInstallIDEExtension: setIDEToInstallExtension,
2148
+ nestedMemoryAttachmentTriggers: new Set(),
2149
+ loadedNestedMemoryPaths: loadedNestedMemoryPathsRef.current,
2150
+ dynamicSkillDirTriggers: new Set(),
2151
+ discoveredSkillNames: discoveredSkillNamesRef.current,
2152
+ setResponseLength,
2153
+ pushApiMetricsEntry: "external" === 'ant' ? (ttftMs) => {
2154
+ const now = Date.now();
2155
+ const baseline = responseLengthRef.current;
2156
+ apiMetricsRef.current.push({
2157
+ ttftMs,
2158
+ firstTokenTime: now,
2159
+ lastTokenTime: now,
2160
+ responseLengthBaseline: baseline,
2161
+ endResponseLength: baseline
2162
+ });
2163
+ } : undefined,
2164
+ setStreamMode,
2165
+ onCompactProgress: event => {
2166
+ switch (event.type) {
2167
+ case 'hooks_start':
2168
+ setSpinnerColor('claudeBlue_FOR_SYSTEM_SPINNER');
2169
+ setSpinnerShimmerColor('claudeBlueShimmer_FOR_SYSTEM_SPINNER');
2170
+ setSpinnerMessage(event.hookType === 'pre_compact' ? 'Running PreCompact hooks\u2026' : event.hookType === 'post_compact' ? 'Running PostCompact hooks\u2026' : 'Running SessionStart hooks\u2026');
2171
+ break;
2172
+ case 'compact_start':
2173
+ setSpinnerMessage('Compacting conversation');
2174
+ break;
2175
+ case 'compact_end':
2176
+ setSpinnerMessage(null);
2177
+ setSpinnerColor(null);
2178
+ setSpinnerShimmerColor(null);
2179
+ break;
2180
+ }
2181
+ },
2182
+ setInProgressToolUseIDs,
2183
+ setHasInterruptibleToolInProgress: (v) => {
2184
+ hasInterruptibleToolInProgressRef.current = v;
2185
+ },
2186
+ resume,
2187
+ setConversationId,
2188
+ requestPrompt: feature('HOOK_PROMPTS') ? requestPrompt : undefined,
2189
+ contentReplacementState: contentReplacementStateRef.current
2190
+ };
2191
+ }, [commands, combinedInitialTools, mainThreadAgentDefinition, debug, initialMcpClients, ideInstallationStatus, dynamicMcpConfig, theme, allowedAgentTypes, store, setAppState, reverify, addNotification, setMessages, onChangeDynamicMcpConfig, resume, requestPrompt, disabled, customSystemPrompt, appendSystemPrompt, setConversationId]);
2192
+ // Session backgrounding (Ctrl+B to background/foreground)
2193
+ const handleBackgroundQuery = useCallback(() => {
2194
+ // Stop the foreground query so the background one takes over
2195
+ abortController?.abort('background');
2196
+ // Aborting subagents may produce task-completed notifications.
2197
+ // Clear task notifications so the queue processor doesn't immediately
2198
+ // start a new foreground query; forward them to the background session.
2199
+ const removedNotifications = removeByFilter(cmd => cmd.mode === 'task-notification');
2200
+ void (async () => {
2201
+ const toolUseContext = getToolUseContext(messagesRef.current, [], new AbortController(), mainLoopModel);
2202
+ const [defaultSystemPrompt, userContext, systemContext] = await Promise.all([getSystemPrompt(toolUseContext.options.tools, mainLoopModel, Array.from(toolPermissionContext.additionalWorkingDirectories.keys()), toolUseContext.options.mcpClients), getUserContext(), getSystemContext()]);
2203
+ const systemPrompt = buildEffectiveSystemPrompt({
2204
+ mainThreadAgentDefinition,
2205
+ toolUseContext,
2206
+ customSystemPrompt,
2207
+ defaultSystemPrompt,
2208
+ appendSystemPrompt
2209
+ });
2210
+ toolUseContext.renderedSystemPrompt = systemPrompt;
2211
+ const notificationAttachments = await getQueuedCommandAttachments(removedNotifications).catch(() => []);
2212
+ const notificationMessages = notificationAttachments.map(createAttachmentMessage);
2213
+ // Deduplicate: if the query loop already yielded a notification into
2214
+ // messagesRef before we removed it from the queue, skip duplicates.
2215
+ // We use prompt text for dedup because source_uuid is not set on
2216
+ // task-notification QueuedCommands (enqueuePendingNotification callers
2217
+ // don't pass uuid), so it would always be undefined.
2218
+ const existingPrompts = new Set();
2219
+ for (const m of messagesRef.current) {
2220
+ if (m.type === 'attachment' && m.attachment.type === 'queued_command' && m.attachment.commandMode === 'task-notification' && typeof m.attachment.prompt === 'string') {
2221
+ existingPrompts.add(m.attachment.prompt);
2222
+ }
2223
+ }
2224
+ const uniqueNotifications = notificationMessages.filter(m => m.attachment.type === 'queued_command' && (typeof m.attachment.prompt !== 'string' || !existingPrompts.has(m.attachment.prompt)));
2225
+ startBackgroundSession({
2226
+ messages: [...messagesRef.current, ...uniqueNotifications],
2227
+ queryParams: {
2228
+ systemPrompt,
2229
+ userContext,
2230
+ systemContext,
2231
+ canUseTool,
2232
+ toolUseContext,
2233
+ querySource: getQuerySourceForREPL()
2234
+ },
2235
+ description: terminalTitle,
2236
+ setAppState,
2237
+ agentDefinition: mainThreadAgentDefinition
2238
+ });
2239
+ })();
2240
+ }, [abortController, mainLoopModel, toolPermissionContext, mainThreadAgentDefinition, getToolUseContext, customSystemPrompt, appendSystemPrompt, canUseTool, setAppState]);
2241
+ const { handleBackgroundSession } = useSessionBackgrounding({
2242
+ setMessages,
2243
+ setIsLoading: setIsExternalLoading,
2244
+ resetLoadingState,
2245
+ setAbortController,
2246
+ onBackgroundQuery: handleBackgroundQuery
2247
+ });
2248
+ const onQueryEvent = useCallback((event) => {
2249
+ handleMessageFromStream(event, newMessage => {
2250
+ if (isCompactBoundaryMessage(newMessage)) {
2251
+ // Fullscreen: keep pre-compact messages for scrollback. query.ts
2252
+ // slices at the boundary for API calls, Messages.tsx skips the
2253
+ // boundary filter in fullscreen, and useLogMessages treats this
2254
+ // as an incremental append (first uuid unchanged). Cap at one
2255
+ // compact-interval of scrollback — normalizeMessages/applyGrouping
2256
+ // are O(n) per render, so drop everything before the previous
2257
+ // boundary to keep n bounded across multi-day sessions.
2258
+ if (isFullscreenEnvEnabled()) {
2259
+ setMessages(old => [...getMessagesAfterCompactBoundary(old, {
2260
+ includeSnipped: true
2261
+ }), newMessage]);
2262
+ }
2263
+ else {
2264
+ setMessages(() => [newMessage]);
2265
+ }
2266
+ // Bump conversationId so Messages.tsx row keys change and
2267
+ // stale memoized rows remount with post-compact content.
2268
+ setConversationId(randomUUID());
2269
+ // Compaction succeeded — clear the context-blocked flag so ticks resume
2270
+ if (feature('PROACTIVE') || feature('KAIROS')) {
2271
+ proactiveModule?.setContextBlocked(false);
2272
+ }
2273
+ }
2274
+ else if (newMessage.type === 'progress' && isEphemeralToolProgress(newMessage.data.type)) {
2275
+ // Replace the previous ephemeral progress tick for the same tool
2276
+ // call instead of appending. Sleep/Bash emit a tick per second and
2277
+ // only the last one is rendered; appending blows up the messages
2278
+ // array (13k+ observed) and the transcript (120MB of sleep_progress
2279
+ // lines). useLogMessages tracks length, so same-length replacement
2280
+ // also skips the transcript write.
2281
+ // agent_progress / hook_progress / skill_progress are NOT ephemeral
2282
+ // — each carries distinct state the UI needs (e.g. subagent tool
2283
+ // history). Replacing those leaves the AgentTool UI stuck at
2284
+ // "Initializing…" because it renders the full progress trail.
2285
+ setMessages(oldMessages => {
2286
+ const last = oldMessages.at(-1);
2287
+ if (last?.type === 'progress' && last.parentToolUseID === newMessage.parentToolUseID && last.data.type === newMessage.data.type) {
2288
+ const copy = oldMessages.slice();
2289
+ copy[copy.length - 1] = newMessage;
2290
+ return copy;
2291
+ }
2292
+ return [...oldMessages, newMessage];
2293
+ });
2294
+ }
2295
+ else {
2296
+ setMessages(oldMessages => [...oldMessages, newMessage]);
2297
+ }
2298
+ // Block ticks on API errors to prevent tick → error → tick
2299
+ // runaway loops (e.g., auth failure, rate limit, blocking limit).
2300
+ // Cleared on compact boundary (above) or successful response (below).
2301
+ if (feature('PROACTIVE') || feature('KAIROS')) {
2302
+ if (newMessage.type === 'assistant' && 'isApiErrorMessage' in newMessage && newMessage.isApiErrorMessage) {
2303
+ proactiveModule?.setContextBlocked(true);
2304
+ }
2305
+ else if (newMessage.type === 'assistant') {
2306
+ proactiveModule?.setContextBlocked(false);
2307
+ }
2308
+ }
2309
+ }, newContent => {
2310
+ // setResponseLength handles updating both responseLengthRef (for
2311
+ // spinner animation) and apiMetricsRef (endResponseLength/lastTokenTime
2312
+ // for OTPS). No separate metrics update needed here.
2313
+ setResponseLength(length => length + newContent.length);
2314
+ }, setStreamMode, setStreamingToolUses, tombstonedMessage => {
2315
+ setMessages(oldMessages => oldMessages.filter(m => m !== tombstonedMessage));
2316
+ void removeTranscriptMessage(tombstonedMessage.uuid);
2317
+ }, setStreamingThinking, metrics => {
2318
+ const now = Date.now();
2319
+ const baseline = responseLengthRef.current;
2320
+ apiMetricsRef.current.push({
2321
+ ...metrics,
2322
+ firstTokenTime: now,
2323
+ lastTokenTime: now,
2324
+ responseLengthBaseline: baseline,
2325
+ endResponseLength: baseline
2326
+ });
2327
+ }, onStreamingText);
2328
+ }, [setMessages, setResponseLength, setStreamMode, setStreamingToolUses, setStreamingThinking, onStreamingText]);
2329
+ const onQueryImpl = useCallback(async (messagesIncludingNewMessages, newMessages, abortController, shouldQuery, additionalAllowedTools, mainLoopModelParam, effort) => {
2330
+ // Prepare IDE integration for new prompt. Read mcpClients fresh from
2331
+ // store — useManageMCPConnections may have populated it since the
2332
+ // render that captured this closure (same pattern as computeTools).
2333
+ if (shouldQuery) {
2334
+ const freshClients = mergeClients(initialMcpClients, store.getState().mcp.clients);
2335
+ void diagnosticTracker.handleQueryStart(freshClients);
2336
+ const ideClient = getConnectedIdeClient(freshClients);
2337
+ if (ideClient) {
2338
+ void closeOpenDiffs(ideClient);
2339
+ }
2340
+ }
2341
+ // Mark onboarding as complete when any user message is sent to Claude
2342
+ void maybeMarkProjectOnboardingComplete();
2343
+ // Extract a session title from the first real user message. One-shot
2344
+ // via ref (was tengu_birch_mist experiment: first-message-only to save
2345
+ // Haiku calls). The ref replaces the old `messages.length <= 1` check,
2346
+ // which was broken by SessionStart hook messages (prepended via
2347
+ // useDeferredHookMessages) and attachment messages (appended by
2348
+ // processTextPrompt) — both pushed length past 1 on turn one, so the
2349
+ // title silently fell through to the "Context Code" default.
2350
+ if (!titleDisabled && !sessionTitle && !agentTitle && !haikuTitleAttemptedRef.current) {
2351
+ const firstUserMessage = newMessages.find(m => m.type === 'user' && !m.isMeta);
2352
+ const text = firstUserMessage?.type === 'user' ? getContentText(firstUserMessage.message.content) : null;
2353
+ // Skip synthetic breadcrumbs — slash-command output, prompt-skill
2354
+ // expansions (/commit → <command-message>), local-command headers
2355
+ // (/help → <command-name>), and bash-mode (!cmd → <bash-input>).
2356
+ // None of these are the user's topic; wait for real prose.
2357
+ if (text && !text.startsWith(`<${LOCAL_COMMAND_STDOUT_TAG}>`) && !text.startsWith(`<${COMMAND_MESSAGE_TAG}>`) && !text.startsWith(`<${COMMAND_NAME_TAG}>`) && !text.startsWith(`<${BASH_INPUT_TAG}>`)) {
2358
+ haikuTitleAttemptedRef.current = true;
2359
+ void generateSessionTitle(text, new AbortController().signal).then(title => {
2360
+ if (title)
2361
+ setHaikuTitle(title);
2362
+ else
2363
+ haikuTitleAttemptedRef.current = false;
2364
+ }, () => {
2365
+ haikuTitleAttemptedRef.current = false;
2366
+ });
2367
+ }
2368
+ }
2369
+ // Apply slash-command-scoped allowedTools (from skill frontmatter) to the
2370
+ // store once per turn. This also covers the reset: the next non-skill turn
2371
+ // passes [] and clears it. Must run before the !shouldQuery gate: forked
2372
+ // commands (executeForkedSlashCommand) return shouldQuery=false, and
2373
+ // createGetAppStateWithAllowedTools in forkedAgent.ts reads this field, so
2374
+ // stale skill tools would otherwise leak into forked agent permissions.
2375
+ // Previously this write was hidden inside getToolUseContext's getAppState
2376
+ // (~85 calls/turn); hoisting it here makes getAppState a pure read and stops
2377
+ // ephemeral contexts (permission dialog, BackgroundTasksDialog) from
2378
+ // accidentally clearing it mid-turn.
2379
+ store.setState(prev => {
2380
+ const cur = prev.toolPermissionContext.alwaysAllowRules.command;
2381
+ if (cur === additionalAllowedTools || cur?.length === additionalAllowedTools.length && cur.every((v, i) => v === additionalAllowedTools[i])) {
2382
+ return prev;
2383
+ }
2384
+ return {
2385
+ ...prev,
2386
+ toolPermissionContext: {
2387
+ ...prev.toolPermissionContext,
2388
+ alwaysAllowRules: {
2389
+ ...prev.toolPermissionContext.alwaysAllowRules,
2390
+ command: additionalAllowedTools
2391
+ }
2392
+ }
2393
+ };
2394
+ });
2395
+ // The last message is an assistant message if the user input was a bash command,
2396
+ // or if the user input was an invalid slash command.
2397
+ if (!shouldQuery) {
2398
+ // Manual /compact sets messages directly (shouldQuery=false) bypassing
2399
+ // handleMessageFromStream. Clear context-blocked if a compact boundary
2400
+ // is present so proactive ticks resume after compaction.
2401
+ if (newMessages.some(isCompactBoundaryMessage)) {
2402
+ // Bump conversationId so Messages.tsx row keys change and
2403
+ // stale memoized rows remount with post-compact content.
2404
+ setConversationId(randomUUID());
2405
+ if (feature('PROACTIVE') || feature('KAIROS')) {
2406
+ proactiveModule?.setContextBlocked(false);
2407
+ }
2408
+ }
2409
+ resetLoadingState();
2410
+ setAbortController(null);
2411
+ return;
2412
+ }
2413
+ const toolUseContext = getToolUseContext(messagesIncludingNewMessages, newMessages, abortController, mainLoopModelParam);
2414
+ // getToolUseContext reads tools/mcpClients fresh from store.getState()
2415
+ // (via computeTools/mergeClients). Use those rather than the closure-
2416
+ // captured `tools`/`mcpClients` — useManageMCPConnections may have
2417
+ // flushed new MCP state between the render that captured this closure
2418
+ // and now. Turn 1 via processInitialMessage is the main beneficiary.
2419
+ const { tools: freshTools, mcpClients: freshMcpClients } = toolUseContext.options;
2420
+ // Scope the skill's effort override to this turn's context only —
2421
+ // wrapping getAppState keeps the override out of the global store so
2422
+ // background agents and UI subscribers (Spinner, LogoV2) never see it.
2423
+ if (effort !== undefined) {
2424
+ const previousGetAppState = toolUseContext.getAppState;
2425
+ toolUseContext.getAppState = () => ({
2426
+ ...previousGetAppState(),
2427
+ effortValue: effort
2428
+ });
2429
+ }
2430
+ queryCheckpoint('query_context_loading_start');
2431
+ const [, , defaultSystemPrompt, baseUserContext, systemContext] = await Promise.all([
2432
+ // IMPORTANT: do this after setMessages() above, to avoid UI jank
2433
+ checkAndDisableBypassPermissionsIfNeeded(toolPermissionContext, setAppState),
2434
+ // Gated on TRANSCRIPT_CLASSIFIER so GrowthBook kill switch runs wherever auto mode is built in
2435
+ feature('TRANSCRIPT_CLASSIFIER') ? checkAndDisableAutoModeIfNeeded(toolPermissionContext, setAppState, store.getState().fastMode) : undefined, getSystemPrompt(freshTools, mainLoopModelParam, Array.from(toolPermissionContext.additionalWorkingDirectories.keys()), freshMcpClients), getUserContext(), getSystemContext()
2436
+ ]);
2437
+ const userContext = {
2438
+ ...baseUserContext,
2439
+ ...getCoordinatorUserContext(freshMcpClients, isScratchpadEnabled() ? getScratchpadDir() : undefined),
2440
+ ...((feature('PROACTIVE') || feature('KAIROS')) && proactiveModule?.isProactiveActive() && !terminalFocusRef.current ? {
2441
+ terminalFocus: 'The terminal is unfocused \u2014 the user is not actively watching.'
2442
+ } : {})
2443
+ };
2444
+ queryCheckpoint('query_context_loading_end');
2445
+ const systemPrompt = buildEffectiveSystemPrompt({
2446
+ mainThreadAgentDefinition,
2447
+ toolUseContext,
2448
+ customSystemPrompt,
2449
+ defaultSystemPrompt,
2450
+ appendSystemPrompt
2451
+ });
2452
+ toolUseContext.renderedSystemPrompt = systemPrompt;
2453
+ queryCheckpoint('query_query_start');
2454
+ resetTurnHookDuration();
2455
+ resetTurnToolDuration();
2456
+ resetTurnClassifierDuration();
2457
+ for await (const event of query({
2458
+ messages: messagesIncludingNewMessages,
2459
+ systemPrompt,
2460
+ userContext,
2461
+ systemContext,
2462
+ canUseTool,
2463
+ toolUseContext,
2464
+ querySource: getQuerySourceForREPL()
2465
+ })) {
2466
+ onQueryEvent(event);
2467
+ }
2468
+ if (feature('BUDDY')) {
2469
+ void fireCompanionObserver(messagesRef.current, reaction => setAppState(prev => prev.companionReaction === reaction ? prev : {
2470
+ ...prev,
2471
+ companionReaction: reaction
2472
+ }));
2473
+ }
2474
+ queryCheckpoint('query_end');
2475
+ // Capture ant-only API metrics before resetLoadingState clears the ref.
2476
+ // For multi-request turns (tool use loops), compute P50 across all requests.
2477
+ if ("external" === 'ant' && apiMetricsRef.current.length > 0) {
2478
+ const entries = apiMetricsRef.current;
2479
+ const ttfts = entries.map(e => e.ttftMs);
2480
+ // Compute per-request OTPS using only active streaming time and
2481
+ // streaming-only content. endResponseLength tracks content added by
2482
+ // streaming deltas only, excluding subagent/compaction inflation.
2483
+ const otpsValues = entries.map(e => {
2484
+ const delta = Math.round((e.endResponseLength - e.responseLengthBaseline) / 4);
2485
+ const samplingMs = e.lastTokenTime - e.firstTokenTime;
2486
+ return samplingMs > 0 ? Math.round(delta / (samplingMs / 1000)) : 0;
2487
+ });
2488
+ const isMultiRequest = entries.length > 1;
2489
+ const hookMs = getTurnHookDurationMs();
2490
+ const hookCount = getTurnHookCount();
2491
+ const toolMs = getTurnToolDurationMs();
2492
+ const toolCount = getTurnToolCount();
2493
+ const classifierMs = getTurnClassifierDurationMs();
2494
+ const classifierCount = getTurnClassifierCount();
2495
+ const turnMs = Date.now() - loadingStartTimeRef.current;
2496
+ setMessages(prev => [...prev, createApiMetricsMessage({
2497
+ ttftMs: isMultiRequest ? median(ttfts) : ttfts[0],
2498
+ otps: isMultiRequest ? median(otpsValues) : otpsValues[0],
2499
+ isP50: isMultiRequest,
2500
+ hookDurationMs: hookMs > 0 ? hookMs : undefined,
2501
+ hookCount: hookCount > 0 ? hookCount : undefined,
2502
+ turnDurationMs: turnMs > 0 ? turnMs : undefined,
2503
+ toolDurationMs: toolMs > 0 ? toolMs : undefined,
2504
+ toolCount: toolCount > 0 ? toolCount : undefined,
2505
+ classifierDurationMs: classifierMs > 0 ? classifierMs : undefined,
2506
+ classifierCount: classifierCount > 0 ? classifierCount : undefined,
2507
+ configWriteCount: getGlobalConfigWriteCount()
2508
+ })]);
2509
+ }
2510
+ resetLoadingState();
2511
+ // Log query profiling report if enabled
2512
+ logQueryProfileReport();
2513
+ // Signal that a query turn has completed successfully
2514
+ await onTurnComplete?.(messagesRef.current);
2515
+ }, [initialMcpClients, resetLoadingState, getToolUseContext, toolPermissionContext, setAppState, customSystemPrompt, onTurnComplete, appendSystemPrompt, canUseTool, mainThreadAgentDefinition, onQueryEvent, sessionTitle, titleDisabled]);
2516
+ const onQuery = useCallback(async (newMessages, abortController, shouldQuery, additionalAllowedTools, mainLoopModelParam, onBeforeQueryCallback, input, effort) => {
2517
+ // If this is a teammate, mark them as active when starting a turn
2518
+ if (isAgentSwarmsEnabled()) {
2519
+ const teamName = getTeamName();
2520
+ const agentName = getAgentName();
2521
+ if (teamName && agentName) {
2522
+ // Fire and forget - turn starts immediately, write happens in background
2523
+ void setMemberActive(teamName, agentName, true);
2524
+ }
2525
+ }
2526
+ // Concurrent guard via state machine. tryStart() atomically checks
2527
+ // and transitions idle→running, returning the generation number.
2528
+ // Returns null if already running — no separate check-then-set.
2529
+ const thisGeneration = queryGuard.tryStart();
2530
+ if (thisGeneration === null) {
2531
+ logEvent('tengu_concurrent_onquery_detected', {});
2532
+ // Extract and enqueue user message text, skipping meta messages
2533
+ // (e.g. expanded skill content, tick prompts) that should not be
2534
+ // replayed as user-visible text.
2535
+ newMessages.filter((m) => m.type === 'user' && !m.isMeta).map(_ => getContentText(_.message.content)).filter(_ => _ !== null).forEach((msg, i) => {
2536
+ enqueue({
2537
+ value: msg,
2538
+ mode: 'prompt'
2539
+ });
2540
+ if (i === 0) {
2541
+ logEvent('tengu_concurrent_onquery_enqueued', {});
2542
+ }
2543
+ });
2544
+ return;
2545
+ }
2546
+ try {
2547
+ // isLoading is derived from queryGuard — tryStart() above already
2548
+ // transitioned dispatching→running, so no setter call needed here.
2549
+ resetTimingRefs();
2550
+ setMessages(oldMessages => [...oldMessages, ...newMessages]);
2551
+ responseLengthRef.current = 0;
2552
+ if (feature('TOKEN_BUDGET')) {
2553
+ const parsedBudget = input ? parseTokenBudget(input) : null;
2554
+ snapshotOutputTokensForTurn(parsedBudget ?? getCurrentTurnTokenBudget());
2555
+ }
2556
+ apiMetricsRef.current = [];
2557
+ setStreamingToolUses([]);
2558
+ setStreamingText(null);
2559
+ // messagesRef is updated synchronously by the setMessages wrapper
2560
+ // above, so it already includes newMessages from the append at the
2561
+ // top of this try block. No reconstruction needed, no waiting for
2562
+ // React's scheduler (previously cost 20-56ms per prompt; the 56ms
2563
+ // case was a GC pause caught during the await).
2564
+ const latestMessages = messagesRef.current;
2565
+ if (input) {
2566
+ await mrOnBeforeQuery(input, latestMessages, newMessages.length);
2567
+ }
2568
+ // Pass full conversation history to callback
2569
+ if (onBeforeQueryCallback && input) {
2570
+ const shouldProceed = await onBeforeQueryCallback(input, latestMessages);
2571
+ if (!shouldProceed) {
2572
+ return;
2573
+ }
2574
+ }
2575
+ await onQueryImpl(latestMessages, newMessages, abortController, shouldQuery, additionalAllowedTools, mainLoopModelParam, effort);
2576
+ }
2577
+ finally {
2578
+ // queryGuard.end() atomically checks generation and transitions
2579
+ // running→idle. Returns false if a newer query owns the guard
2580
+ // (cancel+resubmit race where the stale finally fires as a microtask).
2581
+ if (queryGuard.end(thisGeneration)) {
2582
+ setLastQueryCompletionTime(Date.now());
2583
+ skipIdleCheckRef.current = false;
2584
+ // Always reset loading state in finally - this ensures cleanup even
2585
+ // if onQueryImpl throws. onTurnComplete is called separately in
2586
+ // onQueryImpl only on successful completion.
2587
+ resetLoadingState();
2588
+ await mrOnTurnComplete(messagesRef.current, abortController.signal.aborted);
2589
+ // Notify bridge clients that the turn is complete so mobile apps
2590
+ // can stop the spark animation and show post-turn UI.
2591
+ sendBridgeResultRef.current();
2592
+ // Auto-hide tungsten panel content at turn end (ant-only), but keep
2593
+ // tungstenActiveSession set so the pill stays in the footer and the user
2594
+ // can reopen the panel. Background tmux tasks (e.g. /hunter) run for
2595
+ // minutes — wiping the session made the pill disappear entirely, forcing
2596
+ // the user to re-invoke Tmux just to peek. Skip on abort so the panel
2597
+ // stays open for inspection (matches the turn-duration guard below).
2598
+ if ("external" === 'ant' && !abortController.signal.aborted) {
2599
+ setAppState(prev => {
2600
+ if (prev.tungstenActiveSession === undefined)
2601
+ return prev;
2602
+ if (prev.tungstenPanelAutoHidden === true)
2603
+ return prev;
2604
+ return {
2605
+ ...prev,
2606
+ tungstenPanelAutoHidden: true
2607
+ };
2608
+ });
2609
+ }
2610
+ // Capture budget info before clearing (ant-only)
2611
+ let budgetInfo;
2612
+ if (feature('TOKEN_BUDGET')) {
2613
+ if (getCurrentTurnTokenBudget() !== null && getCurrentTurnTokenBudget() > 0 && !abortController.signal.aborted) {
2614
+ budgetInfo = {
2615
+ tokens: getTurnOutputTokens(),
2616
+ limit: getCurrentTurnTokenBudget(),
2617
+ nudges: getBudgetContinuationCount()
2618
+ };
2619
+ }
2620
+ snapshotOutputTokensForTurn(null);
2621
+ }
2622
+ // Add turn duration message for turns longer than 30s or with a budget
2623
+ // Skip if user aborted or if in loop mode (too noisy between ticks)
2624
+ // Defer if swarm teammates are still running (show when they finish)
2625
+ const turnDurationMs = Date.now() - loadingStartTimeRef.current - totalPausedMsRef.current;
2626
+ if ((turnDurationMs > 30000 || budgetInfo !== undefined) && !abortController.signal.aborted && !proactiveActive) {
2627
+ const hasRunningSwarmAgents = getAllInProcessTeammateTasks(store.getState().tasks).some(t => t.status === 'running');
2628
+ if (hasRunningSwarmAgents) {
2629
+ // Only record start time on the first deferred turn
2630
+ if (swarmStartTimeRef.current === null) {
2631
+ swarmStartTimeRef.current = loadingStartTimeRef.current;
2632
+ }
2633
+ // Always update budget — later turns may carry the actual budget
2634
+ if (budgetInfo) {
2635
+ swarmBudgetInfoRef.current = budgetInfo;
2636
+ }
2637
+ }
2638
+ else {
2639
+ setMessages(prev => [...prev, createTurnDurationMessage(turnDurationMs, budgetInfo, count(prev, isLoggableMessage))]);
2640
+ }
2641
+ }
2642
+ // Clear the controller so CancelRequestHandler's canCancelRunningTask
2643
+ // reads false at the idle prompt. Without this, the stale non-aborted
2644
+ // controller makes ctrl+c fire onCancel() (aborting nothing) instead of
2645
+ // propagating to the double-press exit flow.
2646
+ setAbortController(null);
2647
+ }
2648
+ // Auto-restore: if the user interrupted before any meaningful response
2649
+ // arrived, rewind the conversation and restore their prompt — same as
2650
+ // opening the message selector and picking the last message.
2651
+ // This runs OUTSIDE the queryGuard.end() check because onCancel calls
2652
+ // forceEnd(), which bumps the generation so end() returns false above.
2653
+ // Guards: reason === 'user-cancel' (onCancel/Esc; programmatic aborts
2654
+ // use 'background'/'interrupt' and must not rewind — note abort() with
2655
+ // no args sets reason to a DOMException, not undefined), !isActive (no
2656
+ // newer query started — cancel+resubmit race), empty input (don't
2657
+ // clobber text typed during loading), no queued commands (user queued
2658
+ // B while A was loading → they've moved on, don't restore A; also
2659
+ // avoids removeLastFromHistory removing B's entry instead of A's),
2660
+ // not viewing a teammate (messagesRef is the main conversation — the
2661
+ // old Up-arrow quick-restore had this guard, preserve it).
2662
+ if (abortController.signal.reason === 'user-cancel' && !queryGuard.isActive && inputValueRef.current === '' && getCommandQueueLength() === 0 && !store.getState().viewingAgentTaskId) {
2663
+ const msgs = messagesRef.current;
2664
+ const lastUserMsg = msgs.findLast(selectableUserMessagesFilter);
2665
+ if (lastUserMsg) {
2666
+ const idx = msgs.lastIndexOf(lastUserMsg);
2667
+ if (messagesAfterAreOnlySynthetic(msgs, idx)) {
2668
+ // The submit is being undone — undo its history entry too,
2669
+ // otherwise Up-arrow shows the restored text twice.
2670
+ removeLastFromHistory();
2671
+ restoreMessageSyncRef.current(lastUserMsg);
2672
+ }
2673
+ }
2674
+ }
2675
+ }
2676
+ }, [onQueryImpl, setAppState, resetLoadingState, queryGuard, mrOnBeforeQuery, mrOnTurnComplete]);
2677
+ // Handle initial message (from CLI args or plan mode exit with context clear)
2678
+ // This effect runs when isLoading becomes false and there's a pending message
2679
+ const initialMessageRef = useRef(false);
2680
+ useEffect(() => {
2681
+ const pending = initialMessage;
2682
+ if (!pending || isLoading || initialMessageRef.current)
2683
+ return;
2684
+ // Mark as processing to prevent re-entry
2685
+ initialMessageRef.current = true;
2686
+ async function processInitialMessage(initialMsg) {
2687
+ // Clear context if requested (plan mode exit)
2688
+ if (initialMsg.clearContext) {
2689
+ // Preserve the plan slug before clearing context, so the new session
2690
+ // can access the same plan file after regenerateSessionId()
2691
+ const oldPlanSlug = initialMsg.message.planContent ? getPlanSlug() : undefined;
2692
+ const { clearConversation } = await import('../commands/clear/conversation.js');
2693
+ await clearConversation({
2694
+ setMessages,
2695
+ readFileState: readFileState.current,
2696
+ discoveredSkillNames: discoveredSkillNamesRef.current,
2697
+ loadedNestedMemoryPaths: loadedNestedMemoryPathsRef.current,
2698
+ getAppState: () => store.getState(),
2699
+ setAppState,
2700
+ setConversationId
2701
+ });
2702
+ haikuTitleAttemptedRef.current = false;
2703
+ setHaikuTitle(undefined);
2704
+ bashTools.current.clear();
2705
+ bashToolsProcessedIdx.current = 0;
2706
+ // Restore the plan slug for the new session so getPlan() finds the file
2707
+ if (oldPlanSlug) {
2708
+ setPlanSlug(getSessionId(), oldPlanSlug);
2709
+ }
2710
+ }
2711
+ // Atomically: clear initial message, set permission mode and rules, and store plan for verification
2712
+ const shouldStorePlanForVerification = initialMsg.message.planContent && "external" === 'ant' && isEnvTruthy(undefined);
2713
+ setAppState(prev => {
2714
+ // Build and apply permission updates (mode + allowedPrompts rules)
2715
+ let updatedToolPermissionContext = initialMsg.mode ? applyPermissionUpdates(prev.toolPermissionContext, buildPermissionUpdates(initialMsg.mode, initialMsg.allowedPrompts)) : prev.toolPermissionContext;
2716
+ // For auto, override the mode (buildPermissionUpdates maps
2717
+ // it to 'default' via toExternalPermissionMode) and strip dangerous rules
2718
+ if (feature('TRANSCRIPT_CLASSIFIER') && initialMsg.mode === 'auto') {
2719
+ updatedToolPermissionContext = stripDangerousPermissionsForAutoMode({
2720
+ ...updatedToolPermissionContext,
2721
+ mode: 'auto',
2722
+ prePlanMode: undefined
2723
+ });
2724
+ }
2725
+ return {
2726
+ ...prev,
2727
+ initialMessage: null,
2728
+ toolPermissionContext: updatedToolPermissionContext,
2729
+ ...(shouldStorePlanForVerification && {
2730
+ pendingPlanVerification: {
2731
+ plan: initialMsg.message.planContent,
2732
+ verificationStarted: false,
2733
+ verificationCompleted: false
2734
+ }
2735
+ })
2736
+ };
2737
+ });
2738
+ // Create file history snapshot for code rewind
2739
+ if (fileHistoryEnabled()) {
2740
+ void fileHistoryMakeSnapshot((updater) => {
2741
+ setAppState(prev => ({
2742
+ ...prev,
2743
+ fileHistory: updater(prev.fileHistory)
2744
+ }));
2745
+ }, initialMsg.message.uuid);
2746
+ }
2747
+ // Ensure SessionStart hook context is available before the first API
2748
+ // call. onSubmit calls this internally but the onQuery path below
2749
+ // bypasses onSubmit — hoist here so both paths see hook messages.
2750
+ await awaitPendingHooks();
2751
+ // Route all initial prompts through onSubmit to ensure UserPromptSubmit hooks fire
2752
+ // TODO: Simplify by always routing through onSubmit once it supports
2753
+ // ContentBlockParam arrays (images) as input
2754
+ const content = initialMsg.message.message.content;
2755
+ // Route all string content through onSubmit to ensure hooks fire
2756
+ // For complex content (images, etc.), fall back to direct onQuery
2757
+ // Plan messages bypass onSubmit to preserve planContent metadata for rendering
2758
+ if (typeof content === 'string' && !initialMsg.message.planContent) {
2759
+ // Route through onSubmit for proper processing including UserPromptSubmit hooks
2760
+ void onSubmit(content, {
2761
+ setCursorOffset: () => { },
2762
+ clearBuffer: () => { },
2763
+ resetHistory: () => { }
2764
+ });
2765
+ }
2766
+ else {
2767
+ // Plan messages or complex content (images, etc.) - send directly to model
2768
+ // Plan messages use onQuery to preserve planContent metadata for rendering
2769
+ // TODO: Once onSubmit supports ContentBlockParam arrays, remove this branch
2770
+ const newAbortController = createAbortController();
2771
+ setAbortController(newAbortController);
2772
+ void onQuery([initialMsg.message], newAbortController, true,
2773
+ // shouldQuery
2774
+ [],
2775
+ // additionalAllowedTools
2776
+ mainLoopModel);
2777
+ }
2778
+ // Reset ref after a delay to allow new initial messages
2779
+ setTimeout(ref => {
2780
+ ref.current = false;
2781
+ }, 100, initialMessageRef);
2782
+ }
2783
+ void processInitialMessage(pending);
2784
+ }, [initialMessage, isLoading, setMessages, setAppState, onQuery, mainLoopModel, tools]);
2785
+ const onSubmit = useCallback(async (input, helpers, speculationAccept, options) => {
2786
+ // Re-pin scroll to bottom on submit so the user always sees the new
2787
+ // exchange (matches OpenCode's auto-scroll behavior).
2788
+ repinScroll();
2789
+ // Resume loop mode if paused
2790
+ if (feature('PROACTIVE') || feature('KAIROS')) {
2791
+ proactiveModule?.resumeProactive();
2792
+ }
2793
+ // Handle immediate commands - these bypass the queue and execute right away
2794
+ // even while Claude is processing. Commands opt-in via `immediate: true`.
2795
+ // Commands triggered via keybindings are always treated as immediate.
2796
+ if (!speculationAccept && input.trim().startsWith('/')) {
2797
+ // Expand [Pasted text #N] refs so immediate commands (e.g. /btw) receive
2798
+ // the pasted content, not the placeholder. The non-immediate path gets
2799
+ // this expansion later in handlePromptSubmit.
2800
+ const trimmedInput = expandPastedTextRefs(input, pastedContents).trim();
2801
+ const spaceIndex = trimmedInput.indexOf(' ');
2802
+ const commandName = spaceIndex === -1 ? trimmedInput.slice(1) : trimmedInput.slice(1, spaceIndex);
2803
+ const commandArgs = spaceIndex === -1 ? '' : trimmedInput.slice(spaceIndex + 1).trim();
2804
+ // Find matching command - treat as immediate if:
2805
+ // 1. Command has `immediate: true`, OR
2806
+ // 2. Command was triggered via keybinding (fromKeybinding option)
2807
+ const matchingCommand = commands.find(cmd => isCommandEnabled(cmd) && (cmd.name === commandName || cmd.aliases?.includes(commandName) || getCommandName(cmd) === commandName));
2808
+ if (matchingCommand?.name === 'clear' && idleHintShownRef.current) {
2809
+ logEvent('tengu_idle_return_action', {
2810
+ action: 'hint_converted',
2811
+ variant: idleHintShownRef.current,
2812
+ idleMinutes: Math.round((Date.now() - lastQueryCompletionTimeRef.current) / 60_000),
2813
+ messageCount: messagesRef.current.length,
2814
+ totalInputTokens: getTotalInputTokens()
2815
+ });
2816
+ idleHintShownRef.current = false;
2817
+ }
2818
+ const shouldTreatAsImmediate = queryGuard.isActive && (matchingCommand?.immediate || options?.fromKeybinding);
2819
+ if (matchingCommand && shouldTreatAsImmediate && matchingCommand.type === 'local-jsx') {
2820
+ // Only clear input if the submitted text matches what's in the prompt.
2821
+ // When a command keybinding fires, input is "/<command>" but the actual
2822
+ // input value is the user's existing text - don't clear it in that case.
2823
+ if (input.trim() === inputValueRef.current.trim()) {
2824
+ setInputValue('');
2825
+ helpers.setCursorOffset(0);
2826
+ helpers.clearBuffer();
2827
+ setPastedContents({});
2828
+ }
2829
+ const pastedTextRefs = parseReferences(input).filter(r => pastedContents[r.id]?.type === 'text');
2830
+ const pastedTextCount = pastedTextRefs.length;
2831
+ const pastedTextBytes = pastedTextRefs.reduce((sum, r) => sum + (pastedContents[r.id]?.content.length ?? 0), 0);
2832
+ logEvent('tengu_paste_text', {
2833
+ pastedTextCount,
2834
+ pastedTextBytes
2835
+ });
2836
+ logEvent('tengu_immediate_command_executed', {
2837
+ commandName: matchingCommand.name,
2838
+ fromKeybinding: options?.fromKeybinding ?? false
2839
+ });
2840
+ // Execute the command directly
2841
+ const executeImmediateCommand = async () => {
2842
+ let doneWasCalled = false;
2843
+ const onDone = (result, doneOptions) => {
2844
+ doneWasCalled = true;
2845
+ setToolJSX({
2846
+ jsx: null,
2847
+ shouldHidePromptInput: false,
2848
+ clearLocalJSX: true
2849
+ });
2850
+ const newMessages = [];
2851
+ if (result && doneOptions?.display !== 'skip') {
2852
+ addNotification({
2853
+ key: `immediate-${matchingCommand.name}`,
2854
+ text: result,
2855
+ priority: 'immediate'
2856
+ });
2857
+ // In fullscreen the command just showed as a centered modal
2858
+ // pane — the notification above is enough feedback. Adding
2859
+ // "❯ /config" + "⎿ dismissed" to the transcript is clutter
2860
+ // (those messages are type:system subtype:local_command —
2861
+ // user-visible but NOT sent to the model, so skipping them
2862
+ // doesn't change model context). Outside fullscreen the
2863
+ // transcript entry stays so scrollback shows what ran.
2864
+ if (!isFullscreenEnvEnabled()) {
2865
+ newMessages.push(createCommandInputMessage(formatCommandInputTags(getCommandName(matchingCommand), commandArgs)), createCommandInputMessage(`<${LOCAL_COMMAND_STDOUT_TAG}>${escapeXml(result)}</${LOCAL_COMMAND_STDOUT_TAG}>`));
2866
+ }
2867
+ }
2868
+ // Inject meta messages (model-visible, user-hidden) into the transcript
2869
+ if (doneOptions?.metaMessages?.length) {
2870
+ newMessages.push(...doneOptions.metaMessages.map(content => createUserMessage({
2871
+ content,
2872
+ isMeta: true
2873
+ })));
2874
+ }
2875
+ if (newMessages.length) {
2876
+ setMessages(prev => [...prev, ...newMessages]);
2877
+ }
2878
+ // Restore stashed prompt after local-jsx command completes.
2879
+ // The normal stash restoration path (below) is skipped because
2880
+ // local-jsx commands return early from onSubmit.
2881
+ if (stashedPrompt !== undefined) {
2882
+ setInputValue(stashedPrompt.text);
2883
+ helpers.setCursorOffset(stashedPrompt.cursorOffset);
2884
+ setPastedContents(stashedPrompt.pastedContents);
2885
+ setStashedPrompt(undefined);
2886
+ }
2887
+ };
2888
+ // Build context for the command (reuses existing getToolUseContext).
2889
+ // Read messages via ref to keep onSubmit stable across message
2890
+ // updates — matches the pattern at L2384/L2400/L2662 and avoids
2891
+ // pinning stale REPL render scopes in downstream closures.
2892
+ const context = getToolUseContext(messagesRef.current, [], createAbortController(), mainLoopModel);
2893
+ const mod = await matchingCommand.load();
2894
+ const jsx = await mod.call(onDone, context, commandArgs);
2895
+ // Skip if onDone already fired — prevents stuck isLocalJSXCommand
2896
+ // (see processSlashCommand.tsx local-jsx case for full mechanism).
2897
+ if (jsx && !doneWasCalled) {
2898
+ // shouldHidePromptInput: false keeps Notifications mounted
2899
+ // so the onDone result isn't lost
2900
+ setToolJSX({
2901
+ jsx,
2902
+ shouldHidePromptInput: false,
2903
+ isLocalJSXCommand: true
2904
+ });
2905
+ }
2906
+ };
2907
+ void executeImmediateCommand();
2908
+ return; // Always return early - don't add to history or queue
2909
+ }
2910
+ }
2911
+ // Remote mode: skip empty input early before any state mutations
2912
+ if (activeRemote.isRemoteMode && !input.trim()) {
2913
+ return;
2914
+ }
2915
+ // Idle-return: prompt returning users to start fresh when the
2916
+ // conversation is large and the cache is cold. tengu_willow_mode
2917
+ // controls treatment: "dialog" (blocking), "hint" (notification), "off".
2918
+ {
2919
+ const willowMode = getFeatureValue_CACHED_MAY_BE_STALE('tengu_willow_mode', 'off');
2920
+ const idleThresholdMin = Number(process.env.CLAUDE_CODE_IDLE_THRESHOLD_MINUTES ?? 75);
2921
+ const tokenThreshold = Number(process.env.CLAUDE_CODE_IDLE_TOKEN_THRESHOLD ?? 100_000);
2922
+ if (willowMode !== 'off' && !getGlobalConfig().idleReturnDismissed && !skipIdleCheckRef.current && !speculationAccept && !input.trim().startsWith('/') && lastQueryCompletionTimeRef.current > 0 && getTotalInputTokens() >= tokenThreshold) {
2923
+ const idleMs = Date.now() - lastQueryCompletionTimeRef.current;
2924
+ const idleMinutes = idleMs / 60_000;
2925
+ if (idleMinutes >= idleThresholdMin && willowMode === 'dialog') {
2926
+ setIdleReturnPending({
2927
+ input,
2928
+ idleMinutes
2929
+ });
2930
+ setInputValue('');
2931
+ helpers.setCursorOffset(0);
2932
+ helpers.clearBuffer();
2933
+ return;
2934
+ }
2935
+ }
2936
+ }
2937
+ // Add to history for direct user submissions.
2938
+ // Queued command processing (executeQueuedInput) doesn't call onSubmit,
2939
+ // so notifications and already-queued user input won't be added to history here.
2940
+ // Skip history for keybinding-triggered commands (user didn't type the command).
2941
+ if (!options?.fromKeybinding) {
2942
+ addToHistory({
2943
+ display: speculationAccept ? input : prependModeCharacterToInput(input, inputMode),
2944
+ pastedContents: speculationAccept ? {} : pastedContents
2945
+ });
2946
+ // Add the just-submitted command to the front of the ghost-text
2947
+ // cache so it's suggested immediately (not after the 60s TTL).
2948
+ if (inputMode === 'bash') {
2949
+ prependToShellHistoryCache(input.trim());
2950
+ }
2951
+ }
2952
+ // Restore stash if present, but NOT for slash commands or when loading.
2953
+ // - Slash commands (especially interactive ones like /model, /context) hide
2954
+ // the prompt and show a picker UI. Restoring the stash during a command would
2955
+ // place the text in a hidden input, and the user would lose it by typing the
2956
+ // next command. Instead, preserve the stash so it survives across command runs.
2957
+ // - When loading, the submitted input will be queued and handlePromptSubmit
2958
+ // will clear the input field (onInputChange('')), which would clobber the
2959
+ // restored stash. Defer restoration to after handlePromptSubmit (below).
2960
+ // Remote mode is exempt: it sends via WebSocket and returns early without
2961
+ // calling handlePromptSubmit, so there's no clobbering risk — restore eagerly.
2962
+ // In both deferred cases, the stash is restored after await handlePromptSubmit.
2963
+ const isSlashCommand = !speculationAccept && input.trim().startsWith('/');
2964
+ // Submit runs "now" (not queued) when not already loading, or when
2965
+ // accepting speculation, or in remote mode (which sends via WS and
2966
+ // returns early without calling handlePromptSubmit).
2967
+ const submitsNow = !isLoading || speculationAccept || activeRemote.isRemoteMode;
2968
+ if (stashedPrompt !== undefined && !isSlashCommand && submitsNow) {
2969
+ setInputValue(stashedPrompt.text);
2970
+ helpers.setCursorOffset(stashedPrompt.cursorOffset);
2971
+ setPastedContents(stashedPrompt.pastedContents);
2972
+ setStashedPrompt(undefined);
2973
+ }
2974
+ else if (submitsNow) {
2975
+ if (!options?.fromKeybinding) {
2976
+ // Clear input when not loading or accepting speculation.
2977
+ // Preserve input for keybinding-triggered commands.
2978
+ setInputValue('');
2979
+ helpers.setCursorOffset(0);
2980
+ }
2981
+ setPastedContents({});
2982
+ }
2983
+ if (submitsNow) {
2984
+ setInputMode('prompt');
2985
+ setIDESelection(undefined);
2986
+ setSubmitCount(_ => _ + 1);
2987
+ helpers.clearBuffer();
2988
+ tipPickedThisTurnRef.current = false;
2989
+ // Show the placeholder in the same React batch as setInputValue('').
2990
+ // Skip for slash/bash (they have their own echo), speculation and remote
2991
+ // mode (both setMessages directly with no gap to bridge).
2992
+ if (!isSlashCommand && inputMode === 'prompt' && !speculationAccept && !activeRemote.isRemoteMode) {
2993
+ setUserInputOnProcessing(input);
2994
+ // showSpinner includes userInputOnProcessing, so the spinner appears
2995
+ // on this render. Reset timing refs now (before queryGuard.reserve()
2996
+ // would) so elapsed time doesn't read as Date.now() - 0. The
2997
+ // isQueryActive transition above does the same reset — idempotent.
2998
+ resetTimingRefs();
2999
+ }
3000
+ // Increment prompt count for attribution tracking and save snapshot
3001
+ // The snapshot persists promptCount so it survives compaction
3002
+ if (feature('COMMIT_ATTRIBUTION')) {
3003
+ setAppState(prev => ({
3004
+ ...prev,
3005
+ attribution: incrementPromptCount(prev.attribution, snapshot => {
3006
+ void recordAttributionSnapshot(snapshot).catch(error => {
3007
+ logForDebugging(`Attribution: Failed to save snapshot: ${error}`);
3008
+ });
3009
+ })
3010
+ }));
3011
+ }
3012
+ }
3013
+ // Handle speculation acceptance
3014
+ if (speculationAccept) {
3015
+ const { queryRequired } = await handleSpeculationAccept(speculationAccept.state, speculationAccept.speculationSessionTimeSavedMs, speculationAccept.setAppState, input, {
3016
+ setMessages,
3017
+ readFileState,
3018
+ cwd: getOriginalCwd()
3019
+ });
3020
+ if (queryRequired) {
3021
+ const newAbortController = createAbortController();
3022
+ setAbortController(newAbortController);
3023
+ void onQuery([], newAbortController, true, [], mainLoopModel);
3024
+ }
3025
+ return;
3026
+ }
3027
+ // Remote mode: send input via stream-json instead of local query.
3028
+ // Permission requests from the remote are bridged into toolUseConfirmQueue
3029
+ // and rendered using the standard PermissionRequest component.
3030
+ //
3031
+ // local-jsx slash commands (e.g. /agents, /config) render UI in THIS
3032
+ // process — they have no remote equivalent. Let those fall through to
3033
+ // handlePromptSubmit so they execute locally. Prompt commands and
3034
+ // plain text go to the remote.
3035
+ if (activeRemote.isRemoteMode && !(isSlashCommand && commands.find(c => {
3036
+ const name = input.trim().slice(1).split(/\s/)[0];
3037
+ return isCommandEnabled(c) && (c.name === name || c.aliases?.includes(name) || getCommandName(c) === name);
3038
+ })?.type === 'local-jsx')) {
3039
+ // Build content blocks when there are pasted attachments (images)
3040
+ const pastedValues = Object.values(pastedContents);
3041
+ const imageContents = pastedValues.filter(c => c.type === 'image');
3042
+ const imagePasteIds = imageContents.length > 0 ? imageContents.map(c => c.id) : undefined;
3043
+ let messageContent = input.trim();
3044
+ let remoteContent = input.trim();
3045
+ if (pastedValues.length > 0) {
3046
+ const contentBlocks = [];
3047
+ const remoteBlocks = [];
3048
+ const trimmedInput = input.trim();
3049
+ if (trimmedInput) {
3050
+ contentBlocks.push({
3051
+ type: 'text',
3052
+ text: trimmedInput
3053
+ });
3054
+ remoteBlocks.push({
3055
+ type: 'text',
3056
+ text: trimmedInput
3057
+ });
3058
+ }
3059
+ for (const pasted of pastedValues) {
3060
+ if (pasted.type === 'image') {
3061
+ const source = {
3062
+ type: 'base64',
3063
+ media_type: (pasted.mediaType ?? 'image/png'),
3064
+ data: pasted.content
3065
+ };
3066
+ contentBlocks.push({
3067
+ type: 'image',
3068
+ source
3069
+ });
3070
+ remoteBlocks.push({
3071
+ type: 'image',
3072
+ source
3073
+ });
3074
+ }
3075
+ else {
3076
+ contentBlocks.push({
3077
+ type: 'text',
3078
+ text: pasted.content
3079
+ });
3080
+ remoteBlocks.push({
3081
+ type: 'text',
3082
+ text: pasted.content
3083
+ });
3084
+ }
3085
+ }
3086
+ messageContent = contentBlocks;
3087
+ remoteContent = remoteBlocks;
3088
+ }
3089
+ // Create and add user message to UI
3090
+ // Note: empty input already handled by early return above
3091
+ const userMessage = createUserMessage({
3092
+ content: messageContent,
3093
+ imagePasteIds
3094
+ });
3095
+ setMessages(prev => [...prev, userMessage]);
3096
+ // Send to remote session
3097
+ await activeRemote.sendMessage(remoteContent, {
3098
+ uuid: userMessage.uuid
3099
+ });
3100
+ return;
3101
+ }
3102
+ // Ensure SessionStart hook context is available before the first API call.
3103
+ await awaitPendingHooks();
3104
+ await handlePromptSubmit({
3105
+ input,
3106
+ helpers,
3107
+ queryGuard,
3108
+ isExternalLoading,
3109
+ mode: inputMode,
3110
+ commands,
3111
+ onInputChange: setInputValue,
3112
+ setPastedContents,
3113
+ setToolJSX,
3114
+ getToolUseContext,
3115
+ messages: messagesRef.current,
3116
+ mainLoopModel,
3117
+ pastedContents,
3118
+ ideSelection,
3119
+ setUserInputOnProcessing,
3120
+ setAbortController,
3121
+ abortController,
3122
+ onQuery,
3123
+ setAppState,
3124
+ querySource: getQuerySourceForREPL(),
3125
+ onBeforeQuery,
3126
+ canUseTool,
3127
+ addNotification,
3128
+ setMessages,
3129
+ // Read via ref so streamMode can be dropped from onSubmit deps —
3130
+ // handlePromptSubmit only uses it for debug log + telemetry event.
3131
+ streamMode: streamModeRef.current,
3132
+ hasInterruptibleToolInProgress: hasInterruptibleToolInProgressRef.current
3133
+ });
3134
+ // Restore stash that was deferred above. Two cases:
3135
+ // - Slash command: handlePromptSubmit awaited the full command execution
3136
+ // (including interactive pickers). Restoring now places the stash back in
3137
+ // the visible input.
3138
+ // - Loading (queued): handlePromptSubmit enqueued + cleared input, then
3139
+ // returned quickly. Restoring now places the stash back after the clear.
3140
+ if ((isSlashCommand || isLoading) && stashedPrompt !== undefined) {
3141
+ setInputValue(stashedPrompt.text);
3142
+ helpers.setCursorOffset(stashedPrompt.cursorOffset);
3143
+ setPastedContents(stashedPrompt.pastedContents);
3144
+ setStashedPrompt(undefined);
3145
+ }
3146
+ }, [queryGuard,
3147
+ // isLoading is read at the !isLoading checks above for input-clearing
3148
+ // and submitCount gating. It's derived from isQueryActive || isExternalLoading,
3149
+ // so including it here ensures the closure captures the fresh value.
3150
+ isLoading, isExternalLoading, inputMode, commands, setInputValue, setInputMode, setPastedContents, setSubmitCount, setIDESelection, setToolJSX, getToolUseContext,
3151
+ // messages is read via messagesRef.current inside the callback to
3152
+ // keep onSubmit stable across message updates (see L2384/L2400/L2662).
3153
+ // Without this, each setMessages call (~30× per turn) recreates
3154
+ // onSubmit, pinning the REPL render scope (1776B) + that render's
3155
+ // messages array in downstream closures (PromptInput, handleAutoRunIssue).
3156
+ // Heap analysis showed ~9 REPL scopes and ~15 messages array versions
3157
+ // accumulating after #20174/#20175, all traced to this dep.
3158
+ mainLoopModel, pastedContents, ideSelection, setUserInputOnProcessing, setAbortController, addNotification, onQuery, stashedPrompt, setStashedPrompt, setAppState, onBeforeQuery, canUseTool, remoteSession, setMessages, awaitPendingHooks, repinScroll]);
3159
+ // Callback for when user submits input while viewing a teammate's transcript
3160
+ const onAgentSubmit = useCallback(async (input, task, helpers) => {
3161
+ if (isLocalAgentTask(task)) {
3162
+ appendMessageToLocalAgent(task.id, createUserMessage({
3163
+ content: input
3164
+ }), setAppState);
3165
+ if (task.status === 'running') {
3166
+ queuePendingMessage(task.id, input, setAppState);
3167
+ }
3168
+ else {
3169
+ void resumeAgentBackground({
3170
+ agentId: task.id,
3171
+ prompt: input,
3172
+ toolUseContext: getToolUseContext(messagesRef.current, [], new AbortController(), mainLoopModel),
3173
+ canUseTool
3174
+ }).catch(err => {
3175
+ logForDebugging(`resumeAgentBackground failed: ${errorMessage(err)}`);
3176
+ addNotification({
3177
+ key: `resume-agent-failed-${task.id}`,
3178
+ jsx: _jsxs(Text, { color: "error", children: ["Failed to resume agent: ", errorMessage(err)] }),
3179
+ priority: 'low'
3180
+ });
3181
+ });
3182
+ }
3183
+ }
3184
+ else {
3185
+ injectUserMessageToTeammate(task.id, input, setAppState);
3186
+ }
3187
+ setInputValue('');
3188
+ helpers.setCursorOffset(0);
3189
+ helpers.clearBuffer();
3190
+ }, [setAppState, setInputValue, getToolUseContext, canUseTool, mainLoopModel, addNotification]);
3191
+ // Handlers for auto-run /issue or /good-claude (defined after onSubmit)
3192
+ const handleAutoRunIssue = useCallback(() => {
3193
+ const command = autoRunIssueReason ? getAutoRunCommand(autoRunIssueReason) : '/issue';
3194
+ setAutoRunIssueReason(null); // Clear the state
3195
+ onSubmit(command, {
3196
+ setCursorOffset: () => { },
3197
+ clearBuffer: () => { },
3198
+ resetHistory: () => { }
3199
+ }).catch(err => {
3200
+ logForDebugging(`Auto-run ${command} failed: ${errorMessage(err)}`);
3201
+ });
3202
+ }, [onSubmit, autoRunIssueReason]);
3203
+ const handleCancelAutoRunIssue = useCallback(() => {
3204
+ setAutoRunIssueReason(null);
3205
+ }, []);
3206
+ // Handler for when user presses 1 on survey thanks screen to share details
3207
+ const handleSurveyRequestFeedback = useCallback(() => {
3208
+ const command = "external" === 'ant' ? '/issue' : '/feedback';
3209
+ onSubmit(command, {
3210
+ setCursorOffset: () => { },
3211
+ clearBuffer: () => { },
3212
+ resetHistory: () => { }
3213
+ }).catch(err => {
3214
+ logForDebugging(`Survey feedback request failed: ${err instanceof Error ? err.message : String(err)}`);
3215
+ });
3216
+ }, [onSubmit]);
3217
+ // onSubmit is unstable (deps include `messages` which changes every turn).
3218
+ // `handleOpenRateLimitOptions` is prop-drilled to every MessageRow, and each
3219
+ // MessageRow fiber pins the closure (and transitively the entire REPL render
3220
+ // scope, ~1.8KB) at mount time. Using a ref keeps this callback stable so
3221
+ // old REPL scopes can be GC'd — saves ~35MB over a 1000-turn session.
3222
+ const onSubmitRef = useRef(onSubmit);
3223
+ onSubmitRef.current = onSubmit;
3224
+ const handleOpenRateLimitOptions = useCallback(() => {
3225
+ void onSubmitRef.current('/rate-limit-options', {
3226
+ setCursorOffset: () => { },
3227
+ clearBuffer: () => { },
3228
+ resetHistory: () => { }
3229
+ });
3230
+ }, []);
3231
+ const handleExit = useCallback(async () => {
3232
+ setIsExiting(true);
3233
+ // In bg sessions, always detach instead of kill — even when a worktree is
3234
+ // active. Without this guard, the worktree branch below short-circuits into
3235
+ // ExitFlow (which calls gracefulShutdown) before exit.tsx is ever loaded.
3236
+ if (feature('BG_SESSIONS') && isBgSession()) {
3237
+ spawnSync('tmux', ['detach-client'], {
3238
+ stdio: 'ignore'
3239
+ });
3240
+ setIsExiting(false);
3241
+ return;
3242
+ }
3243
+ const showWorktree = getCurrentWorktreeSession() !== null;
3244
+ if (showWorktree) {
3245
+ setExitFlow(_jsx(ExitFlow, { showWorktree: true, onDone: () => { }, onCancel: () => {
3246
+ setExitFlow(null);
3247
+ setIsExiting(false);
3248
+ } }));
3249
+ return;
3250
+ }
3251
+ const exitMod = await exit.load();
3252
+ const exitFlowResult = await exitMod.call(() => { });
3253
+ setExitFlow(exitFlowResult);
3254
+ // If call() returned without killing the process (bg session detach),
3255
+ // clear isExiting so the UI is usable on reattach. No-op on the normal
3256
+ // path — gracefulShutdown's process.exit() means we never get here.
3257
+ if (exitFlowResult === null) {
3258
+ setIsExiting(false);
3259
+ }
3260
+ }, []);
3261
+ const handleShowMessageSelector = useCallback(() => {
3262
+ setIsMessageSelectorVisible(prev => !prev);
3263
+ }, []);
3264
+ // Rewind conversation state to just before `message`: slice messages,
3265
+ // reset conversation ID, microcompact state, permission mode, prompt suggestion.
3266
+ // Does NOT touch the prompt input. Index is computed from messagesRef (always
3267
+ // fresh via the setMessages wrapper) so callers don't need to worry about
3268
+ // stale closures.
3269
+ const rewindConversationTo = useCallback((message) => {
3270
+ const prev = messagesRef.current;
3271
+ const messageIndex = prev.lastIndexOf(message);
3272
+ if (messageIndex === -1)
3273
+ return;
3274
+ logEvent('tengu_conversation_rewind', {
3275
+ preRewindMessageCount: prev.length,
3276
+ postRewindMessageCount: messageIndex,
3277
+ messagesRemoved: prev.length - messageIndex,
3278
+ rewindToMessageIndex: messageIndex
3279
+ });
3280
+ setMessages(prev.slice(0, messageIndex));
3281
+ // Careful, this has to happen after setMessages
3282
+ setConversationId(randomUUID());
3283
+ // Reset cached microcompact state so stale pinned cache edits
3284
+ // don't reference tool_use_ids from truncated messages
3285
+ resetMicrocompactState();
3286
+ if (feature('CONTEXT_COLLAPSE')) {
3287
+ // Rewind truncates the REPL array. Commits whose archived span
3288
+ // was past the rewind point can't be projected anymore
3289
+ // (projectView silently skips them) but the staged queue and ID
3290
+ // maps reference stale uuids. Simplest safe reset: drop
3291
+ // everything. The ctx-agent will re-stage on the next
3292
+ // threshold crossing.
3293
+ /* eslint-disable @typescript-eslint/no-require-imports */
3294
+ ;
3295
+ require('../services/contextCollapse/index.js').resetContextCollapse();
3296
+ /* eslint-enable @typescript-eslint/no-require-imports */
3297
+ }
3298
+ // Restore state from the message we're rewinding to
3299
+ setAppState(prev => ({
3300
+ ...prev,
3301
+ // Restore permission mode from the message
3302
+ toolPermissionContext: message.permissionMode && prev.toolPermissionContext.mode !== message.permissionMode ? {
3303
+ ...prev.toolPermissionContext,
3304
+ mode: message.permissionMode
3305
+ } : prev.toolPermissionContext,
3306
+ // Clear stale prompt suggestion from previous conversation state
3307
+ promptSuggestion: {
3308
+ text: null,
3309
+ promptId: null,
3310
+ shownAt: 0,
3311
+ acceptedAt: 0,
3312
+ generationRequestId: null
3313
+ }
3314
+ }));
3315
+ }, [setMessages, setAppState]);
3316
+ // Synchronous rewind + input population. Used directly by auto-restore on
3317
+ // interrupt (so React batches with the abort's setMessages → single render,
3318
+ // no flicker). MessageSelector wraps this in setImmediate via handleRestoreMessage.
3319
+ const restoreMessageSync = useCallback((message) => {
3320
+ rewindConversationTo(message);
3321
+ const r = textForResubmit(message);
3322
+ if (r) {
3323
+ setInputValue(r.text);
3324
+ setInputMode(r.mode);
3325
+ }
3326
+ // Restore pasted images
3327
+ if (Array.isArray(message.message.content) && message.message.content.some(block => block.type === 'image')) {
3328
+ const imageBlocks = message.message.content.filter(block => block.type === 'image');
3329
+ if (imageBlocks.length > 0) {
3330
+ const newPastedContents = {};
3331
+ imageBlocks.forEach((block, index) => {
3332
+ if (block.source.type === 'base64') {
3333
+ const id = message.imagePasteIds?.[index] ?? index + 1;
3334
+ newPastedContents[id] = {
3335
+ id,
3336
+ type: 'image',
3337
+ content: block.source.data,
3338
+ mediaType: block.source.media_type
3339
+ };
3340
+ }
3341
+ });
3342
+ setPastedContents(newPastedContents);
3343
+ }
3344
+ }
3345
+ }, [rewindConversationTo, setInputValue]);
3346
+ restoreMessageSyncRef.current = restoreMessageSync;
3347
+ // MessageSelector path: defer via setImmediate so the "Interrupted" message
3348
+ // renders to static output before rewind — otherwise it remains vestigial
3349
+ // at the top of the screen.
3350
+ const handleRestoreMessage = useCallback(async (message) => {
3351
+ setImmediate((restore, message) => restore(message), restoreMessageSync, message);
3352
+ }, [restoreMessageSync]);
3353
+ // Not memoized — hook stores caps via ref, reads latest closure at dispatch.
3354
+ // 24-char prefix: deriveUUID preserves first 24, renderable uuid prefix-matches raw source.
3355
+ const findRawIndex = (uuid) => {
3356
+ const prefix = uuid.slice(0, 24);
3357
+ return messages.findIndex(m => m.uuid.slice(0, 24) === prefix);
3358
+ };
3359
+ const messageActionCaps = {
3360
+ copy: text =>
3361
+ // setClipboard RETURNS OSC 52 — caller must stdout.write (tmux side-effects load-buffer, but that's tmux-only).
3362
+ void setClipboard(text).then(raw => {
3363
+ if (raw)
3364
+ process.stdout.write(raw);
3365
+ addNotification({
3366
+ // Same key as text-selection copy — repeated copies replace toast, don't queue.
3367
+ key: 'selection-copied',
3368
+ text: 'copied',
3369
+ color: 'success',
3370
+ priority: 'immediate',
3371
+ timeoutMs: 2000
3372
+ });
3373
+ }),
3374
+ edit: async (msg) => {
3375
+ // Same skip-confirm check as /rewind: lossless → direct, else confirm dialog.
3376
+ const rawIdx = findRawIndex(msg.uuid);
3377
+ const raw = rawIdx >= 0 ? messages[rawIdx] : undefined;
3378
+ if (!raw || !selectableUserMessagesFilter(raw))
3379
+ return;
3380
+ const noFileChanges = !(await fileHistoryHasAnyChanges(fileHistory, raw.uuid));
3381
+ const onlySynthetic = messagesAfterAreOnlySynthetic(messages, rawIdx);
3382
+ if (noFileChanges && onlySynthetic) {
3383
+ // rewindConversationTo's setMessages races stream appends — cancel first (idempotent).
3384
+ onCancel();
3385
+ // handleRestoreMessage also restores pasted images.
3386
+ void handleRestoreMessage(raw);
3387
+ }
3388
+ else {
3389
+ // Dialog path: onPreRestore (= onCancel) fires when user CONFIRMS, not on nevermind.
3390
+ setMessageSelectorPreselect(raw);
3391
+ setIsMessageSelectorVisible(true);
3392
+ }
3393
+ }
3394
+ };
3395
+ const { enter: enterMessageActions, handlers: messageActionHandlers } = useMessageActions(cursor, setCursor, cursorNavRef, messageActionCaps);
3396
+ async function onInit() {
3397
+ // Always verify API key on startup, so we can show the user an error in the
3398
+ // bottom right corner of the screen if the API key is invalid.
3399
+ void reverify();
3400
+ // Populate readFileState with CLAUDE.md files at startup
3401
+ const memoryFiles = await getMemoryFiles();
3402
+ if (memoryFiles.length > 0) {
3403
+ const fileList = memoryFiles.map(f => ` [${f.type}] ${f.path} (${f.content.length} chars)${f.parent ? ` (included by ${f.parent})` : ''}`).join('\n');
3404
+ logForDebugging(`Loaded ${memoryFiles.length} CLAUDE.md/rules files:\n${fileList}`);
3405
+ }
3406
+ else {
3407
+ logForDebugging('No CLAUDE.md/rules files found');
3408
+ }
3409
+ for (const file of memoryFiles) {
3410
+ // When the injected content doesn't match disk (stripped HTML comments,
3411
+ // stripped frontmatter, MEMORY.md truncation), cache the RAW disk bytes
3412
+ // with isPartialView so Edit/Write require a real Read first while
3413
+ // getChangedFiles + nested_memory dedup still work.
3414
+ readFileState.current.set(file.path, {
3415
+ content: file.contentDiffersFromDisk ? file.rawContent ?? file.content : file.content,
3416
+ timestamp: Date.now(),
3417
+ offset: undefined,
3418
+ limit: undefined,
3419
+ isPartialView: file.contentDiffersFromDisk
3420
+ });
3421
+ }
3422
+ // Initial message handling is done via the initialMessage effect
3423
+ }
3424
+ // Register cost summary tracker
3425
+ useCostSummary(useFpsMetrics());
3426
+ // Record transcripts locally, for debugging and conversation recovery
3427
+ // Don't record conversation if we only have initial messages; optimizes
3428
+ // the case where user resumes a conversation then quites before doing
3429
+ // anything else
3430
+ useLogMessages(messages, messages.length === initialMessages?.length);
3431
+ // REPL Bridge: replicate user/assistant messages to the bridge session
3432
+ // for remote access via claude.ai. No-op in external builds or when not enabled.
3433
+ const { sendBridgeResult } = useReplBridge(messages, setMessages, abortControllerRef, commands, mainLoopModel);
3434
+ sendBridgeResultRef.current = sendBridgeResult;
3435
+ // Telegram mirror: espeja prompts + respuestas + resúmenes de tool_use al
3436
+ // chat primario cuando el bridge está activo, e inyecta mensajes entrantes
3437
+ // desde Telegram al REPL como si fueran prompts del usuario.
3438
+ useTelegramMirror(messages);
3439
+ // WhatsApp mirror: mismo patrón que Telegram, al número primario configurado.
3440
+ useWhatsAppMirror(messages);
3441
+ useAfterFirstRender();
3442
+ // Track prompt queue usage for analytics. Fire once per transition from
3443
+ // empty to non-empty, not on every length change -- otherwise a render loop
3444
+ // (concurrent onQuery thrashing, etc.) spams saveGlobalConfig, which hits
3445
+ // ELOCKED under concurrent sessions and falls back to unlocked writes.
3446
+ // That write storm is the primary trigger for ~/.context.json corruption
3447
+ // (GH #3117).
3448
+ const hasCountedQueueUseRef = useRef(false);
3449
+ useEffect(() => {
3450
+ if (queuedCommands.length < 1) {
3451
+ hasCountedQueueUseRef.current = false;
3452
+ return;
3453
+ }
3454
+ if (hasCountedQueueUseRef.current)
3455
+ return;
3456
+ hasCountedQueueUseRef.current = true;
3457
+ saveGlobalConfig(current => ({
3458
+ ...current,
3459
+ promptQueueUseCount: (current.promptQueueUseCount ?? 0) + 1
3460
+ }));
3461
+ }, [queuedCommands.length]);
3462
+ // Process queued commands when query completes and queue has items
3463
+ const executeQueuedInput = useCallback(async (queuedCommands) => {
3464
+ await handlePromptSubmit({
3465
+ helpers: {
3466
+ setCursorOffset: () => { },
3467
+ clearBuffer: () => { },
3468
+ resetHistory: () => { }
3469
+ },
3470
+ queryGuard,
3471
+ commands,
3472
+ onInputChange: () => { },
3473
+ setPastedContents: () => { },
3474
+ setToolJSX,
3475
+ getToolUseContext,
3476
+ messages,
3477
+ mainLoopModel,
3478
+ ideSelection,
3479
+ setUserInputOnProcessing,
3480
+ setAbortController,
3481
+ onQuery,
3482
+ setAppState,
3483
+ querySource: getQuerySourceForREPL(),
3484
+ onBeforeQuery,
3485
+ canUseTool,
3486
+ addNotification,
3487
+ setMessages,
3488
+ queuedCommands
3489
+ });
3490
+ }, [queryGuard, commands, setToolJSX, getToolUseContext, messages, mainLoopModel, ideSelection, setUserInputOnProcessing, canUseTool, setAbortController, onQuery, addNotification, setAppState, onBeforeQuery]);
3491
+ useQueueProcessor({
3492
+ executeQueuedInput,
3493
+ hasActiveLocalJsxUI: isShowingLocalJSXCommand,
3494
+ queryGuard
3495
+ });
3496
+ // We'll use the global lastInteractionTime from state.ts
3497
+ // Update last interaction time when input changes.
3498
+ // Must be immediate because useEffect runs after the Ink render cycle flush.
3499
+ useEffect(() => {
3500
+ activityManager.recordUserActivity();
3501
+ updateLastInteractionTime(true);
3502
+ }, [inputValue, submitCount]);
3503
+ useEffect(() => {
3504
+ if (submitCount === 1) {
3505
+ startBackgroundHousekeeping();
3506
+ }
3507
+ }, [submitCount]);
3508
+ // Show notification when Claude is done responding and user is idle
3509
+ useEffect(() => {
3510
+ // Don't set up notification if Claude is busy
3511
+ if (isLoading)
3512
+ return;
3513
+ // Only enable notifications after the first new interaction in this session
3514
+ if (submitCount === 0)
3515
+ return;
3516
+ // No query has completed yet
3517
+ if (lastQueryCompletionTime === 0)
3518
+ return;
3519
+ // Set timeout to check idle state
3520
+ const timer = setTimeout((lastQueryCompletionTime, isLoading, toolJSX, focusedInputDialogRef, terminal) => {
3521
+ // Check if user has interacted since the response ended
3522
+ const lastUserInteraction = getLastInteractionTime();
3523
+ if (lastUserInteraction > lastQueryCompletionTime) {
3524
+ // User has interacted since Claude finished - they're not idle, don't notify
3525
+ return;
3526
+ }
3527
+ // User hasn't interacted since response ended, check other conditions
3528
+ const idleTimeSinceResponse = Date.now() - lastQueryCompletionTime;
3529
+ if (!isLoading && !toolJSX &&
3530
+ // Use ref to get current dialog state, avoiding stale closure
3531
+ focusedInputDialogRef.current === undefined && idleTimeSinceResponse >= getGlobalConfig().messageIdleNotifThresholdMs) {
3532
+ void sendNotification({
3533
+ message: 'Claude is waiting for your input',
3534
+ notificationType: 'idle_prompt'
3535
+ }, terminal);
3536
+ }
3537
+ }, getGlobalConfig().messageIdleNotifThresholdMs, lastQueryCompletionTime, isLoading, toolJSX, focusedInputDialogRef, terminal);
3538
+ return () => clearTimeout(timer);
3539
+ }, [isLoading, toolJSX, submitCount, lastQueryCompletionTime, terminal]);
3540
+ // Idle-return hint: show notification when idle threshold is exceeded.
3541
+ // Timer fires after the configured idle period; notification persists until
3542
+ // dismissed or the user submits.
3543
+ useEffect(() => {
3544
+ if (lastQueryCompletionTime === 0)
3545
+ return;
3546
+ if (isLoading)
3547
+ return;
3548
+ const willowMode = getFeatureValue_CACHED_MAY_BE_STALE('tengu_willow_mode', 'off');
3549
+ if (willowMode !== 'hint' && willowMode !== 'hint_v2')
3550
+ return;
3551
+ if (getGlobalConfig().idleReturnDismissed)
3552
+ return;
3553
+ const tokenThreshold = Number(process.env.CLAUDE_CODE_IDLE_TOKEN_THRESHOLD ?? 100_000);
3554
+ if (getTotalInputTokens() < tokenThreshold)
3555
+ return;
3556
+ const idleThresholdMs = Number(process.env.CLAUDE_CODE_IDLE_THRESHOLD_MINUTES ?? 75) * 60_000;
3557
+ const elapsed = Date.now() - lastQueryCompletionTime;
3558
+ const remaining = idleThresholdMs - elapsed;
3559
+ const timer = setTimeout((lqct, addNotif, msgsRef, mode, hintRef) => {
3560
+ if (msgsRef.current.length === 0)
3561
+ return;
3562
+ const totalTokens = getTotalInputTokens();
3563
+ const formattedTokens = formatTokens(totalTokens);
3564
+ const idleMinutes = (Date.now() - lqct) / 60_000;
3565
+ addNotif({
3566
+ key: 'idle-return-hint',
3567
+ jsx: mode === 'hint_v2' ? _jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: "new task? " }), _jsx(Text, { color: "suggestion", children: "/clear" }), _jsx(Text, { dimColor: true, children: " to save " }), _jsxs(Text, { color: "suggestion", children: [formattedTokens, " tokens"] })] }) : _jsxs(Text, { color: "warning", children: ["new task? /clear to save ", formattedTokens, " tokens"] }),
3568
+ priority: 'medium',
3569
+ // Persist until submit — the hint fires at T+75min idle, user may
3570
+ // not return for hours. removeNotification in useEffect cleanup
3571
+ // handles dismissal. 0x7FFFFFFF = setTimeout max (~24.8 days).
3572
+ timeoutMs: 0x7fffffff
3573
+ });
3574
+ hintRef.current = mode;
3575
+ logEvent('tengu_idle_return_action', {
3576
+ action: 'hint_shown',
3577
+ variant: mode,
3578
+ idleMinutes: Math.round(idleMinutes),
3579
+ messageCount: msgsRef.current.length,
3580
+ totalInputTokens: totalTokens
3581
+ });
3582
+ }, Math.max(0, remaining), lastQueryCompletionTime, addNotification, messagesRef, willowMode, idleHintShownRef);
3583
+ return () => {
3584
+ clearTimeout(timer);
3585
+ removeNotification('idle-return-hint');
3586
+ idleHintShownRef.current = false;
3587
+ };
3588
+ }, [lastQueryCompletionTime, isLoading, addNotification, removeNotification]);
3589
+ // Submits incoming prompts from teammate messages or tasks mode as new turns
3590
+ // Returns true if submission succeeded, false if a query is already running
3591
+ const handleIncomingPrompt = useCallback((content, options) => {
3592
+ if (queryGuard.isActive)
3593
+ return false;
3594
+ // Defer to user-queued commands — user input always takes priority
3595
+ // over system messages (teammate messages, task list items, etc.)
3596
+ // Read from the module-level store at call time (not the render-time
3597
+ // snapshot) to avoid a stale closure — this callback's deps don't
3598
+ // include the queue.
3599
+ if (getCommandQueue().some(cmd => cmd.mode === 'prompt' || cmd.mode === 'bash')) {
3600
+ return false;
3601
+ }
3602
+ const newAbortController = createAbortController();
3603
+ setAbortController(newAbortController);
3604
+ // Create a user message with the formatted content (includes XML wrapper)
3605
+ const userMessage = createUserMessage({
3606
+ content,
3607
+ isMeta: options?.isMeta ? true : undefined
3608
+ });
3609
+ void onQuery([userMessage], newAbortController, true, [], mainLoopModel);
3610
+ return true;
3611
+ }, [onQuery, mainLoopModel, store]);
3612
+ // Voice input integration (VOICE_MODE builds only)
3613
+ const voice = feature('VOICE_MODE') ?
3614
+ // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
3615
+ useVoiceIntegration({
3616
+ setInputValueRaw,
3617
+ inputValueRef,
3618
+ insertTextRef
3619
+ }) : {
3620
+ stripTrailing: () => 0,
3621
+ handleKeyEvent: () => { },
3622
+ resetAnchor: () => { },
3623
+ interimRange: null
3624
+ };
3625
+ useInboxPoller({
3626
+ enabled: isAgentSwarmsEnabled(),
3627
+ isLoading,
3628
+ focusedInputDialog,
3629
+ onSubmitMessage: handleIncomingPrompt
3630
+ });
3631
+ useMailboxBridge({
3632
+ isLoading,
3633
+ onSubmitMessage: handleIncomingPrompt
3634
+ });
3635
+ // Scheduled tasks from .claude/scheduled_tasks.json (CronCreate/Delete/List)
3636
+ if (feature('AGENT_TRIGGERS')) {
3637
+ // Assistant mode bypasses the isLoading gate (the proactive tick →
3638
+ // Sleep → tick loop would otherwise starve the scheduler).
3639
+ // kairosEnabled is set once in initialState (main.tsx) and never mutated — no
3640
+ // subscription needed. The tengu_kairos_cron runtime gate is checked inside
3641
+ // useScheduledTasks's effect (not here) since wrapping a hook call in a dynamic
3642
+ // condition would break rules-of-hooks.
3643
+ const assistantMode = store.getState().kairosEnabled;
3644
+ // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
3645
+ useScheduledTasks({
3646
+ isLoading,
3647
+ assistantMode,
3648
+ setMessages
3649
+ });
3650
+ }
3651
+ // Note: Permission polling is now handled by useInboxPoller
3652
+ // - Workers receive permission responses via mailbox messages
3653
+ // - Leaders receive permission requests via mailbox messages
3654
+ if ("external" === 'ant') {
3655
+ // Tasks mode: watch for tasks and auto-process them
3656
+ // eslint-disable-next-line react-hooks/rules-of-hooks
3657
+ // biome-ignore lint/correctness/useHookAtTopLevel: conditional for dead code elimination in external builds
3658
+ useTaskListWatcher({
3659
+ taskListId,
3660
+ isLoading,
3661
+ onSubmitTask: handleIncomingPrompt
3662
+ });
3663
+ // Loop mode: auto-tick when enabled (via /job command)
3664
+ // eslint-disable-next-line react-hooks/rules-of-hooks
3665
+ // biome-ignore lint/correctness/useHookAtTopLevel: conditional for dead code elimination in external builds
3666
+ useProactive?.({
3667
+ // Suppress ticks while an initial message is pending — the initial
3668
+ // message will be processed asynchronously and a premature tick would
3669
+ // race with it, causing concurrent-query enqueue of expanded skill text.
3670
+ isLoading: isLoading || initialMessage !== null,
3671
+ queuedCommandsLength: queuedCommands.length,
3672
+ hasActiveLocalJsxUI: isShowingLocalJSXCommand,
3673
+ isInPlanMode: toolPermissionContext.mode === 'plan',
3674
+ onSubmitTick: (prompt) => handleIncomingPrompt(prompt, {
3675
+ isMeta: true
3676
+ }),
3677
+ onQueueTick: (prompt) => enqueue({
3678
+ mode: 'prompt',
3679
+ value: prompt,
3680
+ isMeta: true
3681
+ })
3682
+ });
3683
+ }
3684
+ // Abort the current operation when a 'now' priority message arrives
3685
+ // (e.g. from a chat UI client via UDS).
3686
+ useEffect(() => {
3687
+ if (queuedCommands.some(cmd => cmd.priority === 'now')) {
3688
+ abortControllerRef.current?.abort('interrupt');
3689
+ }
3690
+ }, [queuedCommands]);
3691
+ // Initial load
3692
+ useEffect(() => {
3693
+ void onInit();
3694
+ // Cleanup on unmount
3695
+ return () => {
3696
+ void diagnosticTracker.shutdown();
3697
+ };
3698
+ // TODO: fix this
3699
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3700
+ }, []);
3701
+ // Listen for suspend/resume events
3702
+ const { internal_eventEmitter } = useStdin();
3703
+ const [remountKey, setRemountKey] = useState(0);
3704
+ useEffect(() => {
3705
+ const handleSuspend = () => {
3706
+ // Print suspension instructions
3707
+ process.stdout.write(`\nContext Code has been suspended. Run \`fg\` to bring Context Code back.\nNote: ctrl + z now suspends Context Code, ctrl + _ undoes input.\n`);
3708
+ };
3709
+ const handleResume = () => {
3710
+ // Force complete component tree replacement instead of terminal clear
3711
+ // Ink now handles line count reset internally on SIGCONT
3712
+ setRemountKey(prev => prev + 1);
3713
+ };
3714
+ internal_eventEmitter?.on('suspend', handleSuspend);
3715
+ internal_eventEmitter?.on('resume', handleResume);
3716
+ return () => {
3717
+ internal_eventEmitter?.off('suspend', handleSuspend);
3718
+ internal_eventEmitter?.off('resume', handleResume);
3719
+ };
3720
+ }, [internal_eventEmitter]);
3721
+ // Derive stop hook spinner suffix from messages state
3722
+ const stopHookSpinnerSuffix = useMemo(() => {
3723
+ if (!isLoading)
3724
+ return null;
3725
+ // Find stop hook progress messages
3726
+ const progressMsgs = messages.filter((m) => m.type === 'progress' && m.data.type === 'hook_progress' && (m.data.hookEvent === 'Stop' || m.data.hookEvent === 'SubagentStop'));
3727
+ if (progressMsgs.length === 0)
3728
+ return null;
3729
+ // Get the most recent stop hook execution
3730
+ const currentToolUseID = progressMsgs.at(-1)?.toolUseID;
3731
+ if (!currentToolUseID)
3732
+ return null;
3733
+ // Check if there's already a summary message for this execution (hooks completed)
3734
+ const hasSummaryForCurrentExecution = messages.some(m => m.type === 'system' && m.subtype === 'stop_hook_summary' && m.toolUseID === currentToolUseID);
3735
+ if (hasSummaryForCurrentExecution)
3736
+ return null;
3737
+ const currentHooks = progressMsgs.filter(p => p.toolUseID === currentToolUseID);
3738
+ const total = currentHooks.length;
3739
+ // Count completed hooks
3740
+ const completedCount = count(messages, m => {
3741
+ if (m.type !== 'attachment')
3742
+ return false;
3743
+ const attachment = m.attachment;
3744
+ return 'hookEvent' in attachment && (attachment.hookEvent === 'Stop' || attachment.hookEvent === 'SubagentStop') && 'toolUseID' in attachment && attachment.toolUseID === currentToolUseID;
3745
+ });
3746
+ // Check if any hook has a custom status message
3747
+ const customMessage = currentHooks.find(p => p.data.statusMessage)?.data.statusMessage;
3748
+ if (customMessage) {
3749
+ // Use custom message with progress counter if multiple hooks
3750
+ return total === 1 ? `${customMessage}…` : `${customMessage}… ${completedCount}/${total}`;
3751
+ }
3752
+ // Fall back to default behavior
3753
+ const hookType = currentHooks[0]?.data.hookEvent === 'SubagentStop' ? 'subagent stop' : 'stop';
3754
+ if ("external" === 'ant') {
3755
+ const cmd = currentHooks[completedCount]?.data.command;
3756
+ const label = cmd ? ` '${truncateToWidth(cmd, 40)}'` : '';
3757
+ return total === 1 ? `running ${hookType} hook${label}` : `running ${hookType} hook${label}\u2026 ${completedCount}/${total}`;
3758
+ }
3759
+ return total === 1 ? `running ${hookType} hook` : `running stop hooks… ${completedCount}/${total}`;
3760
+ }, [messages, isLoading]);
3761
+ // Callback to capture frozen state when entering transcript mode
3762
+ const handleEnterTranscript = useCallback(() => {
3763
+ setFrozenTranscriptState({
3764
+ messagesLength: messages.length,
3765
+ streamingToolUsesLength: streamingToolUses.length
3766
+ });
3767
+ }, [messages.length, streamingToolUses.length]);
3768
+ // Callback to clear frozen state when exiting transcript mode
3769
+ const handleExitTranscript = useCallback(() => {
3770
+ setFrozenTranscriptState(null);
3771
+ }, []);
3772
+ // Props for GlobalKeybindingHandlers component (rendered inside KeybindingSetup)
3773
+ const virtualScrollActive = isFullscreenEnvEnabled() && !disableVirtualScroll;
3774
+ // Transcript search state. Hooks must be unconditional so they live here
3775
+ // (not inside the `if (screen === 'transcript')` branch below); isActive
3776
+ // gates the useInput. Query persists across bar open/close so n/N keep
3777
+ // working after Enter dismisses the bar (less semantics).
3778
+ const jumpRef = useRef(null);
3779
+ const [searchOpen, setSearchOpen] = useState(false);
3780
+ const [searchQuery, setSearchQuery] = useState('');
3781
+ const [searchCount, setSearchCount] = useState(0);
3782
+ const [searchCurrent, setSearchCurrent] = useState(0);
3783
+ const onSearchMatchesChange = useCallback((count, current) => {
3784
+ setSearchCount(count);
3785
+ setSearchCurrent(current);
3786
+ }, []);
3787
+ useInput((input, key, event) => {
3788
+ if (key.ctrl || key.meta)
3789
+ return;
3790
+ // No Esc handling here — less has no navigating mode. Search state
3791
+ // (highlights, n/N) is just state. Esc/q/ctrl+c → transcript:exit
3792
+ // (ungated). Highlights clear on exit via the screen-change effect.
3793
+ if (input === '/') {
3794
+ // Capture scrollTop NOW — typing is a preview, 0-matches snaps
3795
+ // back here. Synchronous ref write, fires before the bar's
3796
+ // mount-effect calls setSearchQuery.
3797
+ jumpRef.current?.setAnchor();
3798
+ setSearchOpen(true);
3799
+ event.stopImmediatePropagation();
3800
+ return;
3801
+ }
3802
+ // Held-key batching: tokenizer coalesces to 'nnn'. Same uniform-batch
3803
+ // pattern as modalPagerAction in ScrollKeybindingHandler.tsx. Each
3804
+ // repeat is a step (n isn't idempotent like g).
3805
+ const c = input[0];
3806
+ if ((c === 'n' || c === 'N') && input === c.repeat(input.length) && searchCount > 0) {
3807
+ const fn = c === 'n' ? jumpRef.current?.nextMatch : jumpRef.current?.prevMatch;
3808
+ if (fn)
3809
+ for (let i = 0; i < input.length; i++)
3810
+ fn();
3811
+ event.stopImmediatePropagation();
3812
+ }
3813
+ },
3814
+ // Search needs virtual scroll (jumpRef drives VirtualMessageList). [
3815
+ // kills it, so !dumpMode — after [ there's nothing to jump in.
3816
+ {
3817
+ isActive: screen === 'transcript' && virtualScrollActive && !searchOpen && !dumpMode
3818
+ });
3819
+ const { setQuery: setHighlight, scanElement, setPositions } = useSearchHighlight();
3820
+ // Resize → abort search. Positions are (msg, query, WIDTH)-keyed —
3821
+ // cached positions are stale after a width change (new layout, new
3822
+ // wrapping). Clearing searchQuery triggers VML's setSearchQuery('')
3823
+ // which clears positionsCache + setPositions(null). Bar closes.
3824
+ // User hits / again → fresh everything.
3825
+ const transcriptCols = useTerminalSize().columns;
3826
+ const prevColsRef = React.useRef(transcriptCols);
3827
+ React.useEffect(() => {
3828
+ if (prevColsRef.current !== transcriptCols) {
3829
+ prevColsRef.current = transcriptCols;
3830
+ if (searchQuery || searchOpen) {
3831
+ setSearchOpen(false);
3832
+ setSearchQuery('');
3833
+ setSearchCount(0);
3834
+ setSearchCurrent(0);
3835
+ jumpRef.current?.disarmSearch();
3836
+ setHighlight('');
3837
+ }
3838
+ }
3839
+ }, [transcriptCols, searchQuery, searchOpen, setHighlight]);
3840
+ // Transcript escape hatches. Bare letters in modal context (no prompt
3841
+ // competing for input) — same class as g/G/j/k in ScrollKeybindingHandler.
3842
+ useInput((input, key, event) => {
3843
+ if (key.ctrl || key.meta)
3844
+ return;
3845
+ if (input === 'q') {
3846
+ // less: q quits the pager. ctrl+o toggles; q is the lineage exit.
3847
+ handleExitTranscript();
3848
+ event.stopImmediatePropagation();
3849
+ return;
3850
+ }
3851
+ if (input === '[' && !dumpMode) {
3852
+ // Force dump-to-scrollback. Also expand + uncap — no point dumping
3853
+ // a subset. Terminal/tmux cmd-F can now find anything. Guard here
3854
+ // (not in isActive) so v still works post-[ — dump-mode footer at
3855
+ // ~4898 wires editorStatus, confirming v is meant to stay live.
3856
+ setDumpMode(true);
3857
+ setShowAllInTranscript(true);
3858
+ event.stopImmediatePropagation();
3859
+ }
3860
+ else if (input === 'v') {
3861
+ // less-style: v opens the file in $VISUAL/$EDITOR. Render the full
3862
+ // transcript (same path /export uses), write to tmp, hand off.
3863
+ // openFileInExternalEditor handles alt-screen suspend/resume for
3864
+ // terminal editors; GUI editors spawn detached.
3865
+ event.stopImmediatePropagation();
3866
+ // Drop double-taps: the render is async and a second press before it
3867
+ // completes would run a second parallel render (double memory, two
3868
+ // tempfiles, two editor spawns). editorGenRef only guards
3869
+ // transcript-exit staleness, not same-session concurrency.
3870
+ if (editorRenderingRef.current)
3871
+ return;
3872
+ editorRenderingRef.current = true;
3873
+ // Capture generation + make a staleness-aware setter. Each write
3874
+ // checks gen (transcript exit bumps it → late writes from the
3875
+ // async render go silent).
3876
+ const gen = editorGenRef.current;
3877
+ const setStatus = (s) => {
3878
+ if (gen !== editorGenRef.current)
3879
+ return;
3880
+ clearTimeout(editorTimerRef.current);
3881
+ setEditorStatus(s);
3882
+ };
3883
+ setStatus(`rendering ${deferredMessages.length} messages…`);
3884
+ void (async () => {
3885
+ try {
3886
+ // Width = terminal minus vim's line-number gutter (4 digits +
3887
+ // space + slack). Floor at 80. PassThrough has no .columns so
3888
+ // without this Ink defaults to 80. Trailing-space strip: right-
3889
+ // aligned timestamps still leave a flexbox spacer run at EOL.
3890
+ // eslint-disable-next-line custom-rules/prefer-use-terminal-size -- one-shot at keypress time, not a reactive render dep
3891
+ const w = Math.max(80, (process.stdout.columns ?? 80) - 6);
3892
+ const raw = await renderMessagesToPlainText(deferredMessages, tools, w);
3893
+ const text = raw.replace(/[ \t]+$/gm, '');
3894
+ const path = join(tmpdir(), `cc-transcript-${Date.now()}.txt`);
3895
+ await writeFile(path, text);
3896
+ const opened = openFileInExternalEditor(path);
3897
+ setStatus(opened ? `opening ${path}` : `wrote ${path} · no $VISUAL/$EDITOR set`);
3898
+ }
3899
+ catch (e) {
3900
+ setStatus(`render failed: ${e instanceof Error ? e.message : String(e)}`);
3901
+ }
3902
+ editorRenderingRef.current = false;
3903
+ if (gen !== editorGenRef.current)
3904
+ return;
3905
+ editorTimerRef.current = setTimeout(s => s(''), 4000, setEditorStatus);
3906
+ })();
3907
+ }
3908
+ },
3909
+ // !searchOpen: typing 'v' or '[' in the search bar is search input, not
3910
+ // a command. No !dumpMode here — v should work after [ (the [ handler
3911
+ // guards itself inline).
3912
+ {
3913
+ isActive: screen === 'transcript' && virtualScrollActive && !searchOpen
3914
+ });
3915
+ // Fresh `less` per transcript entry. Prevents stale highlights matching
3916
+ // unrelated normal-mode text (overlay is alt-screen-global) and avoids
3917
+ // surprise n/N on re-entry. Same exit resets [ dump mode — each ctrl+o
3918
+ // entry is a fresh instance.
3919
+ const inTranscript = screen === 'transcript' && virtualScrollActive;
3920
+ useEffect(() => {
3921
+ if (!inTranscript) {
3922
+ setSearchQuery('');
3923
+ setSearchCount(0);
3924
+ setSearchCurrent(0);
3925
+ setSearchOpen(false);
3926
+ editorGenRef.current++;
3927
+ clearTimeout(editorTimerRef.current);
3928
+ setDumpMode(false);
3929
+ setEditorStatus('');
3930
+ }
3931
+ }, [inTranscript]);
3932
+ useEffect(() => {
3933
+ setHighlight(inTranscript ? searchQuery : '');
3934
+ // Clear the position-based CURRENT (yellow) overlay too. setHighlight
3935
+ // only clears the scan-based inverse. Without this, the yellow box
3936
+ // persists at its last screen coords after ctrl-c exits transcript.
3937
+ if (!inTranscript)
3938
+ setPositions(null);
3939
+ }, [inTranscript, searchQuery, setHighlight, setPositions]);
3940
+ const globalKeybindingProps = {
3941
+ screen,
3942
+ setScreen,
3943
+ showAllInTranscript,
3944
+ setShowAllInTranscript,
3945
+ messageCount: messages.length,
3946
+ onEnterTranscript: handleEnterTranscript,
3947
+ onExitTranscript: handleExitTranscript,
3948
+ virtualScrollActive,
3949
+ // Bar-open is a mode (owns keystrokes — j/k type, Esc cancels).
3950
+ // Navigating (query set, bar closed) is NOT — Esc exits transcript,
3951
+ // same as less q with highlights still visible. useSearchInput
3952
+ // doesn't stopPropagation, so without this gate transcript:exit
3953
+ // would fire on the same Esc that cancels the bar (child registers
3954
+ // first, fires first, bubbles).
3955
+ searchBarOpen: searchOpen
3956
+ };
3957
+ // Use frozen lengths to slice arrays, avoiding memory overhead of cloning
3958
+ const transcriptMessages = frozenTranscriptState ? deferredMessages.slice(0, frozenTranscriptState.messagesLength) : deferredMessages;
3959
+ const transcriptStreamingToolUses = frozenTranscriptState ? streamingToolUses.slice(0, frozenTranscriptState.streamingToolUsesLength) : streamingToolUses;
3960
+ // Handle shift+down for teammate navigation and background task management.
3961
+ // Guard onOpenBackgroundTasks when a local-jsx dialog (e.g. /mcp) is open —
3962
+ // otherwise Shift+Down stacks BackgroundTasksDialog on top and deadlocks input.
3963
+ useBackgroundTaskNavigation({
3964
+ onOpenBackgroundTasks: isShowingLocalJSXCommand ? undefined : () => setShowBashesDialog(true)
3965
+ });
3966
+ // Auto-exit viewing mode when teammate completes or errors
3967
+ useTeammateViewAutoExit();
3968
+ if (screen === 'transcript') {
3969
+ // Virtual scroll replaces the 30-message cap: everything is scrollable
3970
+ // and memory is bounded by the viewport. Without it, wrapping transcript
3971
+ // in a ScrollBox would mount all messages (~250 MB on long sessions —
3972
+ // the exact problem), so the kill switch and non-fullscreen paths must
3973
+ // fall through to the legacy render: no alt screen, dump to terminal
3974
+ // scrollback, 30-cap + Ctrl+E. Reusing scrollRef is safe — normal-mode
3975
+ // and transcript-mode are mutually exclusive (this early return), so
3976
+ // only one ScrollBox is ever mounted at a time.
3977
+ const transcriptScrollRef = isFullscreenEnvEnabled() && !disableVirtualScroll && !dumpMode ? scrollRef : undefined;
3978
+ const transcriptMessagesElement = _jsx(Messages, { messages: transcriptMessages, tools: tools, commands: commands, verbose: true, toolJSX: null, toolUseConfirmQueue: [], inProgressToolUseIDs: inProgressToolUseIDs, isMessageSelectorVisible: false, conversationId: conversationId, screen: screen, agentDefinitions: agentDefinitions, streamingToolUses: transcriptStreamingToolUses, showAllInTranscript: showAllInTranscript, onOpenRateLimitOptions: handleOpenRateLimitOptions, isLoading: isLoading, hidePastThinking: true, streamingThinking: streamingThinking, scrollRef: transcriptScrollRef, jumpRef: jumpRef, onSearchMatchesChange: onSearchMatchesChange, scanElement: scanElement, setPositions: setPositions, disableRenderCap: dumpMode });
3979
+ const transcriptToolJSX = toolJSX && _jsx(Box, { flexDirection: "column", width: "100%", children: toolJSX.jsx });
3980
+ const transcriptReturn = _jsxs(KeybindingSetup, { children: [_jsx(AnimatedTerminalTitle, { isAnimating: titleIsAnimating, title: terminalTitle, disabled: titleDisabled, noPrefix: showStatusInTerminalTab }), _jsx(GlobalKeybindingHandlers, { ...globalKeybindingProps }), feature('VOICE_MODE') ? _jsx(VoiceKeybindingHandler, { voiceHandleKeyEvent: voice.handleKeyEvent, stripTrailing: voice.stripTrailing, resetAnchor: voice.resetAnchor, isActive: !toolJSX?.isLocalJSXCommand }) : null, _jsx(CommandKeybindingHandlers, { onSubmit: onSubmit, isActive: !toolJSX?.isLocalJSXCommand }), transcriptScrollRef ?
3981
+ _jsx(ScrollKeybindingHandler, { scrollRef: scrollRef,
3982
+ // Yield wheel/ctrl+u/d to UltraplanChoiceDialog's own scroll
3983
+ // handler while the modal is showing.
3984
+ isActive: focusedInputDialog !== 'ultraplan-choice',
3985
+ // g/G/j/k/ctrl+u/ctrl+d would eat keystrokes the search bar
3986
+ // wants. Off while searching.
3987
+ isModal: !searchOpen,
3988
+ // Manual scroll exits the search context — clear the yellow
3989
+ // current-match marker. Positions are (msg, rowOffset)-keyed;
3990
+ // j/k changes scrollTop so rowOffset is stale → wrong row
3991
+ // gets yellow. Next n/N re-establishes via step()→jump().
3992
+ onScroll: () => jumpRef.current?.disarmSearch() }) : null, _jsx(CancelRequestHandler, { ...cancelRequestProps }), transcriptScrollRef ? _jsx(FullscreenLayout, { scrollRef: scrollRef, scrollable: _jsxs(_Fragment, { children: [transcriptMessagesElement, transcriptToolJSX, _jsx(SandboxViolationExpandedView, {})] }), bottom: searchOpen ? _jsx(TranscriptSearchBar, { jumpRef: jumpRef,
3993
+ // Seed was tried (c01578c8) — broke /hello muscle
3994
+ // memory (cursor lands after 'foo', /hello → foohello).
3995
+ // Cancel-restore handles the 'don't lose prior search'
3996
+ // concern differently (onCancel re-applies searchQuery).
3997
+ initialQuery: "", count: searchCount, current: searchCurrent, onClose: q => {
3998
+ // Enter — commit. 0-match guard: junk query shouldn't
3999
+ // persist (badge hidden, n/N dead anyway).
4000
+ setSearchQuery(searchCount > 0 ? q : '');
4001
+ setSearchOpen(false);
4002
+ // onCancel path: bar unmounts before its useEffect([query])
4003
+ // can fire with ''. Without this, searchCount stays stale
4004
+ // (n guard at :4956 passes) and VML's matches[] too
4005
+ // (nextMatch walks the old array). Phantom nav, no
4006
+ // highlight. onExit (Enter, q non-empty) still commits.
4007
+ if (!q) {
4008
+ setSearchCount(0);
4009
+ setSearchCurrent(0);
4010
+ jumpRef.current?.setSearchQuery('');
4011
+ }
4012
+ }, onCancel: () => {
4013
+ // Esc/ctrl+c/ctrl+g — undo. Bar's effect last fired
4014
+ // with whatever was typed. searchQuery (REPL state)
4015
+ // is unchanged since / (onClose = commit, didn't run).
4016
+ // Two VML calls: '' restores anchor (0-match else-
4017
+ // branch), then searchQuery re-scans from anchor's
4018
+ // nearest. Both synchronous — one React batch.
4019
+ // setHighlight explicit: REPL's sync-effect dep is
4020
+ // searchQuery (unchanged), wouldn't re-fire.
4021
+ setSearchOpen(false);
4022
+ jumpRef.current?.setSearchQuery('');
4023
+ jumpRef.current?.setSearchQuery(searchQuery);
4024
+ setHighlight(searchQuery);
4025
+ }, setHighlight: setHighlight }) : _jsx(TranscriptModeFooter, { showAllInTranscript: showAllInTranscript, virtualScroll: true, status: editorStatus || undefined, searchBadge: searchQuery && searchCount > 0 ? {
4026
+ current: searchCurrent,
4027
+ count: searchCount
4028
+ } : undefined }) }) : _jsxs(_Fragment, { children: [transcriptMessagesElement, transcriptToolJSX, _jsx(SandboxViolationExpandedView, {}), _jsx(TranscriptModeFooter, { showAllInTranscript: showAllInTranscript, virtualScroll: false, suppressShowAll: dumpMode, status: editorStatus || undefined })] })] });
4029
+ // The virtual-scroll branch (FullscreenLayout above) needs
4030
+ // <AlternateScreen>'s <Box height={rows}> constraint — without it,
4031
+ // ScrollBox's flexGrow has no ceiling, viewport = content height,
4032
+ // scrollTop pins at 0, and Ink's screen buffer sizes to the full
4033
+ // spacer (200×5k+ rows on long sessions). Same root type + props as
4034
+ // normal mode's wrap below so React reconciles and the alt buffer
4035
+ // stays entered across toggle. The 30-cap dump branch stays
4036
+ // unwrapped — it wants native terminal scrollback.
4037
+ if (transcriptScrollRef) {
4038
+ return _jsx(AlternateScreen, { mouseTracking: isMouseTrackingEnabled(), children: transcriptReturn });
4039
+ }
4040
+ return transcriptReturn;
4041
+ }
4042
+ // Get viewed agent task (inlined from selectors for explicit data flow).
4043
+ // viewedAgentTask: teammate OR local_agent — drives the boolean checks
4044
+ // below. viewedTeammateTask: teammate-only narrowed, for teammate-specific
4045
+ // field access (inProgressToolUseIDs).
4046
+ const viewedTask = viewingAgentTaskId ? tasks[viewingAgentTaskId] : undefined;
4047
+ const viewedTeammateTask = viewedTask && isInProcessTeammateTask(viewedTask) ? viewedTask : undefined;
4048
+ const viewedAgentTask = viewedTeammateTask ?? (viewedTask && isLocalAgentTask(viewedTask) ? viewedTask : undefined);
4049
+ // Bypass useDeferredValue when streaming text is showing so Messages renders
4050
+ // the final message in the same frame streaming text clears. Also bypass when
4051
+ // not loading — deferredMessages only matters during streaming (keeps input
4052
+ // responsive); after the turn ends, showing messages immediately prevents a
4053
+ // jitter gap where the spinner is gone but the answer hasn't appeared yet.
4054
+ // Only reducedMotion users keep the deferred path during loading.
4055
+ const usesSyncMessages = showStreamingText || !isLoading;
4056
+ // When viewing an agent, never fall through to leader — empty until
4057
+ // bootstrap/stream fills. Closes the see-leader-type-agent footgun.
4058
+ const displayedMessages = viewedAgentTask ? viewedAgentTask.messages ?? [] : usesSyncMessages ? messages : deferredMessages;
4059
+ // Show the placeholder until the real user message appears in
4060
+ // displayedMessages. userInputOnProcessing stays set for the whole turn
4061
+ // (cleared in resetLoadingState); this length check hides it once
4062
+ // displayedMessages grows past the baseline captured at submit time.
4063
+ // Covers both gaps: before setMessages is called (processUserInput), and
4064
+ // while deferredMessages lags behind messages. Suppressed when viewing an
4065
+ // agent — displayedMessages is a different array there, and onAgentSubmit
4066
+ // doesn't use the placeholder anyway.
4067
+ const placeholderText = userInputOnProcessing && !viewedAgentTask && displayedMessages.length <= userInputBaselineRef.current ? userInputOnProcessing : undefined;
4068
+ const toolPermissionOverlay = focusedInputDialog === 'tool-permission' ? _jsx(PermissionRequest, { onDone: () => setToolUseConfirmQueue(([_, ...tail]) => tail), onReject: handleQueuedCommandOnCancel, toolUseConfirm: toolUseConfirmQueue[0], toolUseContext: getToolUseContext(messages, messages, abortController ?? createAbortController(), mainLoopModel), verbose: verbose, workerBadge: toolUseConfirmQueue[0]?.workerBadge, setStickyFooter: isFullscreenEnvEnabled() ? setPermissionStickyFooter : undefined }, toolUseConfirmQueue[0]?.toolUseID) : null;
4069
+ // Narrow terminals: companion collapses to a one-liner that REPL stacks
4070
+ // on its own row (above input in fullscreen, below in scrollback) instead
4071
+ // of row-beside. Wide terminals keep the row layout with sprite on the right.
4072
+ const companionNarrow = transcriptCols < MIN_COLS_FOR_FULL_SPRITE;
4073
+ // Hide the sprite when PromptInput early-returns BackgroundTasksDialog.
4074
+ // The sprite sits as a row sibling of PromptInput, so the dialog's Pane
4075
+ // divider draws at useTerminalSize() width but only gets terminalWidth -
4076
+ // spriteWidth — divider stops short and dialog text wraps early. Don't
4077
+ // check footerSelection: pill FOCUS (arrow-down to tasks pill) must keep
4078
+ // the sprite visible so arrow-right can navigate to it.
4079
+ const companionVisible = !toolJSX?.shouldHidePromptInput && !focusedInputDialog && !showBashesDialog;
4080
+ // In fullscreen, ALL local-jsx slash commands float in the modal slot —
4081
+ // FullscreenLayout wraps them in an absolute-positioned bottom-anchored
4082
+ // pane (▔ divider, ModalContext). Pane/Dialog inside detect the context
4083
+ // and skip their own top-level frame. Non-fullscreen keeps the inline
4084
+ // render paths below. Commands that used to route through bottom
4085
+ // (immediate: /model, /mcp, /btw, ...) and scrollable (non-immediate:
4086
+ // /config, /theme, /diff, ...) both go here now.
4087
+ const toolJsxCentered = isFullscreenEnvEnabled() && toolJSX?.isLocalJSXCommand === true;
4088
+ const centeredModal = toolJsxCentered ? toolJSX.jsx : null;
4089
+ // <AlternateScreen> at the root: everything below is inside its
4090
+ // <Box height={rows}>. Handlers/contexts are zero-height so ScrollBox's
4091
+ // flexGrow in FullscreenLayout resolves against this Box. The transcript
4092
+ // early return above wraps its virtual-scroll branch the same way; only
4093
+ // the 30-cap dump branch stays unwrapped for native terminal scrollback.
4094
+ const mainReturn = _jsxs(KeybindingSetup, { children: [_jsx(AnimatedTerminalTitle, { isAnimating: titleIsAnimating, title: terminalTitle, disabled: titleDisabled, noPrefix: showStatusInTerminalTab }), _jsx(GlobalKeybindingHandlers, { ...globalKeybindingProps }), feature('VOICE_MODE') ? _jsx(VoiceKeybindingHandler, { voiceHandleKeyEvent: voice.handleKeyEvent, stripTrailing: voice.stripTrailing, resetAnchor: voice.resetAnchor, isActive: !toolJSX?.isLocalJSXCommand }) : null, _jsx(CommandKeybindingHandlers, { onSubmit: onSubmit, isActive: !toolJSX?.isLocalJSXCommand }), _jsx(ScrollKeybindingHandler, { scrollRef: scrollRef, isActive: isFullscreenEnvEnabled() && (centeredModal != null || !focusedInputDialog || focusedInputDialog === 'tool-permission'), onScroll: centeredModal || toolPermissionOverlay || viewedAgentTask ? undefined : composedOnScroll }), feature('MESSAGE_ACTIONS') && isFullscreenEnvEnabled() && !disableMessageActions ? _jsx(MessageActionsKeybindings, { handlers: messageActionHandlers, isActive: cursor !== null }) : null, _jsx(CancelRequestHandler, { ...cancelRequestProps }), _jsx(MCPConnectionManager, { dynamicMcpConfig: dynamicMcpConfig, isStrictMcpConfig: strictMcpConfig, children: _jsx(FullscreenLayout, { scrollRef: scrollRef, overlay: toolPermissionOverlay, bottomFloat: feature('BUDDY') && companionVisible && !companionNarrow ? _jsx(CompanionFloatingBubble, {}) : undefined, modal: centeredModal, modalScrollRef: modalScrollRef, dividerYRef: dividerYRef, hidePill: !!viewedAgentTask, hideSticky: !!viewedTeammateTask, newMessageCount: unseenDivider?.count ?? 0, onPillClick: () => {
4095
+ setCursor(null);
4096
+ jumpToNew(scrollRef.current);
4097
+ }, scrollable: _jsxs(_Fragment, { children: [_jsx(TeammateViewHeader, {}), _jsx(Messages, { messages: displayedMessages, tools: tools, commands: commands, verbose: verbose, toolJSX: toolJSX, toolUseConfirmQueue: toolUseConfirmQueue, inProgressToolUseIDs: viewedTeammateTask ? viewedTeammateTask.inProgressToolUseIDs ?? new Set() : inProgressToolUseIDs, isMessageSelectorVisible: isMessageSelectorVisible, conversationId: conversationId, screen: screen, streamingToolUses: streamingToolUses, showAllInTranscript: showAllInTranscript, agentDefinitions: agentDefinitions, onOpenRateLimitOptions: handleOpenRateLimitOptions, isLoading: isLoading, streamingText: isLoading && !viewedAgentTask ? visibleStreamingText : null, isBriefOnly: viewedAgentTask ? false : isBriefOnly, unseenDivider: viewedAgentTask ? undefined : unseenDivider, scrollRef: isFullscreenEnvEnabled() ? scrollRef : undefined, trackStickyPrompt: isFullscreenEnvEnabled() ? true : undefined, cursor: cursor, setCursor: setCursor, cursorNavRef: cursorNavRef }), _jsx(AwsAuthStatusBox, {}), !disabled && placeholderText && !centeredModal && _jsx(UserTextMessage, { param: {
4098
+ text: placeholderText,
4099
+ type: 'text'
4100
+ }, addMargin: true, verbose: verbose }), toolJSX && !(toolJSX.isLocalJSXCommand && toolJSX.isImmediate) && !toolJsxCentered && _jsx(Box, { flexDirection: "column", width: "100%", children: toolJSX.jsx }), "external" === 'ant' && _jsx(TungstenLiveMonitor, {}), feature('WEB_BROWSER_TOOL') ? WebBrowserPanelModule && _jsx(WebBrowserPanelModule.WebBrowserPanel, {}) : null, _jsx(Box, { flexGrow: 1 }), showSpinner && _jsx(SpinnerWithVerb, { mode: streamMode, spinnerTip: spinnerTip, responseLengthRef: responseLengthRef, apiMetricsRef: apiMetricsRef, overrideMessage: spinnerMessage, spinnerSuffix: stopHookSpinnerSuffix, verbose: verbose, loadingStartTimeRef: loadingStartTimeRef, totalPausedMsRef: totalPausedMsRef, pauseStartTimeRef: pauseStartTimeRef, overrideColor: spinnerColor, overrideShimmerColor: spinnerShimmerColor, hasActiveTools: inProgressToolUseIDs.size > 0, leaderIsIdle: !isLoading }), !showSpinner && !isLoading && !userInputOnProcessing && !hasRunningTeammates && isBriefOnly && !viewedAgentTask && _jsx(BriefIdleStatus, {}), isFullscreenEnvEnabled() && _jsx(PromptInputQueuedCommands, {})] }), bottom: _jsxs(Box, { flexDirection: feature('BUDDY') && companionNarrow ? 'column' : 'row', width: "100%", alignItems: feature('BUDDY') && companionNarrow ? undefined : 'flex-end', children: [feature('BUDDY') && companionNarrow && isFullscreenEnvEnabled() && companionVisible ? _jsx(CompanionSprite, {}) : null, _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [permissionStickyFooter, toolJSX?.isLocalJSXCommand && toolJSX.isImmediate && !toolJsxCentered && _jsx(Box, { flexDirection: "column", width: "100%", children: toolJSX.jsx }), !showSpinner && !toolJSX?.isLocalJSXCommand && showExpandedTodos && tasksV2 && tasksV2.length > 0 && _jsx(Box, { width: "100%", flexDirection: "column", children: _jsx(TaskListV2, { tasks: tasksV2, isStandalone: true }) }), focusedInputDialog === 'sandbox-permission' && _jsx(SandboxPermissionRequest, { hostPattern: sandboxPermissionRequestQueue[0].hostPattern, onUserResponse: (response) => {
4101
+ const { allow, persistToSettings } = response;
4102
+ const currentRequest = sandboxPermissionRequestQueue[0];
4103
+ if (!currentRequest)
4104
+ return;
4105
+ const approvedHost = currentRequest.hostPattern.host;
4106
+ if (persistToSettings) {
4107
+ const update = {
4108
+ type: 'addRules',
4109
+ rules: [{
4110
+ toolName: WEB_FETCH_TOOL_NAME,
4111
+ ruleContent: `domain:${approvedHost}`
4112
+ }],
4113
+ behavior: (allow ? 'allow' : 'deny'),
4114
+ destination: 'localSettings'
4115
+ };
4116
+ setAppState(prev => ({
4117
+ ...prev,
4118
+ toolPermissionContext: applyPermissionUpdate(prev.toolPermissionContext, update)
4119
+ }));
4120
+ persistPermissionUpdate(update);
4121
+ // Immediately update sandbox in-memory config to prevent race conditions
4122
+ // where pending requests slip through before settings change is detected
4123
+ SandboxManager.refreshConfig();
4124
+ }
4125
+ // Resolve ALL pending requests for the same host (not just the first one)
4126
+ // This handles the case where multiple parallel requests came in for the same domain
4127
+ setSandboxPermissionRequestQueue(queue => {
4128
+ queue.filter(item => item.hostPattern.host === approvedHost).forEach(item => item.resolvePromise(allow));
4129
+ return queue.filter(item => item.hostPattern.host !== approvedHost);
4130
+ });
4131
+ // Clean up bridge subscriptions and cancel remote prompts
4132
+ // for this host since the local user already responded.
4133
+ const cleanups = sandboxBridgeCleanupRef.current.get(approvedHost);
4134
+ if (cleanups) {
4135
+ for (const fn of cleanups) {
4136
+ fn();
4137
+ }
4138
+ sandboxBridgeCleanupRef.current.delete(approvedHost);
4139
+ }
4140
+ } }, sandboxPermissionRequestQueue[0].hostPattern.host), focusedInputDialog === 'prompt' && _jsx(PromptDialog, { title: promptQueue[0].title, toolInputSummary: promptQueue[0].toolInputSummary, request: promptQueue[0].request, onRespond: selectedKey => {
4141
+ const item = promptQueue[0];
4142
+ if (!item)
4143
+ return;
4144
+ item.resolve({
4145
+ prompt_response: item.request.prompt,
4146
+ selected: selectedKey
4147
+ });
4148
+ setPromptQueue(([, ...tail]) => tail);
4149
+ }, onAbort: () => {
4150
+ const item = promptQueue[0];
4151
+ if (!item)
4152
+ return;
4153
+ item.reject(new Error('Prompt cancelled by user'));
4154
+ setPromptQueue(([, ...tail]) => tail);
4155
+ } }, promptQueue[0].request.prompt), pendingWorkerRequest && _jsx(WorkerPendingPermission, { toolName: pendingWorkerRequest.toolName, description: pendingWorkerRequest.description }), pendingSandboxRequest && _jsx(WorkerPendingPermission, { toolName: "Network Access", description: `Waiting for leader to approve network access to ${pendingSandboxRequest.host}` }), focusedInputDialog === 'worker-sandbox-permission' && _jsx(SandboxPermissionRequest, { hostPattern: {
4156
+ host: workerSandboxPermissions.queue[0].host,
4157
+ port: undefined
4158
+ }, onUserResponse: (response) => {
4159
+ const { allow, persistToSettings } = response;
4160
+ const currentRequest = workerSandboxPermissions.queue[0];
4161
+ if (!currentRequest)
4162
+ return;
4163
+ const approvedHost = currentRequest.host;
4164
+ // Send response via mailbox to the worker
4165
+ void sendSandboxPermissionResponseViaMailbox(currentRequest.workerName, currentRequest.requestId, approvedHost, allow, teamContext?.teamName);
4166
+ if (persistToSettings && allow) {
4167
+ const update = {
4168
+ type: 'addRules',
4169
+ rules: [{
4170
+ toolName: WEB_FETCH_TOOL_NAME,
4171
+ ruleContent: `domain:${approvedHost}`
4172
+ }],
4173
+ behavior: 'allow',
4174
+ destination: 'localSettings'
4175
+ };
4176
+ setAppState(prev => ({
4177
+ ...prev,
4178
+ toolPermissionContext: applyPermissionUpdate(prev.toolPermissionContext, update)
4179
+ }));
4180
+ persistPermissionUpdate(update);
4181
+ SandboxManager.refreshConfig();
4182
+ }
4183
+ // Remove from queue
4184
+ setAppState(prev => ({
4185
+ ...prev,
4186
+ workerSandboxPermissions: {
4187
+ ...prev.workerSandboxPermissions,
4188
+ queue: prev.workerSandboxPermissions.queue.slice(1)
4189
+ }
4190
+ }));
4191
+ } }, workerSandboxPermissions.queue[0].requestId), focusedInputDialog === 'elicitation' && _jsx(ElicitationDialog, { event: elicitation.queue[0], onResponse: (action, content) => {
4192
+ const currentRequest = elicitation.queue[0];
4193
+ if (!currentRequest)
4194
+ return;
4195
+ // Call respond callback to resolve Promise
4196
+ currentRequest.respond({
4197
+ action,
4198
+ content
4199
+ });
4200
+ // For URL accept, keep in queue for phase 2
4201
+ const isUrlAccept = currentRequest.params.mode === 'url' && action === 'accept';
4202
+ if (!isUrlAccept) {
4203
+ setAppState(prev => ({
4204
+ ...prev,
4205
+ elicitation: {
4206
+ queue: prev.elicitation.queue.slice(1)
4207
+ }
4208
+ }));
4209
+ }
4210
+ }, onWaitingDismiss: action => {
4211
+ const currentRequest = elicitation.queue[0];
4212
+ // Remove from queue
4213
+ setAppState(prev => ({
4214
+ ...prev,
4215
+ elicitation: {
4216
+ queue: prev.elicitation.queue.slice(1)
4217
+ }
4218
+ }));
4219
+ currentRequest?.onWaitingDismiss?.(action);
4220
+ } }, elicitation.queue[0].serverName + ':' + String(elicitation.queue[0].requestId)), focusedInputDialog === 'cost' && _jsx(CostThresholdDialog, { onDone: () => {
4221
+ setShowCostDialog(false);
4222
+ setHaveShownCostDialog(true);
4223
+ saveGlobalConfig(current => ({
4224
+ ...current,
4225
+ hasAcknowledgedCostThreshold: true
4226
+ }));
4227
+ logEvent('tengu_cost_threshold_acknowledged', {});
4228
+ } }), focusedInputDialog === 'idle-return' && idleReturnPending && _jsx(IdleReturnDialog, { idleMinutes: idleReturnPending.idleMinutes, totalInputTokens: getTotalInputTokens(), onDone: async (action) => {
4229
+ const pending = idleReturnPending;
4230
+ setIdleReturnPending(null);
4231
+ logEvent('tengu_idle_return_action', {
4232
+ action: action,
4233
+ idleMinutes: Math.round(pending.idleMinutes),
4234
+ messageCount: messagesRef.current.length,
4235
+ totalInputTokens: getTotalInputTokens()
4236
+ });
4237
+ if (action === 'dismiss') {
4238
+ setInputValue(pending.input);
4239
+ return;
4240
+ }
4241
+ if (action === 'never') {
4242
+ saveGlobalConfig(current => {
4243
+ if (current.idleReturnDismissed)
4244
+ return current;
4245
+ return {
4246
+ ...current,
4247
+ idleReturnDismissed: true
4248
+ };
4249
+ });
4250
+ }
4251
+ if (action === 'clear') {
4252
+ const { clearConversation } = await import('../commands/clear/conversation.js');
4253
+ await clearConversation({
4254
+ setMessages,
4255
+ readFileState: readFileState.current,
4256
+ discoveredSkillNames: discoveredSkillNamesRef.current,
4257
+ loadedNestedMemoryPaths: loadedNestedMemoryPathsRef.current,
4258
+ getAppState: () => store.getState(),
4259
+ setAppState,
4260
+ setConversationId
4261
+ });
4262
+ haikuTitleAttemptedRef.current = false;
4263
+ setHaikuTitle(undefined);
4264
+ bashTools.current.clear();
4265
+ bashToolsProcessedIdx.current = 0;
4266
+ }
4267
+ skipIdleCheckRef.current = true;
4268
+ void onSubmitRef.current(pending.input, {
4269
+ setCursorOffset: () => { },
4270
+ clearBuffer: () => { },
4271
+ resetHistory: () => { }
4272
+ });
4273
+ } }), focusedInputDialog === 'ide-onboarding' && _jsx(IdeOnboardingDialog, { onDone: () => setShowIdeOnboarding(false), installationStatus: ideInstallationStatus }), "external" === 'ant' && focusedInputDialog === 'model-switch' && AntModelSwitchCallout && _jsx(AntModelSwitchCallout, { onDone: (selection, modelAlias) => {
4274
+ setShowModelSwitchCallout(false);
4275
+ if (selection === 'switch' && modelAlias) {
4276
+ setAppState(prev => ({
4277
+ ...prev,
4278
+ mainLoopModel: modelAlias,
4279
+ mainLoopModelForSession: null
4280
+ }));
4281
+ }
4282
+ } }), "external" === 'ant' && focusedInputDialog === 'undercover-callout' && UndercoverAutoCallout && _jsx(UndercoverAutoCallout, { onDone: () => setShowUndercoverCallout(false) }), focusedInputDialog === 'effort-callout' && _jsx(EffortCallout, { model: mainLoopModel, onDone: selection => {
4283
+ setShowEffortCallout(false);
4284
+ if (selection !== 'dismiss') {
4285
+ setAppState(prev => ({
4286
+ ...prev,
4287
+ effortValue: selection
4288
+ }));
4289
+ }
4290
+ } }), focusedInputDialog === 'remote-callout' && _jsx(RemoteCallout, { onDone: selection => {
4291
+ setAppState(prev => {
4292
+ if (!prev.showRemoteCallout)
4293
+ return prev;
4294
+ return {
4295
+ ...prev,
4296
+ showRemoteCallout: false,
4297
+ ...(selection === 'enable' && {
4298
+ replBridgeEnabled: true,
4299
+ replBridgeExplicit: true,
4300
+ replBridgeOutboundOnly: false
4301
+ })
4302
+ };
4303
+ });
4304
+ } }), exitFlow, focusedInputDialog === 'plugin-hint' && hintRecommendation && _jsx(PluginHintMenu, { pluginName: hintRecommendation.pluginName, pluginDescription: hintRecommendation.pluginDescription, marketplaceName: hintRecommendation.marketplaceName, sourceCommand: hintRecommendation.sourceCommand, onResponse: handleHintResponse }), focusedInputDialog === 'lsp-recommendation' && lspRecommendation && _jsx(LspRecommendationMenu, { pluginName: lspRecommendation.pluginName, pluginDescription: lspRecommendation.pluginDescription, fileExtension: lspRecommendation.fileExtension, onResponse: handleLspResponse }), focusedInputDialog === 'desktop-upsell' && _jsx(DesktopUpsellStartup, { onDone: () => setShowDesktopUpsellStartup(false) }), feature('ULTRAPLAN') ? focusedInputDialog === 'ultraplan-choice' && ultraplanPendingChoice && _jsx(UltraplanChoiceDialog, { plan: ultraplanPendingChoice.plan, sessionId: ultraplanPendingChoice.sessionId, taskId: ultraplanPendingChoice.taskId, setMessages: setMessages, readFileState: readFileState.current, getAppState: () => store.getState(), setConversationId: setConversationId }) : null, feature('ULTRAPLAN') ? focusedInputDialog === 'ultraplan-launch' && ultraplanLaunchPending && _jsx(UltraplanLaunchDialog, { onChoice: (choice, opts) => {
4305
+ const blurb = ultraplanLaunchPending.blurb;
4306
+ setAppState(prev => prev.ultraplanLaunchPending ? {
4307
+ ...prev,
4308
+ ultraplanLaunchPending: undefined
4309
+ } : prev);
4310
+ if (choice === 'cancel')
4311
+ return;
4312
+ // Command's onDone used display:'skip', so add the
4313
+ // echo here — gives immediate feedback before the
4314
+ // ~5s teleportToRemote resolves.
4315
+ setMessages(prev => [...prev, createCommandInputMessage(formatCommandInputTags('ultraplan', blurb))]);
4316
+ const appendStdout = (msg) => setMessages(prev => [...prev, createCommandInputMessage(`<${LOCAL_COMMAND_STDOUT_TAG}>${escapeXml(msg)}</${LOCAL_COMMAND_STDOUT_TAG}>`)]);
4317
+ // Defer the second message if a query is mid-turn
4318
+ // so it lands after the assistant reply, not
4319
+ // between the user's prompt and the reply.
4320
+ const appendWhenIdle = (msg) => {
4321
+ if (!queryGuard.isActive) {
4322
+ appendStdout(msg);
4323
+ return;
4324
+ }
4325
+ const unsub = queryGuard.subscribe(() => {
4326
+ if (queryGuard.isActive)
4327
+ return;
4328
+ unsub();
4329
+ // Skip if the user stopped ultraplan while we
4330
+ // were waiting — avoids a stale "Monitoring
4331
+ // <url>" message for a session that's gone.
4332
+ if (!store.getState().ultraplanSessionUrl)
4333
+ return;
4334
+ appendStdout(msg);
4335
+ });
4336
+ };
4337
+ void launchUltraplan({
4338
+ blurb,
4339
+ getAppState: () => store.getState(),
4340
+ setAppState,
4341
+ signal: createAbortController().signal,
4342
+ disconnectedBridge: opts?.disconnectedBridge,
4343
+ onSessionReady: appendWhenIdle
4344
+ }).then(appendStdout).catch(logError);
4345
+ } }) : null, mrRender(), !toolJSX?.shouldHidePromptInput && !focusedInputDialog && !isExiting && !disabled && !cursor && _jsxs(_Fragment, { children: [autoRunIssueReason && _jsx(AutoRunIssueNotification, { onRun: handleAutoRunIssue, onCancel: handleCancelAutoRunIssue, reason: getAutoRunIssueReasonText(autoRunIssueReason) }), postCompactSurvey.state !== 'closed' ? _jsx(FeedbackSurvey, { state: postCompactSurvey.state, lastResponse: postCompactSurvey.lastResponse, handleSelect: postCompactSurvey.handleSelect, inputValue: inputValue, setInputValue: setInputValue, onRequestFeedback: handleSurveyRequestFeedback }) : memorySurvey.state !== 'closed' ? _jsx(FeedbackSurvey, { state: memorySurvey.state, lastResponse: memorySurvey.lastResponse, handleSelect: memorySurvey.handleSelect, handleTranscriptSelect: memorySurvey.handleTranscriptSelect, inputValue: inputValue, setInputValue: setInputValue, onRequestFeedback: handleSurveyRequestFeedback, message: "How well did Claude use its memory? (optional)" }) : _jsx(FeedbackSurvey, { state: feedbackSurvey.state, lastResponse: feedbackSurvey.lastResponse, handleSelect: feedbackSurvey.handleSelect, handleTranscriptSelect: feedbackSurvey.handleTranscriptSelect, inputValue: inputValue, setInputValue: setInputValue, onRequestFeedback: didAutoRunIssueRef.current ? undefined : handleSurveyRequestFeedback }), frustrationDetection.state !== 'closed' && _jsx(FeedbackSurvey, { state: frustrationDetection.state, lastResponse: null, handleSelect: () => { }, handleTranscriptSelect: frustrationDetection.handleTranscriptSelect, inputValue: inputValue, setInputValue: setInputValue }), "external" === 'ant' && skillImprovementSurvey.suggestion && _jsx(SkillImprovementSurvey, { isOpen: skillImprovementSurvey.isOpen, skillName: skillImprovementSurvey.suggestion.skillName, updates: skillImprovementSurvey.suggestion.updates, handleSelect: skillImprovementSurvey.handleSelect, inputValue: inputValue, setInputValue: setInputValue }), showIssueFlagBanner && _jsx(IssueFlagBanner, {}), _jsx(PromptInput, { debug: debug, ideSelection: ideSelection, hasSuppressedDialogs: !!hasSuppressedDialogs, isLocalJSXCommandActive: isShowingLocalJSXCommand, getToolUseContext: getToolUseContext, toolPermissionContext: toolPermissionContext, setToolPermissionContext: setToolPermissionContext, apiKeyStatus: apiKeyStatus, commands: commands, agents: agentDefinitions.activeAgents, isLoading: isLoading, onExit: handleExit, verbose: verbose, messages: messages, onAutoUpdaterResult: setAutoUpdaterResult, autoUpdaterResult: autoUpdaterResult, input: inputValue, onInputChange: setInputValue, mode: inputMode, onModeChange: setInputMode, stashedPrompt: stashedPrompt, setStashedPrompt: setStashedPrompt, submitCount: submitCount, onShowMessageSelector: handleShowMessageSelector, onMessageActionsEnter:
4346
+ // Works during isLoading — edit cancels first; uuid selection survives appends.
4347
+ feature('MESSAGE_ACTIONS') && isFullscreenEnvEnabled() && !disableMessageActions ? enterMessageActions : undefined, mcpClients: mcpClients, pastedContents: pastedContents, setPastedContents: setPastedContents, vimMode: vimMode, setVimMode: setVimMode, showBashesDialog: showBashesDialog, setShowBashesDialog: setShowBashesDialog, onSubmit: onSubmit, onAgentSubmit: onAgentSubmit, isSearchingHistory: isSearchingHistory, setIsSearchingHistory: setIsSearchingHistory, helpOpen: isHelpOpen, setHelpOpen: setIsHelpOpen, insertTextRef: feature('VOICE_MODE') ? insertTextRef : undefined, voiceInterimRange: voice.interimRange }), _jsx(SessionBackgroundHint, { onBackgroundSession: handleBackgroundSession, isLoading: isLoading })] }), cursor &&
4348
+ _jsx(MessageActionsBar, { cursor: cursor }), focusedInputDialog === 'message-selector' && _jsx(MessageSelector, { messages: messages, preselectedMessage: messageSelectorPreselect, onPreRestore: onCancel, onRestoreCode: async (message) => {
4349
+ await fileHistoryRewind((updater) => {
4350
+ setAppState(prev => ({
4351
+ ...prev,
4352
+ fileHistory: updater(prev.fileHistory)
4353
+ }));
4354
+ }, message.uuid);
4355
+ }, onSummarize: async (message, feedback, direction = 'from') => {
4356
+ // Project snipped messages so the compact model
4357
+ // doesn't summarize content that was intentionally removed.
4358
+ const compactMessages = getMessagesAfterCompactBoundary(messages);
4359
+ const messageIndex = compactMessages.indexOf(message);
4360
+ if (messageIndex === -1) {
4361
+ // Selected a snipped or pre-compact message that the
4362
+ // selector still shows (REPL keeps full history for
4363
+ // scrollback). Surface why nothing happened instead
4364
+ // of silently no-oping.
4365
+ setMessages(prev => [...prev, createSystemMessage('That message is no longer in the active context (snipped or pre-compact). Choose a more recent message.', 'warning')]);
4366
+ return;
4367
+ }
4368
+ const newAbortController = createAbortController();
4369
+ const context = getToolUseContext(compactMessages, [], newAbortController, mainLoopModel);
4370
+ const appState = context.getAppState();
4371
+ const defaultSysPrompt = await getSystemPrompt(context.options.tools, context.options.mainLoopModel, Array.from(appState.toolPermissionContext.additionalWorkingDirectories.keys()), context.options.mcpClients);
4372
+ const systemPrompt = buildEffectiveSystemPrompt({
4373
+ mainThreadAgentDefinition: undefined,
4374
+ toolUseContext: context,
4375
+ customSystemPrompt: context.options.customSystemPrompt,
4376
+ defaultSystemPrompt: defaultSysPrompt,
4377
+ appendSystemPrompt: context.options.appendSystemPrompt
4378
+ });
4379
+ const [userContext, systemContext] = await Promise.all([getUserContext(), getSystemContext()]);
4380
+ const result = await partialCompactConversation(compactMessages, messageIndex, context, {
4381
+ systemPrompt,
4382
+ userContext,
4383
+ systemContext,
4384
+ toolUseContext: context,
4385
+ forkContextMessages: compactMessages
4386
+ }, feedback, direction);
4387
+ const kept = result.messagesToKeep ?? [];
4388
+ const ordered = direction === 'up_to' ? [...result.summaryMessages, ...kept] : [...kept, ...result.summaryMessages];
4389
+ const postCompact = [result.boundaryMarker, ...ordered, ...result.attachments, ...result.hookResults];
4390
+ // Fullscreen 'from' keeps scrollback; 'up_to' must not
4391
+ // (old[0] unchanged + grown array means incremental
4392
+ // useLogMessages path, so boundary never persisted).
4393
+ // Find by uuid since old is raw REPL history and snipped
4394
+ // entries can shift the projected messageIndex.
4395
+ if (isFullscreenEnvEnabled() && direction === 'from') {
4396
+ setMessages(old => {
4397
+ const rawIdx = old.findIndex(m => m.uuid === message.uuid);
4398
+ return [...old.slice(0, rawIdx === -1 ? 0 : rawIdx), ...postCompact];
4399
+ });
4400
+ }
4401
+ else {
4402
+ setMessages(postCompact);
4403
+ }
4404
+ // Partial compact bypasses handleMessageFromStream — clear
4405
+ // the context-blocked flag so proactive ticks resume.
4406
+ if (feature('PROACTIVE') || feature('KAIROS')) {
4407
+ proactiveModule?.setContextBlocked(false);
4408
+ }
4409
+ setConversationId(randomUUID());
4410
+ runPostCompactCleanup(context.options.querySource);
4411
+ if (direction === 'from') {
4412
+ const r = textForResubmit(message);
4413
+ if (r) {
4414
+ setInputValue(r.text);
4415
+ setInputMode(r.mode);
4416
+ }
4417
+ }
4418
+ // Show notification with ctrl+o hint
4419
+ const historyShortcut = getShortcutDisplay('app:toggleTranscript', 'Global', 'ctrl+o');
4420
+ addNotification({
4421
+ key: 'summarize-ctrl-o-hint',
4422
+ text: `Conversation summarized (${historyShortcut} for history)`,
4423
+ priority: 'medium',
4424
+ timeoutMs: 8000
4425
+ });
4426
+ }, onRestoreMessage: handleRestoreMessage, onClose: () => {
4427
+ setIsMessageSelectorVisible(false);
4428
+ setMessageSelectorPreselect(undefined);
4429
+ } }), "external" === 'ant' && _jsx(DevBar, {})] }), feature('BUDDY') && !(companionNarrow && isFullscreenEnvEnabled()) && companionVisible ? _jsx(CompanionSprite, {}) : null] }) }) }, remountKey)] });
4430
+ if (isFullscreenEnvEnabled()) {
4431
+ return _jsx(AlternateScreen, { mouseTracking: isMouseTrackingEnabled(), children: mainReturn });
4432
+ }
4433
+ return mainReturn;
4434
+ }