turboflux 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -0
  3. package/bin/turboflux.mjs +28 -0
  4. package/dist/cli/brand.d.ts +7 -0
  5. package/dist/cli/brand.js +33 -0
  6. package/dist/cli/brand.js.map +1 -0
  7. package/dist/cli/commands/index.d.ts +4 -0
  8. package/dist/cli/commands/index.js +671 -0
  9. package/dist/cli/commands/index.js.map +1 -0
  10. package/dist/cli/commands/registry.d.ts +24 -0
  11. package/dist/cli/commands/registry.js +124 -0
  12. package/dist/cli/commands/registry.js.map +1 -0
  13. package/dist/cli/commands/types.d.ts +69 -0
  14. package/dist/cli/commands/types.js +2 -0
  15. package/dist/cli/commands/types.js.map +1 -0
  16. package/dist/cli/components/App.d.ts +15 -0
  17. package/dist/cli/components/App.js +1738 -0
  18. package/dist/cli/components/App.js.map +1 -0
  19. package/dist/cli/components/ConversationHistory.d.ts +15 -0
  20. package/dist/cli/components/ConversationHistory.js +92 -0
  21. package/dist/cli/components/ConversationHistory.js.map +1 -0
  22. package/dist/cli/components/TranscriptViewport.d.ts +25 -0
  23. package/dist/cli/components/TranscriptViewport.js +77 -0
  24. package/dist/cli/components/TranscriptViewport.js.map +1 -0
  25. package/dist/cli/components/agentActivityModel.d.ts +24 -0
  26. package/dist/cli/components/agentActivityModel.js +45 -0
  27. package/dist/cli/components/agentActivityModel.js.map +1 -0
  28. package/dist/cli/components/appHelpers.d.ts +34 -0
  29. package/dist/cli/components/appHelpers.js +209 -0
  30. package/dist/cli/components/appHelpers.js.map +1 -0
  31. package/dist/cli/components/design-system/StatusIcon.d.ts +5 -0
  32. package/dist/cli/components/design-system/StatusIcon.js +25 -0
  33. package/dist/cli/components/design-system/StatusIcon.js.map +1 -0
  34. package/dist/cli/components/developerFlowModel.d.ts +36 -0
  35. package/dist/cli/components/developerFlowModel.js +88 -0
  36. package/dist/cli/components/developerFlowModel.js.map +1 -0
  37. package/dist/cli/components/diff/DiffCard.d.ts +16 -0
  38. package/dist/cli/components/diff/DiffCard.js +30 -0
  39. package/dist/cli/components/diff/DiffCard.js.map +1 -0
  40. package/dist/cli/components/diff/DiffHunks.d.ts +11 -0
  41. package/dist/cli/components/diff/DiffHunks.js +58 -0
  42. package/dist/cli/components/diff/DiffHunks.js.map +1 -0
  43. package/dist/cli/components/header/Header.d.ts +13 -0
  44. package/dist/cli/components/header/Header.js +25 -0
  45. package/dist/cli/components/header/Header.js.map +1 -0
  46. package/dist/cli/components/header/Mascot.d.ts +4 -0
  47. package/dist/cli/components/header/Mascot.js +53 -0
  48. package/dist/cli/components/header/Mascot.js.map +1 -0
  49. package/dist/cli/components/header/StatusLine.d.ts +21 -0
  50. package/dist/cli/components/header/StatusLine.js +110 -0
  51. package/dist/cli/components/header/StatusLine.js.map +1 -0
  52. package/dist/cli/components/input/EffortPicker.d.ts +30 -0
  53. package/dist/cli/components/input/EffortPicker.js +126 -0
  54. package/dist/cli/components/input/EffortPicker.js.map +1 -0
  55. package/dist/cli/components/input/ModelPicker.d.ts +15 -0
  56. package/dist/cli/components/input/ModelPicker.js +95 -0
  57. package/dist/cli/components/input/ModelPicker.js.map +1 -0
  58. package/dist/cli/components/input/PromptInput.d.ts +79 -0
  59. package/dist/cli/components/input/PromptInput.js +496 -0
  60. package/dist/cli/components/input/PromptInput.js.map +1 -0
  61. package/dist/cli/components/input/RewindSelector.d.ts +8 -0
  62. package/dist/cli/components/input/RewindSelector.js +45 -0
  63. package/dist/cli/components/input/RewindSelector.js.map +1 -0
  64. package/dist/cli/components/input/pasteState.d.ts +6 -0
  65. package/dist/cli/components/input/pasteState.js +42 -0
  66. package/dist/cli/components/input/pasteState.js.map +1 -0
  67. package/dist/cli/components/input/terminalInputStateMachine.d.ts +29 -0
  68. package/dist/cli/components/input/terminalInputStateMachine.js +67 -0
  69. package/dist/cli/components/input/terminalInputStateMachine.js.map +1 -0
  70. package/dist/cli/components/layout/CockpitRails.d.ts +6 -0
  71. package/dist/cli/components/layout/CockpitRails.js +13 -0
  72. package/dist/cli/components/layout/CockpitRails.js.map +1 -0
  73. package/dist/cli/components/layout/LandingView.d.ts +15 -0
  74. package/dist/cli/components/layout/LandingView.js +12 -0
  75. package/dist/cli/components/layout/LandingView.js.map +1 -0
  76. package/dist/cli/components/layout/SessionSidebar.d.ts +18 -0
  77. package/dist/cli/components/layout/SessionSidebar.js +70 -0
  78. package/dist/cli/components/layout/SessionSidebar.js.map +1 -0
  79. package/dist/cli/components/layout/StartupAnimation.d.ts +14 -0
  80. package/dist/cli/components/layout/StartupAnimation.js +30 -0
  81. package/dist/cli/components/layout/StartupAnimation.js.map +1 -0
  82. package/dist/cli/components/markdown/highlighter.d.ts +1 -0
  83. package/dist/cli/components/markdown/highlighter.js +110 -0
  84. package/dist/cli/components/markdown/highlighter.js.map +1 -0
  85. package/dist/cli/components/markdown/index.d.ts +11 -0
  86. package/dist/cli/components/markdown/index.js +86 -0
  87. package/dist/cli/components/markdown/index.js.map +1 -0
  88. package/dist/cli/components/messages/MessageList.d.ts +16 -0
  89. package/dist/cli/components/messages/MessageList.js +56 -0
  90. package/dist/cli/components/messages/MessageList.js.map +1 -0
  91. package/dist/cli/components/messages/Messages.d.ts +26 -0
  92. package/dist/cli/components/messages/Messages.js +32 -0
  93. package/dist/cli/components/messages/Messages.js.map +1 -0
  94. package/dist/cli/components/messages/ThinkingBlock.d.ts +9 -0
  95. package/dist/cli/components/messages/ThinkingBlock.js +51 -0
  96. package/dist/cli/components/messages/ThinkingBlock.js.map +1 -0
  97. package/dist/cli/components/messages/WindowedMessageList.d.ts +21 -0
  98. package/dist/cli/components/messages/WindowedMessageList.js +44 -0
  99. package/dist/cli/components/messages/WindowedMessageList.js.map +1 -0
  100. package/dist/cli/components/permissions/PermissionDialog.d.ts +23 -0
  101. package/dist/cli/components/permissions/PermissionDialog.js +83 -0
  102. package/dist/cli/components/permissions/PermissionDialog.js.map +1 -0
  103. package/dist/cli/components/spinner/SpinnerGlyph.d.ts +6 -0
  104. package/dist/cli/components/spinner/SpinnerGlyph.js +55 -0
  105. package/dist/cli/components/spinner/SpinnerGlyph.js.map +1 -0
  106. package/dist/cli/components/spinner/constants.d.ts +4 -0
  107. package/dist/cli/components/spinner/constants.js +5 -0
  108. package/dist/cli/components/spinner/constants.js.map +1 -0
  109. package/dist/cli/components/tools/ActiveWorkPanel.d.ts +22 -0
  110. package/dist/cli/components/tools/ActiveWorkPanel.js +70 -0
  111. package/dist/cli/components/tools/ActiveWorkPanel.js.map +1 -0
  112. package/dist/cli/components/tools/AgentActivityLine.d.ts +13 -0
  113. package/dist/cli/components/tools/AgentActivityLine.js +88 -0
  114. package/dist/cli/components/tools/AgentActivityLine.js.map +1 -0
  115. package/dist/cli/components/tools/QueuedPromptList.d.ts +10 -0
  116. package/dist/cli/components/tools/QueuedPromptList.js +26 -0
  117. package/dist/cli/components/tools/QueuedPromptList.js.map +1 -0
  118. package/dist/cli/components/tools/TaskFlowHud.d.ts +15 -0
  119. package/dist/cli/components/tools/TaskFlowHud.js +55 -0
  120. package/dist/cli/components/tools/TaskFlowHud.js.map +1 -0
  121. package/dist/cli/components/tools/TerminalSessionsFooter.d.ts +6 -0
  122. package/dist/cli/components/tools/TerminalSessionsFooter.js +36 -0
  123. package/dist/cli/components/tools/TerminalSessionsFooter.js.map +1 -0
  124. package/dist/cli/components/tools/ToolActivityList.d.ts +11 -0
  125. package/dist/cli/components/tools/ToolActivityList.js +73 -0
  126. package/dist/cli/components/tools/ToolActivityList.js.map +1 -0
  127. package/dist/cli/components/tools/ToolCallTree.d.ts +11 -0
  128. package/dist/cli/components/tools/ToolCallTree.js +14 -0
  129. package/dist/cli/components/tools/ToolCallTree.js.map +1 -0
  130. package/dist/cli/components/tools/toolLifecycleModel.d.ts +16 -0
  131. package/dist/cli/components/tools/toolLifecycleModel.js +30 -0
  132. package/dist/cli/components/tools/toolLifecycleModel.js.map +1 -0
  133. package/dist/cli/components/tools/toolPresentation.d.ts +9 -0
  134. package/dist/cli/components/tools/toolPresentation.js +171 -0
  135. package/dist/cli/components/tools/toolPresentation.js.map +1 -0
  136. package/dist/cli/components/tools/toolTypes.d.ts +16 -0
  137. package/dist/cli/components/tools/toolTypes.js +2 -0
  138. package/dist/cli/components/tools/toolTypes.js.map +1 -0
  139. package/dist/cli/components/transcriptWindowing.d.ts +19 -0
  140. package/dist/cli/components/transcriptWindowing.js +81 -0
  141. package/dist/cli/components/transcriptWindowing.js.map +1 -0
  142. package/dist/cli/config.d.ts +1 -0
  143. package/dist/cli/config.js +2 -0
  144. package/dist/cli/config.js.map +1 -0
  145. package/dist/cli/conversations/journalWriter.d.ts +50 -0
  146. package/dist/cli/conversations/journalWriter.js +191 -0
  147. package/dist/cli/conversations/journalWriter.js.map +1 -0
  148. package/dist/cli/conversations/manager.d.ts +65 -0
  149. package/dist/cli/conversations/manager.js +435 -0
  150. package/dist/cli/conversations/manager.js.map +1 -0
  151. package/dist/cli/conversations/recoveryExport.d.ts +18 -0
  152. package/dist/cli/conversations/recoveryExport.js +39 -0
  153. package/dist/cli/conversations/recoveryExport.js.map +1 -0
  154. package/dist/cli/conversations/recoveryMessages.d.ts +2 -0
  155. package/dist/cli/conversations/recoveryMessages.js +3 -0
  156. package/dist/cli/conversations/recoveryMessages.js.map +1 -0
  157. package/dist/cli/conversations/store.d.ts +14 -0
  158. package/dist/cli/conversations/store.js +603 -0
  159. package/dist/cli/conversations/store.js.map +1 -0
  160. package/dist/cli/conversations/types.d.ts +142 -0
  161. package/dist/cli/conversations/types.js +2 -0
  162. package/dist/cli/conversations/types.js.map +1 -0
  163. package/dist/cli/globalConfiguration.d.ts +13 -0
  164. package/dist/cli/globalConfiguration.js +61 -0
  165. package/dist/cli/globalConfiguration.js.map +1 -0
  166. package/dist/cli/hooks/useMessageCursor.d.ts +16 -0
  167. package/dist/cli/hooks/useMessageCursor.js +75 -0
  168. package/dist/cli/hooks/useMessageCursor.js.map +1 -0
  169. package/dist/cli/hooks/useOverlayStack.d.ts +9 -0
  170. package/dist/cli/hooks/useOverlayStack.js +26 -0
  171. package/dist/cli/hooks/useOverlayStack.js.map +1 -0
  172. package/dist/cli/hooks/useTerminalSize.d.ts +16 -0
  173. package/dist/cli/hooks/useTerminalSize.js +37 -0
  174. package/dist/cli/hooks/useTerminalSize.js.map +1 -0
  175. package/dist/cli/i18n/index.d.ts +15 -0
  176. package/dist/cli/i18n/index.js +32 -0
  177. package/dist/cli/i18n/index.js.map +1 -0
  178. package/dist/cli/i18n/messages.d.ts +1305 -0
  179. package/dist/cli/i18n/messages.js +1305 -0
  180. package/dist/cli/i18n/messages.js.map +1 -0
  181. package/dist/cli/imageAttachments.d.ts +22 -0
  182. package/dist/cli/imageAttachments.js +329 -0
  183. package/dist/cli/imageAttachments.js.map +1 -0
  184. package/dist/cli/index.d.ts +2 -0
  185. package/dist/cli/index.js +146 -0
  186. package/dist/cli/index.js.map +1 -0
  187. package/dist/cli/platform/terminalAttention.d.ts +46 -0
  188. package/dist/cli/platform/terminalAttention.js +152 -0
  189. package/dist/cli/platform/terminalAttention.js.map +1 -0
  190. package/dist/cli/platform/terminalTransparency.d.ts +11 -0
  191. package/dist/cli/platform/terminalTransparency.js +193 -0
  192. package/dist/cli/platform/terminalTransparency.js.map +1 -0
  193. package/dist/cli/repl.d.ts +15 -0
  194. package/dist/cli/repl.js +38 -0
  195. package/dist/cli/repl.js.map +1 -0
  196. package/dist/cli/setup.d.ts +17 -0
  197. package/dist/cli/setup.js +991 -0
  198. package/dist/cli/setup.js.map +1 -0
  199. package/dist/cli/singleShot.d.ts +30 -0
  200. package/dist/cli/singleShot.js +167 -0
  201. package/dist/cli/singleShot.js.map +1 -0
  202. package/dist/cli/state/adaptiveStreamScheduler.d.ts +53 -0
  203. package/dist/cli/state/adaptiveStreamScheduler.js +136 -0
  204. package/dist/cli/state/adaptiveStreamScheduler.js.map +1 -0
  205. package/dist/cli/state/agentFlowController.d.ts +37 -0
  206. package/dist/cli/state/agentFlowController.js +402 -0
  207. package/dist/cli/state/agentFlowController.js.map +1 -0
  208. package/dist/cli/state/approvalPresentationScheduler.d.ts +28 -0
  209. package/dist/cli/state/approvalPresentationScheduler.js +72 -0
  210. package/dist/cli/state/approvalPresentationScheduler.js.map +1 -0
  211. package/dist/cli/state/flowFeatureFlags.d.ts +18 -0
  212. package/dist/cli/state/flowFeatureFlags.js +45 -0
  213. package/dist/cli/state/flowFeatureFlags.js.map +1 -0
  214. package/dist/cli/state/flowReducer.d.ts +120 -0
  215. package/dist/cli/state/flowReducer.js +535 -0
  216. package/dist/cli/state/flowReducer.js.map +1 -0
  217. package/dist/cli/state/flowSelectors.d.ts +45 -0
  218. package/dist/cli/state/flowSelectors.js +144 -0
  219. package/dist/cli/state/flowSelectors.js.map +1 -0
  220. package/dist/cli/state/flowStore.d.ts +19 -0
  221. package/dist/cli/state/flowStore.js +51 -0
  222. package/dist/cli/state/flowStore.js.map +1 -0
  223. package/dist/cli/state/globalCommandActivity.d.ts +25 -0
  224. package/dist/cli/state/globalCommandActivity.js +57 -0
  225. package/dist/cli/state/globalCommandActivity.js.map +1 -0
  226. package/dist/cli/state/goldenTraces.d.ts +2 -0
  227. package/dist/cli/state/goldenTraces.js +15 -0
  228. package/dist/cli/state/goldenTraces.js.map +1 -0
  229. package/dist/cli/state/notificationCoordinator.d.ts +38 -0
  230. package/dist/cli/state/notificationCoordinator.js +101 -0
  231. package/dist/cli/state/notificationCoordinator.js.map +1 -0
  232. package/dist/cli/stateProvider.d.ts +5 -0
  233. package/dist/cli/stateProvider.js +7 -0
  234. package/dist/cli/stateProvider.js.map +1 -0
  235. package/dist/cli/telemetry/localFlowTelemetry.d.ts +42 -0
  236. package/dist/cli/telemetry/localFlowTelemetry.js +131 -0
  237. package/dist/cli/telemetry/localFlowTelemetry.js.map +1 -0
  238. package/dist/cli/telemetry/terminalBaseline.d.ts +93 -0
  239. package/dist/cli/telemetry/terminalBaseline.js +199 -0
  240. package/dist/cli/telemetry/terminalBaseline.js.map +1 -0
  241. package/dist/cli/telemetry/terminalLatencyTracker.d.ts +24 -0
  242. package/dist/cli/telemetry/terminalLatencyTracker.js +59 -0
  243. package/dist/cli/telemetry/terminalLatencyTracker.js.map +1 -0
  244. package/dist/cli/terminalLayout.d.ts +2 -0
  245. package/dist/cli/terminalLayout.js +16 -0
  246. package/dist/cli/terminalLayout.js.map +1 -0
  247. package/dist/cli/terminalMouse.d.ts +10 -0
  248. package/dist/cli/terminalMouse.js +29 -0
  249. package/dist/cli/terminalMouse.js.map +1 -0
  250. package/dist/cli/theme/ThemeProvider.d.ts +9 -0
  251. package/dist/cli/theme/ThemeProvider.js +26 -0
  252. package/dist/cli/theme/ThemeProvider.js.map +1 -0
  253. package/dist/cli/theme/dark.d.ts +2 -0
  254. package/dist/cli/theme/dark.js +27 -0
  255. package/dist/cli/theme/dark.js.map +1 -0
  256. package/dist/cli/theme/index.d.ts +5 -0
  257. package/dist/cli/theme/index.js +5 -0
  258. package/dist/cli/theme/index.js.map +1 -0
  259. package/dist/cli/theme/light.d.ts +2 -0
  260. package/dist/cli/theme/light.js +26 -0
  261. package/dist/cli/theme/light.js.map +1 -0
  262. package/dist/cli/theme/palette.d.ts +7 -0
  263. package/dist/cli/theme/palette.js +8 -0
  264. package/dist/cli/theme/palette.js.map +1 -0
  265. package/dist/cli/theme/resolveColor.d.ts +4 -0
  266. package/dist/cli/theme/resolveColor.js +43 -0
  267. package/dist/cli/theme/resolveColor.js.map +1 -0
  268. package/dist/cli/theme/types.d.ts +27 -0
  269. package/dist/cli/theme/types.js +2 -0
  270. package/dist/cli/theme/types.js.map +1 -0
  271. package/dist/cli/toolExecutor.d.ts +1 -0
  272. package/dist/cli/toolExecutor.js +2 -0
  273. package/dist/cli/toolExecutor.js.map +1 -0
  274. package/dist/core/agentEngine.d.ts +425 -0
  275. package/dist/core/agentEngine.js +5170 -0
  276. package/dist/core/agentEngine.js.map +1 -0
  277. package/dist/core/agents/loader.d.ts +9 -0
  278. package/dist/core/agents/loader.js +144 -0
  279. package/dist/core/agents/loader.js.map +1 -0
  280. package/dist/core/applyPatch.d.ts +24 -0
  281. package/dist/core/applyPatch.js +158 -0
  282. package/dist/core/applyPatch.js.map +1 -0
  283. package/dist/core/cacheMonitor.d.ts +24 -0
  284. package/dist/core/cacheMonitor.js +211 -0
  285. package/dist/core/cacheMonitor.js.map +1 -0
  286. package/dist/core/clientIdentity.d.ts +6 -0
  287. package/dist/core/clientIdentity.js +44 -0
  288. package/dist/core/clientIdentity.js.map +1 -0
  289. package/dist/core/config.d.ts +99 -0
  290. package/dist/core/config.js +715 -0
  291. package/dist/core/config.js.map +1 -0
  292. package/dist/core/contextCompaction.d.ts +20 -0
  293. package/dist/core/contextCompaction.js +158 -0
  294. package/dist/core/contextCompaction.js.map +1 -0
  295. package/dist/core/contextManager.d.ts +44 -0
  296. package/dist/core/contextManager.js +590 -0
  297. package/dist/core/contextManager.js.map +1 -0
  298. package/dist/core/contextPolicy.d.ts +18 -0
  299. package/dist/core/contextPolicy.js +39 -0
  300. package/dist/core/contextPolicy.js.map +1 -0
  301. package/dist/core/credentialStore.d.ts +8 -0
  302. package/dist/core/credentialStore.js +37 -0
  303. package/dist/core/credentialStore.js.map +1 -0
  304. package/dist/core/diffCompute.d.ts +31 -0
  305. package/dist/core/diffCompute.js +116 -0
  306. package/dist/core/diffCompute.js.map +1 -0
  307. package/dist/core/editHelpers.d.ts +8 -0
  308. package/dist/core/editHelpers.js +35 -0
  309. package/dist/core/editHelpers.js.map +1 -0
  310. package/dist/core/fileIO.d.ts +12 -0
  311. package/dist/core/fileIO.js +199 -0
  312. package/dist/core/fileIO.js.map +1 -0
  313. package/dist/core/gitService.d.ts +83 -0
  314. package/dist/core/gitService.js +522 -0
  315. package/dist/core/gitService.js.map +1 -0
  316. package/dist/core/index.d.ts +30 -0
  317. package/dist/core/index.js +19 -0
  318. package/dist/core/index.js.map +1 -0
  319. package/dist/core/mcp/client.d.ts +32 -0
  320. package/dist/core/mcp/client.js +186 -0
  321. package/dist/core/mcp/client.js.map +1 -0
  322. package/dist/core/mcp/settings.d.ts +2 -0
  323. package/dist/core/mcp/settings.js +26 -0
  324. package/dist/core/mcp/settings.js.map +1 -0
  325. package/dist/core/mcp/toolBridge.d.ts +20 -0
  326. package/dist/core/mcp/toolBridge.js +74 -0
  327. package/dist/core/mcp/toolBridge.js.map +1 -0
  328. package/dist/core/mcp/types.d.ts +29 -0
  329. package/dist/core/mcp/types.js +2 -0
  330. package/dist/core/mcp/types.js.map +1 -0
  331. package/dist/core/modelDiscovery.d.ts +14 -0
  332. package/dist/core/modelDiscovery.js +566 -0
  333. package/dist/core/modelDiscovery.js.map +1 -0
  334. package/dist/core/modelProtocol.d.ts +43 -0
  335. package/dist/core/modelProtocol.js +238 -0
  336. package/dist/core/modelProtocol.js.map +1 -0
  337. package/dist/core/modelRegistry.d.ts +56 -0
  338. package/dist/core/modelRegistry.js +308 -0
  339. package/dist/core/modelRegistry.js.map +1 -0
  340. package/dist/core/networkProxy.d.ts +20 -0
  341. package/dist/core/networkProxy.js +145 -0
  342. package/dist/core/networkProxy.js.map +1 -0
  343. package/dist/core/normalizeBaseUrl.d.ts +1 -0
  344. package/dist/core/normalizeBaseUrl.js +9 -0
  345. package/dist/core/normalizeBaseUrl.js.map +1 -0
  346. package/dist/core/pathUtils.d.ts +3 -0
  347. package/dist/core/pathUtils.js +46 -0
  348. package/dist/core/pathUtils.js.map +1 -0
  349. package/dist/core/permissions.d.ts +29 -0
  350. package/dist/core/permissions.js +245 -0
  351. package/dist/core/permissions.js.map +1 -0
  352. package/dist/core/persona/voiceProfile.d.ts +7 -0
  353. package/dist/core/persona/voiceProfile.js +14 -0
  354. package/dist/core/persona/voiceProfile.js.map +1 -0
  355. package/dist/core/profile.d.ts +35 -0
  356. package/dist/core/profile.js +373 -0
  357. package/dist/core/profile.js.map +1 -0
  358. package/dist/core/requestCompatibility.d.ts +13 -0
  359. package/dist/core/requestCompatibility.js +181 -0
  360. package/dist/core/requestCompatibility.js.map +1 -0
  361. package/dist/core/runtime/agentRuntime.d.ts +43 -0
  362. package/dist/core/runtime/agentRuntime.js +137 -0
  363. package/dist/core/runtime/agentRuntime.js.map +1 -0
  364. package/dist/core/runtime/approvalCoordinator.d.ts +36 -0
  365. package/dist/core/runtime/approvalCoordinator.js +113 -0
  366. package/dist/core/runtime/approvalCoordinator.js.map +1 -0
  367. package/dist/core/runtime/capabilityBoundary.d.ts +18 -0
  368. package/dist/core/runtime/capabilityBoundary.js +89 -0
  369. package/dist/core/runtime/capabilityBoundary.js.map +1 -0
  370. package/dist/core/runtime/nodeToolExecutor.d.ts +200 -0
  371. package/dist/core/runtime/nodeToolExecutor.js +2446 -0
  372. package/dist/core/runtime/nodeToolExecutor.js.map +1 -0
  373. package/dist/core/runtime/runtimeLogWriter.d.ts +32 -0
  374. package/dist/core/runtime/runtimeLogWriter.js +129 -0
  375. package/dist/core/runtime/runtimeLogWriter.js.map +1 -0
  376. package/dist/core/runtime/runtimeTaskManager.d.ts +78 -0
  377. package/dist/core/runtime/runtimeTaskManager.js +374 -0
  378. package/dist/core/runtime/runtimeTaskManager.js.map +1 -0
  379. package/dist/core/runtime/sessionRegistry.d.ts +19 -0
  380. package/dist/core/runtime/sessionRegistry.js +41 -0
  381. package/dist/core/runtime/sessionRegistry.js.map +1 -0
  382. package/dist/core/runtime/stateProvider.d.ts +69 -0
  383. package/dist/core/runtime/stateProvider.js +131 -0
  384. package/dist/core/runtime/stateProvider.js.map +1 -0
  385. package/dist/core/runtime/subAgentTaskManager.d.ts +111 -0
  386. package/dist/core/runtime/subAgentTaskManager.js +386 -0
  387. package/dist/core/runtime/subAgentTaskManager.js.map +1 -0
  388. package/dist/core/schemaValidation.d.ts +5 -0
  389. package/dist/core/schemaValidation.js +153 -0
  390. package/dist/core/schemaValidation.js.map +1 -0
  391. package/dist/core/skills/loader.d.ts +7 -0
  392. package/dist/core/skills/loader.js +91 -0
  393. package/dist/core/skills/loader.js.map +1 -0
  394. package/dist/core/skills/runtime.d.ts +19 -0
  395. package/dist/core/skills/runtime.js +61 -0
  396. package/dist/core/skills/runtime.js.map +1 -0
  397. package/dist/core/subAgent.d.ts +50 -0
  398. package/dist/core/subAgent.js +1258 -0
  399. package/dist/core/subAgent.js.map +1 -0
  400. package/dist/core/systemPrompt.d.ts +27 -0
  401. package/dist/core/systemPrompt.js +219 -0
  402. package/dist/core/systemPrompt.js.map +1 -0
  403. package/dist/core/taskManager.d.ts +105 -0
  404. package/dist/core/taskManager.js +617 -0
  405. package/dist/core/taskManager.js.map +1 -0
  406. package/dist/core/tokenCompressor.d.ts +14 -0
  407. package/dist/core/tokenCompressor.js +161 -0
  408. package/dist/core/tokenCompressor.js.map +1 -0
  409. package/dist/core/tokenCounter.d.ts +28 -0
  410. package/dist/core/tokenCounter.js +101 -0
  411. package/dist/core/tokenCounter.js.map +1 -0
  412. package/dist/core/toolDispatcher.d.ts +11 -0
  413. package/dist/core/toolDispatcher.js +38 -0
  414. package/dist/core/toolDispatcher.js.map +1 -0
  415. package/dist/core/toolExecutionLedger.d.ts +9 -0
  416. package/dist/core/toolExecutionLedger.js +84 -0
  417. package/dist/core/toolExecutionLedger.js.map +1 -0
  418. package/dist/core/toolRegistry.d.ts +19 -0
  419. package/dist/core/toolRegistry.js +820 -0
  420. package/dist/core/toolRegistry.js.map +1 -0
  421. package/dist/core/turnStrategy.d.ts +20 -0
  422. package/dist/core/turnStrategy.js +103 -0
  423. package/dist/core/turnStrategy.js.map +1 -0
  424. package/dist/platform/process.d.ts +14 -0
  425. package/dist/platform/process.js +28 -0
  426. package/dist/platform/process.js.map +1 -0
  427. package/dist/platform/runtime.d.ts +8 -0
  428. package/dist/platform/runtime.js +21 -0
  429. package/dist/platform/runtime.js.map +1 -0
  430. package/dist/server/adminPage.d.ts +1 -0
  431. package/dist/server/adminPage.js +755 -0
  432. package/dist/server/adminPage.js.map +1 -0
  433. package/dist/server/index.d.ts +22 -0
  434. package/dist/server/index.js +566 -0
  435. package/dist/server/index.js.map +1 -0
  436. package/dist/shared/agentTypes.d.ts +227 -0
  437. package/dist/shared/agentTypes.js +79 -0
  438. package/dist/shared/agentTypes.js.map +1 -0
  439. package/dist/shared/codeIndexTypes.d.ts +32 -0
  440. package/dist/shared/codeIndexTypes.js +2 -0
  441. package/dist/shared/codeIndexTypes.js.map +1 -0
  442. package/dist/shared/flowEvents.d.ts +192 -0
  443. package/dist/shared/flowEvents.js +37 -0
  444. package/dist/shared/flowEvents.js.map +1 -0
  445. package/dist/shared/memoryTypes.d.ts +100 -0
  446. package/dist/shared/memoryTypes.js +2 -0
  447. package/dist/shared/memoryTypes.js.map +1 -0
  448. package/dist/shared/pluginTypes.d.ts +345 -0
  449. package/dist/shared/pluginTypes.js +2 -0
  450. package/dist/shared/pluginTypes.js.map +1 -0
  451. package/dist/shared/runtimeTaskTypes.d.ts +43 -0
  452. package/dist/shared/runtimeTaskTypes.js +2 -0
  453. package/dist/shared/runtimeTaskTypes.js.map +1 -0
  454. package/dist/shared/skillTypes.d.ts +1 -0
  455. package/dist/shared/skillTypes.js +2 -0
  456. package/dist/shared/skillTypes.js.map +1 -0
  457. package/dist/shared/subAgentTypes.d.ts +99 -0
  458. package/dist/shared/subAgentTypes.js +2 -0
  459. package/dist/shared/subAgentTypes.js.map +1 -0
  460. package/dist/shared/terminalTypes.d.ts +102 -0
  461. package/dist/shared/terminalTypes.js +2 -0
  462. package/dist/shared/terminalTypes.js.map +1 -0
  463. package/dist/shared/toolCallMarkup.d.ts +10 -0
  464. package/dist/shared/toolCallMarkup.js +107 -0
  465. package/dist/shared/toolCallMarkup.js.map +1 -0
  466. package/dist/shared/toolTypes.d.ts +24 -0
  467. package/dist/shared/toolTypes.js +2 -0
  468. package/dist/shared/toolTypes.js.map +1 -0
  469. package/dist/shared/types.d.ts +81 -0
  470. package/dist/shared/types.js +2 -0
  471. package/dist/shared/types.js.map +1 -0
  472. package/dist/state/types.d.ts +69 -0
  473. package/dist/state/types.js +2 -0
  474. package/dist/state/types.js.map +1 -0
  475. package/dist/tools/executor.d.ts +133 -0
  476. package/dist/tools/executor.js +2 -0
  477. package/dist/tools/executor.js.map +1 -0
  478. package/dist/tools/memory/loaders.d.ts +6 -0
  479. package/dist/tools/memory/loaders.js +292 -0
  480. package/dist/tools/memory/loaders.js.map +1 -0
  481. package/dist/tools/memory/sanitizer.d.ts +6 -0
  482. package/dist/tools/memory/sanitizer.js +48 -0
  483. package/dist/tools/memory/sanitizer.js.map +1 -0
  484. package/dist/tools/memory/service.d.ts +24 -0
  485. package/dist/tools/memory/service.js +273 -0
  486. package/dist/tools/memory/service.js.map +1 -0
  487. package/dist/tools/memory/writer.d.ts +17 -0
  488. package/dist/tools/memory/writer.js +192 -0
  489. package/dist/tools/memory/writer.js.map +1 -0
  490. package/package.json +68 -0
