@mragentix/cli 4.2.37

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 (461) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +149 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +772 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config.d.ts +16 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +29 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/core/agent-session.d.ts +87 -0
  12. package/dist/core/agent-session.d.ts.map +1 -0
  13. package/dist/core/agent-session.js +498 -0
  14. package/dist/core/agent-session.js.map +1 -0
  15. package/dist/core/agents.d.ts +30 -0
  16. package/dist/core/agents.d.ts.map +1 -0
  17. package/dist/core/agents.js +91 -0
  18. package/dist/core/agents.js.map +1 -0
  19. package/dist/core/auth-storage.d.ts +35 -0
  20. package/dist/core/auth-storage.d.ts.map +1 -0
  21. package/dist/core/auth-storage.js +144 -0
  22. package/dist/core/auth-storage.js.map +1 -0
  23. package/dist/core/auto-update.d.ts +8 -0
  24. package/dist/core/auto-update.d.ts.map +1 -0
  25. package/dist/core/auto-update.js +152 -0
  26. package/dist/core/auto-update.js.map +1 -0
  27. package/dist/core/compaction/compactor.d.ts +69 -0
  28. package/dist/core/compaction/compactor.d.ts.map +1 -0
  29. package/dist/core/compaction/compactor.js +405 -0
  30. package/dist/core/compaction/compactor.js.map +1 -0
  31. package/dist/core/compaction/compactor.test.d.ts +2 -0
  32. package/dist/core/compaction/compactor.test.d.ts.map +1 -0
  33. package/dist/core/compaction/compactor.test.js +461 -0
  34. package/dist/core/compaction/compactor.test.js.map +1 -0
  35. package/dist/core/compaction/token-estimator.d.ts +10 -0
  36. package/dist/core/compaction/token-estimator.d.ts.map +1 -0
  37. package/dist/core/compaction/token-estimator.js +75 -0
  38. package/dist/core/compaction/token-estimator.js.map +1 -0
  39. package/dist/core/compaction/token-estimator.test.d.ts +2 -0
  40. package/dist/core/compaction/token-estimator.test.d.ts.map +1 -0
  41. package/dist/core/compaction/token-estimator.test.js +137 -0
  42. package/dist/core/compaction/token-estimator.test.js.map +1 -0
  43. package/dist/core/custom-commands.d.ts +13 -0
  44. package/dist/core/custom-commands.d.ts.map +1 -0
  45. package/dist/core/custom-commands.js +40 -0
  46. package/dist/core/custom-commands.js.map +1 -0
  47. package/dist/core/event-bus.d.ts +95 -0
  48. package/dist/core/event-bus.d.ts.map +1 -0
  49. package/dist/core/event-bus.js +99 -0
  50. package/dist/core/event-bus.js.map +1 -0
  51. package/dist/core/extensions/loader.d.ts +8 -0
  52. package/dist/core/extensions/loader.d.ts.map +1 -0
  53. package/dist/core/extensions/loader.js +48 -0
  54. package/dist/core/extensions/loader.js.map +1 -0
  55. package/dist/core/extensions/types.d.ts +19 -0
  56. package/dist/core/extensions/types.d.ts.map +1 -0
  57. package/dist/core/extensions/types.js +2 -0
  58. package/dist/core/extensions/types.js.map +1 -0
  59. package/dist/core/file-lock.d.ts +6 -0
  60. package/dist/core/file-lock.d.ts.map +1 -0
  61. package/dist/core/file-lock.js +76 -0
  62. package/dist/core/file-lock.js.map +1 -0
  63. package/dist/core/index.d.ts +14 -0
  64. package/dist/core/index.d.ts.map +1 -0
  65. package/dist/core/index.js +13 -0
  66. package/dist/core/index.js.map +1 -0
  67. package/dist/core/logger.d.ts +26 -0
  68. package/dist/core/logger.d.ts.map +1 -0
  69. package/dist/core/logger.js +132 -0
  70. package/dist/core/logger.js.map +1 -0
  71. package/dist/core/mcp/client.d.ts +9 -0
  72. package/dist/core/mcp/client.d.ts.map +1 -0
  73. package/dist/core/mcp/client.js +126 -0
  74. package/dist/core/mcp/client.js.map +1 -0
  75. package/dist/core/mcp/defaults.d.ts +9 -0
  76. package/dist/core/mcp/defaults.d.ts.map +1 -0
  77. package/dist/core/mcp/defaults.js +47 -0
  78. package/dist/core/mcp/defaults.js.map +1 -0
  79. package/dist/core/mcp/index.d.ts +4 -0
  80. package/dist/core/mcp/index.d.ts.map +1 -0
  81. package/dist/core/mcp/index.js +3 -0
  82. package/dist/core/mcp/index.js.map +1 -0
  83. package/dist/core/mcp/types.d.ts +15 -0
  84. package/dist/core/mcp/types.d.ts.map +1 -0
  85. package/dist/core/mcp/types.js +2 -0
  86. package/dist/core/mcp/types.js.map +1 -0
  87. package/dist/core/model-registry.d.ts +25 -0
  88. package/dist/core/model-registry.d.ts.map +1 -0
  89. package/dist/core/model-registry.js +135 -0
  90. package/dist/core/model-registry.js.map +1 -0
  91. package/dist/core/oauth/anthropic.d.ts +4 -0
  92. package/dist/core/oauth/anthropic.d.ts.map +1 -0
  93. package/dist/core/oauth/anthropic.js +75 -0
  94. package/dist/core/oauth/anthropic.js.map +1 -0
  95. package/dist/core/oauth/openai.d.ts +4 -0
  96. package/dist/core/oauth/openai.d.ts.map +1 -0
  97. package/dist/core/oauth/openai.js +186 -0
  98. package/dist/core/oauth/openai.js.map +1 -0
  99. package/dist/core/oauth/pkce.d.ts +5 -0
  100. package/dist/core/oauth/pkce.d.ts.map +1 -0
  101. package/dist/core/oauth/pkce.js +17 -0
  102. package/dist/core/oauth/pkce.js.map +1 -0
  103. package/dist/core/oauth/types.d.ts +12 -0
  104. package/dist/core/oauth/types.d.ts.map +1 -0
  105. package/dist/core/oauth/types.js +2 -0
  106. package/dist/core/oauth/types.js.map +1 -0
  107. package/dist/core/process-manager.d.ts +30 -0
  108. package/dist/core/process-manager.d.ts.map +1 -0
  109. package/dist/core/process-manager.js +130 -0
  110. package/dist/core/process-manager.js.map +1 -0
  111. package/dist/core/prompt-commands.d.ts +14 -0
  112. package/dist/core/prompt-commands.d.ts.map +1 -0
  113. package/dist/core/prompt-commands.js +496 -0
  114. package/dist/core/prompt-commands.js.map +1 -0
  115. package/dist/core/session-manager.d.ts +112 -0
  116. package/dist/core/session-manager.d.ts.map +1 -0
  117. package/dist/core/session-manager.js +326 -0
  118. package/dist/core/session-manager.js.map +1 -0
  119. package/dist/core/settings-manager.d.ts +43 -0
  120. package/dist/core/settings-manager.d.ts.map +1 -0
  121. package/dist/core/settings-manager.js +64 -0
  122. package/dist/core/settings-manager.js.map +1 -0
  123. package/dist/core/skills.d.ts +23 -0
  124. package/dist/core/skills.d.ts.map +1 -0
  125. package/dist/core/skills.js +89 -0
  126. package/dist/core/skills.js.map +1 -0
  127. package/dist/core/slash-commands.d.ts +35 -0
  128. package/dist/core/slash-commands.d.ts.map +1 -0
  129. package/dist/core/slash-commands.js +183 -0
  130. package/dist/core/slash-commands.js.map +1 -0
  131. package/dist/core/telegram.d.ts +94 -0
  132. package/dist/core/telegram.d.ts.map +1 -0
  133. package/dist/core/telegram.js +227 -0
  134. package/dist/core/telegram.js.map +1 -0
  135. package/dist/index.d.ts +10 -0
  136. package/dist/index.d.ts.map +1 -0
  137. package/dist/index.js +15 -0
  138. package/dist/index.js.map +1 -0
  139. package/dist/interactive.d.ts +3 -0
  140. package/dist/interactive.d.ts.map +1 -0
  141. package/dist/interactive.js +173 -0
  142. package/dist/interactive.js.map +1 -0
  143. package/dist/modes/index.d.ts +3 -0
  144. package/dist/modes/index.d.ts.map +1 -0
  145. package/dist/modes/index.js +3 -0
  146. package/dist/modes/index.js.map +1 -0
  147. package/dist/modes/json-mode.d.ts +13 -0
  148. package/dist/modes/json-mode.d.ts.map +1 -0
  149. package/dist/modes/json-mode.js +74 -0
  150. package/dist/modes/json-mode.js.map +1 -0
  151. package/dist/modes/print-mode.d.ts +12 -0
  152. package/dist/modes/print-mode.d.ts.map +1 -0
  153. package/dist/modes/print-mode.js +49 -0
  154. package/dist/modes/print-mode.js.map +1 -0
  155. package/dist/modes/rpc-mode.d.ts +28 -0
  156. package/dist/modes/rpc-mode.d.ts.map +1 -0
  157. package/dist/modes/rpc-mode.js +145 -0
  158. package/dist/modes/rpc-mode.js.map +1 -0
  159. package/dist/modes/serve-mode.d.ts +21 -0
  160. package/dist/modes/serve-mode.d.ts.map +1 -0
  161. package/dist/modes/serve-mode.js +649 -0
  162. package/dist/modes/serve-mode.js.map +1 -0
  163. package/dist/session.d.ts +16 -0
  164. package/dist/session.d.ts.map +1 -0
  165. package/dist/session.js +129 -0
  166. package/dist/session.js.map +1 -0
  167. package/dist/system-prompt.d.ts +6 -0
  168. package/dist/system-prompt.d.ts.map +1 -0
  169. package/dist/system-prompt.js +115 -0
  170. package/dist/system-prompt.js.map +1 -0
  171. package/dist/tools/bash.d.ts +13 -0
  172. package/dist/tools/bash.d.ts.map +1 -0
  173. package/dist/tools/bash.js +165 -0
  174. package/dist/tools/bash.js.map +1 -0
  175. package/dist/tools/edit-diff.d.ts +18 -0
  176. package/dist/tools/edit-diff.d.ts.map +1 -0
  177. package/dist/tools/edit-diff.js +92 -0
  178. package/dist/tools/edit-diff.js.map +1 -0
  179. package/dist/tools/edit.d.ts +11 -0
  180. package/dist/tools/edit.d.ts.map +1 -0
  181. package/dist/tools/edit.js +57 -0
  182. package/dist/tools/edit.js.map +1 -0
  183. package/dist/tools/find.d.ts +9 -0
  184. package/dist/tools/find.d.ts.map +1 -0
  185. package/dist/tools/find.js +59 -0
  186. package/dist/tools/find.js.map +1 -0
  187. package/dist/tools/grep.d.ts +13 -0
  188. package/dist/tools/grep.d.ts.map +1 -0
  189. package/dist/tools/grep.js +121 -0
  190. package/dist/tools/grep.js.map +1 -0
  191. package/dist/tools/index.d.ts +30 -0
  192. package/dist/tools/index.d.ts.map +1 -0
  193. package/dist/tools/index.js +50 -0
  194. package/dist/tools/index.js.map +1 -0
  195. package/dist/tools/ls.d.ts +10 -0
  196. package/dist/tools/ls.d.ts.map +1 -0
  197. package/dist/tools/ls.js +56 -0
  198. package/dist/tools/ls.js.map +1 -0
  199. package/dist/tools/operations.d.ts +39 -0
  200. package/dist/tools/operations.d.ts.map +1 -0
  201. package/dist/tools/operations.js +27 -0
  202. package/dist/tools/operations.js.map +1 -0
  203. package/dist/tools/path-utils.d.ts +7 -0
  204. package/dist/tools/path-utils.d.ts.map +1 -0
  205. package/dist/tools/path-utils.js +27 -0
  206. package/dist/tools/path-utils.js.map +1 -0
  207. package/dist/tools/read.d.ts +12 -0
  208. package/dist/tools/read.d.ts.map +1 -0
  209. package/dist/tools/read.js +113 -0
  210. package/dist/tools/read.js.map +1 -0
  211. package/dist/tools/subagent.d.ts +26 -0
  212. package/dist/tools/subagent.d.ts.map +1 -0
  213. package/dist/tools/subagent.js +210 -0
  214. package/dist/tools/subagent.js.map +1 -0
  215. package/dist/tools/task-output.d.ts +10 -0
  216. package/dist/tools/task-output.d.ts.map +1 -0
  217. package/dist/tools/task-output.js +33 -0
  218. package/dist/tools/task-output.js.map +1 -0
  219. package/dist/tools/task-stop.d.ts +9 -0
  220. package/dist/tools/task-stop.d.ts.map +1 -0
  221. package/dist/tools/task-stop.js +15 -0
  222. package/dist/tools/task-stop.js.map +1 -0
  223. package/dist/tools/tasks.d.ts +16 -0
  224. package/dist/tools/tasks.d.ts.map +1 -0
  225. package/dist/tools/tasks.js +132 -0
  226. package/dist/tools/tasks.js.map +1 -0
  227. package/dist/tools/truncate.d.ts +19 -0
  228. package/dist/tools/truncate.d.ts.map +1 -0
  229. package/dist/tools/truncate.js +59 -0
  230. package/dist/tools/truncate.js.map +1 -0
  231. package/dist/tools/truncate.test.d.ts +2 -0
  232. package/dist/tools/truncate.test.d.ts.map +1 -0
  233. package/dist/tools/truncate.test.js +100 -0
  234. package/dist/tools/truncate.test.js.map +1 -0
  235. package/dist/tools/web-fetch.d.ts +9 -0
  236. package/dist/tools/web-fetch.d.ts.map +1 -0
  237. package/dist/tools/web-fetch.js +97 -0
  238. package/dist/tools/web-fetch.js.map +1 -0
  239. package/dist/tools/write.d.ts +10 -0
  240. package/dist/tools/write.d.ts.map +1 -0
  241. package/dist/tools/write.js +30 -0
  242. package/dist/tools/write.js.map +1 -0
  243. package/dist/tools/write.test.d.ts +2 -0
  244. package/dist/tools/write.test.d.ts.map +1 -0
  245. package/dist/tools/write.test.js +84 -0
  246. package/dist/tools/write.test.js.map +1 -0
  247. package/dist/types.d.ts +36 -0
  248. package/dist/types.d.ts.map +1 -0
  249. package/dist/types.js +2 -0
  250. package/dist/types.js.map +1 -0
  251. package/dist/ui/App.d.ts +148 -0
  252. package/dist/ui/App.d.ts.map +1 -0
  253. package/dist/ui/App.js +1191 -0
  254. package/dist/ui/App.js.map +1 -0
  255. package/dist/ui/components/ActivityIndicator.d.ts +13 -0
  256. package/dist/ui/components/ActivityIndicator.d.ts.map +1 -0
  257. package/dist/ui/components/ActivityIndicator.js +313 -0
  258. package/dist/ui/components/ActivityIndicator.js.map +1 -0
  259. package/dist/ui/components/AnimationContext.d.ts +22 -0
  260. package/dist/ui/components/AnimationContext.d.ts.map +1 -0
  261. package/dist/ui/components/AnimationContext.js +35 -0
  262. package/dist/ui/components/AnimationContext.js.map +1 -0
  263. package/dist/ui/components/AssistantMessage.d.ts +9 -0
  264. package/dist/ui/components/AssistantMessage.d.ts.map +1 -0
  265. package/dist/ui/components/AssistantMessage.js +11 -0
  266. package/dist/ui/components/AssistantMessage.js.map +1 -0
  267. package/dist/ui/components/BackgroundTasksBar.d.ts +15 -0
  268. package/dist/ui/components/BackgroundTasksBar.d.ts.map +1 -0
  269. package/dist/ui/components/BackgroundTasksBar.js +74 -0
  270. package/dist/ui/components/BackgroundTasksBar.js.map +1 -0
  271. package/dist/ui/components/Banner.d.ts +11 -0
  272. package/dist/ui/components/Banner.d.ts.map +1 -0
  273. package/dist/ui/components/Banner.js +55 -0
  274. package/dist/ui/components/Banner.js.map +1 -0
  275. package/dist/ui/components/CompactionNotice.d.ts +10 -0
  276. package/dist/ui/components/CompactionNotice.d.ts.map +1 -0
  277. package/dist/ui/components/CompactionNotice.js +27 -0
  278. package/dist/ui/components/CompactionNotice.js.map +1 -0
  279. package/dist/ui/components/DiffView.d.ts +4 -0
  280. package/dist/ui/components/DiffView.d.ts.map +1 -0
  281. package/dist/ui/components/DiffView.js +20 -0
  282. package/dist/ui/components/DiffView.js.map +1 -0
  283. package/dist/ui/components/Footer.d.ts +10 -0
  284. package/dist/ui/components/Footer.d.ts.map +1 -0
  285. package/dist/ui/components/Footer.js +105 -0
  286. package/dist/ui/components/Footer.js.map +1 -0
  287. package/dist/ui/components/InputArea.d.ts +21 -0
  288. package/dist/ui/components/InputArea.d.ts.map +1 -0
  289. package/dist/ui/components/InputArea.js +465 -0
  290. package/dist/ui/components/InputArea.js.map +1 -0
  291. package/dist/ui/components/Markdown.d.ts +9 -0
  292. package/dist/ui/components/Markdown.d.ts.map +1 -0
  293. package/dist/ui/components/Markdown.js +246 -0
  294. package/dist/ui/components/Markdown.js.map +1 -0
  295. package/dist/ui/components/ModelSelector.d.ts +11 -0
  296. package/dist/ui/components/ModelSelector.d.ts.map +1 -0
  297. package/dist/ui/components/ModelSelector.js +20 -0
  298. package/dist/ui/components/ModelSelector.js.map +1 -0
  299. package/dist/ui/components/Overlay.d.ts +8 -0
  300. package/dist/ui/components/Overlay.d.ts.map +1 -0
  301. package/dist/ui/components/Overlay.js +9 -0
  302. package/dist/ui/components/Overlay.js.map +1 -0
  303. package/dist/ui/components/SelectList.d.ts +14 -0
  304. package/dist/ui/components/SelectList.d.ts.map +1 -0
  305. package/dist/ui/components/SelectList.js +46 -0
  306. package/dist/ui/components/SelectList.js.map +1 -0
  307. package/dist/ui/components/ServerToolExecution.d.ts +17 -0
  308. package/dist/ui/components/ServerToolExecution.d.ts.map +1 -0
  309. package/dist/ui/components/ServerToolExecution.js +26 -0
  310. package/dist/ui/components/ServerToolExecution.js.map +1 -0
  311. package/dist/ui/components/SessionSelector.d.ts +9 -0
  312. package/dist/ui/components/SessionSelector.d.ts.map +1 -0
  313. package/dist/ui/components/SessionSelector.js +13 -0
  314. package/dist/ui/components/SessionSelector.js.map +1 -0
  315. package/dist/ui/components/SettingsSelector.d.ts +9 -0
  316. package/dist/ui/components/SettingsSelector.d.ts.map +1 -0
  317. package/dist/ui/components/SettingsSelector.js +13 -0
  318. package/dist/ui/components/SettingsSelector.js.map +1 -0
  319. package/dist/ui/components/SlashCommandMenu.d.ts +15 -0
  320. package/dist/ui/components/SlashCommandMenu.d.ts.map +1 -0
  321. package/dist/ui/components/SlashCommandMenu.js +32 -0
  322. package/dist/ui/components/SlashCommandMenu.js.map +1 -0
  323. package/dist/ui/components/Spinner.d.ts +4 -0
  324. package/dist/ui/components/Spinner.d.ts.map +1 -0
  325. package/dist/ui/components/Spinner.js +13 -0
  326. package/dist/ui/components/Spinner.js.map +1 -0
  327. package/dist/ui/components/StreamingArea.d.ts +10 -0
  328. package/dist/ui/components/StreamingArea.d.ts.map +1 -0
  329. package/dist/ui/components/StreamingArea.js +58 -0
  330. package/dist/ui/components/StreamingArea.js.map +1 -0
  331. package/dist/ui/components/SubAgentPanel.d.ts +21 -0
  332. package/dist/ui/components/SubAgentPanel.d.ts.map +1 -0
  333. package/dist/ui/components/SubAgentPanel.js +71 -0
  334. package/dist/ui/components/SubAgentPanel.js.map +1 -0
  335. package/dist/ui/components/TaskOverlay.d.ts +10 -0
  336. package/dist/ui/components/TaskOverlay.d.ts.map +1 -0
  337. package/dist/ui/components/TaskOverlay.js +263 -0
  338. package/dist/ui/components/TaskOverlay.js.map +1 -0
  339. package/dist/ui/components/ThinkingBlock.d.ts +11 -0
  340. package/dist/ui/components/ThinkingBlock.d.ts.map +1 -0
  341. package/dist/ui/components/ThinkingBlock.js +43 -0
  342. package/dist/ui/components/ThinkingBlock.js.map +1 -0
  343. package/dist/ui/components/ThinkingIndicator.d.ts +6 -0
  344. package/dist/ui/components/ThinkingIndicator.d.ts.map +1 -0
  345. package/dist/ui/components/ThinkingIndicator.js +144 -0
  346. package/dist/ui/components/ThinkingIndicator.js.map +1 -0
  347. package/dist/ui/components/ToolExecution.d.ts +16 -0
  348. package/dist/ui/components/ToolExecution.d.ts.map +1 -0
  349. package/dist/ui/components/ToolExecution.js +490 -0
  350. package/dist/ui/components/ToolExecution.js.map +1 -0
  351. package/dist/ui/components/ToolGroupExecution.d.ts +7 -0
  352. package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -0
  353. package/dist/ui/components/ToolGroupExecution.js +115 -0
  354. package/dist/ui/components/ToolGroupExecution.js.map +1 -0
  355. package/dist/ui/components/UserMessage.d.ts +7 -0
  356. package/dist/ui/components/UserMessage.d.ts.map +1 -0
  357. package/dist/ui/components/UserMessage.js +16 -0
  358. package/dist/ui/components/UserMessage.js.map +1 -0
  359. package/dist/ui/components/index.d.ts +15 -0
  360. package/dist/ui/components/index.d.ts.map +1 -0
  361. package/dist/ui/components/index.js +15 -0
  362. package/dist/ui/components/index.js.map +1 -0
  363. package/dist/ui/hooks/useAgentLoop.d.ts +69 -0
  364. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -0
  365. package/dist/ui/hooks/useAgentLoop.js +483 -0
  366. package/dist/ui/hooks/useAgentLoop.js.map +1 -0
  367. package/dist/ui/hooks/useSessionManager.d.ts +13 -0
  368. package/dist/ui/hooks/useSessionManager.d.ts.map +1 -0
  369. package/dist/ui/hooks/useSessionManager.js +43 -0
  370. package/dist/ui/hooks/useSessionManager.js.map +1 -0
  371. package/dist/ui/hooks/useSlashCommands.d.ts +7 -0
  372. package/dist/ui/hooks/useSlashCommands.d.ts.map +1 -0
  373. package/dist/ui/hooks/useSlashCommands.js +11 -0
  374. package/dist/ui/hooks/useSlashCommands.js.map +1 -0
  375. package/dist/ui/hooks/useTerminalSize.d.ts +20 -0
  376. package/dist/ui/hooks/useTerminalSize.d.ts.map +1 -0
  377. package/dist/ui/hooks/useTerminalSize.js +55 -0
  378. package/dist/ui/hooks/useTerminalSize.js.map +1 -0
  379. package/dist/ui/hooks/useTerminalTitle.d.ts +3 -0
  380. package/dist/ui/hooks/useTerminalTitle.d.ts.map +1 -0
  381. package/dist/ui/hooks/useTerminalTitle.js +41 -0
  382. package/dist/ui/hooks/useTerminalTitle.js.map +1 -0
  383. package/dist/ui/live-item-flush.d.ts +59 -0
  384. package/dist/ui/live-item-flush.d.ts.map +1 -0
  385. package/dist/ui/live-item-flush.js +135 -0
  386. package/dist/ui/live-item-flush.js.map +1 -0
  387. package/dist/ui/live-item-flush.test.d.ts +2 -0
  388. package/dist/ui/live-item-flush.test.d.ts.map +1 -0
  389. package/dist/ui/live-item-flush.test.js +307 -0
  390. package/dist/ui/live-item-flush.test.js.map +1 -0
  391. package/dist/ui/login.d.ts +3 -0
  392. package/dist/ui/login.d.ts.map +1 -0
  393. package/dist/ui/login.js +117 -0
  394. package/dist/ui/login.js.map +1 -0
  395. package/dist/ui/render.d.ts +38 -0
  396. package/dist/ui/render.d.ts.map +1 -0
  397. package/dist/ui/render.js +72 -0
  398. package/dist/ui/render.js.map +1 -0
  399. package/dist/ui/sessions.d.ts +2 -0
  400. package/dist/ui/sessions.d.ts.map +1 -0
  401. package/dist/ui/sessions.js +208 -0
  402. package/dist/ui/sessions.js.map +1 -0
  403. package/dist/ui/spinner-frames.d.ts +3 -0
  404. package/dist/ui/spinner-frames.d.ts.map +1 -0
  405. package/dist/ui/spinner-frames.js +7 -0
  406. package/dist/ui/spinner-frames.js.map +1 -0
  407. package/dist/ui/theme/dark.json +24 -0
  408. package/dist/ui/theme/detect-theme.d.ts +12 -0
  409. package/dist/ui/theme/detect-theme.d.ts.map +1 -0
  410. package/dist/ui/theme/detect-theme.js +152 -0
  411. package/dist/ui/theme/detect-theme.js.map +1 -0
  412. package/dist/ui/theme/light.json +24 -0
  413. package/dist/ui/theme/theme.d.ts +29 -0
  414. package/dist/ui/theme/theme.d.ts.map +1 -0
  415. package/dist/ui/theme/theme.js +11 -0
  416. package/dist/ui/theme/theme.js.map +1 -0
  417. package/dist/ui/utils/highlight.d.ts +8 -0
  418. package/dist/ui/utils/highlight.d.ts.map +1 -0
  419. package/dist/ui/utils/highlight.js +49 -0
  420. package/dist/ui/utils/highlight.js.map +1 -0
  421. package/dist/ui/utils/table-text.d.ts +25 -0
  422. package/dist/ui/utils/table-text.d.ts.map +1 -0
  423. package/dist/ui/utils/table-text.js +78 -0
  424. package/dist/ui/utils/table-text.js.map +1 -0
  425. package/dist/ui/utils/table-text.test.d.ts +2 -0
  426. package/dist/ui/utils/table-text.test.d.ts.map +1 -0
  427. package/dist/ui/utils/table-text.test.js +202 -0
  428. package/dist/ui/utils/table-text.test.js.map +1 -0
  429. package/dist/utils/error-handler.d.ts +5 -0
  430. package/dist/utils/error-handler.d.ts.map +1 -0
  431. package/dist/utils/error-handler.js +120 -0
  432. package/dist/utils/error-handler.js.map +1 -0
  433. package/dist/utils/format.d.ts +21 -0
  434. package/dist/utils/format.d.ts.map +1 -0
  435. package/dist/utils/format.js +120 -0
  436. package/dist/utils/format.js.map +1 -0
  437. package/dist/utils/git.d.ts +2 -0
  438. package/dist/utils/git.d.ts.map +1 -0
  439. package/dist/utils/git.js +13 -0
  440. package/dist/utils/git.js.map +1 -0
  441. package/dist/utils/image.d.ts +30 -0
  442. package/dist/utils/image.d.ts.map +1 -0
  443. package/dist/utils/image.js +231 -0
  444. package/dist/utils/image.js.map +1 -0
  445. package/dist/utils/markdown.d.ts +6 -0
  446. package/dist/utils/markdown.d.ts.map +1 -0
  447. package/dist/utils/markdown.js +25 -0
  448. package/dist/utils/markdown.js.map +1 -0
  449. package/dist/utils/process.d.ts +6 -0
  450. package/dist/utils/process.d.ts.map +1 -0
  451. package/dist/utils/process.js +19 -0
  452. package/dist/utils/process.js.map +1 -0
  453. package/dist/utils/shell.d.ts +3 -0
  454. package/dist/utils/shell.d.ts.map +1 -0
  455. package/dist/utils/shell.js +8 -0
  456. package/dist/utils/shell.js.map +1 -0
  457. package/dist/utils/sound.d.ts +2 -0
  458. package/dist/utils/sound.d.ts.map +1 -0
  459. package/dist/utils/sound.js +11 -0
  460. package/dist/utils/sound.js.map +1 -0
  461. package/package.json +56 -0