@@ -0,0 +1,1738 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useCallback, useRef, useMemo, useSyncExternalStore } from 'react';
3
+ import { render, Box, Static, Text, useInput, useApp, useBoxMetrics } from 'ink';
4
+ import { ThemeProvider, resolveBackground, useTheme } from '../theme/index';
5
+ import { Header } from './header/Header';
6
+ import { StatusLine } from './header/StatusLine';
7
+ import { ActiveWorkPanel } from './tools/ActiveWorkPanel';
8
+ import { ConversationHistory } from './ConversationHistory';
9
+ import { RewindSelector } from './input/RewindSelector';
10
+ import { ModelPicker } from './input/ModelPicker';
11
+ import { EffortPicker } from './input/EffortPicker';
12
+ import { PermissionDialog } from './permissions/PermissionDialog';
13
+ import { MessageList } from './messages/MessageList';
14
+ import { WindowedMessageList } from './messages/WindowedMessageList';
15
+ import { useOverlayStack } from '../hooks/useOverlayStack';
16
+ import { useMessageCursor } from '../hooks/useMessageCursor';
17
+ import { PromptInput } from './input/PromptInput';
18
+ import { formatMarkdown, getMarkdownCacheStats } from './markdown/index';
19
+ import { createAgentRuntime } from '../../core/runtime/agentRuntime';
20
+ import { applyPreset, saveConfig, setConfigValue } from '../../core/config';
21
+ import { loadProfile } from '../../core/profile';
22
+ import { discoverModelPresets, readCachedModelDiscovery } from '../../core/modelDiscovery';
23
+ import { formatNativeReasoningSetting, getModelReasoningCapabilities } from '../../core/modelRegistry';
24
+ import { commandRegistry } from '../commands/index';
25
+ import { ConversationManager } from '../conversations/manager';
26
+ import { LARGE_PASTE_CHAR_THRESHOLD, createPendingPastePlaceholder, expandPendingPastes, replacePastedText, retainPendingPastes, } from './input/pasteState';
27
+ import { AgentFlowController } from '../state/agentFlowController';
28
+ import { GlobalCommandActivityController } from '../state/globalCommandActivity';
29
+ import { ApprovalPresentationScheduler } from '../state/approvalPresentationScheduler';
30
+ import { AdaptiveStreamScheduler } from '../state/adaptiveStreamScheduler';
31
+ import { selectInputReceipt, selectAgentRunState, selectAgentMode, selectActiveTask, selectIsForegroundBusy, selectPendingSteeringInputs, selectPrimaryActivity, selectQueueCount, selectQueuedInputs, selectRunningBackgroundCount, selectTokenUsage, selectToolDraft, } from '../state/flowSelectors';
32
+ import { LocalFlowTelemetry } from '../telemetry/localFlowTelemetry';
33
+ import { TerminalLatencyTracker } from '../telemetry/terminalLatencyTracker';
34
+ import { TerminalAttentionAdapter } from '../platform/terminalAttention';
35
+ import { isPersistenceRecoveryCommand, resolveFlowFeatureFlags, } from '../state/flowFeatureFlags';
36
+ import { NotificationCoordinator, sanitizeTerminalTitle, } from '../state/notificationCoordinator';
37
+ import { globalConfigurationFingerprint, watchGlobalConfiguration } from '../globalConfiguration';
38
+ import { createTranslator, I18nProvider, useI18n } from '../i18n/index';
39
+ import { stripTextToolCallMarkup } from '../../shared/toolCallMarkup';
40
+ import { useTerminalSize } from '../hooks/useTerminalSize';
41
+ import { getSafeViewportWidth } from '../terminalLayout';
42
+ import { TerminalSessionsFooter } from './tools/TerminalSessionsFooter';
43
+ import { AgentActivityLine } from './tools/AgentActivityLine';
44
+ import { TaskFlowHud } from './tools/TaskFlowHud';
45
+ import { QueuedPromptList } from './tools/QueuedPromptList';
46
+ import { beginToolCall, settleToolCall } from './tools/toolLifecycleModel';
47
+ import { resolveCockpitLayout } from './layout/CockpitRails';
48
+ import { SessionSidebar } from './layout/SessionSidebar';
49
+ import { LandingView } from './layout/LandingView';
50
+ import { getStartupAnimationFrame, shouldAnimateStartup, STARTUP_ANIMATION_MS } from './layout/StartupAnimation';
51
+ import { DISABLE_MOUSE_TRACKING, ENABLE_MOUSE_TRACKING, parseTerminalMouseWheel, shouldEnableMouseTracking } from '../terminalMouse';
52
+ import { captureClipboardImageAttachment, hasImageReference, imageAttachmentFingerprint, imagePlaceholderForIndex, reconcileDraftImagePrompt, resolveImagePrompt } from '../imageAttachments';
53
+ import { DEFAULT_MOUSE_WHEEL_ROWS, TranscriptViewport, clampTranscriptScroll, getTranscriptPageRows, revealTranscriptRange, } from './TranscriptViewport';
54
+ import { createThinkingTrace, estimateOutputTokensForDisplay, formatElapsed, getEngineUserOrdinalForUiMessage, isProvisionalAssistantTurn, isThinkingToggleShortcut, resolveAssistantStreamDisplay, resolveLandingFrameWidth, serializeToolArgsForUi, selectAutoMountedModel, shouldUseNoFlicker, shouldShowLandingView, sliceTurnsBeforeNthUserTurn, turnsToMessages, } from './appHelpers';
55
+ export { createThinkingTrace, formatTaskProgressLabel, formatTaskToolSummary, getEngineUserOrdinalForUiMessage, isProvisionalAssistantTurn, isThinkingToggleShortcut, resolveAssistantStreamDisplay, resolveLandingFrameWidth, selectAutoMountedModel, shouldUseFlowUi, shouldShowLandingView, shouldUseNoFlicker, sliceTurnsBeforeNthUserTurn, turnsToMessages, } from './appHelpers';
56
+ const MAX_TRANSCRIPT_MESSAGES = 1_000;
57
+ const MAX_TRANSCRIPT_CHARS = 8 * 1024 * 1024;
58
+ function transcriptMessageChars(message) {
59
+ let chars = message.content.length + (message.thinking?.content.length ?? 0);
60
+ for (const change of message.changes ?? []) {
61
+ chars += (change.before?.length ?? 0) + (change.after?.length ?? 0);
62
+ chars += (change.preview?.length ?? 0) + (change.oldPreview?.length ?? 0);
63
+ }
64
+ return chars;
65
+ }
66
+ function retainTranscriptTail(messages) {
67
+ if (messages.length <= MAX_TRANSCRIPT_MESSAGES) {
68
+ let chars = 0;
69
+ for (const message of messages)
70
+ chars += transcriptMessageChars(message);
71
+ if (chars <= MAX_TRANSCRIPT_CHARS)
72
+ return messages;
73
+ }
74
+ let start = messages.length;
75
+ let chars = 0;
76
+ while (start > 0) {
77
+ const next = messages[start - 1];
78
+ const nextChars = transcriptMessageChars(next);
79
+ if (start < messages.length && (start <= messages.length - MAX_TRANSCRIPT_MESSAGES || chars + nextChars > MAX_TRANSCRIPT_CHARS))
80
+ break;
81
+ chars += nextChars;
82
+ start -= 1;
83
+ }
84
+ const retained = messages.slice(start);
85
+ return [
86
+ {
87
+ id: `transcript-trim-${retained[0]?.id || Date.now()}`,
88
+ role: 'system',
89
+ content: 'Older transcript entries were trimmed from the live UI buffer; the saved conversation remains available.',
90
+ },
91
+ ...retained,
92
+ ];
93
+ }
94
+ function describeFlowInputReceipt(receipt, t) {
95
+ switch (receipt.kind) {
96
+ case 'pending':
97
+ return receipt.intent === 'steer'
98
+ ? t('ui.flow.input.steeringPending')
99
+ : t('ui.flow.input.pending');
100
+ case 'steering':
101
+ return t('ui.flow.input.steering');
102
+ case 'queued':
103
+ return t('ui.flow.input.queued', { count: receipt.queueCount });
104
+ case 'committed':
105
+ return receipt.intent === 'steer'
106
+ ? t('ui.flow.input.steered')
107
+ : t('ui.flow.input.committed');
108
+ case 'restored':
109
+ return t('ui.flow.input.restored');
110
+ }
111
+ }
112
+ function describeSubAgentEvent(event, t) {
113
+ if (event.type === 'turn_start')
114
+ return t('ui.subagent.turn', { turn: event.turn, maxTurns: event.maxTurns });
115
+ if (event.type === 'model_wait')
116
+ return t('ui.subagent.waitingModel', { seconds: Math.floor(event.elapsedMs / 1000) });
117
+ if (event.type === 'model_retry')
118
+ return t('ui.subagent.retry', { attempt: event.attempt, reason: event.reason.slice(0, 72) });
119
+ if (event.type === 'tool_call')
120
+ return event.tool;
121
+ if (event.type === 'tool_result')
122
+ return event.summary.slice(0, 90);
123
+ if (event.type === 'evidence')
124
+ return event.evidence.path;
125
+ if (event.type === 'final')
126
+ return t('ui.subagent.finalizing');
127
+ if (event.type === 'error')
128
+ return event.message.slice(0, 90);
129
+ return t('ui.subagent.turnComplete', { turn: event.turn });
130
+ }
131
+ function SubAgentProgressLine({ activities }) {
132
+ const theme = useTheme();
133
+ const { t } = useI18n();
134
+ if (activities.length === 0)
135
+ return null;
136
+ return (_jsx(Box, { flexDirection: "column", children: activities.slice(-3).map(activity => (_jsxs(Box, { children: [_jsx(Text, { color: activity.status === 'failed' ? theme.error : activity.status === 'completed' ? theme.success : theme.brand, children: activity.status === 'failed' ? '! ' : activity.status === 'completed' ? '✓ ' : '● ' }), _jsx(Text, { children: activity.label }), _jsx(Text, { dimColor: true, children: activity.status === 'running'
137
+ ? ` · ${activity.detail || activity.objective} · ${formatElapsed(Date.now() - activity.startedAt)}`
138
+ : activity.status === 'completed' ? ` · ${t('ui.subagent.resultReady')}` : ` · ${t('common.failed')}` })] }, activity.id))) }));
139
+ }
140
+ function CockpitRoot({ width, height, children }) {
141
+ const theme = useTheme();
142
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, width: width, height: height, overflow: "hidden", backgroundColor: resolveBackground(theme, 'background'), children: children }));
143
+ }
144
+ function SessionPane({ children }) {
145
+ const theme = useTheme();
146
+ return (_jsx(Box, { flexDirection: "column", flexBasis: 0, flexGrow: 1, flexShrink: 1, minHeight: 0, minWidth: 0, backgroundColor: resolveBackground(theme, 'background'), overflow: "hidden", children: _jsx(Box, { flexDirection: "column", flexBasis: 0, flexGrow: 1, flexShrink: 1, minHeight: 0, paddingX: 1, overflow: "hidden", children: children }) }));
147
+ }
148
+ function App({ workspacePath, workspaceName, config: initialConfig, singleShot, verbose, noFlicker, approvalPolicy, capabilityProfile, mcpServers, startupAnimation = true, transparentBackground = false, flowTelemetry: providedFlowTelemetry, terminalLatencyTracker: providedTerminalLatencyTracker }) {
149
+ const { exit } = useApp();
150
+ const layoutBackground = transparentBackground ? undefined : '#000000';
151
+ const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
152
+ const terminal = useTerminalSize();
153
+ const noFlickerActive = noFlicker && isInteractive && !singleShot;
154
+ const [flowFeatures] = useState(() => resolveFlowFeatureFlags());
155
+ const flowUiEnabled = flowFeatures.flowUi;
156
+ const startupAnimationEnabled = shouldAnimateStartup(isInteractive, singleShot, startupAnimation && noFlickerActive);
157
+ const startupStartedAtRef = useRef(Date.now());
158
+ const [startupElapsed, setStartupElapsed] = useState(startupAnimationEnabled ? 0 : STARTUP_ANIMATION_MS);
159
+ const startupFrame = getStartupAnimationFrame(startupElapsed);
160
+ const [config, setConfig] = useState(initialConfig);
161
+ const [profile, setProfile] = useState(loadProfile);
162
+ const t = useMemo(() => createTranslator(profile.interfaceLanguage), [profile.interfaceLanguage]);
163
+ const [globalCommandActivityController] = useState(() => new GlobalCommandActivityController());
164
+ const globalCommandActivity = useSyncExternalStore(globalCommandActivityController.subscribe, globalCommandActivityController.getSnapshot, globalCommandActivityController.getSnapshot);
165
+ const [messages, setMessages] = useState([]);
166
+ const [staticTranscriptRevision, setStaticTranscriptRevision] = useState(0);
167
+ const [input, setInput] = useState('');
168
+ const [draftAttachments, setDraftAttachments] = useState([]);
169
+ const [streamText, setStreamText] = useState('');
170
+ const [streamThinkingText, setStreamThinkingText] = useState('');
171
+ const [streamThinkingStartedAt, setStreamThinkingStartedAt] = useState();
172
+ const [showThinking, setShowThinking] = useState(false);
173
+ const [showToolDetails, setShowToolDetails] = useState(verbose);
174
+ const [currentTurnOutputTokens, setCurrentTurnOutputTokens] = useState(0);
175
+ const [currentTools, setCurrentTools] = useState([]);
176
+ const [mood, setMood] = useState('idle');
177
+ const [gitState, setGitState] = useState(() => ({
178
+ enabled: initialConfig.gitEnabled !== false,
179
+ phase: initialConfig.gitEnabled === false ? 'disabled' : 'detecting',
180
+ snapshot: null,
181
+ updatedAt: Date.now(),
182
+ }));
183
+ const [modelPresets, setModelPresets] = useState([]);
184
+ const [modelDiscoveryStatus, setModelDiscoveryStatus] = useState({
185
+ isRefreshing: false,
186
+ stale: false,
187
+ error: undefined,
188
+ });
189
+ const modelDiscoveryRequestRef = useRef(0);
190
+ const [lastActivity, setLastActivity] = useState(Date.now());
191
+ const [convListRevision, setConvListRevision] = useState(0);
192
+ const [conversationEntries, setConversationEntries] = useState([]);
193
+ const [subAgentActivities, setSubAgentActivities] = useState([]);
194
+ const [terminalSessions, setTerminalSessions] = useState([]);
195
+ const [, setChangeSummaries] = useState([]);
196
+ const [interruptHint, setInterruptHint] = useState(null);
197
+ const [exitHint, setExitHint] = useState(null);
198
+ const [runControlHint, setRunControlHint] = useState(null);
199
+ const [persistenceWarning, setPersistenceWarning] = useState(null);
200
+ const [pendingAsk, setPendingAsk] = useState(null);
201
+ const [askModalVisible, setAskModalVisible] = useState(false);
202
+ const [askInput, setAskInput] = useState('');
203
+ const [approvalPresentationScheduler] = useState(() => new ApprovalPresentationScheduler());
204
+ const [notificationCoordinator] = useState(() => new NotificationCoordinator(Date.now, flowFeatures.notifications));
205
+ const [terminalAttention] = useState(() => new TerminalAttentionAdapter({
206
+ enabled: flowFeatures.notifications,
207
+ interactive: isInteractive,
208
+ }));
209
+ const [flowTelemetry] = useState(() => providedFlowTelemetry ?? new LocalFlowTelemetry(workspacePath));
210
+ const [terminalLatencyTracker] = useState(() => providedTerminalLatencyTracker ?? new TerminalLatencyTracker((metric, value) => flowTelemetry.observe(metric, value)));
211
+ const [notificationSnapshot, setNotificationSnapshot] = useState(() => notificationCoordinator.getSnapshot());
212
+ const { active: activeOverlay, push, pop } = useOverlayStack();
213
+ const { cursor, enter, navigatePrev, navigateNext, clear } = useMessageCursor(messages);
214
+ const [cursorMode, setCursorMode] = useState(false);
215
+ const [scrollRowsFromBottom, setScrollRowsFromBottom] = useState(0);
216
+ const [transcriptMetrics, setTranscriptMetrics] = useState({
217
+ contentRows: 0,
218
+ viewportRows: 1,
219
+ maxScrollRows: 0,
220
+ });
221
+ const transcriptMetricsRef = useRef(transcriptMetrics);
222
+ const selectedMessageRef = useRef(null);
223
+ const selectedMessageMetrics = useBoxMetrics(selectedMessageRef);
224
+ const messageIdRef = useRef(0);
225
+ const streamBufferRef = useRef('');
226
+ const streamThinkingBufferRef = useRef('');
227
+ const streamThinkingStartedAtRef = useRef(undefined);
228
+ const streamTransitionTimerRef = useRef(null);
229
+ const lastAssistantTurnInterruptedRef = useRef(false);
230
+ const lastActivityPaintRef = useRef(0);
231
+ const inputRef = useRef('');
232
+ const draftAttachmentsRef = useRef([]);
233
+ const pendingPastesRef = useRef([]);
234
+ const pendingAskRef = useRef(null);
235
+ const activePromptRef = useRef(null);
236
+ const abortingRef = useRef(false);
237
+ const abortRestoredPromptRef = useRef(false);
238
+ const runPromptRef = useRef(null);
239
+ const exitPressRef = useRef(0);
240
+ const lastCtrlCEventAtRef = useRef(0);
241
+ const runControlHintTimerRef = useRef(null);
242
+ const handleInterruptRef = useRef(() => { });
243
+ const lastClipboardImageRef = useRef(null);
244
+ const globalConfigurationFingerprintRef = useRef(globalConfigurationFingerprint({ config: initialConfig, profile }));
245
+ const pendingGlobalConfigurationRef = useRef(null);
246
+ const promptHistoryRef = useRef([]);
247
+ const [streamScheduler] = useState(() => new AdaptiveStreamScheduler(batch => {
248
+ setStreamText(streamBufferRef.current);
249
+ setStreamThinkingText(streamThinkingBufferRef.current);
250
+ setCurrentTurnOutputTokens(previous => Math.max(previous, estimateOutputTokensForDisplay(streamBufferRef.current)));
251
+ flowTelemetry.count('ui.stream_flush');
252
+ flowTelemetry.observe('ui.stream_batch_depth', batch.depth);
253
+ flowTelemetry.observe('ui.stream_oldest_age_ms', batch.oldestAgeMs);
254
+ }));
255
+ const genMsgId = useCallback(() => {
256
+ messageIdRef.current += 1;
257
+ return `msg-${messageIdRef.current}`;
258
+ }, []);
259
+ const currentToolsRef = useRef([]);
260
+ const changeSummariesRef = useRef([]);
261
+ const updateCurrentTools = useCallback((update) => {
262
+ const next = update(currentToolsRef.current);
263
+ currentToolsRef.current = next;
264
+ setCurrentTools(next);
265
+ }, []);
266
+ const updateChangeSummaries = useCallback((update) => {
267
+ const next = update(changeSummariesRef.current);
268
+ changeSummariesRef.current = next;
269
+ setChangeSummaries(next);
270
+ }, []);
271
+ useEffect(() => { draftAttachmentsRef.current = draftAttachments; }, [draftAttachments]);
272
+ const [runtime] = useState(() => createAgentRuntime({
273
+ workspacePath,
274
+ workspaceName,
275
+ config: initialConfig,
276
+ profile,
277
+ conversationPrefix: 'cli',
278
+ approvalPolicy,
279
+ capabilityProfile,
280
+ connectMcp: Boolean(mcpServers?.length),
281
+ mcpServers,
282
+ registerSkills: skillRuntime => commandRegistry.registerSkills(skillRuntime),
283
+ }));
284
+ const { engine, stateProvider, skillRuntime, mcpClient } = runtime;
285
+ const terminalPollingActive = terminalSessions.some(session => session.status === 'running' || session.status === 'starting');
286
+ useEffect(() => {
287
+ let cancelled = false;
288
+ const refresh = async () => {
289
+ const result = await runtime.toolExecutor.ptyList?.();
290
+ if (!cancelled && result?.success) {
291
+ setTerminalSessions((result.sessions || result.data || []));
292
+ }
293
+ };
294
+ void refresh();
295
+ if (!terminalPollingActive)
296
+ return () => { cancelled = true; };
297
+ const timer = setInterval(() => { void refresh(); }, 1000);
298
+ return () => {
299
+ cancelled = true;
300
+ clearInterval(timer);
301
+ };
302
+ }, [runtime.toolExecutor, terminalPollingActive]);
303
+ const [flowBridge] = useState(() => new AgentFlowController(runtime.sessionRegistry.getCurrentId()));
304
+ const [convManager] = useState(() => new ConversationManager(engine, config, workspacePath, error => {
305
+ setPersistenceWarning(error ? t('ui.app.persistenceUnavailable', { message: error.message }) : null);
306
+ flowBridge.setPersistenceStatus(error);
307
+ }, runtime.sessionRegistry, { batchJournalStreaming: flowFeatures.journalBatching }));
308
+ const flowSnapshot = useSyncExternalStore(flowBridge.store.subscribe, flowBridge.store.getSnapshot, flowBridge.store.getSnapshot);
309
+ const [flowPresentationNow, setFlowPresentationNow] = useState(Date.now);
310
+ const activeFlowState = flowSnapshot.activeThreadId
311
+ ? flowSnapshot.threads[flowSnapshot.activeThreadId]
312
+ : undefined;
313
+ const isRunning = activeFlowState ? selectIsForegroundBusy(activeFlowState) : false;
314
+ const runState = activeFlowState
315
+ ? selectAgentRunState(activeFlowState)
316
+ : { phase: 'idle', updatedAt: 0 };
317
+ const currentMode = activeFlowState ? selectAgentMode(activeFlowState) : 'vibe';
318
+ const tokenUsage = activeFlowState ? selectTokenUsage(activeFlowState) : { source: 'unknown' };
319
+ const activeTask = activeFlowState ? selectActiveTask(activeFlowState) : null;
320
+ const streamingToolDraft = activeFlowState ? selectToolDraft(activeFlowState) : null;
321
+ const activeObjective = activeFlowState?.run.objective && activeFlowState.run.startedAt
322
+ ? { prompt: activeFlowState.run.objective, startedAt: activeFlowState.run.startedAt }
323
+ : null;
324
+ const queuedPrompts = useMemo(() => activeFlowState ? selectQueuedInputs(activeFlowState) : [], [activeFlowState?.inputQueue, activeFlowState?.inputs]);
325
+ const pendingSteeringPrompts = useMemo(() => activeFlowState ? selectPendingSteeringInputs(activeFlowState) : [], [activeFlowState?.inputs]);
326
+ const primaryFlowActivity = flowUiEnabled && activeFlowState ? selectPrimaryActivity(activeFlowState) : undefined;
327
+ const flowIsRunning = isRunning;
328
+ const developerFlowActive = flowIsRunning || globalCommandActivity !== null;
329
+ const flowQueueCount = activeFlowState ? selectQueueCount(activeFlowState) : 0;
330
+ const flowBackgroundCount = flowUiEnabled && activeFlowState ? selectRunningBackgroundCount(activeFlowState) : 0;
331
+ const flowInputReceipt = flowUiEnabled && activeFlowState
332
+ ? selectInputReceipt(activeFlowState, flowPresentationNow)
333
+ : null;
334
+ useEffect(() => {
335
+ if (!flowInputReceipt?.expiresAt)
336
+ return;
337
+ const remaining = flowInputReceipt.expiresAt - Date.now();
338
+ if (remaining <= 0) {
339
+ setFlowPresentationNow(Date.now());
340
+ return;
341
+ }
342
+ const timer = setTimeout(() => setFlowPresentationNow(Date.now()), remaining + 1);
343
+ return () => clearTimeout(timer);
344
+ }, [flowInputReceipt?.expiresAt, flowSnapshot.revision]);
345
+ useEffect(() => {
346
+ convManager.recordQueueState(queuedPrompts);
347
+ }, [convManager, queuedPrompts]);
348
+ useEffect(() => {
349
+ convManager.recordDraftState({ text: input, attachments: draftAttachments, pendingPastes: pendingPastesRef.current });
350
+ flowBridge.draftChanged(input, draftAttachments.map(attachment => attachment.id));
351
+ }, [convManager, flowBridge, input, draftAttachments]);
352
+ useEffect(() => runtime.sessionRegistry.subscribe(({ currentId }) => {
353
+ flowBridge.activateThread(currentId);
354
+ }), [runtime.sessionRegistry, flowBridge]);
355
+ useEffect(() => {
356
+ if (!startupAnimationEnabled) {
357
+ setStartupElapsed(STARTUP_ANIMATION_MS);
358
+ return;
359
+ }
360
+ startupStartedAtRef.current = Date.now();
361
+ setStartupElapsed(0);
362
+ const timer = setInterval(() => {
363
+ const elapsed = Date.now() - startupStartedAtRef.current;
364
+ setStartupElapsed(Math.min(STARTUP_ANIMATION_MS, elapsed));
365
+ if (elapsed >= STARTUP_ANIMATION_MS)
366
+ clearInterval(timer);
367
+ }, 40);
368
+ return () => clearInterval(timer);
369
+ }, [startupAnimationEnabled]);
370
+ const skipStartupAnimation = useCallback(() => {
371
+ setStartupElapsed(STARTUP_ANIMATION_MS);
372
+ }, []);
373
+ useEffect(() => {
374
+ if (!shouldEnableMouseTracking(isInteractive, noFlickerActive))
375
+ return;
376
+ process.stdout.write(ENABLE_MOUSE_TRACKING);
377
+ return () => {
378
+ process.stdout.write(DISABLE_MOUSE_TRACKING);
379
+ };
380
+ }, [isInteractive, noFlickerActive]);
381
+ const persistConfig = useCallback((nextConfig) => {
382
+ const savedConfig = saveConfig(nextConfig);
383
+ runtime.applyConfiguration(savedConfig, { profile, approvalPolicy, capabilityProfile });
384
+ convManager.updateConfig(savedConfig);
385
+ setConfig(savedConfig);
386
+ globalConfigurationFingerprintRef.current = globalConfigurationFingerprint({ config: savedConfig, profile });
387
+ }, [runtime, profile, approvalPolicy, capabilityProfile, convManager]);
388
+ const clearStreamFlushTimer = useCallback(() => {
389
+ streamScheduler.cancel();
390
+ if (streamTransitionTimerRef.current) {
391
+ clearTimeout(streamTransitionTimerRef.current);
392
+ streamTransitionTimerRef.current = null;
393
+ }
394
+ }, [streamScheduler]);
395
+ const markActivity = useCallback((timestamp = Date.now()) => {
396
+ if (timestamp - lastActivityPaintRef.current < 80)
397
+ return;
398
+ lastActivityPaintRef.current = timestamp;
399
+ setLastActivity(timestamp);
400
+ }, []);
401
+ const showRunControlHint = useCallback((message) => {
402
+ if (runControlHintTimerRef.current)
403
+ clearTimeout(runControlHintTimerRef.current);
404
+ setRunControlHint(message);
405
+ runControlHintTimerRef.current = setTimeout(() => {
406
+ runControlHintTimerRef.current = null;
407
+ setRunControlHint(null);
408
+ }, 1800);
409
+ }, []);
410
+ const syncNotificationSnapshot = useCallback(() => {
411
+ setNotificationSnapshot(notificationCoordinator.getSnapshot());
412
+ }, [notificationCoordinator]);
413
+ const dismissPendingAsk = useCallback((requestId) => {
414
+ const current = pendingAskRef.current;
415
+ approvalPresentationScheduler.cancel(requestId);
416
+ if (!current || (requestId !== undefined && current.id !== requestId))
417
+ return false;
418
+ pendingAskRef.current = null;
419
+ setPendingAsk(null);
420
+ setAskModalVisible(false);
421
+ setAskInput('');
422
+ notificationCoordinator.acknowledgeSource('action-required', current.id);
423
+ syncNotificationSnapshot();
424
+ return true;
425
+ }, [approvalPresentationScheduler, notificationCoordinator, syncNotificationSnapshot]);
426
+ const schedulePendingAsk = useCallback((ask) => {
427
+ const requestedAt = Date.now();
428
+ pendingAskRef.current = ask;
429
+ setPendingAsk(ask);
430
+ setAskModalVisible(false);
431
+ setAskInput('');
432
+ notificationCoordinator.raise({
433
+ id: `approval:${ask.id}`,
434
+ category: 'action-required',
435
+ title: ask.options?.includes('allow-once') ? t('ui.app.reviewRequired') : t('ui.app.inputRequired'),
436
+ detail: ask.toolName || ask.reason,
437
+ sourceId: ask.id,
438
+ });
439
+ flowTelemetry.count('ui.approval_requested');
440
+ syncNotificationSnapshot();
441
+ approvalPresentationScheduler.request(ask.id, () => {
442
+ if (pendingAskRef.current?.id === ask.id) {
443
+ flowBridge.presentApproval(ask.id);
444
+ flowTelemetry.observe('ui.approval_presented_ms', Date.now() - requestedAt);
445
+ setAskModalVisible(true);
446
+ }
447
+ }, requestedAt);
448
+ }, [approvalPresentationScheduler, flowBridge, flowTelemetry, notificationCoordinator, syncNotificationSnapshot, t]);
449
+ const noteComposerActivity = useCallback(() => {
450
+ flowTelemetry.count('ui.key_received');
451
+ terminalAttention.noteUserActivity();
452
+ approvalPresentationScheduler.noteComposerActivity();
453
+ streamScheduler.noteInput();
454
+ }, [approvalPresentationScheduler, flowTelemetry, streamScheduler, terminalAttention]);
455
+ const noteInputMutation = useCallback(() => {
456
+ terminalLatencyTracker.noteKeyReceived();
457
+ }, [terminalLatencyTracker]);
458
+ const clearResultInbox = useCallback(() => {
459
+ const cleared = notificationCoordinator.acknowledgeCategory('result-ready');
460
+ if (cleared > 0) {
461
+ setSubAgentActivities(current => current.filter(activity => activity.status === 'running'));
462
+ syncNotificationSnapshot();
463
+ }
464
+ return cleared;
465
+ }, [notificationCoordinator, syncNotificationSnapshot]);
466
+ useEffect(() => {
467
+ if (!isInteractive || !flowFeatures.notifications)
468
+ return;
469
+ const title = sanitizeTerminalTitle(notificationSnapshot.terminalTitle);
470
+ process.stdout.write(`\u001b]0;${title}\u0007`);
471
+ }, [isInteractive, notificationSnapshot.terminalTitle, flowFeatures.notifications]);
472
+ useEffect(() => {
473
+ terminalAttention.start();
474
+ return () => terminalAttention.stop();
475
+ }, [terminalAttention]);
476
+ useEffect(() => () => {
477
+ globalCommandActivityController.destroy();
478
+ }, [globalCommandActivityController]);
479
+ useEffect(() => {
480
+ if (notificationSnapshot.active)
481
+ terminalAttention.notify(notificationSnapshot.active);
482
+ }, [notificationSnapshot.active, terminalAttention]);
483
+ useEffect(() => () => {
484
+ approvalPresentationScheduler.destroy();
485
+ const markdownStats = getMarkdownCacheStats();
486
+ flowTelemetry.observe('ui.markdown_cache_hit_rate', markdownStats.hitRate * 100);
487
+ const journalStats = convManager.getJournalStats();
488
+ flowTelemetry.count('journal.physical_writes', journalStats.physicalWrites);
489
+ flowTelemetry.count('journal.streaming_batches', journalStats.streamingBatchesWritten);
490
+ const reducerViolations = Object.values(flowBridge.store.getSnapshot().threads)
491
+ .reduce((count, thread) => count + thread.violations.length, 0);
492
+ if (reducerViolations > 0)
493
+ flowTelemetry.count('flow.reducer_violation', reducerViolations);
494
+ flowTelemetry.destroy();
495
+ if (isInteractive)
496
+ process.stdout.write('\u001b]0;\u0007');
497
+ }, [approvalPresentationScheduler, convManager, flowBridge, flowTelemetry, isInteractive]);
498
+ const appendMessages = useCallback((nextMessages, options) => {
499
+ if (nextMessages.length === 0)
500
+ return;
501
+ setMessages(msgs => retainTranscriptTail([...msgs, ...nextMessages]));
502
+ if (noFlickerActive && options?.forceLatest === true)
503
+ setScrollRowsFromBottom(0);
504
+ }, [noFlickerActive]);
505
+ const replaceMessages = useCallback((nextMessages) => {
506
+ setStaticTranscriptRevision(revision => revision + 1);
507
+ setMessages(previous => retainTranscriptTail(typeof nextMessages === 'function' ? nextMessages(previous) : nextMessages));
508
+ }, []);
509
+ const restoreCliStateFromTurns = useCallback((activeTurns, nextInput = '', contextSegments = [], contextReservoir = [], transcriptTurns = activeTurns) => {
510
+ engine.restoreFromTurns(activeTurns);
511
+ engine.setContextSegments(contextSegments);
512
+ engine.setContextReservoir(contextReservoir);
513
+ replaceMessages(turnsToMessages(transcriptTurns));
514
+ inputRef.current = nextInput;
515
+ pendingPastesRef.current = [];
516
+ setInput(nextInput);
517
+ draftAttachmentsRef.current = [];
518
+ setDraftAttachments([]);
519
+ setScrollRowsFromBottom(0);
520
+ flowBridge.updateUsage(engine.getContextUsage());
521
+ setGitState(engine.getGitState());
522
+ updateCurrentTools(() => []);
523
+ updateChangeSummaries(() => []);
524
+ setCurrentTurnOutputTokens(0);
525
+ streamBufferRef.current = '';
526
+ streamThinkingBufferRef.current = '';
527
+ streamThinkingStartedAtRef.current = undefined;
528
+ setStreamThinkingStartedAt(undefined);
529
+ clearStreamFlushTimer();
530
+ setStreamText('');
531
+ setStreamThinkingText('');
532
+ setTerminalSessions([]);
533
+ dismissPendingAsk();
534
+ flowBridge.replaceQueue([]);
535
+ activePromptRef.current = null;
536
+ abortingRef.current = false;
537
+ setInterruptHint(null);
538
+ setExitHint(null);
539
+ setRunControlHint(null);
540
+ setCursorMode(false);
541
+ clear();
542
+ setMood('idle');
543
+ }, [engine, stateProvider, clearStreamFlushTimer, clear, replaceMessages, dismissPendingAsk, flowBridge]);
544
+ const getRewindContextSegments = useCallback((turns) => {
545
+ const boundaryTime = turns.reduce((max, turn) => Math.max(max, turn.timestamp), 0);
546
+ return stateProvider.getContextSegments().filter(segment => {
547
+ if (typeof segment.createdAt !== 'number')
548
+ return true;
549
+ return segment.createdAt <= boundaryTime;
550
+ });
551
+ }, [stateProvider]);
552
+ const setComposedInput = useCallback((nextValue) => {
553
+ const rawValue = typeof nextValue === 'function' ? nextValue(inputRef.current) : nextValue;
554
+ const reconciled = reconcileDraftImagePrompt(rawValue, draftAttachmentsRef.current);
555
+ pendingPastesRef.current = retainPendingPastes(reconciled.prompt, pendingPastesRef.current);
556
+ inputRef.current = reconciled.prompt;
557
+ draftAttachmentsRef.current = reconciled.attachments;
558
+ setDraftAttachments(reconciled.attachments);
559
+ setInput(reconciled.prompt);
560
+ }, []);
561
+ useEffect(() => {
562
+ runtime.applyConfiguration(config, { profile, approvalPolicy, capabilityProfile });
563
+ convManager.updateConfig(config);
564
+ }, [runtime, convManager, config, profile, approvalPolicy, capabilityProfile]);
565
+ const applyGlobalConfiguration = useCallback((snapshot) => {
566
+ const fingerprint = globalConfigurationFingerprint(snapshot);
567
+ if (fingerprint === globalConfigurationFingerprintRef.current)
568
+ return;
569
+ globalConfigurationFingerprintRef.current = fingerprint;
570
+ pendingGlobalConfigurationRef.current = null;
571
+ runtime.applyConfiguration(snapshot.config, { profile: snapshot.profile, approvalPolicy, capabilityProfile });
572
+ convManager.updateConfig(snapshot.config);
573
+ setConfig(snapshot.config);
574
+ setProfile(snapshot.profile);
575
+ setGitState(engine.getGitState());
576
+ const nextT = createTranslator(snapshot.profile.interfaceLanguage);
577
+ appendMessages([{
578
+ id: genMsgId(),
579
+ role: 'system',
580
+ content: nextT('ui.app.globalReloaded', {
581
+ provider: snapshot.config.provider,
582
+ model: snapshot.config.model || nextT('common.notSet'),
583
+ persona: snapshot.profile.defaultPersonaId,
584
+ }),
585
+ }], { forceLatest: true });
586
+ }, [runtime, approvalPolicy, capabilityProfile, convManager, engine, appendMessages, genMsgId]);
587
+ useEffect(() => {
588
+ const accept = (snapshot) => {
589
+ const fingerprint = globalConfigurationFingerprint(snapshot);
590
+ if (fingerprint === globalConfigurationFingerprintRef.current)
591
+ return;
592
+ if (engine.isRunning()) {
593
+ pendingGlobalConfigurationRef.current = snapshot;
594
+ showRunControlHint(t('ui.app.globalPending'));
595
+ return;
596
+ }
597
+ applyGlobalConfiguration(snapshot);
598
+ };
599
+ const stopWatching = watchGlobalConfiguration(accept, {
600
+ onError: error => showRunControlHint(t('ui.app.globalReloadFailed', { message: error.message })),
601
+ });
602
+ const pendingTimer = setInterval(() => {
603
+ const pending = pendingGlobalConfigurationRef.current;
604
+ if (pending && !engine.isRunning())
605
+ applyGlobalConfiguration(pending);
606
+ }, 250);
607
+ return () => {
608
+ stopWatching();
609
+ clearInterval(pendingTimer);
610
+ };
611
+ }, [engine, applyGlobalConfiguration, showRunControlHint, t]);
612
+ const loadModelPresets = useCallback(async (targetConfig, force = false) => {
613
+ const requestId = ++modelDiscoveryRequestRef.current;
614
+ setModelDiscoveryStatus(current => ({ ...current, isRefreshing: true, error: force ? undefined : current.error }));
615
+ const result = await discoverModelPresets(targetConfig, { force });
616
+ if (requestId !== modelDiscoveryRequestRef.current)
617
+ return;
618
+ setModelPresets(result.models);
619
+ setModelDiscoveryStatus({ isRefreshing: false, stale: result.stale, error: result.error });
620
+ const firstDiscovered = selectAutoMountedModel(targetConfig.model, result.source, result.models);
621
+ if (!targetConfig.model && firstDiscovered) {
622
+ persistConfig(applyPreset(targetConfig, firstDiscovered));
623
+ showRunControlHint(t('ui.app.modelMounted', { model: firstDiscovered.model }));
624
+ }
625
+ }, [persistConfig, showRunControlHint, t]);
626
+ useEffect(() => {
627
+ const cached = readCachedModelDiscovery(config, true);
628
+ if (cached) {
629
+ setModelPresets(cached.models);
630
+ setModelDiscoveryStatus({ isRefreshing: cached.stale, stale: cached.stale, error: undefined });
631
+ }
632
+ void loadModelPresets(config);
633
+ return () => { modelDiscoveryRequestRef.current += 1; };
634
+ }, [config.activeApiConfigId, config.apiKey, config.baseUrl, config.provider, loadModelPresets]);
635
+ useEffect(() => {
636
+ engine.setEventRecorder(event => convManager.recordEvent(event));
637
+ const unsub = engine.subscribe((event) => {
638
+ flowBridge.handle(event);
639
+ switch (event.type) {
640
+ case 'run:state':
641
+ setLastActivity(event.state.updatedAt);
642
+ if (event.state.phase === 'awaiting_approval' || event.state.phase === 'awaiting_input')
643
+ setMood('thinking');
644
+ break;
645
+ case 'input:state':
646
+ if (event.state === 'committed') {
647
+ appendMessages([{ id: event.inputId, role: 'user', content: event.text }], { forceLatest: true });
648
+ setLastActivity(Date.now());
649
+ }
650
+ else if (event.state === 'rejected') {
651
+ replaceMessages(previous => previous.filter(message => message.id !== event.inputId));
652
+ setComposedInput(current => current.trim()
653
+ ? `${current}\n\n${event.text}`
654
+ : event.text);
655
+ showRunControlHint(event.reason || t('ui.app.guidanceRestored'));
656
+ }
657
+ break;
658
+ case 'turn:complete': {
659
+ if (event.turn.role !== 'assistant')
660
+ break;
661
+ const interrupted = event.turn.metadata?.interrupted === true;
662
+ lastAssistantTurnInterruptedRef.current = interrupted;
663
+ if (isProvisionalAssistantTurn(event.turn)) {
664
+ clearStreamFlushTimer();
665
+ setStreamText('');
666
+ setStreamThinkingText('');
667
+ setMood('thinking');
668
+ break;
669
+ }
670
+ const toolsSnapshot = currentToolsRef.current;
671
+ const changesSnapshot = changeSummariesRef.current;
672
+ const visibleText = stripTextToolCallMarkup(event.turn.content, { stripIncomplete: true });
673
+ const thinking = event.turn.metadata?.thinking
674
+ ? {
675
+ ...event.turn.metadata.thinking,
676
+ ...(event.turn.metadata.reasoningEffort ? { effort: event.turn.metadata.reasoningEffort } : {}),
677
+ }
678
+ : undefined;
679
+ if (visibleText || toolsSnapshot.length > 0 || changesSnapshot.length > 0 || thinking) {
680
+ appendMessages([{
681
+ id: event.turn.id,
682
+ role: 'assistant',
683
+ content: visibleText,
684
+ tools: [...toolsSnapshot],
685
+ changes: [...changesSnapshot],
686
+ interrupted,
687
+ thinking,
688
+ }], { forceLatest: true });
689
+ }
690
+ updateCurrentTools(() => []);
691
+ updateChangeSummaries(() => []);
692
+ setMood(interrupted ? 'idle' : 'thinking');
693
+ break;
694
+ }
695
+ case 'stream:start': {
696
+ const streamStartedAt = Date.now();
697
+ setCurrentTurnOutputTokens(0);
698
+ streamBufferRef.current = '';
699
+ streamThinkingBufferRef.current = '';
700
+ streamThinkingStartedAtRef.current = streamStartedAt;
701
+ setStreamThinkingStartedAt(streamStartedAt);
702
+ setStreamThinkingText('');
703
+ setStreamText('');
704
+ clearStreamFlushTimer();
705
+ break;
706
+ }
707
+ case 'stream:delta':
708
+ if (activePromptRef.current)
709
+ activePromptRef.current.responseStarted = true;
710
+ streamBufferRef.current += event.text;
711
+ terminalLatencyTracker.noteDeltaReceived();
712
+ if (flowFeatures.streamScheduler) {
713
+ streamScheduler.enqueue(Buffer.byteLength(event.text, 'utf8'));
714
+ }
715
+ else {
716
+ setStreamText(streamBufferRef.current);
717
+ setCurrentTurnOutputTokens(previous => Math.max(previous, estimateOutputTokensForDisplay(streamBufferRef.current)));
718
+ }
719
+ markActivity();
720
+ break;
721
+ case 'stream:thinking_delta':
722
+ if (activePromptRef.current)
723
+ activePromptRef.current.responseStarted = true;
724
+ if (!streamThinkingStartedAtRef.current) {
725
+ const thinkingStartedAt = Date.now();
726
+ streamThinkingStartedAtRef.current = thinkingStartedAt;
727
+ setStreamThinkingStartedAt(thinkingStartedAt);
728
+ }
729
+ streamThinkingBufferRef.current += event.text;
730
+ terminalLatencyTracker.noteDeltaReceived();
731
+ if (flowFeatures.streamScheduler) {
732
+ streamScheduler.enqueue(Buffer.byteLength(event.text, 'utf8'));
733
+ }
734
+ else {
735
+ setStreamThinkingText(streamThinkingBufferRef.current);
736
+ }
737
+ markActivity();
738
+ break;
739
+ case 'stream:usage':
740
+ if (typeof event.usage.output === 'number') {
741
+ setCurrentTurnOutputTokens(previous => Math.max(previous, event.usage.output ?? 0));
742
+ }
743
+ break;
744
+ case 'stream:end': {
745
+ clearStreamFlushTimer();
746
+ const bufferedStreamText = streamBufferRef.current;
747
+ const bufferedThinkingText = streamThinkingBufferRef.current;
748
+ const thinkingStartedAt = streamThinkingStartedAtRef.current;
749
+ streamBufferRef.current = '';
750
+ streamThinkingBufferRef.current = '';
751
+ streamThinkingStartedAtRef.current = undefined;
752
+ setStreamThinkingStartedAt(undefined);
753
+ const display = resolveAssistantStreamDisplay(stripTextToolCallMarkup(bufferedStreamText, { stripIncomplete: true }), bufferedThinkingText, currentToolsRef.current.length > 0 || changeSummariesRef.current.length > 0, event.interrupted === true);
754
+ void thinkingStartedAt;
755
+ if (display.visibleText || display.thinkingText) {
756
+ setStreamText(display.visibleText);
757
+ setStreamThinkingText(display.thinkingText);
758
+ }
759
+ if (noFlickerActive) {
760
+ setStreamText('');
761
+ setStreamThinkingText('');
762
+ }
763
+ else {
764
+ streamTransitionTimerRef.current = setTimeout(() => {
765
+ streamTransitionTimerRef.current = null;
766
+ setStreamText('');
767
+ setStreamThinkingText('');
768
+ }, 120);
769
+ }
770
+ setCurrentTurnOutputTokens(0);
771
+ setMood(event.interrupted ? 'idle' : 'thinking');
772
+ flowBridge.updateUsage(engine.getContextUsage());
773
+ break;
774
+ }
775
+ case 'session:complete': {
776
+ const interrupted = lastAssistantTurnInterruptedRef.current || abortingRef.current;
777
+ setMood(interrupted ? 'idle' : 'happy');
778
+ if (!interrupted) {
779
+ notificationCoordinator.acknowledgeCategory('turn-complete');
780
+ notificationCoordinator.raise({
781
+ id: `turn-complete:${Date.now()}`,
782
+ category: 'turn-complete',
783
+ title: t('ui.app.agentTurnComplete'),
784
+ sourceId: 'foreground-run',
785
+ });
786
+ syncNotificationSnapshot();
787
+ setTimeout(() => setMood('idle'), 3000);
788
+ }
789
+ break;
790
+ }
791
+ case 'tool:call':
792
+ if (activePromptRef.current)
793
+ activePromptRef.current.responseStarted = true;
794
+ updateCurrentTools(previous => beginToolCall(previous, {
795
+ id: event.toolCall.id,
796
+ name: event.toolCall.name,
797
+ args: serializeToolArgsForUi(event.toolCall.arguments),
798
+ startedAt: Date.now(),
799
+ }));
800
+ markActivity();
801
+ break;
802
+ case 'stream:tool_call_delta':
803
+ if (activePromptRef.current)
804
+ activePromptRef.current.responseStarted = true;
805
+ markActivity();
806
+ break;
807
+ case 'tool:result':
808
+ updateCurrentTools(previous => settleToolCall(previous, {
809
+ id: event.toolResult.toolCallId,
810
+ name: event.toolResult.name,
811
+ status: event.toolResult.isError ? 'error' : 'done',
812
+ output: event.toolResult.output?.slice(0, 200),
813
+ settledAt: Date.now(),
814
+ }));
815
+ if (event.toolResult.changeSummary) {
816
+ updateChangeSummaries(previous => [...previous, event.toolResult.changeSummary]);
817
+ }
818
+ markActivity();
819
+ break;
820
+ case 'subagent:start':
821
+ setSubAgentActivities(current => [
822
+ ...current.filter(activity => activity.id !== event.agentId),
823
+ {
824
+ id: event.agentId,
825
+ label: event.label,
826
+ objective: event.objective,
827
+ detail: t('ui.subagent.starting'),
828
+ startedAt: Date.now(),
829
+ status: 'running',
830
+ },
831
+ ]);
832
+ break;
833
+ case 'subagent:progress':
834
+ setSubAgentActivities(current => current.map(activity => activity.id === event.agentId
835
+ ? { ...activity, detail: describeSubAgentEvent(event.event, t), status: 'running' }
836
+ : activity));
837
+ break;
838
+ case 'subagent:end':
839
+ setSubAgentActivities(current => current.map(activity => activity.id === event.agentId
840
+ ? {
841
+ ...activity,
842
+ status: event.ok ? 'completed' : 'failed',
843
+ completedAt: Date.now(),
844
+ detail: event.ok ? t('ui.subagent.resultReady') : t('common.failed'),
845
+ }
846
+ : activity));
847
+ notificationCoordinator.raise({
848
+ id: `subagent-result:${event.agentId}`,
849
+ category: event.ok ? 'result-ready' : 'error',
850
+ title: event.ok
851
+ ? t('ui.app.subagentResultReady', { agent: event.agentType })
852
+ : t('ui.app.subagentFailed', { agent: event.agentType }),
853
+ sourceId: event.agentId,
854
+ });
855
+ syncNotificationSnapshot();
856
+ break;
857
+ case 'active:task':
858
+ break;
859
+ case 'terminal:sessions':
860
+ setTerminalSessions(event.sessions);
861
+ break;
862
+ case 'git:state':
863
+ setGitState(event.state);
864
+ break;
865
+ case 'runtime-task:finished': {
866
+ const sessionId = event.task.metadata?.sessionId;
867
+ if (event.task.kind === 'terminal' && typeof sessionId === 'string') {
868
+ setTerminalSessions(current => current.map(session => session.id === sessionId
869
+ ? {
870
+ ...session,
871
+ status: event.task.status === 'failed' ? 'error' : 'exited',
872
+ exitCode: event.task.exitCode,
873
+ error: event.task.error,
874
+ updatedAt: event.task.updatedAt,
875
+ }
876
+ : session));
877
+ const durationMs = (event.task.endedAt || event.task.updatedAt) - event.task.startedAt;
878
+ const duration = formatElapsed(durationMs);
879
+ const exit = typeof event.task.exitCode === 'number' ? t('ui.app.exitCode', { code: event.task.exitCode }) : '';
880
+ const log = event.task.logPath ? t('ui.app.logPath', { path: event.task.logPath }) : '';
881
+ appendMessages([{
882
+ id: genMsgId(),
883
+ role: 'system',
884
+ content: t('ui.app.backgroundFinished', {
885
+ session: sessionId,
886
+ status: event.task.status,
887
+ duration,
888
+ exit,
889
+ command: event.task.command || t('ui.app.shellSession'),
890
+ log,
891
+ }),
892
+ }], { forceLatest: true });
893
+ notificationCoordinator.raise({
894
+ id: `terminal-result:${sessionId}`,
895
+ category: event.task.status === 'failed' ? 'error' : 'result-ready',
896
+ title: t('ui.app.backgroundTerminalStatus', { status: event.task.status }),
897
+ detail: sessionId,
898
+ sourceId: sessionId,
899
+ });
900
+ syncNotificationSnapshot();
901
+ }
902
+ markActivity();
903
+ break;
904
+ }
905
+ case 'approval:state':
906
+ if (event.state === 'resolved' || event.state === 'cancelled') {
907
+ dismissPendingAsk(event.requestId);
908
+ }
909
+ break;
910
+ case 'ask:user':
911
+ schedulePendingAsk({
912
+ id: event.requestId || `ask-${Date.now()}`,
913
+ question: event.question,
914
+ options: event.options,
915
+ reason: event.reason,
916
+ command: event.command,
917
+ toolName: event.toolName,
918
+ path: event.path,
919
+ });
920
+ setMood('thinking');
921
+ break;
922
+ case 'context:segment_created':
923
+ convManager.scheduleSave();
924
+ markActivity();
925
+ break;
926
+ case 'notification':
927
+ notificationCoordinator.raise({
928
+ id: `engine:${event.level}:${event.message}`,
929
+ category: event.level === 'error' ? 'error' : event.level === 'warning' ? 'warning' : event.level === 'success' ? 'turn-complete' : 'info',
930
+ title: event.message,
931
+ sourceId: `${event.level}:${event.message}`,
932
+ });
933
+ syncNotificationSnapshot();
934
+ if (event.level === 'warning' || event.level === 'error') {
935
+ appendMessages([{ id: genMsgId(), role: 'system', content: event.message }]);
936
+ }
937
+ else {
938
+ showRunControlHint(event.message);
939
+ }
940
+ break;
941
+ case 'model:protocol':
942
+ if (event.phase === 'fallback') {
943
+ appendMessages([{
944
+ id: genMsgId(),
945
+ role: 'system',
946
+ content: t('ui.app.protocolFallback', {
947
+ message: event.message || t('ui.app.protocolMismatch'),
948
+ url: event.url,
949
+ }),
950
+ }], { forceLatest: true });
951
+ }
952
+ break;
953
+ case 'error':
954
+ streamBufferRef.current = '';
955
+ streamThinkingBufferRef.current = '';
956
+ streamThinkingStartedAtRef.current = undefined;
957
+ setStreamThinkingStartedAt(undefined);
958
+ clearStreamFlushTimer();
959
+ setStreamText('');
960
+ setStreamThinkingText('');
961
+ appendMessages([{ id: genMsgId(), role: 'system', content: t('common.error', { message: event.error }) }]);
962
+ notificationCoordinator.raise({
963
+ id: `run-error:${Date.now()}`,
964
+ category: 'error',
965
+ title: t('ui.app.runFailed'),
966
+ detail: event.error,
967
+ sourceId: 'foreground-run',
968
+ });
969
+ syncNotificationSnapshot();
970
+ setMood('error');
971
+ setTimeout(() => setMood('idle'), 4000);
972
+ break;
973
+ case 'mode:change':
974
+ setGitState(engine.getGitState());
975
+ break;
976
+ }
977
+ });
978
+ return () => {
979
+ clearStreamFlushTimer();
980
+ if (runControlHintTimerRef.current)
981
+ clearTimeout(runControlHintTimerRef.current);
982
+ unsub();
983
+ void runtime.destroy().catch(() => { }).finally(() => {
984
+ engine.setEventRecorder(null);
985
+ convManager.destroy();
986
+ });
987
+ };
988
+ }, [engine, runtime, convManager, flowBridge, clearStreamFlushTimer, appendMessages, replaceMessages, setComposedInput, markActivity, showRunControlHint, genMsgId, noFlickerActive, t, dismissPendingAsk, schedulePendingAsk, notificationCoordinator, syncNotificationSnapshot, streamScheduler, terminalLatencyTracker, flowFeatures.streamScheduler]);
989
+ const loadConversationEntries = useCallback(async () => {
990
+ const convs = await convManager.listAsync();
991
+ const currentId = convManager.getCurrentId();
992
+ return convs.map(c => ({
993
+ id: c.id,
994
+ title: c.title || c.id.slice(0, 12),
995
+ turnCount: c.turnCount,
996
+ updatedAt: c.updatedAt,
997
+ isCurrent: c.id === currentId,
998
+ }));
999
+ }, [convManager]);
1000
+ const restoreInteractionState = useCallback((state) => {
1001
+ const recoveredSteering = (state?.pendingSteering || []).map(pending => ({
1002
+ id: pending.id,
1003
+ prompt: pending.text,
1004
+ }));
1005
+ const recoveredQueue = [...recoveredSteering, ...(state?.queuedInputs || [])];
1006
+ flowBridge.replaceQueue(recoveredQueue);
1007
+ const draftText = state?.draft?.text ?? '';
1008
+ const draftStateAttachments = state?.draft?.attachments ?? [];
1009
+ inputRef.current = draftText;
1010
+ pendingPastesRef.current = retainPendingPastes(draftText, state?.draft?.pendingPastes ?? []);
1011
+ setInput(draftText);
1012
+ draftAttachmentsRef.current = draftStateAttachments;
1013
+ setDraftAttachments(draftStateAttachments);
1014
+ const recoveredApprovalCount = state?.pendingApprovals?.length ?? 0;
1015
+ if (recoveredApprovalCount > 0) {
1016
+ appendMessages([{
1017
+ id: genMsgId(),
1018
+ role: 'system',
1019
+ content: t('ui.app.recoveredApprovals', { count: recoveredApprovalCount }),
1020
+ }], { forceLatest: true });
1021
+ }
1022
+ }, [appendMessages, flowBridge, genMsgId, t]);
1023
+ const reportGlobalCommandError = useCallback((command, error) => {
1024
+ showRunControlHint(t('ui.app.commandFailed', {
1025
+ command,
1026
+ message: error instanceof Error ? error.message : String(error),
1027
+ }));
1028
+ }, [showRunControlHint, t]);
1029
+ const openConversationHistory = useCallback(async () => {
1030
+ try {
1031
+ await globalCommandActivityController.run('/resume', t('ui.app.loadingConversations'), async () => {
1032
+ setConversationEntries(await loadConversationEntries());
1033
+ setConvListRevision(revision => revision + 1);
1034
+ push('history');
1035
+ });
1036
+ }
1037
+ catch (error) {
1038
+ reportGlobalCommandError('/resume', error);
1039
+ }
1040
+ }, [globalCommandActivityController, loadConversationEntries, push, reportGlobalCommandError, t]);
1041
+ const selectConversation = useCallback(async (id) => {
1042
+ pop();
1043
+ try {
1044
+ await globalCommandActivityController.run('/resume', t('ui.app.restoringConversation'), async () => {
1045
+ const conv = await convManager.switchToAsync(id);
1046
+ if (!conv)
1047
+ throw new Error(t('ui.app.conversationUnavailable'));
1048
+ restoreCliStateFromTurns(conv.activeTurns ?? conv.turns, '', conv.contextSegments ?? [], conv.contextReservoir ?? [], conv.turns);
1049
+ restoreInteractionState(conv.interactionState);
1050
+ });
1051
+ }
1052
+ catch (error) {
1053
+ reportGlobalCommandError('/resume', error);
1054
+ }
1055
+ }, [convManager, globalCommandActivityController, pop, reportGlobalCommandError, restoreCliStateFromTurns, restoreInteractionState, t]);
1056
+ const deleteSavedConversation = useCallback(async (id) => {
1057
+ try {
1058
+ await globalCommandActivityController.run('/resume', t('ui.app.deletingConversation'), async () => {
1059
+ if (!await convManager.deleteAsync(id))
1060
+ throw new Error(t('ui.app.conversationUnavailable'));
1061
+ setConversationEntries(await loadConversationEntries());
1062
+ setConvListRevision(revision => revision + 1);
1063
+ });
1064
+ }
1065
+ catch (error) {
1066
+ reportGlobalCommandError('/resume', error);
1067
+ }
1068
+ }, [convManager, globalCommandActivityController, loadConversationEntries, reportGlobalCommandError, t]);
1069
+ useEffect(() => {
1070
+ if (singleShot)
1071
+ runPrompt(singleShot);
1072
+ }, []);
1073
+ const transcriptRowBudget = useMemo(() => {
1074
+ if (!noFlickerActive)
1075
+ return Number.MAX_SAFE_INTEGER;
1076
+ return Math.max(4, terminal.rows - 5);
1077
+ }, [noFlickerActive, terminal.rows]);
1078
+ const normalizedScrollRows = noFlickerActive
1079
+ ? clampTranscriptScroll(scrollRowsFromBottom, transcriptMetrics.maxScrollRows)
1080
+ : 0;
1081
+ const pageStep = getTranscriptPageRows(transcriptMetrics.viewportRows > 1 ? transcriptMetrics.viewportRows : transcriptRowBudget);
1082
+ const isViewingHistory = normalizedScrollRows > 0;
1083
+ const selectedMessageId = cursorMode && cursor ? messages[cursor.index]?.id : undefined;
1084
+ const cockpit = resolveCockpitLayout(terminal.columns);
1085
+ const handleTranscriptMetrics = useCallback((metrics) => {
1086
+ transcriptMetricsRef.current = metrics;
1087
+ setTranscriptMetrics(previous => {
1088
+ if (previous.contentRows === metrics.contentRows &&
1089
+ previous.viewportRows === metrics.viewportRows &&
1090
+ previous.maxScrollRows === metrics.maxScrollRows) {
1091
+ return previous;
1092
+ }
1093
+ return metrics;
1094
+ });
1095
+ }, []);
1096
+ const recordTranscriptWindowMetrics = useCallback((metrics) => {
1097
+ flowTelemetry.observe('ui.transcript_mounted_cells', metrics.mountedCells);
1098
+ flowTelemetry.observe('ui.transcript_total_cells', metrics.totalCells);
1099
+ }, [flowTelemetry]);
1100
+ const scrollTranscriptBy = useCallback((delta) => {
1101
+ setScrollRowsFromBottom(rows => clampTranscriptScroll(rows + delta, transcriptMetricsRef.current.maxScrollRows));
1102
+ }, []);
1103
+ useEffect(() => {
1104
+ if (!noFlickerActive || !cursorMode || !cursor || !selectedMessageMetrics.hasMeasured)
1105
+ return;
1106
+ setScrollRowsFromBottom(rows => revealTranscriptRange(rows, transcriptMetrics.maxScrollRows, transcriptMetrics.viewportRows, selectedMessageMetrics.top, selectedMessageMetrics.height));
1107
+ }, [
1108
+ noFlickerActive,
1109
+ cursorMode,
1110
+ cursor?.index,
1111
+ selectedMessageMetrics.hasMeasured,
1112
+ selectedMessageMetrics.top,
1113
+ selectedMessageMetrics.height,
1114
+ transcriptMetrics.maxScrollRows,
1115
+ transcriptMetrics.viewportRows,
1116
+ ]);
1117
+ const runNextQueuedPrompt = useCallback(() => {
1118
+ if (flowBridge.isForegroundBusy() || engine.isRunning() || runPromptRef.current === null)
1119
+ return;
1120
+ if (!convManager.isPersistenceHealthy()) {
1121
+ showRunControlHint(t('ui.app.persistenceBlocked'));
1122
+ return;
1123
+ }
1124
+ const next = flowBridge.takeNextQueuedInput();
1125
+ if (!next)
1126
+ return;
1127
+ void runPromptRef.current(next.prompt, next.attachments, next.id);
1128
+ }, [convManager, engine, flowBridge, showRunControlHint, t]);
1129
+ const runPrompt = useCallback(async (prompt, attachments, queuedMessageId) => {
1130
+ if (!convManager.isPersistenceHealthy()) {
1131
+ showRunControlHint(t('ui.app.persistenceBlocked'));
1132
+ return;
1133
+ }
1134
+ if (flowBridge.isForegroundBusy() || engine.isRunning()) {
1135
+ flowBridge.enqueueInput({ id: queuedMessageId ?? genMsgId(), prompt, attachments });
1136
+ showRunControlHint(t('ui.flow.input.queued', { count: flowBridge.getQueuedInputs().length }));
1137
+ return;
1138
+ }
1139
+ const userMessageId = queuedMessageId ?? genMsgId();
1140
+ lastAssistantTurnInterruptedRef.current = false;
1141
+ activePromptRef.current = { prompt, attachments, messageId: userMessageId, responseStarted: false, priorTurns: [...engine.getSession().turns] };
1142
+ abortingRef.current = false;
1143
+ abortRestoredPromptRef.current = false;
1144
+ appendMessages([{ id: userMessageId, role: 'user', content: prompt }], { forceLatest: true });
1145
+ if (!config.apiKey) {
1146
+ activePromptRef.current = null;
1147
+ appendMessages([{ id: genMsgId(), role: 'system', content: t('ui.app.noProvider') }]);
1148
+ if (singleShot)
1149
+ exit();
1150
+ return;
1151
+ }
1152
+ if (!config.model) {
1153
+ activePromptRef.current = null;
1154
+ appendMessages([{
1155
+ id: genMsgId(),
1156
+ role: 'system',
1157
+ content: modelDiscoveryStatus.isRefreshing
1158
+ ? t('ui.app.modelDiscoveryRunning')
1159
+ : t('ui.app.noModelMounted'),
1160
+ }]);
1161
+ return;
1162
+ }
1163
+ flowBridge.startRun(prompt);
1164
+ setMood('thinking');
1165
+ streamBufferRef.current = '';
1166
+ streamThinkingBufferRef.current = '';
1167
+ streamThinkingStartedAtRef.current = undefined;
1168
+ setStreamThinkingStartedAt(undefined);
1169
+ clearStreamFlushTimer();
1170
+ setStreamText('');
1171
+ setStreamThinkingText('');
1172
+ updateCurrentTools(() => []);
1173
+ updateChangeSummaries(() => []);
1174
+ dismissPendingAsk();
1175
+ notificationCoordinator.acknowledgeCategory('turn-complete');
1176
+ notificationCoordinator.acknowledgeSource('error', 'foreground-run');
1177
+ syncNotificationSnapshot();
1178
+ setInterruptHint(null);
1179
+ setExitHint(null);
1180
+ setLastActivity(Date.now());
1181
+ let runOutcome = 'failed';
1182
+ let runError;
1183
+ try {
1184
+ const turns = await engine.run(prompt, { attachments, userTurnId: userMessageId });
1185
+ runOutcome = 'succeeded';
1186
+ if (singleShot) {
1187
+ const finalAssistantTurn = [...turns].reverse().find(turn => turn.role === 'assistant' && turn.content.trim());
1188
+ const finalText = finalAssistantTurn
1189
+ ? stripTextToolCallMarkup(finalAssistantTurn.content, { stripIncomplete: true }).trim()
1190
+ : '';
1191
+ if (finalText) {
1192
+ process.stdout.write(`\n${formatMarkdown(finalText)}\n`);
1193
+ }
1194
+ }
1195
+ }
1196
+ catch (e) {
1197
+ const bufferedStreamText = streamBufferRef.current;
1198
+ const bufferedThinkingText = streamThinkingBufferRef.current;
1199
+ const thinkingStartedAt = streamThinkingStartedAtRef.current;
1200
+ const visibleInterruptedText = stripTextToolCallMarkup(bufferedStreamText, { stripIncomplete: true });
1201
+ const toolsSnapshot = currentToolsRef.current;
1202
+ const changesSnapshot = changeSummariesRef.current;
1203
+ const interrupted = abortingRef.current || e?.aborted === true || /aborted/i.test(String(e?.message || ''));
1204
+ runOutcome = interrupted ? 'interrupted' : 'failed';
1205
+ runError = interrupted ? undefined : String(e?.message || e);
1206
+ streamBufferRef.current = '';
1207
+ streamThinkingBufferRef.current = '';
1208
+ streamThinkingStartedAtRef.current = undefined;
1209
+ setStreamThinkingStartedAt(undefined);
1210
+ clearStreamFlushTimer();
1211
+ setStreamText('');
1212
+ setStreamThinkingText('');
1213
+ if (abortRestoredPromptRef.current) {
1214
+ }
1215
+ else if (interrupted && (visibleInterruptedText || bufferedThinkingText || toolsSnapshot.length > 0 || changesSnapshot.length > 0)) {
1216
+ appendMessages([{
1217
+ id: genMsgId(),
1218
+ role: 'assistant',
1219
+ content: visibleInterruptedText,
1220
+ tools: [...toolsSnapshot],
1221
+ changes: [...changesSnapshot],
1222
+ interrupted: true,
1223
+ thinking: createThinkingTrace(bufferedThinkingText, thinkingStartedAt, true),
1224
+ }]);
1225
+ }
1226
+ else if (interrupted) {
1227
+ appendMessages([{ id: genMsgId(), role: 'system', content: t('common.interrupted') }]);
1228
+ }
1229
+ else {
1230
+ appendMessages([{ id: genMsgId(), role: 'system', content: t('common.error', { message: e.message }) }]);
1231
+ }
1232
+ updateCurrentTools(() => []);
1233
+ updateChangeSummaries(() => []);
1234
+ setMood(abortingRef.current ? 'idle' : 'error');
1235
+ if (!abortingRef.current)
1236
+ setTimeout(() => setMood('idle'), 4000);
1237
+ }
1238
+ finally {
1239
+ activePromptRef.current = null;
1240
+ flowBridge.finishRun(runOutcome, runError);
1241
+ abortingRef.current = false;
1242
+ abortRestoredPromptRef.current = false;
1243
+ if (flowBridge.getQueuedInputs().length > 0)
1244
+ setTimeout(runNextQueuedPrompt, 0);
1245
+ }
1246
+ if (singleShot)
1247
+ exit();
1248
+ }, [appendMessages, engine, singleShot, config, clearStreamFlushTimer, exit, runNextQueuedPrompt, genMsgId, showRunControlHint, modelDiscoveryStatus.isRefreshing, t, dismissPendingAsk, notificationCoordinator, syncNotificationSnapshot, convManager, flowBridge]);
1249
+ useEffect(() => {
1250
+ runPromptRef.current = runPrompt;
1251
+ }, [runPrompt]);
1252
+ useEffect(() => {
1253
+ if (isRunning || queuedPrompts.length === 0 || !convManager.isPersistenceHealthy())
1254
+ return;
1255
+ const timer = setTimeout(runNextQueuedPrompt, 0);
1256
+ return () => clearTimeout(timer);
1257
+ }, [convManager, isRunning, persistenceWarning, queuedPrompts.length, runNextQueuedPrompt]);
1258
+ const submitAskResponse = useCallback((response) => {
1259
+ const trimmed = response.trim();
1260
+ if (!trimmed)
1261
+ return;
1262
+ appendMessages([{ id: genMsgId(), role: 'user', content: trimmed }], { forceLatest: true });
1263
+ const requestId = pendingAskRef.current?.id;
1264
+ engine.submitAskUserResponse(trimmed, requestId);
1265
+ dismissPendingAsk(requestId);
1266
+ setMood('thinking');
1267
+ setLastActivity(Date.now());
1268
+ }, [appendMessages, engine, genMsgId, dismissPendingAsk]);
1269
+ const submitPermissionDecision = useCallback((requestId, decision) => {
1270
+ engine.submitAskUserResponse(decision, requestId);
1271
+ dismissPendingAsk(requestId);
1272
+ setMood('thinking');
1273
+ setLastActivity(Date.now());
1274
+ }, [engine, dismissPendingAsk]);
1275
+ const isPermissionAsk = pendingAsk?.options?.includes('allow-once') ?? false;
1276
+ const attachClipboardImage = useCallback((options) => {
1277
+ const nextIndex = draftAttachmentsRef.current.length + 1;
1278
+ const warnings = [];
1279
+ const attachment = captureClipboardImageAttachment(nextIndex, warnings, workspacePath, t);
1280
+ if (!attachment) {
1281
+ if (!options?.silentNoImage) {
1282
+ const visibleWarnings = warnings.length > 0 ? warnings : [t('ui.app.clipboardImageMissing')];
1283
+ for (const warning of visibleWarnings) {
1284
+ appendMessages([{ id: genMsgId(), role: 'system', content: warning }]);
1285
+ }
1286
+ }
1287
+ return false;
1288
+ }
1289
+ const fingerprint = imageAttachmentFingerprint(attachment);
1290
+ const lastClipboardImage = lastClipboardImageRef.current;
1291
+ if (fingerprint && lastClipboardImage?.fingerprint === fingerprint && Date.now() - lastClipboardImage.at < 1500)
1292
+ return false;
1293
+ if (fingerprint)
1294
+ lastClipboardImageRef.current = { fingerprint, at: Date.now() };
1295
+ const placeholder = imagePlaceholderForIndex(nextIndex);
1296
+ const nextAttachments = [...draftAttachmentsRef.current, { ...attachment, id: `image${nextIndex}` }];
1297
+ draftAttachmentsRef.current = nextAttachments;
1298
+ setDraftAttachments(nextAttachments);
1299
+ setComposedInput(current => {
1300
+ const spacer = current && !/\s$/.test(current) ? ' ' : '';
1301
+ return `${current}${spacer}${placeholder} `;
1302
+ });
1303
+ return true;
1304
+ }, [appendMessages, genMsgId, setComposedInput, t, workspacePath]);
1305
+ const handlePasteImage = useCallback(() => {
1306
+ const attached = attachClipboardImage();
1307
+ if (attached)
1308
+ terminalLatencyTracker.noteKeyReceived();
1309
+ return attached;
1310
+ }, [attachClipboardImage, terminalLatencyTracker]);
1311
+ const handlePasteText = useCallback((pastedText, nextValue, insertionStart) => {
1312
+ if (Array.from(pastedText).length > LARGE_PASTE_CHAR_THRESHOLD) {
1313
+ const placeholder = createPendingPastePlaceholder(pastedText, pendingPastesRef.current);
1314
+ const replacedValue = replacePastedText(nextValue, pastedText, insertionStart, placeholder);
1315
+ if (replacedValue !== nextValue) {
1316
+ pendingPastesRef.current = [...pendingPastesRef.current, { placeholder, text: pastedText }];
1317
+ return { value: replacedValue, cursorOffset: insertionStart + placeholder.length };
1318
+ }
1319
+ }
1320
+ if (!hasImageReference(pastedText))
1321
+ return null;
1322
+ const resolved = resolveImagePrompt(nextValue, workspacePath, { existingAttachments: draftAttachmentsRef.current, t });
1323
+ if (resolved.attachments.length === draftAttachmentsRef.current.length)
1324
+ return null;
1325
+ for (const warning of resolved.warnings) {
1326
+ appendMessages([{ id: genMsgId(), role: 'system', content: warning }]);
1327
+ }
1328
+ draftAttachmentsRef.current = resolved.attachments;
1329
+ setDraftAttachments(resolved.attachments);
1330
+ return { value: resolved.prompt, cursorOffset: resolved.prompt.length };
1331
+ }, [appendMessages, genMsgId, t, workspacePath]);
1332
+ const handleInterrupt = useCallback(() => {
1333
+ const pressedAt = Date.now();
1334
+ if (pressedAt - lastCtrlCEventAtRef.current < 120)
1335
+ return;
1336
+ lastCtrlCEventAtRef.current = pressedAt;
1337
+ if (flowBridge.isForegroundBusy() || engine.isRunning()) {
1338
+ const activePrompt = activePromptRef.current;
1339
+ abortingRef.current = true;
1340
+ if (activePrompt && !activePrompt.responseStarted) {
1341
+ inputRef.current = activePrompt.prompt;
1342
+ setInput(activePrompt.prompt);
1343
+ draftAttachmentsRef.current = activePrompt.attachments ?? [];
1344
+ setDraftAttachments(activePrompt.attachments ?? []);
1345
+ }
1346
+ engine.abort();
1347
+ dismissPendingAsk();
1348
+ setInterruptHint(t('ui.app.runInterrupted'));
1349
+ setTimeout(() => setInterruptHint(null), 2500);
1350
+ if (activePrompt && !activePrompt.responseStarted) {
1351
+ engine.restoreFromTurns(activePrompt.priorTurns);
1352
+ replaceMessages(prev => prev.filter(message => message.id !== activePrompt.messageId));
1353
+ abortRestoredPromptRef.current = true;
1354
+ }
1355
+ return;
1356
+ }
1357
+ if (pressedAt - exitPressRef.current < 1800) {
1358
+ exit();
1359
+ return;
1360
+ }
1361
+ exitPressRef.current = pressedAt;
1362
+ setExitHint(t('ui.app.exitHint'));
1363
+ setTimeout(() => {
1364
+ if (Date.now() - exitPressRef.current >= 1800)
1365
+ setExitHint(null);
1366
+ }, 1800);
1367
+ }, [engine, exit, replaceMessages, dismissPendingAsk, flowBridge]);
1368
+ useEffect(() => {
1369
+ handleInterruptRef.current = handleInterrupt;
1370
+ }, [handleInterrupt]);
1371
+ useEffect(() => {
1372
+ if (!isInteractive || singleShot)
1373
+ return;
1374
+ const onSigint = () => {
1375
+ handleInterruptRef.current();
1376
+ };
1377
+ process.on('SIGINT', onSigint);
1378
+ return () => {
1379
+ process.off('SIGINT', onSigint);
1380
+ };
1381
+ }, [isInteractive, singleShot]);
1382
+ const executeRegisteredCommand = useCallback(async (input, ctx) => {
1383
+ const progress = commandRegistry.getProgress(input);
1384
+ const command = `/${progress?.name ?? commandRegistry.parse(input)?.name ?? input.replace(/^\//, '')}`;
1385
+ try {
1386
+ const execute = () => commandRegistry.executeAsync(input, ctx);
1387
+ const result = progress
1388
+ ? await globalCommandActivityController.run(command, t('ui.app.executingCommand'), execute)
1389
+ : await execute();
1390
+ flowBridge.updateUsage(engine.getContextUsage());
1391
+ setGitState(engine.getGitState());
1392
+ switch (result.type) {
1393
+ case 'text':
1394
+ appendMessages([{ id: genMsgId(), role: 'system', content: result.text }]);
1395
+ break;
1396
+ case 'prompt':
1397
+ void runPrompt(result.prompt);
1398
+ break;
1399
+ case 'jsx':
1400
+ case 'none':
1401
+ break;
1402
+ }
1403
+ }
1404
+ catch (error) {
1405
+ reportGlobalCommandError(command, error);
1406
+ }
1407
+ }, [appendMessages, engine, flowBridge, genMsgId, globalCommandActivityController, reportGlobalCommandError, runPrompt, t]);
1408
+ const handleSubmit = useCallback((value) => {
1409
+ const submittedValue = expandPendingPastes(value, pendingPastesRef.current);
1410
+ const trimmed = submittedValue.trim();
1411
+ if (!trimmed)
1412
+ return submittedValue;
1413
+ terminalLatencyTracker.noteSubmit();
1414
+ const isCommand = commandRegistry.isCommand(trimmed);
1415
+ const recoveryCommand = isPersistenceRecoveryCommand(trimmed);
1416
+ if (!convManager.isPersistenceHealthy() && !recoveryCommand) {
1417
+ showRunControlHint(t('ui.app.persistenceBlocked'));
1418
+ return submittedValue;
1419
+ }
1420
+ const pendingDraftAttachments = draftAttachmentsRef.current;
1421
+ pendingPastesRef.current = [];
1422
+ inputRef.current = '';
1423
+ setInput('');
1424
+ draftAttachmentsRef.current = [];
1425
+ setDraftAttachments([]);
1426
+ if (isCommand && (flowBridge.isForegroundBusy() || engine.isRunning()) && !recoveryCommand) {
1427
+ runPrompt(trimmed, pendingDraftAttachments);
1428
+ return submittedValue;
1429
+ }
1430
+ if (flowBridge.isForegroundBusy() || engine.isRunning()) {
1431
+ const steeringMessageId = genMsgId();
1432
+ if (pendingDraftAttachments.length === 0 && engine.submitSteeringMessage(trimmed, steeringMessageId)) {
1433
+ setLastActivity(Date.now());
1434
+ return submittedValue;
1435
+ }
1436
+ runPrompt(trimmed, pendingDraftAttachments);
1437
+ return submittedValue;
1438
+ }
1439
+ if (isCommand) {
1440
+ if (trimmed === '/model') {
1441
+ push('modelPicker');
1442
+ return submittedValue;
1443
+ }
1444
+ if (trimmed === '/effort') {
1445
+ const capability = getModelReasoningCapabilities(config.model, config.provider, config.modelCapabilities);
1446
+ const adjustable = capability && capability.control !== 'fixed'
1447
+ && (capability.efforts.length > 0 || capability.supportsToggle || capability.control === 'budget');
1448
+ if (adjustable) {
1449
+ push('effortPicker');
1450
+ return submittedValue;
1451
+ }
1452
+ }
1453
+ if (trimmed === '/resume') {
1454
+ void openConversationHistory();
1455
+ return submittedValue;
1456
+ }
1457
+ const ctx = {
1458
+ engine,
1459
+ config,
1460
+ modelPresets,
1461
+ workspacePath,
1462
+ setConfig: persistConfig,
1463
+ setMessages: replaceMessages,
1464
+ restoreConversation: (turns, nextInput) => restoreCliStateFromTurns(turns, nextInput),
1465
+ exit,
1466
+ conversationManager: convManager,
1467
+ skillRuntime,
1468
+ mcpClient,
1469
+ runtimeTaskManager: runtime.runtimeTaskManager,
1470
+ flowFeatures,
1471
+ notificationInbox: {
1472
+ snapshot: () => notificationCoordinator.getSnapshot(),
1473
+ clearResults: clearResultInbox,
1474
+ },
1475
+ t,
1476
+ };
1477
+ void executeRegisteredCommand(trimmed, ctx);
1478
+ return submittedValue;
1479
+ }
1480
+ const resolved = resolveImagePrompt(trimmed, workspacePath, { existingAttachments: pendingDraftAttachments, t });
1481
+ for (const warning of resolved.warnings) {
1482
+ appendMessages([{ id: genMsgId(), role: 'system', content: warning }]);
1483
+ }
1484
+ runPrompt(resolved.prompt, resolved.attachments);
1485
+ return submittedValue;
1486
+ }, [appendMessages, config, convManager, engine, executeRegisteredCommand, exit, mcpClient, modelPresets, openConversationHistory, persistConfig, push, restoreCliStateFromTurns, runPrompt, runtime.runtimeTaskManager, skillRuntime, t, workspacePath, genMsgId, notificationCoordinator, clearResultInbox, terminalLatencyTracker, showRunControlHint, flowFeatures, flowBridge]);
1487
+ const handleAlternateSubmit = useCallback((value) => {
1488
+ if (!flowBridge.isForegroundBusy() && !engine.isRunning()) {
1489
+ return handleSubmit(value);
1490
+ }
1491
+ const submittedValue = expandPendingPastes(value, pendingPastesRef.current);
1492
+ const trimmed = submittedValue.trim();
1493
+ if (!trimmed)
1494
+ return submittedValue;
1495
+ terminalLatencyTracker.noteSubmit();
1496
+ if (!convManager.isPersistenceHealthy()) {
1497
+ showRunControlHint(t('ui.app.persistenceBlocked'));
1498
+ return submittedValue;
1499
+ }
1500
+ const attachments = draftAttachmentsRef.current;
1501
+ pendingPastesRef.current = [];
1502
+ inputRef.current = '';
1503
+ setInput('');
1504
+ draftAttachmentsRef.current = [];
1505
+ setDraftAttachments([]);
1506
+ runPrompt(trimmed, attachments);
1507
+ return submittedValue;
1508
+ }, [handleSubmit, runPrompt, terminalLatencyTracker, convManager, showRunControlHint, t, engine, flowBridge]);
1509
+ useInput((ch, key) => {
1510
+ if (terminalAttention.handleInput(ch)) {
1511
+ const activeNotification = notificationCoordinator.getSnapshot().active;
1512
+ if (activeNotification)
1513
+ terminalAttention.notify(activeNotification);
1514
+ return;
1515
+ }
1516
+ if (!startupFrame.complete) {
1517
+ skipStartupAnimation();
1518
+ return;
1519
+ }
1520
+ if (key.ctrl && ch === 'c') {
1521
+ handleInterrupt();
1522
+ return;
1523
+ }
1524
+ if (activeOverlay !== null)
1525
+ return;
1526
+ if (isThinkingToggleShortcut(ch, key.ctrl)) {
1527
+ setShowThinking(current => !current);
1528
+ return;
1529
+ }
1530
+ if (key.ctrl && ch.toLowerCase() === 'e') {
1531
+ setShowToolDetails(current => !current);
1532
+ return;
1533
+ }
1534
+ if (noFlickerActive && !cursorMode && !pendingAsk) {
1535
+ const mouseEvents = parseTerminalMouseWheel(ch);
1536
+ if (mouseEvents.length > 0) {
1537
+ const transcriptTop = 1;
1538
+ const transcriptBottom = terminal.rows - 5;
1539
+ const transcriptLeft = 1;
1540
+ const transcriptRight = cockpit.contentWidth;
1541
+ const delta = mouseEvents.reduce((total, event) => {
1542
+ const insideTranscript = event.x >= transcriptLeft
1543
+ && event.x <= transcriptRight
1544
+ && event.y >= transcriptTop
1545
+ && event.y <= transcriptBottom;
1546
+ if (!insideTranscript)
1547
+ return total;
1548
+ return total + (event.direction === 'up' ? DEFAULT_MOUSE_WHEEL_ROWS : -DEFAULT_MOUSE_WHEEL_ROWS);
1549
+ }, 0);
1550
+ if (delta !== 0)
1551
+ scrollTranscriptBy(delta);
1552
+ return;
1553
+ }
1554
+ }
1555
+ if (noFlickerActive && !cursorMode) {
1556
+ if (key.pageUp || (key.ctrl && key.upArrow)) {
1557
+ scrollTranscriptBy(pageStep);
1558
+ return;
1559
+ }
1560
+ if (key.pageDown || (key.ctrl && key.downArrow)) {
1561
+ scrollTranscriptBy(-pageStep);
1562
+ return;
1563
+ }
1564
+ if (key.shift && key.upArrow) {
1565
+ scrollTranscriptBy(1);
1566
+ return;
1567
+ }
1568
+ if (key.shift && key.downArrow) {
1569
+ scrollTranscriptBy(-1);
1570
+ return;
1571
+ }
1572
+ if (key.ctrl && ch.toLowerCase() === 'u') {
1573
+ scrollTranscriptBy(pageStep);
1574
+ return;
1575
+ }
1576
+ if (key.ctrl && ch.toLowerCase() === 'd') {
1577
+ scrollTranscriptBy(-pageStep);
1578
+ return;
1579
+ }
1580
+ }
1581
+ if (key.ctrl && ch === 'h') {
1582
+ void openConversationHistory();
1583
+ return;
1584
+ }
1585
+ if (cursorMode) {
1586
+ if (key.upArrow) {
1587
+ navigatePrev();
1588
+ return;
1589
+ }
1590
+ if (key.downArrow) {
1591
+ navigateNext();
1592
+ return;
1593
+ }
1594
+ if (key.escape || key.return) {
1595
+ setCursorMode(false);
1596
+ clear();
1597
+ return;
1598
+ }
1599
+ }
1600
+ if (key.ctrl && ch === 'm' && messages.length > 0) {
1601
+ setCursorMode(true);
1602
+ enter();
1603
+ }
1604
+ }, { isActive: isInteractive });
1605
+ const visibleStreamText = stripTextToolCallMarkup(streamText, { stripIncomplete: true });
1606
+ const streamTextForDisplay = visibleStreamText;
1607
+ const reasoningLabel = formatNativeReasoningSetting(config.model, config.reasoning, config.provider, config.modelCapabilities);
1608
+ const reasoningActive = Boolean(reasoningLabel && reasoningLabel !== 'off' && isRunning && runState.phase === 'thinking');
1609
+ const conversationFrameWidth = Math.max(24, cockpit.contentWidth - 2);
1610
+ const runningNode = (isRunning || subAgentActivities.length > 0 || queuedPrompts.length > 0) ? (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [!noFlickerActive && _jsx(SubAgentProgressLine, { activities: subAgentActivities }), _jsx(ActiveWorkPanel, { tools: currentTools, draft: streamingToolDraft, streamText: streamTextForDisplay, outputTokens: currentTurnOutputTokens, lastActivity: lastActivity, runState: runState, queuedCount: queuedPrompts.length, thinkingText: streamThinkingText, thinkingStartedAt: streamThinkingStartedAt, reasoningEffort: config.reasoning?.effort, reasoningActive: reasoningActive, showThinking: showThinking, verbose: verbose, idleLabel: isRunning && !visibleStreamText && currentTools.length === 0 && !pendingAsk ? t('ui.activity.phase.thinking') : null, availableWidth: noFlickerActive
1611
+ ? cockpit.contentWidth - 4
1612
+ : terminal.columns - 4 }), _jsx(QueuedPromptList, { width: noFlickerActive ? cockpit.contentWidth - 4 : terminal.columns - 4, prompts: [
1613
+ ...pendingSteeringPrompts.map(pending => ({
1614
+ id: pending.id,
1615
+ prompt: pending.prompt,
1616
+ attachmentCount: pending.attachments?.length,
1617
+ kind: 'steering',
1618
+ })),
1619
+ ...queuedPrompts.map(queued => ({
1620
+ id: queued.id,
1621
+ prompt: queued.prompt,
1622
+ attachmentCount: queued.attachments?.length,
1623
+ kind: 'queued',
1624
+ })),
1625
+ ] })] })) : null;
1626
+ const pendingAskNode = pendingAsk && askModalVisible ? (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: isPermissionAsk ? (_jsx(PermissionDialog, { toolName: pendingAsk.toolName || (pendingAsk.command ? 'run_command' : 'tool'), description: pendingAsk.reason || pendingAsk.question, command: pendingAsk.command, path: pendingAsk.path, onDecision: (decision) => submitPermissionDecision(pendingAsk.id, decision) }, pendingAsk.id)) : (_jsxs(Box, { flexDirection: "column", borderStyle: "round", paddingX: 1, marginY: 1, children: [_jsx(Text, { bold: true, children: t('ui.app.confirmationNeeded') }), _jsx(Text, { children: pendingAsk.question }), pendingAsk.reason && _jsx(Text, { dimColor: true, children: pendingAsk.reason }), pendingAsk.command && _jsx(Text, { children: pendingAsk.command }), pendingAsk.options?.length ? _jsx(Text, { dimColor: true, children: pendingAsk.options.join(' / ') }) : null, _jsx(PromptInput, { value: askInput, onChange: setAskInput, onSubmit: submitAskResponse, mode: currentMode, width: conversationFrameWidth })] })) })) : null;
1627
+ const handleRewind = useCallback((messageIndex) => {
1628
+ const targetMessage = messages[messageIndex];
1629
+ if (!targetMessage || targetMessage.role !== 'user')
1630
+ return;
1631
+ const currentTurns = engine.getFullConversationTurns();
1632
+ const engineUserOrdinal = getEngineUserOrdinalForUiMessage(messages, currentTurns, messageIndex);
1633
+ const truncatedTurns = sliceTurnsBeforeNthUserTurn(currentTurns, engineUserOrdinal);
1634
+ pop();
1635
+ restoreCliStateFromTurns(truncatedTurns, targetMessage.content, getRewindContextSegments(truncatedTurns), [], truncatedTurns);
1636
+ convManager.scheduleSave();
1637
+ }, [messages, engine, pop, restoreCliStateFromTurns, getRewindContextSegments, convManager]);
1638
+ const historyOverlay = activeOverlay === 'history' ? (_jsx(ConversationHistory, { conversations: conversationEntries, onSelect: (id) => { void selectConversation(id); }, onDelete: (id) => { void deleteSavedConversation(id); }, onCancel: () => pop() }, convListRevision)) : null;
1639
+ const rewindOverlay = activeOverlay === 'rewind' ? (_jsx(RewindSelector, { messages: messages, onRewind: handleRewind, onCancel: () => pop() })) : null;
1640
+ const modelOverlay = activeOverlay === 'modelPicker' ? (_jsx(ModelPicker, { currentModel: config.model, models: modelPresets, isRefreshing: modelDiscoveryStatus.isRefreshing, stale: modelDiscoveryStatus.stale, error: modelDiscoveryStatus.error, onRefresh: () => {
1641
+ void globalCommandActivityController.run('/model', t('ui.app.refreshingModels'), () => loadModelPresets(config, true)).catch(error => reportGlobalCommandError('/model', error));
1642
+ }, onSelect: (preset) => {
1643
+ pop();
1644
+ const newConfig = applyPreset(config, preset);
1645
+ persistConfig(newConfig);
1646
+ appendMessages([{ id: genMsgId(), role: 'system', content: t('ui.app.modelSwitched', { model: preset.model }) }]);
1647
+ }, onCancel: () => pop() })) : null;
1648
+ const effortCapability = getModelReasoningCapabilities(config.model, config.provider, config.modelCapabilities);
1649
+ const effortOverlay = activeOverlay === 'effortPicker' && effortCapability ? (_jsx(EffortPicker, { model: config.model, capability: effortCapability, current: config.reasoning, onSelect: (selection) => {
1650
+ pop();
1651
+ let newConfig = config;
1652
+ if (selection.type === 'effort') {
1653
+ newConfig = setConfigValue(newConfig, 'reasoningEnabled', 'on');
1654
+ newConfig = setConfigValue(newConfig, 'reasoningEffort', selection.effort);
1655
+ }
1656
+ else if (selection.type === 'toggle') {
1657
+ newConfig = setConfigValue(newConfig, 'reasoningEnabled', selection.enabled ? 'on' : 'off');
1658
+ }
1659
+ else {
1660
+ newConfig = setConfigValue(newConfig, 'reasoningEnabled', 'on');
1661
+ newConfig = setConfigValue(newConfig, 'reasoningBudgetTokens', String(selection.budgetTokens));
1662
+ }
1663
+ persistConfig(newConfig);
1664
+ const value = formatNativeReasoningSetting(newConfig.model, newConfig.reasoning, newConfig.provider, newConfig.modelCapabilities);
1665
+ appendMessages([{ id: genMsgId(), role: 'system', content: t('ui.app.reasoningSet', { value: value || t('common.providerDefault') }) }]);
1666
+ }, onCancel: () => pop() })) : null;
1667
+ const overlayNode = historyOverlay ?? rewindOverlay ?? modelOverlay ?? effortOverlay;
1668
+ const showPrompt = !singleShot && activeOverlay === null && !cursorMode && !askModalVisible && !globalCommandActivity;
1669
+ const cursorPreviewMessage = cursorMode && !noFlickerActive && cursor ? messages[cursor.index] : undefined;
1670
+ const cursorHint = cursorMode ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: t('ui.app.cursorHint') }) })) : null;
1671
+ const cursorPreviewNode = cursorPreviewMessage ? (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { dimColor: true, children: t('ui.app.selectedMessage', { current: cursor.index + 1, total: messages.length }) }), _jsx(MessageList, { messages: [cursorPreviewMessage], verbose: verbose, availableWidth: conversationFrameWidth, selectedIndex: 0 })] })) : null;
1672
+ const flowInputHint = flowInputReceipt ? describeFlowInputReceipt(flowInputReceipt, t) : null;
1673
+ const flowResultHint = notificationSnapshot.resultCount > 0
1674
+ ? t('ui.flow.resultsReady', { count: notificationSnapshot.resultCount })
1675
+ : null;
1676
+ const semanticFlowHint = flowInputHint ?? flowResultHint;
1677
+ const globalActivityNode = globalCommandActivity ? (_jsx(Box, { paddingLeft: 1, flexShrink: 0, children: _jsx(Text, { color: "cyan", children: t('ui.app.commandActivity', {
1678
+ detail: globalCommandActivity.detail,
1679
+ }) }) })) : null;
1680
+ const promptNode = showPrompt ? (_jsxs(Box, { flexDirection: "column", children: [(flowIsRunning || flowQueueCount > 0 || semanticFlowHint || interruptHint || exitHint || runControlHint || persistenceWarning) && (_jsx(Box, { paddingLeft: 1, children: _jsx(Text, { dimColor: !persistenceWarning, children: persistenceWarning || interruptHint || exitHint || runControlHint || semanticFlowHint || (flowIsRunning
1681
+ ? t('ui.flow.controls.running', { count: flowQueueCount })
1682
+ : t('ui.flow.controls.queued', { count: flowQueueCount })) }) })), pendingAsk && !askModalVisible && (_jsx(Box, { paddingLeft: 1, children: _jsx(Text, { color: "yellow", bold: true, children: isPermissionAsk ? t('ui.app.actionReviewDelayed') : t('ui.app.actionInputDelayed') }) })), _jsx(PromptInput, { value: input, onChange: setComposedInput, onSubmit: handleSubmit, onAlternateSubmit: handleAlternateSubmit, onDoubleEsc: () => {
1683
+ if (messages.length > 0)
1684
+ push('rewind');
1685
+ }, onPasteImage: handlePasteImage, onPasteText: handlePasteText, onUserActivity: noteComposerActivity, onInputMutation: noteInputMutation, mode: currentMode, width: conversationFrameWidth, historyRef: promptHistoryRef })] })) : null;
1686
+ const transcriptNode = (_jsx(Box, { flexDirection: "column", flexBasis: 0, flexGrow: 1, flexShrink: 1, minHeight: 0, overflow: "hidden", children: _jsxs(TranscriptViewport, { scrollRowsFromBottom: normalizedScrollRows, onScrollRowsChange: setScrollRowsFromBottom, onMetricsChange: handleTranscriptMetrics, children: [flowFeatures.transcriptWindowing ? (_jsx(WindowedMessageList, { messages: messages, verbose: verbose, viewportRows: transcriptMetrics.viewportRows > 1 ? transcriptMetrics.viewportRows : transcriptRowBudget, scrollRowsFromBottom: normalizedScrollRows, showToolDetails: showToolDetails, availableWidth: conversationFrameWidth, selectedMessageId: selectedMessageId, selectedMessageRef: cursorMode ? selectedMessageRef : undefined, showThinking: showThinking, onWindowMetrics: recordTranscriptWindowMetrics })) : (_jsx(MessageList, { messages: messages, verbose: verbose, showToolDetails: showToolDetails, availableWidth: conversationFrameWidth, selectedMessageId: selectedMessageId, selectedMessageRef: cursorMode ? selectedMessageRef : undefined, showThinking: showThinking })), runningNode] }) }));
1687
+ const staticTranscriptItems = useMemo(() => [
1688
+ { kind: 'header', id: 'startup-header' },
1689
+ ...messages.map(message => ({ kind: 'message', id: message.id, message })),
1690
+ ], [messages]);
1691
+ const taskFlowNode = (_jsx(TaskFlowHud, { task: isRunning ? activeTask : null, objective: isRunning ? activeObjective?.prompt : null, isRunning: isRunning, runState: runState, tools: currentTools, draft: streamingToolDraft, queuedCount: queuedPrompts.length, width: conversationFrameWidth }));
1692
+ const mcpCount = mcpClient.getAllConnections().filter(connection => connection.status === 'connected').length;
1693
+ const activeTerminalCount = terminalSessions.filter(session => session.status === 'running' || session.status === 'starting').length;
1694
+ const landingFrameWidth = resolveLandingFrameWidth(terminal.columns);
1695
+ const showLandingView = shouldShowLandingView({
1696
+ messageCount: messages.length,
1697
+ isRunning: developerFlowActive,
1698
+ hasPendingAsk: Boolean(pendingAsk),
1699
+ cursorMode,
1700
+ hasOverlay: overlayNode !== null,
1701
+ queuedCount: queuedPrompts.length,
1702
+ });
1703
+ if (noFlickerActive) {
1704
+ return (_jsx(I18nProvider, { locale: profile.interfaceLanguage, children: _jsx(ThemeProvider, { transparentBackground: transparentBackground, children: _jsx(CockpitRoot, { width: getSafeViewportWidth(terminal.columns), height: terminal.rows, children: showLandingView ? (_jsx(LandingView, { frameWidth: landingFrameWidth, workspacePath: workspacePath, mood: mood, hasApiKey: !!config.apiKey, logoReveal: startupFrame.logoReveal, showVersion: startupFrame.showVersion, showWorkspace: startupFrame.showWorkspace, showPrompt: startupFrame.showPrompt && showPrompt, prompt: (_jsx(PromptInput, { value: input, onChange: setComposedInput, onSubmit: handleSubmit, onAlternateSubmit: handleAlternateSubmit, onPasteImage: handlePasteImage, onPasteText: handlePasteText, onUserActivity: noteComposerActivity, mode: currentMode, width: landingFrameWidth, placeholder: "", appearance: "landing", historyRef: promptHistoryRef })) })) : (_jsxs(Box, { flexDirection: "row", flexGrow: 1, flexShrink: 1, minHeight: 0, overflow: "hidden", backgroundColor: layoutBackground, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 1, minWidth: 0, minHeight: 0, overflow: "hidden", children: [_jsx(SessionPane, { children: overlayNode ?? (_jsxs(Box, { flexDirection: "column", flexBasis: 0, flexGrow: 1, flexShrink: 1, minHeight: 0, overflow: "hidden", children: [transcriptNode, pendingAskNode] })) }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, backgroundColor: layoutBackground, paddingX: 1, children: [cursorHint, globalActivityNode, _jsx(AgentActivityLine, { active: developerFlowActive, persistent: true, width: conversationFrameWidth }), promptNode, taskFlowNode, !cockpit.showSidebar && (_jsx(StatusLine, { config: config, tokenUsage: tokenUsage, mode: currentMode, viewingHistory: isViewingHistory, gitState: gitState, mcpCount: mcpCount, terminalCount: activeTerminalCount, attentionLabel: (!flowUiEnabled || !flowFeatures.notifications) && pendingAsk ? (isPermissionAsk ? t('ui.app.reviewRequired') : t('ui.app.inputRequired')) : undefined, activity: primaryFlowActivity, backgroundCount: flowBackgroundCount, queueCount: flowQueueCount, resultCount: notificationSnapshot.resultCount, width: conversationFrameWidth }))] })] }), cockpit.showSidebar && (_jsx(SessionSidebar, { width: cockpit.sidebarWidth, workspacePath: workspacePath, model: config.model, mode: currentMode, reasoning: reasoningLabel || undefined, contextWindow: config.contextWindow, tokenUsage: tokenUsage, queuedCount: queuedPrompts.length, terminals: terminalSessions, mcpCount: mcpCount, gitState: gitState }))] })) }) }) }));
1705
+ }
1706
+ return (_jsx(I18nProvider, { locale: profile.interfaceLanguage, children: _jsxs(ThemeProvider, { transparentBackground: transparentBackground, children: [_jsx(Static, { items: staticTranscriptItems, children: item => (item.kind === 'header'
1707
+ ? (_jsx(Box, { flexDirection: "column", paddingX: 1, children: _jsx(Header, { workspacePath: workspacePath, mood: "idle", hasApiKey: !!config.apiKey }) }, item.id))
1708
+ : (_jsx(Box, { flexDirection: "column", paddingX: 1, children: _jsx(MessageList, { messages: [item.message], verbose: verbose, availableWidth: Math.max(24, terminal.columns - 4) }) }, item.id))) }, staticTranscriptRevision), _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [runningNode, pendingAskNode, historyOverlay, rewindOverlay, modelOverlay, effortOverlay, cursorHint, cursorPreviewNode, globalActivityNode, promptNode, taskFlowNode, _jsx(TerminalSessionsFooter, { sessions: terminalSessions }), _jsx(StatusLine, { config: config, tokenUsage: tokenUsage, mode: currentMode, viewingHistory: isViewingHistory, gitState: gitState, attentionLabel: (!flowUiEnabled || !flowFeatures.notifications) && pendingAsk ? (isPermissionAsk ? t('ui.app.reviewRequired') : t('ui.app.inputRequired')) : undefined, activity: primaryFlowActivity, backgroundCount: flowBackgroundCount, queueCount: flowQueueCount, resultCount: notificationSnapshot.resultCount }), _jsx(AgentActivityLine, { active: developerFlowActive })] })] }) }));
1709
+ }
1710
+ export function startInkApp(options) {
1711
+ const workspaceName = options.workspacePath.split(/[\\/]/).pop() || 'workspace';
1712
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
1713
+ const noFlicker = shouldUseNoFlicker(interactive, options.singleShot, options.noFlicker === true);
1714
+ const flowTelemetry = new LocalFlowTelemetry(options.workspacePath);
1715
+ const terminalLatencyTracker = new TerminalLatencyTracker((metric, value) => flowTelemetry.observe(metric, value));
1716
+ render(_jsx(App, { workspacePath: options.workspacePath, workspaceName: workspaceName, config: options.config, singleShot: options.singleShot, verbose: options.verbose, noFlicker: noFlicker, approvalPolicy: options.approvalPolicy, capabilityProfile: options.capabilityProfile, mcpServers: options.mcpServers, startupAnimation: options.startupAnimation, transparentBackground: options.transparentBackground, flowTelemetry: flowTelemetry, terminalLatencyTracker: terminalLatencyTracker }), {
1717
+ maxFps: noFlicker ? 24 : 18,
1718
+ incrementalRendering: noFlicker,
1719
+ interactive,
1720
+ alternateScreen: noFlicker,
1721
+ exitOnCtrlC: false,
1722
+ onRender: ({ renderTime }) => {
1723
+ if (!interactive)
1724
+ return;
1725
+ flowTelemetry.observe('ui.frame_render_ms', renderTime);
1726
+ if (!terminalLatencyTracker.beginTerminalFlush())
1727
+ return;
1728
+ setImmediate(() => {
1729
+ if (process.stdout.destroyed || process.stdout.writableEnded) {
1730
+ terminalLatencyTracker.cancelTerminalFlush();
1731
+ return;
1732
+ }
1733
+ process.stdout.write('', () => terminalLatencyTracker.completeTerminalFlush());
1734
+ });
1735
+ },
1736
+ });
1737
+ }
1738
+ //# sourceMappingURL=App.js.map