@@ -0,0 +1,55 @@
1
+ import { useState, useEffect, useRef, useCallback } from "react";
2
+ import { useStdout } from "ink";
3
+ /**
4
+ * Returns { columns, rows, resizeKey } and forces a React re-render whenever
5
+ * the terminal is resized.
6
+ *
7
+ * `columns` and `rows` update immediately on every resize event so layout
8
+ * stays responsive while the user drags.
9
+ *
10
+ * `resizeKey` increments once after resize events settle (300ms debounce).
11
+ * Use it as a React `key` on the root content wrapper to force a full
12
+ * remount — this is the only reliable way to make Ink re-render <Static>
13
+ * content that was already printed to scrollback and got corrupted by
14
+ * terminal text reflow. Debounces 300ms then clears screen+scrollback
15
+ * and remounts.
16
+ */
17
+ export function useTerminalSize() {
18
+ const { stdout } = useStdout();
19
+ const [size, setSize] = useState({
20
+ columns: stdout?.columns ?? 80,
21
+ rows: stdout?.rows ?? 24,
22
+ });
23
+ const [resizeKey, setResizeKey] = useState(0);
24
+ const debounceRef = useRef(null);
25
+ const onResize = useCallback(() => {
26
+ if (!stdout)
27
+ return;
28
+ // Update dimensions immediately for responsive layout
29
+ setSize({ columns: stdout.columns ?? 80, rows: stdout.rows ?? 24 });
30
+ // Debounce the resizeKey bump — only fires after the user stops dragging
31
+ if (debounceRef.current)
32
+ clearTimeout(debounceRef.current);
33
+ debounceRef.current = setTimeout(() => {
34
+ // Clear visible screen + scrollback to remove deformed ghost renders
35
+ // left behind by Ink re-rendering at different terminal widths during
36
+ // a resize drag.
37
+ stdout.write("\x1b[2J" + // clear visible screen
38
+ "\x1b[3J" + // clear scrollback buffer
39
+ "\x1b[H");
40
+ setResizeKey((k) => k + 1);
41
+ }, 300);
42
+ }, [stdout]);
43
+ useEffect(() => {
44
+ if (!stdout)
45
+ return;
46
+ stdout.on("resize", onResize);
47
+ return () => {
48
+ stdout.off("resize", onResize);
49
+ if (debounceRef.current)
50
+ clearTimeout(debounceRef.current);
51
+ };
52
+ }, [stdout, onResize]);
53
+ return { ...size, resizeKey };
54
+ }
55
+ //# sourceMappingURL=useTerminalSize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTerminalSize.js","sourceRoot":"","sources":["../../../src/ui/hooks/useTerminalSize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAEhC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAC/B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC;QAC/B,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE;QAC9B,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE;KACzB,CAAC,CAAC;IACH,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,MAAM,CAAuC,IAAI,CAAC,CAAC;IAEvE,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;QAChC,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,sDAAsD;QACtD,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;QAEpE,yEAAyE;QACzE,IAAI,WAAW,CAAC,OAAO;YAAE,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC3D,WAAW,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACpC,qEAAqE;YACrE,sEAAsE;YACtE,iBAAiB;YACjB,MAAM,CAAC,KAAK,CACV,SAAS,GAAG,uBAAuB;gBACjC,SAAS,GAAG,0BAA0B;gBACtC,QAAQ,CACX,CAAC;YACF,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7B,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC9B,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC/B,IAAI,WAAW,CAAC,OAAO;gBAAE,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC7D,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEvB,OAAO,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ActivityPhase } from "./useAgentLoop.js";
2
+ export declare function useTerminalTitle(phase: ActivityPhase, isRunning: boolean): void;
3
+ //# sourceMappingURL=useTerminalTitle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTerminalTitle.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useTerminalTitle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAqBvD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAqB/E"}
@@ -0,0 +1,41 @@
1
+ import { useEffect } from "react";
2
+ import { useStdout } from "ink";
3
+ import { SPINNER_FRAMES, SPINNER_INTERVAL } from "../spinner-frames.js";
4
+ import { useAnimationTick, deriveFrame } from "../components/AnimationContext.js";
5
+ function getTitleText(phase, isRunning) {
6
+ if (!isRunning)
7
+ return "MR Agentix";
8
+ switch (phase) {
9
+ case "thinking":
10
+ return "Thinking...";
11
+ case "generating":
12
+ return "Generating...";
13
+ case "tools":
14
+ return "Running tools...";
15
+ case "waiting":
16
+ return "Thinking...";
17
+ default:
18
+ return "MR Agentix";
19
+ }
20
+ }
21
+ export function useTerminalTitle(phase, isRunning) {
22
+ const { stdout } = useStdout();
23
+ // Derive spinner frame from global animation tick — no independent timer
24
+ const tick = useAnimationTick();
25
+ const spinnerFrame = isRunning ? deriveFrame(tick, SPINNER_INTERVAL, SPINNER_FRAMES.length) : 0;
26
+ // Write terminal title
27
+ useEffect(() => {
28
+ if (!stdout)
29
+ return;
30
+ const text = getTitleText(phase, isRunning);
31
+ const title = isRunning ? `${SPINNER_FRAMES[spinnerFrame]} ${text}` : text;
32
+ stdout.write(`\x1b]0;${title}\x1b\\`);
33
+ }, [stdout, phase, isRunning, spinnerFrame]);
34
+ // Reset title on unmount
35
+ useEffect(() => {
36
+ return () => {
37
+ stdout?.write(`\x1b]0;MR Agentix\x1b\\`);
38
+ };
39
+ }, [stdout]);
40
+ }
41
+ //# sourceMappingURL=useTerminalTitle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTerminalTitle.js","sourceRoot":"","sources":["../../../src/ui/hooks/useTerminalTitle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAGhC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAElF,SAAS,YAAY,CAAC,KAAoB,EAAE,SAAkB;IAC5D,IAAI,CAAC,SAAS;QAAE,OAAO,YAAY,CAAC;IACpC,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,UAAU;YACb,OAAO,aAAa,CAAC;QACvB,KAAK,YAAY;YACf,OAAO,eAAe,CAAC;QACzB,KAAK,OAAO;YACV,OAAO,kBAAkB,CAAC;QAC5B,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC;QACvB;YACE,OAAO,YAAY,CAAC;IACxB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAoB,EAAE,SAAkB;IACvE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAE/B,yEAAyE;IACzE,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAC;IAChC,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,gBAAgB,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhG,uBAAuB;IACvB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3E,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC;IACxC,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;IAE7C,yBAAyB;IACzB,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE;YACV,MAAM,EAAE,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC3C,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Pure functions for flushing completed live items to Static history.
3
+ *
4
+ * During an agent run, items (text blocks, tool calls, etc.) accumulate in
5
+ * the live area. Ink re-renders ALL live items on every state change, so if
6
+ * they grow unbounded the live area becomes very tall, making Ink's cursor
7
+ * math expensive and causing visible jank.
8
+ *
9
+ * These functions determine which items can be safely moved to Static history
10
+ * (where Ink writes them once and never re-renders them).
11
+ */
12
+ /** Minimal item shape needed for flush logic. */
13
+ export interface FlushableItem {
14
+ kind: string;
15
+ id: string;
16
+ }
17
+ /**
18
+ * Max history items kept in React state. Ink's <Static> renders items once
19
+ * to stdout, so pruned items remain visible in terminal scrollback — we just
20
+ * release the JS object references to avoid unbounded memory growth.
21
+ */
22
+ export declare const MAX_HISTORY_ITEMS = 200;
23
+ /**
24
+ * Prune history to keep at most MAX_HISTORY_ITEMS. Oldest items are dropped
25
+ * first; they remain visible in terminal scrollback.
26
+ */
27
+ export declare function pruneHistory<T>(items: T[]): T[];
28
+ /**
29
+ * Trim large payload data from items that have been flushed to Static history.
30
+ * Ink already rendered them to the terminal — we only keep a truncated version
31
+ * to prevent multi-GB memory retention from tool results, server tool data, and
32
+ * sub-agent results.
33
+ *
34
+ * Works with any item shape via duck-typing: truncates `result` strings,
35
+ * clears `input`/`data` fields, and trims sub-agent result strings.
36
+ */
37
+ export declare function trimFlushedItems<T extends FlushableItem>(items: T[]): T[];
38
+ /**
39
+ * Called when `onTurnText` fires (end of each LLM turn that produced text).
40
+ * All previous items are guaranteed complete — tool calls from this turn
41
+ * finished before `turn_end` fired, and `onTurnText` fires inside `turn_end`.
42
+ *
43
+ * Returns the items to flush to history. The caller should then set liveItems
44
+ * to contain only the new text item.
45
+ */
46
+ export declare function flushOnTurnText<T extends FlushableItem>(liveItems: T[]): T[];
47
+ /**
48
+ * Called when `onTurnEnd` fires with a tool_use stop reason (LLM responded
49
+ * with only tool calls, no text). Flushes all items IF none are still pending
50
+ * (no `tool_start` without a corresponding `tool_done`).
51
+ *
52
+ * Returns { flushed, remaining } — flushed items go to history, remaining
53
+ * items stay in liveItems.
54
+ */
55
+ export declare function flushOnTurnEnd<T extends FlushableItem>(liveItems: T[], stopReason: string): {
56
+ flushed: T[];
57
+ remaining: T[];
58
+ };
59
+ //# sourceMappingURL=live-item-flush.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-item-flush.d.ts","sourceRoot":"","sources":["../../src/ui/live-item-flush.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAUrC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAG/C;AAQD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,aAAa,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CA0DzE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,aAAa,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE5E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EACpD,SAAS,EAAE,CAAC,EAAE,EACd,UAAU,EAAE,MAAM,GACjB;IAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAAC,SAAS,EAAE,CAAC,EAAE,CAAA;CAAE,CAoBlC"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Pure functions for flushing completed live items to Static history.
3
+ *
4
+ * During an agent run, items (text blocks, tool calls, etc.) accumulate in
5
+ * the live area. Ink re-renders ALL live items on every state change, so if
6
+ * they grow unbounded the live area becomes very tall, making Ink's cursor
7
+ * math expensive and causing visible jank.
8
+ *
9
+ * These functions determine which items can be safely moved to Static history
10
+ * (where Ink writes them once and never re-renders them).
11
+ */
12
+ /**
13
+ * Max history items kept in React state. Ink's <Static> renders items once
14
+ * to stdout, so pruned items remain visible in terminal scrollback — we just
15
+ * release the JS object references to avoid unbounded memory growth.
16
+ */
17
+ export const MAX_HISTORY_ITEMS = 200;
18
+ /**
19
+ * Max characters to keep in a tool result string after the item has been
20
+ * flushed to Static history. Ink already rendered the full result, so we
21
+ * only need enough for potential re-renders (which shouldn't happen for
22
+ * Static items, but keep a small buffer for safety).
23
+ */
24
+ const MAX_RESULT_CHARS_IN_HISTORY = 2_000;
25
+ /**
26
+ * Prune history to keep at most MAX_HISTORY_ITEMS. Oldest items are dropped
27
+ * first; they remain visible in terminal scrollback.
28
+ */
29
+ export function pruneHistory(items) {
30
+ if (items.length <= MAX_HISTORY_ITEMS)
31
+ return items;
32
+ return items.slice(items.length - MAX_HISTORY_ITEMS);
33
+ }
34
+ /** Truncate a string if it exceeds the history limit. */
35
+ function truncateResult(s) {
36
+ if (s.length <= MAX_RESULT_CHARS_IN_HISTORY)
37
+ return s;
38
+ return s.slice(0, MAX_RESULT_CHARS_IN_HISTORY) + "\n… (truncated)";
39
+ }
40
+ /**
41
+ * Trim large payload data from items that have been flushed to Static history.
42
+ * Ink already rendered them to the terminal — we only keep a truncated version
43
+ * to prevent multi-GB memory retention from tool results, server tool data, and
44
+ * sub-agent results.
45
+ *
46
+ * Works with any item shape via duck-typing: truncates `result` strings,
47
+ * clears `input`/`data` fields, and trims sub-agent result strings.
48
+ */
49
+ export function trimFlushedItems(items) {
50
+ return items.map((item) => {
51
+ const rec = item;
52
+ const patches = {};
53
+ let changed = false;
54
+ // Truncate tool result strings (ToolDoneItem, SubAgentInfo, etc.)
55
+ if (typeof rec.result === "string" && rec.result.length > MAX_RESULT_CHARS_IN_HISTORY) {
56
+ patches.result = truncateResult(rec.result);
57
+ changed = true;
58
+ }
59
+ // Clear server tool input/data — potentially large JSON payloads
60
+ if (rec.input !== undefined && rec.kind === "server_tool_done") {
61
+ patches.input = undefined;
62
+ changed = true;
63
+ }
64
+ if (rec.data !== undefined && rec.kind === "server_tool_done") {
65
+ patches.data = undefined;
66
+ changed = true;
67
+ }
68
+ // Trim tool group results
69
+ if (rec.kind === "tool_group" && Array.isArray(rec.tools)) {
70
+ const tools = rec.tools;
71
+ let toolsChanged = false;
72
+ const trimmedTools = tools.map((t) => {
73
+ if (typeof t.result === "string" && t.result.length > MAX_RESULT_CHARS_IN_HISTORY) {
74
+ toolsChanged = true;
75
+ return { ...t, result: truncateResult(t.result) };
76
+ }
77
+ return t;
78
+ });
79
+ if (toolsChanged) {
80
+ patches.tools = trimmedTools;
81
+ changed = true;
82
+ }
83
+ }
84
+ // Trim sub-agent group results
85
+ if (rec.kind === "subagent_group" && Array.isArray(rec.agents)) {
86
+ const agents = rec.agents;
87
+ let agentsChanged = false;
88
+ const trimmedAgents = agents.map((a) => {
89
+ if (typeof a.result === "string" && a.result.length > MAX_RESULT_CHARS_IN_HISTORY) {
90
+ agentsChanged = true;
91
+ return { ...a, result: truncateResult(a.result) };
92
+ }
93
+ return a;
94
+ });
95
+ if (agentsChanged) {
96
+ patches.agents = trimmedAgents;
97
+ changed = true;
98
+ }
99
+ }
100
+ return changed ? { ...item, ...patches } : item;
101
+ });
102
+ }
103
+ /**
104
+ * Called when `onTurnText` fires (end of each LLM turn that produced text).
105
+ * All previous items are guaranteed complete — tool calls from this turn
106
+ * finished before `turn_end` fired, and `onTurnText` fires inside `turn_end`.
107
+ *
108
+ * Returns the items to flush to history. The caller should then set liveItems
109
+ * to contain only the new text item.
110
+ */
111
+ export function flushOnTurnText(liveItems) {
112
+ return liveItems;
113
+ }
114
+ /**
115
+ * Called when `onTurnEnd` fires with a tool_use stop reason (LLM responded
116
+ * with only tool calls, no text). Flushes all items IF none are still pending
117
+ * (no `tool_start` without a corresponding `tool_done`).
118
+ *
119
+ * Returns { flushed, remaining } — flushed items go to history, remaining
120
+ * items stay in liveItems.
121
+ */
122
+ export function flushOnTurnEnd(liveItems, stopReason) {
123
+ if (stopReason !== "tool_use") {
124
+ return { flushed: [], remaining: liveItems };
125
+ }
126
+ const hasPendingToolStart = liveItems.some((item) => item.kind === "tool_start" ||
127
+ item.kind === "server_tool_start" ||
128
+ (item.kind === "tool_group" &&
129
+ (item.tools ?? []).some((t) => t.status === "running")));
130
+ if (hasPendingToolStart || liveItems.length === 0) {
131
+ return { flushed: [], remaining: liveItems };
132
+ }
133
+ return { flushed: liveItems, remaining: [] };
134
+ }
135
+ //# sourceMappingURL=live-item-flush.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-item-flush.js","sourceRoot":"","sources":["../../src/ui/live-item-flush.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=live-item-flush.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-item-flush.test.d.ts","sourceRoot":"","sources":["../../src/ui/live-item-flush.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,307 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { pruneHistory, flushOnTurnText, flushOnTurnEnd, MAX_HISTORY_ITEMS, } from "./live-item-flush.js";
3
+ // ── Test helpers ──────────────────────────────────────────
4
+ let idCounter = 0;
5
+ function id() {
6
+ return `test-${++idCounter}`;
7
+ }
8
+ function userItem() {
9
+ return { kind: "user", id: id() };
10
+ }
11
+ function assistantItem() {
12
+ return { kind: "assistant", id: id() };
13
+ }
14
+ function toolStart(toolCallId) {
15
+ return { kind: "tool_start", id: id(), toolCallId: toolCallId ?? id() };
16
+ }
17
+ function toolDone() {
18
+ return { kind: "tool_done", id: id() };
19
+ }
20
+ function subagentGroup() {
21
+ return { kind: "subagent_group", id: id() };
22
+ }
23
+ function errorItem() {
24
+ return { kind: "error", id: id() };
25
+ }
26
+ // ── pruneHistory ──────────────────────────────────────────
27
+ describe("pruneHistory", () => {
28
+ it("returns items unchanged when under the limit", () => {
29
+ const items = [userItem(), assistantItem(), toolDone()];
30
+ const result = pruneHistory(items);
31
+ expect(result).toBe(items); // same reference, no copy
32
+ });
33
+ it("returns items unchanged at exactly the limit", () => {
34
+ const items = Array.from({ length: MAX_HISTORY_ITEMS }, () => userItem());
35
+ const result = pruneHistory(items);
36
+ expect(result).toBe(items);
37
+ });
38
+ it("drops oldest items when over the limit", () => {
39
+ const items = Array.from({ length: MAX_HISTORY_ITEMS + 50 }, (_, i) => ({
40
+ kind: "user",
41
+ id: `item-${i}`,
42
+ }));
43
+ const result = pruneHistory(items);
44
+ expect(result).toHaveLength(MAX_HISTORY_ITEMS);
45
+ // Should keep the LAST 500 items (newest)
46
+ expect(result[0].id).toBe("item-50");
47
+ expect(result[result.length - 1].id).toBe(`item-${MAX_HISTORY_ITEMS + 49}`);
48
+ });
49
+ it("handles empty array", () => {
50
+ const result = pruneHistory([]);
51
+ expect(result).toEqual([]);
52
+ });
53
+ it("handles single item", () => {
54
+ const items = [userItem()];
55
+ const result = pruneHistory(items);
56
+ expect(result).toBe(items);
57
+ });
58
+ });
59
+ // ── flushOnTurnText ───────────────────────────────────────
60
+ describe("flushOnTurnText", () => {
61
+ it("returns all items for flushing when liveItems is non-empty", () => {
62
+ const items = [assistantItem(), toolDone(), toolDone()];
63
+ const flushed = flushOnTurnText(items);
64
+ expect(flushed).toBe(items);
65
+ expect(flushed).toHaveLength(3);
66
+ });
67
+ it("returns empty array when liveItems is empty", () => {
68
+ const flushed = flushOnTurnText([]);
69
+ expect(flushed).toEqual([]);
70
+ });
71
+ it("flushes tool_start items (they are from a completed turn)", () => {
72
+ // When onTurnText fires, even stale tool_start items from the same turn
73
+ // are complete (tool_end already replaced them in the normal flow, but
74
+ // if somehow a tool_start remains, the turn is still over).
75
+ const items = [assistantItem(), toolStart(), toolDone()];
76
+ const flushed = flushOnTurnText(items);
77
+ expect(flushed).toHaveLength(3);
78
+ });
79
+ it("flushes mixed item types from multi-turn accumulation", () => {
80
+ // Simulates items accumulated across multiple turns before flush was added
81
+ const items = [
82
+ userItem(),
83
+ assistantItem(),
84
+ toolDone(),
85
+ toolDone(),
86
+ assistantItem(),
87
+ toolDone(),
88
+ toolDone(),
89
+ toolDone(),
90
+ assistantItem(),
91
+ ];
92
+ const flushed = flushOnTurnText(items);
93
+ expect(flushed).toHaveLength(9);
94
+ });
95
+ it("flushes subagent group items", () => {
96
+ const items = [assistantItem(), subagentGroup(), toolDone()];
97
+ const flushed = flushOnTurnText(items);
98
+ expect(flushed).toHaveLength(3);
99
+ });
100
+ it("flushes error items", () => {
101
+ const items = [errorItem(), assistantItem()];
102
+ const flushed = flushOnTurnText(items);
103
+ expect(flushed).toHaveLength(2);
104
+ });
105
+ });
106
+ // ── flushOnTurnEnd ────────────────────────────────────────
107
+ describe("flushOnTurnEnd", () => {
108
+ describe("with tool_use stop reason", () => {
109
+ it("flushes all items when no pending tool_start exists", () => {
110
+ const items = [assistantItem(), toolDone(), toolDone()];
111
+ const { flushed, remaining } = flushOnTurnEnd(items, "tool_use");
112
+ expect(flushed).toBe(items);
113
+ expect(flushed).toHaveLength(3);
114
+ expect(remaining).toEqual([]);
115
+ });
116
+ it("does NOT flush when a tool_start is still pending", () => {
117
+ const items = [assistantItem(), toolStart(), toolDone()];
118
+ const { flushed, remaining } = flushOnTurnEnd(items, "tool_use");
119
+ expect(flushed).toEqual([]);
120
+ expect(remaining).toBe(items);
121
+ expect(remaining).toHaveLength(3);
122
+ });
123
+ it("does NOT flush when liveItems is empty", () => {
124
+ const { flushed, remaining } = flushOnTurnEnd([], "tool_use");
125
+ expect(flushed).toEqual([]);
126
+ expect(remaining).toEqual([]);
127
+ });
128
+ it("does NOT flush when only tool_start items exist (all pending)", () => {
129
+ const items = [toolStart(), toolStart()];
130
+ const { flushed, remaining } = flushOnTurnEnd(items, "tool_use");
131
+ expect(flushed).toEqual([]);
132
+ expect(remaining).toBe(items);
133
+ });
134
+ it("flushes when items include subagent groups with no pending tool_start", () => {
135
+ const items = [subagentGroup(), toolDone()];
136
+ const { flushed, remaining } = flushOnTurnEnd(items, "tool_use");
137
+ expect(flushed).toHaveLength(2);
138
+ expect(remaining).toEqual([]);
139
+ });
140
+ });
141
+ describe("with non-tool_use stop reasons", () => {
142
+ it("does NOT flush for end_turn stop reason", () => {
143
+ const items = [assistantItem(), toolDone()];
144
+ const { flushed, remaining } = flushOnTurnEnd(items, "end_turn");
145
+ expect(flushed).toEqual([]);
146
+ expect(remaining).toBe(items);
147
+ });
148
+ it("does NOT flush for stop_sequence stop reason", () => {
149
+ const items = [assistantItem()];
150
+ const { flushed, remaining } = flushOnTurnEnd(items, "stop_sequence");
151
+ expect(flushed).toEqual([]);
152
+ expect(remaining).toBe(items);
153
+ });
154
+ it("does NOT flush for max_tokens stop reason", () => {
155
+ const items = [assistantItem(), toolDone(), toolDone()];
156
+ const { flushed, remaining } = flushOnTurnEnd(items, "max_tokens");
157
+ expect(flushed).toEqual([]);
158
+ expect(remaining).toBe(items);
159
+ });
160
+ });
161
+ });
162
+ // ── Integration: simulated agent run scenarios ────────────
163
+ describe("flush integration scenarios", () => {
164
+ /**
165
+ * Simulates the state management that App.tsx does with setLiveItems
166
+ * and setHistory, using the extracted pure functions.
167
+ */
168
+ function simulateRun(turns) {
169
+ let liveItems = [];
170
+ let history = [];
171
+ for (const turn of turns) {
172
+ // Simulate tool calls happening before turn_end
173
+ for (let i = 0; i < (turn.tools ?? 0); i++) {
174
+ liveItems = [...liveItems, toolStart()];
175
+ // Tool completes (replaces tool_start with tool_done in real code)
176
+ liveItems = liveItems.map((item) => item.kind === "tool_start" && liveItems.indexOf(item) === liveItems.length - 1
177
+ ? toolDone()
178
+ : item);
179
+ }
180
+ // onTurnEnd fires first
181
+ const turnEndResult = flushOnTurnEnd(liveItems, turn.stopReason);
182
+ if (turnEndResult.flushed.length > 0) {
183
+ history = pruneHistory([...history, ...turnEndResult.flushed]);
184
+ }
185
+ liveItems = turnEndResult.remaining;
186
+ // onTurnText fires second (only if text exists)
187
+ if (turn.text) {
188
+ const flushed = flushOnTurnText(liveItems);
189
+ if (flushed.length > 0) {
190
+ history = pruneHistory([...history, ...flushed]);
191
+ }
192
+ liveItems = [assistantItem()]; // new text item
193
+ }
194
+ }
195
+ return { liveItems, history };
196
+ }
197
+ it("scenario: simple text-only conversation stays bounded", () => {
198
+ const { liveItems, history } = simulateRun([
199
+ { text: "Hello!", stopReason: "end_turn" },
200
+ { text: "How can I help?", stopReason: "end_turn" },
201
+ { text: "Sure thing.", stopReason: "end_turn" },
202
+ ]);
203
+ // Only the last text item should be live
204
+ expect(liveItems).toHaveLength(1);
205
+ expect(liveItems[0].kind).toBe("assistant");
206
+ // Previous text items moved to history
207
+ expect(history).toHaveLength(2);
208
+ });
209
+ it("scenario: text + tools cycle stays bounded", () => {
210
+ const { liveItems, history } = simulateRun([
211
+ { text: "Let me check.", tools: 3, stopReason: "end_turn" },
212
+ { text: "Found it.", tools: 2, stopReason: "end_turn" },
213
+ { text: "Done.", stopReason: "end_turn" },
214
+ ]);
215
+ expect(liveItems).toHaveLength(1);
216
+ // 3 tool_done + 1 assistant + 2 tool_done + 1 assistant = 7
217
+ expect(history).toHaveLength(7);
218
+ });
219
+ it("scenario: tool-only turns flush correctly", () => {
220
+ const { liveItems, history } = simulateRun([
221
+ { tools: 3, stopReason: "tool_use" }, // tool-only, flushed by onTurnEnd
222
+ { tools: 2, stopReason: "tool_use" }, // tool-only, flushed by onTurnEnd
223
+ { text: "All done.", stopReason: "end_turn" },
224
+ ]);
225
+ expect(liveItems).toHaveLength(1);
226
+ expect(liveItems[0].kind).toBe("assistant");
227
+ expect(history).toHaveLength(5); // 3 + 2 tool_done items
228
+ });
229
+ it("scenario: many turns — liveItems never exceeds current turn size", () => {
230
+ const turns = Array.from({ length: 50 }, (_, i) => ({
231
+ text: `Turn ${i}`,
232
+ tools: 5,
233
+ stopReason: "end_turn",
234
+ }));
235
+ const { liveItems } = simulateRun(turns);
236
+ // Only the last text item should remain live
237
+ expect(liveItems).toHaveLength(1);
238
+ });
239
+ it("scenario: alternating text and tool-only turns", () => {
240
+ const { liveItems, history } = simulateRun([
241
+ { text: "Searching...", tools: 2, stopReason: "end_turn" },
242
+ { tools: 3, stopReason: "tool_use" }, // tool-only
243
+ { text: "Found results.", tools: 1, stopReason: "end_turn" },
244
+ { tools: 4, stopReason: "tool_use" }, // tool-only
245
+ { text: "Complete.", stopReason: "end_turn" },
246
+ ]);
247
+ expect(liveItems).toHaveLength(1);
248
+ // History: (2 tools + 1 text) + (3 tools) + (1 tool + 1 text) + (4 tools) = 12
249
+ expect(history).toHaveLength(12);
250
+ });
251
+ it("scenario: stress test — 100 turns with 10 tools each", () => {
252
+ const turns = Array.from({ length: 100 }, () => ({
253
+ text: "Working...",
254
+ tools: 10,
255
+ stopReason: "end_turn",
256
+ }));
257
+ const { liveItems, history } = simulateRun(turns);
258
+ expect(liveItems).toHaveLength(1);
259
+ // Total items: 100 turns * (10 tools + 1 text) - 1 (last text is live) = 1099
260
+ // But pruneHistory caps at 500
261
+ expect(history).toHaveLength(MAX_HISTORY_ITEMS);
262
+ });
263
+ it("REGRESSION: liveItems retained after agent finishes, flushed on next submit", () => {
264
+ // After the agent finishes its last turn, the final AssistantItem stays
265
+ // in liveItems (NOT moved to Static via useEffect). This prevents Ink
266
+ // cursor-math glitches that caused text to get cut off during the
267
+ // live→Static transition. Items are flushed when the user submits the
268
+ // next message (simulated here by flushing before adding a new userItem).
269
+ let liveItems = [];
270
+ let history = [];
271
+ // Agent produces a response (turn 1)
272
+ const flushed1 = flushOnTurnText(liveItems);
273
+ if (flushed1.length > 0)
274
+ history = pruneHistory([...history, ...flushed1]);
275
+ liveItems = [assistantItem()];
276
+ // Agent finishes — liveItems NOT cleared (no useEffect flush)
277
+ // The final response stays in liveItems
278
+ expect(liveItems).toHaveLength(1);
279
+ expect(liveItems[0].kind).toBe("assistant");
280
+ // User submits next message — NOW flush to Static
281
+ if (liveItems.length > 0) {
282
+ history = pruneHistory([...history, ...liveItems]);
283
+ }
284
+ liveItems = [userItem()];
285
+ // Previous AssistantItem is now in history
286
+ expect(history).toHaveLength(1);
287
+ expect(history[0].kind).toBe("assistant");
288
+ // New user message is in liveItems
289
+ expect(liveItems).toHaveLength(1);
290
+ expect(liveItems[0].kind).toBe("user");
291
+ });
292
+ it("REGRESSION: without flush, liveItems would grow unbounded", () => {
293
+ // This test documents the old behavior — liveItems would contain
294
+ // ALL items from ALL turns. With 20 turns of 5 tools each, that's
295
+ // 20 * (5 + 1) = 120 items re-rendered on every timer tick.
296
+ // With the fix, liveItems is always at most 1 item (current text).
297
+ const turns = Array.from({ length: 20 }, () => ({
298
+ text: "Working...",
299
+ tools: 5,
300
+ stopReason: "end_turn",
301
+ }));
302
+ const { liveItems } = simulateRun(turns);
303
+ // The whole point: liveItems MUST be bounded
304
+ expect(liveItems.length).toBeLessThanOrEqual(1);
305
+ });
306
+ });
307
+ //# sourceMappingURL=live-item-flush.test.js.map