@pheem49/mint 1.17.0 → 1.18.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 (421) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +2 -2
  3. package/README.md +8 -2
  4. package/Release_Note.md +222 -71
  5. package/crates/mint-cli/src/agent/diff_render.rs +66 -44
  6. package/crates/mint-cli/src/agent/live_status.rs +21 -4
  7. package/crates/mint-cli/src/agent/markdown_render.rs +342 -5
  8. package/crates/mint-cli/src/agent.rs +25 -10
  9. package/crates/mint-cli/src/commands/agent.rs +4 -92
  10. package/crates/mint-cli/src/commands/browser.rs +2 -15
  11. package/crates/mint-cli/src/commands/integrations.rs +3 -3
  12. package/crates/mint-cli/src/commands/mod.rs +0 -3
  13. package/crates/mint-cli/src/commands/tailnet_web.rs +1 -0
  14. package/crates/mint-cli/src/interactive/chat_tui/dialog.rs +572 -0
  15. package/crates/mint-cli/src/interactive/chat_tui/mod.rs +1427 -0
  16. package/crates/mint-cli/src/interactive/chat_tui/render.rs +1524 -0
  17. package/crates/mint-cli/src/interactive/chat_tui/selection.rs +401 -0
  18. package/crates/mint-cli/src/interactive/chat_tui/state.rs +1833 -0
  19. package/crates/mint-cli/src/interactive/chat_tui/tests.rs +3414 -0
  20. package/crates/mint-cli/src/interactive/chat_tui/thought.rs +202 -0
  21. package/crates/mint-cli/src/interactive/chat_tui/transcript.rs +728 -0
  22. package/crates/mint-cli/src/interactive/chat_tui/watermark.rs +148 -0
  23. package/crates/mint-cli/src/interactive/command_ui.rs +53 -5
  24. package/crates/mint-cli/src/interactive/composer.rs +186 -0
  25. package/crates/mint-cli/src/interactive/format.rs +78 -3
  26. package/crates/mint-cli/src/interactive/input_box.rs +61 -1
  27. package/crates/mint-cli/src/interactive/resume_picker.rs +367 -25
  28. package/crates/mint-cli/src/interactive/slash_commands.rs +648 -371
  29. package/crates/mint-cli/src/interactive/tool_activity.rs +93 -20
  30. package/crates/mint-cli/src/interactive.rs +37 -0
  31. package/crates/mint-cli/src/main.rs +6 -3
  32. package/crates/mint-cli/src/markdown.rs +2 -37
  33. package/crates/mint-cli/src/onboard.rs +700 -282
  34. package/crates/mint-cli/src/setup.rs +1342 -624
  35. package/crates/mint-cli/src/terminal_theme.rs +159 -35
  36. package/crates/mint-cli/tests/library_intake.rs +1 -0
  37. package/crates/mint-core/src/agent/chat/anthropic.rs +318 -0
  38. package/crates/mint-core/src/agent/chat/custom.rs +149 -0
  39. package/crates/mint-core/src/agent/chat/gemini.rs +518 -0
  40. package/crates/mint-core/src/agent/chat/mod.rs +857 -0
  41. package/crates/mint-core/src/agent/chat/ollama.rs +321 -0
  42. package/crates/mint-core/src/agent/chat/openai.rs +582 -0
  43. package/crates/mint-core/src/agent/chat/streaming.rs +469 -0
  44. package/crates/mint-core/src/agent/chat/tests.rs +1479 -0
  45. package/crates/mint-core/src/agent/memory.rs +56 -2
  46. package/crates/mint-core/src/agent/tasks.rs +2 -2
  47. package/crates/mint-core/src/api_server/routes/chat.rs +24 -2
  48. package/crates/mint-core/src/api_server/routes/gemini_live.rs +14 -1
  49. package/crates/mint-core/src/api_server/routes/html_preview.rs +1 -8
  50. package/crates/mint-core/src/api_server/routes/sessions.rs +21 -0
  51. package/crates/mint-core/src/avatar_bridge.rs +0 -22
  52. package/crates/mint-core/src/browser/cdp.rs +10 -4
  53. package/crates/mint-core/src/browser/headless.rs +582 -0
  54. package/crates/mint-core/src/browser/input.rs +7 -62
  55. package/crates/mint-core/src/browser/interact.rs +0 -14
  56. package/crates/mint-core/src/browser/mod.rs +8 -29
  57. package/crates/mint-core/src/browser/navigate.rs +2 -36
  58. package/crates/mint-core/src/browser/runtime.rs +2 -4
  59. package/crates/mint-core/src/browser/session.rs +50 -466
  60. package/crates/mint-core/src/browser/workflow.rs +142 -32
  61. package/crates/mint-core/src/cron/store.rs +2 -2
  62. package/crates/mint-core/src/integrations/mcp.rs +6 -8
  63. package/crates/mint-core/src/lib.rs +0 -5
  64. package/crates/mint-core/src/media/gemini_live.rs +636 -86
  65. package/crates/mint-core/src/media/library/migration.rs +26 -0
  66. package/crates/mint-core/src/media/library/tests.rs +1 -1
  67. package/crates/mint-core/src/media/library/thumbnails.rs +8 -2
  68. package/crates/mint-core/src/media/pictures.rs +1 -1
  69. package/crates/mint-core/src/media/tool_images.rs +92 -1
  70. package/crates/mint-core/src/mods/manager.rs +101 -35
  71. package/crates/mint-core/src/orchestration/context_render.rs +441 -24
  72. package/crates/mint-core/src/orchestration/decision_parsing.rs +531 -16
  73. package/crates/mint-core/src/orchestration/full_skill_result_tests.rs +12 -0
  74. package/crates/mint-core/src/orchestration/loop_guard.rs +145 -0
  75. package/crates/mint-core/src/orchestration/mcp_outcome_tests.rs +864 -0
  76. package/crates/mint-core/src/orchestration/memory_skill.rs +88 -28
  77. package/crates/mint-core/src/orchestration/mod.rs +320 -4249
  78. package/crates/mint-core/src/orchestration/reliability.rs +67 -27
  79. package/crates/mint-core/src/orchestration/review_regressions.rs +5 -0
  80. package/crates/mint-core/src/orchestration/tests.rs +1665 -0
  81. package/crates/mint-core/src/orchestration/tools/dispatch.rs +967 -0
  82. package/crates/mint-core/src/orchestration/tools/files.rs +1 -0
  83. package/crates/mint-core/src/orchestration/tools/mod.rs +5 -1
  84. package/crates/mint-core/src/orchestration/tools/planning.rs +102 -42
  85. package/crates/mint-core/src/orchestration/tools/shell.rs +129 -58
  86. package/crates/mint-core/src/orchestration/truncation.rs +170 -13
  87. package/crates/mint-core/src/orchestration/types.rs +648 -0
  88. package/crates/mint-core/src/orchestration/verification.rs +0 -3
  89. package/crates/mint-core/src/orchestration/workspace_helpers.rs +24 -6
  90. package/crates/mint-core/src/prompts/agent.rs +52 -76
  91. package/crates/mint-core/src/prompts/tool_catalog.rs +8 -151
  92. package/crates/mint-core/src/search/files.rs +20 -0
  93. package/crates/mint-core/src/slash/mod.rs +228 -265
  94. package/crates/mint-core/src/slash/models.rs +6 -1
  95. package/crates/mint-core/src/system/bg_shell.rs +1 -0
  96. package/crates/mint-core/src/system/config.rs +176 -34
  97. package/crates/mint-mod-runtime/src/lib.rs +37 -23
  98. package/out/web/assets/ArtifactPreviewPanel-Cx6BVOOF.js +2 -0
  99. package/out/web/assets/CalculationCard-D2E74u5q.js +1 -0
  100. package/out/web/assets/CliSessionsView-ZbdtSjvK.css +1 -0
  101. package/out/web/assets/{CliSessionsView-MI8kXMAb.js → CliSessionsView-z5i6qGyb.js} +1 -1
  102. package/out/web/assets/CodeReviewPage-BK2bOScd.css +1 -0
  103. package/out/web/assets/CodeReviewPage-Dp6zHuHu.js +2 -0
  104. package/out/web/assets/{CommandPalette-BD8SlTE5.js → CommandPalette-DaKlzbKs.js} +1 -1
  105. package/out/web/assets/ImageGenCard-BMCJHP9O.js +1 -0
  106. package/out/web/assets/ImageSearchCard-Cld_SOUg.js +1 -0
  107. package/out/web/assets/{ImageStudioPanel-BP7N4rLr.js → ImageStudioPanel-B5N7TjeB.js} +1 -1
  108. package/out/web/assets/LibraryPage-CMB89rJ0.css +1 -0
  109. package/out/web/assets/LibraryPage-DbZEi4W_.js +2 -0
  110. package/out/web/assets/LinkedFoldersView-9B9DIPoZ.js +1 -0
  111. package/out/web/assets/{McpRegistryPicker-CoS83vtc.js → McpRegistryPicker-DnXPlzI7.js} +1 -1
  112. package/out/web/assets/McpServersView-CiNBNUl4.js +1 -0
  113. package/out/web/assets/MermaidCard-BBDNEXbY.js +2 -0
  114. package/out/web/assets/MintDashboard-C_UYPoNV.js +119 -0
  115. package/out/web/assets/MintDashboard-DTd8C_c8.css +1 -0
  116. package/out/web/assets/PlanCard-C2x5pajq.js +1 -0
  117. package/out/web/assets/{PluginsView-jBYmcKh4.js → PluginsView-DoTg9MVa.js} +1 -1
  118. package/out/web/assets/RewindModal-B5oowrR0.js +1 -0
  119. package/out/web/assets/{ScheduledTasksView-5uw9-V9H.js → ScheduledTasksView-BD5DVPkZ.js} +1 -1
  120. package/out/web/assets/{SelectField-DXBQtrUK.js → SelectField-Cy6CghrU.js} +1 -1
  121. package/out/web/assets/SettingsWindow-BYl_oCFh.js +4 -0
  122. package/out/web/assets/SettingsWindow-Ckcgnjt8.css +1 -0
  123. package/out/web/assets/{SkillsView-C-gp9J7m.js → SkillsView-CIeWwq5z.js} +1 -1
  124. package/out/web/assets/StockCard-DxUciKyp.js +1 -0
  125. package/out/web/assets/ToolSurfacePage-qBgs_VHq.css +1 -0
  126. package/out/web/assets/ToolSurfacePage-saVbVrkE.js +38 -0
  127. package/out/web/assets/{UiFeatureCard-6BwxTOCe.js → UiFeatureCard-CdY0hGWy.js} +1 -1
  128. package/out/web/assets/{UiGridCard-DvZcLNxb.js → UiGridCard-BBK3I5hC.js} +1 -1
  129. package/out/web/assets/UiMockupWidget-CpR9EDuc.js +1 -0
  130. package/out/web/assets/{VeoStudioPanel-DOj7_WR3.css → VeoStudioPanel-9kU9YLEN.css} +1 -1
  131. package/out/web/assets/{VeoStudioPanel-DfsOJKv0.js → VeoStudioPanel-Co6-wEWM.js} +2 -2
  132. package/out/web/assets/WeatherCard-Buzi7Vfu.js +1 -0
  133. package/out/web/assets/WorkspacePdfPreview-Bi8Zzyir.js +2 -0
  134. package/out/web/assets/{abnfDiagram-VCTEODGH-BOo3Ckng.js → abnfDiagram-VCTEODGH-CMfR95N2.js} +1 -1
  135. package/out/web/assets/{architecture-7GRP2DOG-ZOwheW3n.js → architecture-7GRP2DOG-DhAM4HyO.js} +1 -1
  136. package/out/web/assets/{architectureDiagram-5GKGNRK7-DC2FDzVd.js → architectureDiagram-5GKGNRK7-Yk3TUJ_F.js} +1 -1
  137. package/out/web/assets/{blockDiagram-I7D4REHJ-BrKFWdAH.js → blockDiagram-I7D4REHJ-X9fBgJik.js} +1 -1
  138. package/out/web/assets/{c4Diagram-7LVT6UL2-BHTHK1Yd.js → c4Diagram-7LVT6UL2-Bk9x7N-8.js} +1 -1
  139. package/out/web/assets/{chunk-4HAMMTFA-BYFkZwNj.js → chunk-4HAMMTFA-Dx5fk1_0.js} +1 -1
  140. package/out/web/assets/{chunk-75Z2AOVW-BSKfSKSA.js → chunk-75Z2AOVW-BszTQoM4.js} +1 -1
  141. package/out/web/assets/{chunk-DU6HZSFF-vIhsiW6n.js → chunk-DU6HZSFF-DK-As3rk.js} +1 -1
  142. package/out/web/assets/{chunk-F27PBJKO-BlYpW9xq.js → chunk-F27PBJKO-LmncZZIS.js} +1 -1
  143. package/out/web/assets/{chunk-GMAD6QVW-BIBz_Ug5.js → chunk-GMAD6QVW-DSQ_sDRJ.js} +1 -1
  144. package/out/web/assets/{chunk-GVQU2GXP-NPSHxeqC.js → chunk-GVQU2GXP-Cno3ulje.js} +1 -1
  145. package/out/web/assets/{chunk-IMKFNOWR-YlJK8_Fp.js → chunk-IMKFNOWR-bIzfY9Jc.js} +1 -1
  146. package/out/web/assets/{chunk-L3NEJ4N5-DMYi4-af.js → chunk-L3NEJ4N5-Dl6eOa0D.js} +1 -1
  147. package/out/web/assets/{chunk-OSK3NFVY-Begd3-jA.js → chunk-OSK3NFVY-BaUGJ-cM.js} +1 -1
  148. package/out/web/assets/{chunk-P2QGCYS3-D1I4xFHN.js → chunk-P2QGCYS3-_066ndhH.js} +1 -1
  149. package/out/web/assets/{chunk-POPQ4Y6H-CafZZzNE.js → chunk-POPQ4Y6H-YT1AKhTv.js} +1 -1
  150. package/out/web/assets/{chunk-PWAF6VOD-Dwh6_Lnt.js → chunk-PWAF6VOD-CrEgDsxh.js} +1 -1
  151. package/out/web/assets/{chunk-SHT3W25Y-B0XrCSEb.js → chunk-SHT3W25Y-_KwaRLOl.js} +1 -1
  152. package/out/web/assets/{chunk-SVP7TREG-CxLsddxC.js → chunk-SVP7TREG-BQ1qb32L.js} +1 -1
  153. package/out/web/assets/{chunk-TICWLB2K-y156QInb.js → chunk-TICWLB2K-BDyehCtg.js} +1 -1
  154. package/out/web/assets/classDiagram-ZZMXUADV-BoSpzRCJ.js +1 -0
  155. package/out/web/assets/classDiagram-v2-VYDZK3BY-BoSpzRCJ.js +1 -0
  156. package/out/web/assets/{config-DqXJXeZM.css → config-2suVFPZg.css} +1 -1
  157. package/out/web/assets/config-CkRCdkxn.js +2 -0
  158. package/out/web/assets/{cynefin-OW5HDTMX-SMVvrLf-.js → cynefin-OW5HDTMX-Dl_31aaN.js} +1 -1
  159. package/out/web/assets/{cynefinDiagram-5FMLGOSQ-rZznN24T.js → cynefinDiagram-5FMLGOSQ-Bpir8jha.js} +1 -1
  160. package/out/web/assets/{dagre-GXQ25YYZ-BOToydFu.js → dagre-GXQ25YYZ-D9xu3nsq.js} +1 -1
  161. package/out/web/assets/{diagram-S7CK7UJ4-TMVaGQEJ.js → diagram-S7CK7UJ4-DeveoEeB.js} +1 -1
  162. package/out/web/assets/{diagram-UQ7AKVKN-C2JK-GP2.js → diagram-UQ7AKVKN-p4xWRESV.js} +1 -1
  163. package/out/web/assets/{diagram-VSXAHHWV-Cw8VSi2K.js → diagram-VSXAHHWV-C7qz9NDF.js} +1 -1
  164. package/out/web/assets/{diagram-VX7I27RA-DXp4fKk9.js → diagram-VX7I27RA-DRiCa3Vd.js} +1 -1
  165. package/out/web/assets/{diagram-Z3DM3KII-CzSicYiW.js → diagram-Z3DM3KII-Bt7zduQX.js} +1 -1
  166. package/out/web/assets/{ebnfDiagram-PWID7BFC-Dqj-zqPu.js → ebnfDiagram-PWID7BFC-DV9WpwdR.js} +1 -1
  167. package/out/web/assets/{erDiagram-RLTQ6QDP-DZwFZAuD.js → erDiagram-RLTQ6QDP-BJ_nwG2m.js} +1 -1
  168. package/out/web/assets/{eventmodeling-NTZA5JFV-C0O-Lvgp.js → eventmodeling-NTZA5JFV-BTBMrfVa.js} +1 -1
  169. package/out/web/assets/flowDiagram-HODETNUW-Cdlq3cJL.js +1 -0
  170. package/out/web/assets/{ganttDiagram-EL5Y4UJY-Da4ULXTl.js → ganttDiagram-EL5Y4UJY-DQLdsXrC.js} +1 -1
  171. package/out/web/assets/{gitGraph-4MIJSDKK-B9wNFZIP.js → gitGraph-4MIJSDKK-CL1OEOQ_.js} +1 -1
  172. package/out/web/assets/{gitGraphDiagram-WWUBYQGX-RjrhB0MP.js → gitGraphDiagram-WWUBYQGX-8cSi42H7.js} +1 -1
  173. package/out/web/assets/index-BocxnrDG.js +168 -0
  174. package/out/web/assets/index-yeh_r55a.css +1 -0
  175. package/out/web/assets/{info-A6RAGUB7-B3dfCpeY.js → info-A6RAGUB7-C1zsX5sc.js} +1 -1
  176. package/out/web/assets/{infoDiagram-27XIBGKW-QVWkLVx7.js → infoDiagram-27XIBGKW-DsRYtIyI.js} +1 -1
  177. package/out/web/assets/{ishikawaDiagram-5VMMS53U-CLFNIRI9.js → ishikawaDiagram-5VMMS53U-B2crwIXj.js} +1 -1
  178. package/out/web/assets/{journeyDiagram-3NMN7TZE-fgOQadYF.js → journeyDiagram-3NMN7TZE-BTH-GyVR.js} +1 -1
  179. package/out/web/assets/{kanban-definition-UXKFOSKX-G3Q_9g9O.js → kanban-definition-UXKFOSKX-BeRfCnuA.js} +1 -1
  180. package/out/web/assets/{mermaid-parser.core-v3Jm7nco.js → mermaid-parser.core-BJwgCBBc.js} +3 -3
  181. package/out/web/assets/{mermaid.core-DTEjLUA1.js → mermaid.core-CsDDodN8.js} +4 -4
  182. package/out/web/assets/{mindmap-definition-YA3MSWOX-C_D42_fF.js → mindmap-definition-YA3MSWOX-B42efOQ1.js} +1 -1
  183. package/out/web/assets/{modelManager-BPLycQrN.js → modelManager-B3ZFMh8v.js} +2 -2
  184. package/out/web/assets/{oauthManager-BUK7ic7q.js → oauthManager-Ch5Du2fF.js} +1 -1
  185. package/out/web/assets/{packet-AYTQ26CC-B-R-SHlF.js → packet-AYTQ26CC-B6xUyE6r.js} +1 -1
  186. package/out/web/assets/{pegDiagram-XKGWAZYB-Da2q_giz.js → pegDiagram-XKGWAZYB-1LabVycL.js} +1 -1
  187. package/out/web/assets/{pie-WAS4IAKB-BC8F0N7-.js → pie-WAS4IAKB-DJwo-nf_.js} +1 -1
  188. package/out/web/assets/{pieDiagram-E7YTZNPT-Iy9REJNC.js → pieDiagram-E7YTZNPT-icmAqzy4.js} +1 -1
  189. package/out/web/assets/{quadrantDiagram-AXDQQJYC-ztsiYw7h.js → quadrantDiagram-AXDQQJYC-CZ-wEx41.js} +1 -1
  190. package/out/web/assets/{radar-RG4KPBEZ-B_1P8Z_z.js → radar-RG4KPBEZ-D7d-Xriw.js} +1 -1
  191. package/out/web/assets/{railroad-74A4TZTK-DuTqczSo.js → railroad-74A4TZTK-viuhdYbs.js} +1 -1
  192. package/out/web/assets/{railroad-abnf-HS5TGJTU-Df29WLmp.js → railroad-abnf-HS5TGJTU-DNXiCNea.js} +1 -1
  193. package/out/web/assets/{railroad-ebnf-LZEXJU2U-ePFMo-qp.js → railroad-ebnf-LZEXJU2U-nkSu1-0C.js} +1 -1
  194. package/out/web/assets/{railroad-peg-WCYAUIDC-DYXLW4_E.js → railroad-peg-WCYAUIDC-BhbMeQzz.js} +1 -1
  195. package/out/web/assets/{railroadDiagram-O6MQD6OU-LStnZf2S.js → railroadDiagram-O6MQD6OU-CLpG1jUX.js} +1 -1
  196. package/out/web/assets/{requirementDiagram-BXWQKSXE-DmzonMH-.js → requirementDiagram-BXWQKSXE-kRDrvWrc.js} +1 -1
  197. package/out/web/assets/{sankeyDiagram-P5KCCOFB-B8vjmUU1.js → sankeyDiagram-P5KCCOFB-DxAwmq4x.js} +1 -1
  198. package/out/web/assets/{sequenceDiagram-WJ2MYXX4-CTnplOAX.js → sequenceDiagram-WJ2MYXX4-aLgY-6Wz.js} +1 -1
  199. package/out/web/assets/{stateDiagram-D77RDMKH-BFrIaLdI.js → stateDiagram-D77RDMKH-D5QWfOhp.js} +1 -1
  200. package/out/web/assets/stateDiagram-v2-MP3YSRHH-D6OiZoAP.js +1 -0
  201. package/out/web/assets/{swimlanes-42K2YHIH-BXGngUNU.js → swimlanes-42K2YHIH-CRs5UyA-.js} +1 -1
  202. package/out/web/assets/swimlanesDiagram-VR7AAH4N-UgEXIsV0.js +8 -0
  203. package/out/web/assets/syntaxHighlight-C-wAGT84.js +3 -0
  204. package/out/web/assets/{timeline-definition-24CTP7MA-CegH7n43.js → timeline-definition-24CTP7MA-Denqd9qb.js} +1 -1
  205. package/out/web/assets/{treeView-Q6P3EWNA-DTpJv2Tw.js → treeView-Q6P3EWNA-0my3Z3s4.js} +1 -1
  206. package/out/web/assets/{treemap-WGGIJYW6-i8M9iJU6.js → treemap-WGGIJYW6-B_EjkLYq.js} +1 -1
  207. package/out/web/assets/{vennDiagram-4TSXK5OY-Dpzo1S8U.js → vennDiagram-4TSXK5OY-DxVg3Ewr.js} +1 -1
  208. package/out/web/assets/{wardley-WFR3VGLG-BrNsgtfw.js → wardley-WFR3VGLG-CGD-Tj4p.js} +1 -1
  209. package/out/web/assets/{wardleyDiagram-VM6X3IG4-lQ92LFE1.js → wardleyDiagram-VM6X3IG4-CmfS39Xm.js} +1 -1
  210. package/out/web/assets/{xychartDiagram-S5SC5T6Z-BaOaEoBd.js → xychartDiagram-S5SC5T6Z-BVM9bKis.js} +1 -1
  211. package/out/web/index-web.html +2 -2
  212. package/out/web/index.html +2 -2
  213. package/package.json +2 -2
  214. package/shared-modules/CODEOWNERS +3 -3
  215. package/shared-modules/SDL2/SDL2_mixer.json +27 -0
  216. package/shared-modules/appstream/README.md +13 -0
  217. package/shared-modules/appstream/appstream-compose.json +111 -0
  218. package/shared-modules/dbus-glib/dbus-glib.json +2 -2
  219. package/shared-modules/libportal/libportal.json +23 -0
  220. package/shared-modules/linux-audio/fluidsynth2-static.json +2 -2
  221. package/shared-modules/linux-audio/fluidsynth2.json +2 -2
  222. package/shared-modules/linux-audio/libinstpatch.json +2 -2
  223. package/shared-modules/linux-audio/liblo-static.json +3 -0
  224. package/shared-modules/luajit/luajit.json +2 -2
  225. package/shared-modules/squashfs-tools/squashfs-tools.json +0 -3
  226. package/slash-commands.json +7 -2
  227. package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  228. package/src/renderer/shared/agentProgress.ts +51 -3
  229. package/src/renderer/shared/components/Settings/AutomationTab.tsx +2 -56
  230. package/src/renderer/shared/components/Settings/AvatarCropModal.tsx +15 -1
  231. package/src/renderer/shared/components/Settings/GeneralTab.tsx +25 -12
  232. package/src/renderer/shared/components/Settings/ThemeTab.test.tsx +45 -0
  233. package/src/renderer/shared/components/Settings/ThemeTab.tsx +106 -3
  234. package/src/renderer/shared/components/activity/AgentActivityDrawer.tsx +67 -17
  235. package/src/renderer/shared/components/activity/AgentActivityTable.tsx +376 -134
  236. package/src/renderer/shared/components/activity/AskUserActivityCard.test.tsx +122 -0
  237. package/src/renderer/shared/components/activity/AskUserActivityCard.tsx +70 -0
  238. package/src/renderer/shared/components/activity/McpToolResults.tsx +39 -2
  239. package/src/renderer/shared/components/activity/SubagentDagView.tsx +7 -7
  240. package/src/renderer/shared/components/activity/ViewedImages.tsx +41 -2
  241. package/src/renderer/shared/components/app/ChunkErrorBoundary.tsx +3 -3
  242. package/src/renderer/shared/components/app/CommandPalette.tsx +7 -7
  243. package/src/renderer/shared/components/app/DesktopTitlebar.tsx +2 -2
  244. package/src/renderer/shared/components/app/MintDashboard.tsx +27 -17
  245. package/src/renderer/shared/components/app/ModalErrorBoundary.tsx +3 -3
  246. package/src/renderer/shared/components/cards/CalculationCard.tsx +3 -3
  247. package/src/renderer/shared/components/cards/ImageGenCard.tsx +2 -2
  248. package/src/renderer/shared/components/cards/ImageSearchCard.tsx +2 -2
  249. package/src/renderer/shared/components/cards/MermaidCard.tsx +1 -1
  250. package/src/renderer/shared/components/cards/PlanCard.test.tsx +41 -0
  251. package/src/renderer/shared/components/cards/PlanCard.tsx +32 -0
  252. package/src/renderer/shared/components/cards/ReleaseNotesCard.test.tsx +29 -0
  253. package/src/renderer/shared/components/cards/ReleaseNotesCard.tsx +137 -0
  254. package/src/renderer/shared/components/cards/StockCard.tsx +7 -7
  255. package/src/renderer/shared/components/cards/ToolResultCards.tsx +11 -3
  256. package/src/renderer/shared/components/cards/UiMockupWidget.tsx +4 -4
  257. package/src/renderer/shared/components/cards/WeatherCard.tsx +5 -5
  258. package/src/renderer/shared/components/chat/ApprovalCard.test.tsx +11 -0
  259. package/src/renderer/shared/components/chat/ApprovalCard.tsx +21 -2
  260. package/src/renderer/shared/components/chat/ArtifactCards.test.tsx +69 -0
  261. package/src/renderer/shared/components/chat/ArtifactCards.tsx +238 -0
  262. package/src/renderer/shared/components/chat/AttachedDocumentCard.test.tsx +39 -0
  263. package/src/renderer/shared/components/chat/AttachedDocumentCard.tsx +220 -0
  264. package/src/renderer/shared/components/chat/ChatCodeBlock.test.tsx +33 -0
  265. package/src/renderer/shared/components/chat/ChatCodeBlock.tsx +15 -1
  266. package/src/renderer/shared/components/chat/ChatMessageItem.test.tsx +40 -2
  267. package/src/renderer/shared/components/chat/ChatMessageItem.tsx +180 -34
  268. package/src/renderer/shared/components/chat/ChatModeSelector.css +227 -0
  269. package/src/renderer/shared/components/chat/ChatModeSelector.test.tsx +45 -0
  270. package/src/renderer/shared/components/chat/ChatModeSelector.tsx +198 -0
  271. package/src/renderer/shared/components/chat/ChatPanel.test.tsx +22 -0
  272. package/src/renderer/shared/components/chat/ChatPanel.tsx +227 -53
  273. package/src/renderer/shared/components/chat/DiffReviewModal.tsx +6 -6
  274. package/src/renderer/shared/components/chat/GeminiLiveOverlay.test.tsx +142 -1
  275. package/src/renderer/shared/components/chat/GeminiLiveOverlay.tsx +45 -3
  276. package/src/renderer/shared/components/chat/ModelSelectorPopover.tsx +42 -14
  277. package/src/renderer/shared/components/chat/RewindModal.tsx +10 -10
  278. package/src/renderer/shared/components/chat/SlashSuggestions.test.tsx +14 -0
  279. package/src/renderer/shared/components/chat/SourcesBlock.test.tsx +53 -0
  280. package/src/renderer/shared/components/chat/SourcesBlock.tsx +236 -90
  281. package/src/renderer/shared/components/chat/TurnFileChanges.test.tsx +45 -1
  282. package/src/renderer/shared/components/chat/TurnFileChanges.tsx +202 -169
  283. package/src/renderer/shared/components/extensions/CliSessionsView.tsx +5 -4
  284. package/src/renderer/shared/components/extensions/McpRegistryPicker.tsx +3 -3
  285. package/src/renderer/shared/components/studio/LibraryPage.tsx +48 -5
  286. package/src/renderer/shared/components/workspace/ArtifactPreviewPanel.tsx +125 -13
  287. package/src/renderer/shared/components/workspace/CodeReviewPage.test.tsx +50 -0
  288. package/src/renderer/shared/components/workspace/CodeReviewPage.tsx +77 -16
  289. package/src/renderer/shared/components/workspace/GitBranchSelector.tsx +78 -22
  290. package/src/renderer/shared/components/workspace/ToolSurfacePage.test.tsx +13 -0
  291. package/src/renderer/shared/components/workspace/ToolSurfacePage.tsx +48 -28
  292. package/src/renderer/shared/components/workspace/WorkspacePdfPreview.tsx +266 -47
  293. package/src/renderer/shared/components/workspace/WorkspacePreviewActions.test.tsx +18 -6
  294. package/src/renderer/shared/components/workspace/background-terminals.css +11 -11
  295. package/src/renderer/shared/constants/config.ts +1 -5
  296. package/src/renderer/shared/constants/models.test.ts +51 -0
  297. package/src/renderer/shared/constants/models.ts +139 -32
  298. package/src/renderer/shared/conversation/lifecycle.test.ts +91 -3
  299. package/src/renderer/shared/conversation/lifecycle.ts +104 -5
  300. package/src/renderer/shared/conversation/progressiveStream.test.ts +83 -0
  301. package/src/renderer/shared/conversation/progressiveStream.ts +219 -0
  302. package/src/renderer/shared/css/agent-observability.css +133 -0
  303. package/src/renderer/shared/css/chat-ui-cards.css +13 -13
  304. package/src/renderer/shared/css/code-review.css +63 -34
  305. package/src/renderer/shared/css/code-sessions.css +47 -18
  306. package/src/renderer/shared/css/collapsed-paste.css +1 -1
  307. package/src/renderer/shared/css/error-notice.css +1 -1
  308. package/src/renderer/shared/css/file-changes.css +165 -80
  309. package/src/renderer/shared/css/library.css +105 -10
  310. package/src/renderer/shared/css/management-views.css +1 -1
  311. package/src/renderer/shared/css/mcp-results.css +5 -5
  312. package/src/renderer/shared/css/model-selector.css +18 -18
  313. package/src/renderer/shared/css/mods.css +7 -7
  314. package/src/renderer/shared/css/notification-notice.css +2 -2
  315. package/src/renderer/shared/css/queued-messages.css +5 -5
  316. package/src/renderer/shared/css/settings/general.css +18 -13
  317. package/src/renderer/shared/css/settings/profile.css +5 -5
  318. package/src/renderer/shared/css/settings/theme.css +5 -5
  319. package/src/renderer/shared/css/slash-choice.css +2 -2
  320. package/src/renderer/shared/css/slash-suggestions.css +3 -3
  321. package/src/renderer/shared/css/sources-block.css +41 -2
  322. package/src/renderer/shared/css/tools-panel.css +2 -2
  323. package/src/renderer/shared/css/ui-tailwind.css +8 -0
  324. package/src/renderer/shared/css/veo-studio.css +2 -2
  325. package/src/renderer/shared/css/viewed-images.css +7 -7
  326. package/src/renderer/shared/platform.ts +2 -1
  327. package/src/renderer/shared/theme/themeManager.test.ts +17 -0
  328. package/src/renderer/shared/theme/themeManager.ts +21 -4
  329. package/src/renderer/shared/theme/themes.ts +54 -0
  330. package/src/renderer/shared/theme/tokens.css +6 -4
  331. package/src/renderer/shared/types.ts +1 -1
  332. package/src/renderer/shared/utils/agentActivity.test.ts +468 -2
  333. package/src/renderer/shared/utils/agentActivity.ts +602 -35
  334. package/src/renderer/shared/utils/artifactBrowser.test.ts +37 -32
  335. package/src/renderer/shared/utils/artifactBrowser.ts +17 -19
  336. package/src/renderer/shared/utils/attachedDocument.test.ts +73 -0
  337. package/src/renderer/shared/utils/attachedDocument.ts +49 -0
  338. package/src/renderer/shared/utils/fileIcons.test.ts +59 -0
  339. package/src/renderer/shared/utils/fileIcons.ts +149 -16
  340. package/src/renderer/shared/utils/markdown.tsx +72 -19
  341. package/src/renderer/shared/utils/markdown.webImages.test.tsx +16 -0
  342. package/src/renderer/shared/utils/mcpResults.test.tsx +29 -0
  343. package/src/renderer/shared/utils/slashCommandProcessor.ts +4 -1
  344. package/src/renderer/shared/utils/syntaxHighlight.test.tsx +11 -1
  345. package/src/renderer/shared/utils/syntaxHighlight.tsx +51 -3
  346. package/src/renderer/shared/utils/useGeminiLiveVoice.ts +324 -51
  347. package/src/renderer/shared/utils/workspaceDrag.test.ts +153 -0
  348. package/src/renderer/shared/utils/workspaceDrag.ts +142 -0
  349. package/src/renderer/shared/utils/workspaceMedia.ts +10 -2
  350. package/src/renderer/src/App.tsx +14 -8
  351. package/src/renderer/src/agentProgress.ts +1 -0
  352. package/src/renderer/src/components/ScreenPicker.css +1 -1
  353. package/src/renderer/src/components/WorkspacePanel.tsx +6 -5
  354. package/src/renderer/src/css/app-shell.css +4 -4
  355. package/src/renderer/src/css/chat.css +1261 -241
  356. package/src/renderer/src/css/desktop-tools.css +11 -11
  357. package/src/renderer/src/css/sidebar-workspace.css +2 -2
  358. package/src/renderer/src/css/tool-surfaces.css +12 -12
  359. package/src/renderer/src/index.css +38 -20
  360. package/src/renderer/src/tauri.ts +22 -2
  361. package/src/renderer/src-web/App.tsx +14 -0
  362. package/src/renderer/src-web/agentProgress.ts +1 -0
  363. package/src/renderer/src-web/css/app-shell.css +4 -4
  364. package/src/renderer/src-web/css/chat.css +1278 -190
  365. package/src/renderer/src-web/css/mobile.css +29 -13
  366. package/src/renderer/src-web/css/settings/mobile.css +2 -2
  367. package/src/renderer/src-web/css/sidebar-workspace.css +1 -1
  368. package/src/renderer/src-web/index.css +38 -20
  369. package/src/renderer/src-web/tauri.ts +22 -2
  370. package/src/renderer/tests/browserArtifacts.browser.tsx +1 -1
  371. package/src/renderer/tests/library.browser.tsx +3 -0
  372. package/src/renderer/tests/liveVoice.browser.html +1 -0
  373. package/src/renderer/tests/workspaceDrop.browser.tsx +1 -1
  374. package/src-tauri/src/app/lifecycle.rs +1 -14
  375. package/src-tauri/src/background/headless.rs +14 -8
  376. package/src-tauri/src/commands/conversation.rs +38 -0
  377. package/src-tauri/src/native/mod.rs +0 -1
  378. package/src-tauri/src/native/system.rs +4 -47
  379. package/crates/mint-cli/src/interactive/chat_tui.rs +0 -8855
  380. package/crates/mint-core/src/agent/chat.rs +0 -4394
  381. package/crates/mint-core/src/browser/lifecycle.rs +0 -291
  382. package/crates/mint-core/src/browser/logging.rs +0 -20
  383. package/crates/mint-core/src/orchestration/tools/browser.rs +0 -31
  384. package/out/web/assets/ArtifactPreviewPanel-S0dPfYFx.js +0 -2
  385. package/out/web/assets/CalculationCard-CRjP3E1j.js +0 -1
  386. package/out/web/assets/CliSessionsView-C3Br2v5Y.css +0 -1
  387. package/out/web/assets/CodeReviewPage-CUJceQme.css +0 -1
  388. package/out/web/assets/CodeReviewPage-VCUgPla_.js +0 -2
  389. package/out/web/assets/ImageGenCard-BCXxkVJX.js +0 -1
  390. package/out/web/assets/ImageSearchCard-N3yQOr8Z.js +0 -1
  391. package/out/web/assets/LibraryPage-DAxUTAg7.css +0 -1
  392. package/out/web/assets/LibraryPage-WlviRdR8.js +0 -2
  393. package/out/web/assets/LinkedFoldersView-CKRgroWK.js +0 -1
  394. package/out/web/assets/McpServersView-D3wHnuHg.js +0 -1
  395. package/out/web/assets/MermaidCard-NqrHdyJW.js +0 -2
  396. package/out/web/assets/MintDashboard-B2DJsDDO.css +0 -1
  397. package/out/web/assets/MintDashboard-COw_xol7.js +0 -63
  398. package/out/web/assets/RewindModal-DempuDod.js +0 -1
  399. package/out/web/assets/SettingsWindow-2YP3yj7s.js +0 -4
  400. package/out/web/assets/SettingsWindow-Dz8LQVgD.css +0 -1
  401. package/out/web/assets/StockCard-BDMCe7mx.js +0 -1
  402. package/out/web/assets/ToolSurfacePage-B0J_q0Jl.js +0 -38
  403. package/out/web/assets/ToolSurfacePage-CRIQ1owD.css +0 -1
  404. package/out/web/assets/UiMockupWidget-DD73QWg1.js +0 -1
  405. package/out/web/assets/WeatherCard-CPucIfq8.js +0 -1
  406. package/out/web/assets/WorkspacePdfPreview-Dx_lkjNu.js +0 -2
  407. package/out/web/assets/classDiagram-ZZMXUADV-BgJ5WtSp.js +0 -1
  408. package/out/web/assets/classDiagram-v2-VYDZK3BY-BgJ5WtSp.js +0 -1
  409. package/out/web/assets/config-CvIO5hkn.js +0 -2
  410. package/out/web/assets/flowDiagram-HODETNUW-uqCHJond.js +0 -1
  411. package/out/web/assets/index-BmpulIkg.js +0 -168
  412. package/out/web/assets/index-CkvBwhX_.css +0 -1
  413. package/out/web/assets/stateDiagram-v2-MP3YSRHH-uf3kdNW4.js +0 -1
  414. package/out/web/assets/swimlanesDiagram-VR7AAH4N-Y8q5bQce.js +0 -8
  415. package/shared-modules/libsecret/libsecret.json +0 -23
  416. package/shared-modules/libusb/libusb.json +0 -27
  417. package/shared-modules/lzo/lzo.json +0 -27
  418. package/src/renderer/src/components/BrowserShell.tsx +0 -108
  419. package/src/renderer/src/components/BrowserSuggestionsPopup.tsx +0 -91
  420. package/src/renderer/src/components/browser-shell.css +0 -185
  421. package/src-tauri/src/native/browser.rs +0 -252
@@ -7,6 +7,11 @@ import type { AgentProgress, ActivePlan, RunTelemetrySummary } from '../types'
7
7
  import { isInternalCot } from '../agentProgress'
8
8
  import { viewedImageDataUri, viewedImagesFrom, type ViewedImageItem } from './viewedImages'
9
9
 
10
+ export interface AskUserItem {
11
+ question: string
12
+ answer?: string
13
+ }
14
+
10
15
  export interface AgentActivity {
11
16
  label: string
12
17
  target: string
@@ -17,6 +22,21 @@ export interface AgentActivity {
17
22
  /** Raw ToolEnd output text, shown when the row is expanded in the UI. */
18
23
  result?: string
19
24
  viewedImage?: ViewedImageItem
25
+ /** Semantic verb (e.g. 'Analyzed', 'Edited', 'Created', 'Ran', 'Searched') */
26
+ semanticVerb?: string
27
+ /** Human-readable intent or title (e.g. toolSummary for commands, or skill title) */
28
+ intentTitle?: string
29
+ /** Line range suffix if available, e.g. '#L25-50' */
30
+ lineRange?: string
31
+ /** Git diff additions/deletions if available */
32
+ diffStats?: { additions: number; deletions: number }
33
+ /** Elapsed duration in ms or s */
34
+ elapsedMs?: number
35
+ /** Structured ask_user questions and answers */
36
+ askUserData?: {
37
+ header?: string
38
+ questions: AskUserItem[]
39
+ }
20
40
  }
21
41
 
22
42
  export interface AgentActivityGroup {
@@ -28,6 +48,7 @@ export interface AgentActivityGroup {
28
48
  count: number
29
49
  state: 'active' | 'done' | 'error' | 'retry'
30
50
  items: AgentActivity[]
51
+ semanticVerb?: string
31
52
  }
32
53
 
33
54
  export type TimelineItem =
@@ -38,6 +59,10 @@ export type TimelineItem =
38
59
 
39
60
  export interface AgentActivityView {
40
61
  summary: string
62
+ /** Natural composite summary phrase, e.g. "Explored files, ran commands, edited files" */
63
+ naturalSummary?: string
64
+ /** Total elapsed time string if available, e.g. "2m", "14s" */
65
+ durationText?: string
41
66
  items: AgentActivity[]
42
67
  groups?: AgentActivityGroup[]
43
68
  /** Unified chronological list of thoughts, extended thinking, and tool activities. */
@@ -69,6 +94,7 @@ function activityDetail(input: Record<string, unknown>, key: string): string {
69
94
  }
70
95
 
71
96
  export function formatActivityTarget(value: string): string {
97
+ if (!value || value === '.' || value === './' || value === 'list files') return 'workspace'
72
98
  const compact = value.replace(/^\/home\/([^/]+)/, '~')
73
99
  return compact || 'workspace'
74
100
  }
@@ -76,8 +102,8 @@ export function formatActivityTarget(value: string): string {
76
102
  export function activityKind(action: string, target: string): AgentActivity['kind'] {
77
103
  if (['search_code', 'semantic_search', 'knowledge_search', 'web_search', 'image_search', 'memory_recall', 'find_definition', 'find_references'].includes(action)) return 'search'
78
104
  if (['run_shell', 'verify', 'run_tests', 'run_typecheck', 'run_linter'].includes(action)) return 'terminal'
79
- if (['list_files', 'detect_project'].includes(action)) return 'folder'
80
- if (['read_file', 'symbols', 'read_diagnostics', 'git_diff', 'apply_patch', 'write_file', 'note_write', 'view_image'].includes(action)) return 'file'
105
+ if (['list_files', 'list_directory', 'list_dir', 'detect_project'].includes(action)) return 'folder'
106
+ if (['read_file', 'symbols', 'read_diagnostics', 'git_diff', 'apply_patch', 'write_file', 'edit_file', 'apply_code_edits', 'note_write', 'view_image'].includes(action)) return 'file'
81
107
  if (['calculation', 'calculator'].includes(action)) return 'calc'
82
108
  return target.includes('/') && !/\.[^/]+$/.test(target) ? 'folder' : 'tool'
83
109
  }
@@ -182,19 +208,41 @@ export function describeTool(action: string, input: Record<string, unknown>): Ag
182
208
  case 'stock':
183
209
  rawTarget = query || symbol || '(symbol)'
184
210
  break
185
- case 'browser_open':
186
- case 'browser_navigate':
187
- rawTarget = url || '(url)'
188
- break
189
- case 'browser_click':
190
- rawTarget = selector || '(element)'
191
- break
192
211
  case 'mcp_tool':
193
212
  rawTarget = tool ? (server ? `${server}/${tool}` : tool) : (name || 'mcp tool')
194
213
  break
195
214
  case 'note_write':
196
215
  rawTarget = name || path || '(note)'
197
216
  break
217
+ case 'exit_plan_mode':
218
+ rawTarget = 'plan approval'
219
+ break
220
+ case 'list_files':
221
+ case 'list_directory':
222
+ case 'list_dir': {
223
+ const targetPath =
224
+ path ||
225
+ activityDetail(input, 'directory') ||
226
+ activityDetail(input, 'dir') ||
227
+ activityDetail(input, 'folder') ||
228
+ activityDetail(input, 'target')
229
+ if (!targetPath || targetPath === '.' || targetPath === './' || targetPath === 'list files') {
230
+ rawTarget = 'workspace'
231
+ } else {
232
+ rawTarget = formatActivityTarget(targetPath)
233
+ }
234
+ break
235
+ }
236
+ case 'create_plan':
237
+ case 'update_plan': {
238
+ const summary = activityDetail(input, 'summary') || activityDetail(input, 'title')
239
+ const stepsCount = Array.isArray(input.steps) ? input.steps.length : 0
240
+ const stepsLabel = stepsCount > 0 ? `${stepsCount} step${stepsCount === 1 ? '' : 's'}` : ''
241
+ rawTarget = summary && stepsLabel
242
+ ? `${summary} (${stepsLabel})`
243
+ : summary || stepsLabel || 'task plan'
244
+ break
245
+ }
198
246
  default:
199
247
  rawTarget = path || query || command || expression || symbol || city || url || selector || filter || name || tool || action.replaceAll('_', ' ')
200
248
  break
@@ -202,23 +250,96 @@ export function describeTool(action: string, input: Record<string, unknown>): Ag
202
250
 
203
251
  // Append line range for read_file when startLine / endLine are available
204
252
  let target = rawTarget
253
+ let lineRange: string | undefined
205
254
  if (action === 'read_file' && path) {
206
255
  const startLine =
207
256
  typeof input.startLine === 'number'
208
257
  ? input.startLine
209
258
  : typeof input.start_line === 'number'
210
259
  ? input.start_line
211
- : undefined
260
+ : typeof input.StartLine === 'number'
261
+ ? input.StartLine
262
+ : undefined
212
263
  const endLine =
213
264
  typeof input.endLine === 'number'
214
265
  ? input.endLine
215
266
  : typeof input.end_line === 'number'
216
267
  ? input.end_line
217
- : undefined
268
+ : typeof input.EndLine === 'number'
269
+ ? input.EndLine
270
+ : undefined
218
271
  if (startLine !== undefined && endLine !== undefined) {
219
- target = `${rawTarget} #L${startLine}-${endLine}`
272
+ lineRange = `#L${startLine}-${endLine}`
273
+ target = `${rawTarget} ${lineRange}`
220
274
  } else if (startLine !== undefined) {
221
- target = `${rawTarget} #L${startLine}`
275
+ lineRange = `#L${startLine}`
276
+ target = `${rawTarget} ${lineRange}`
277
+ }
278
+ }
279
+
280
+ // Diff stats extraction for edit_file / write_file
281
+ let diffStats: { additions: number; deletions: number } | undefined
282
+ if (action === 'edit_file') {
283
+ const targetText = typeof input.Target === 'string' ? input.Target : typeof input.target === 'string' ? input.target : ''
284
+ const replacementText = typeof input.Replacement === 'string' ? input.Replacement : typeof input.replacement === 'string' ? input.replacement : ''
285
+ const deletions = targetText ? targetText.split('\n').length : 0
286
+ const additions = replacementText ? replacementText.split('\n').length : 0
287
+ diffStats = { additions, deletions }
288
+ } else if (action === 'write_file' || action === 'note_write') {
289
+ const rawContent = input.fileContent ?? input.file_content ?? input.Content ?? input.content ?? input.text ?? input.body
290
+ const content = typeof rawContent === 'string' ? rawContent : ''
291
+ const additions = content ? content.split('\n').length : 0
292
+ diffStats = { additions, deletions: 0 }
293
+ }
294
+
295
+ // Intent / summary extraction for commands
296
+ let intentTitle: string | undefined
297
+ if (action === 'run_shell' || action === 'run_command' || action === 'bash') {
298
+ intentTitle = activityDetail(input, 'toolSummary') || activityDetail(input, 'summary') || activityDetail(input, 'description')
299
+ }
300
+
301
+ let semanticVerb = 'Executed'
302
+ let askUserData: AgentActivity['askUserData']
303
+ if (action === 'read_file') {
304
+ semanticVerb = 'Analyzed'
305
+ } else if (action === 'list_files' || action === 'list_directory' || action === 'list_dir') {
306
+ semanticVerb = 'Explored'
307
+ } else if (action === 'edit_file' || action === 'apply_patch' || action === 'apply_code_edits') {
308
+ semanticVerb = 'Edited'
309
+ } else if (action === 'write_file' || action === 'note_write') {
310
+ semanticVerb = 'Edited'
311
+ } else if (action === 'run_shell' || action === 'run_command' || action === 'bash') {
312
+ semanticVerb = 'Ran'
313
+ } else if (action?.includes('search')) {
314
+ semanticVerb = 'Searched'
315
+ } else if (action === 'calculation' || action === 'calculator') {
316
+ semanticVerb = 'Calculated'
317
+ } else if (action === 'view_image') {
318
+ semanticVerb = 'Viewed'
319
+ } else if (action === 'create_plan' || action === 'update_plan') {
320
+ semanticVerb = 'Planned'
321
+ } else if (action === 'ask_user') {
322
+ semanticVerb = 'Asked'
323
+ let questions: AskUserItem[] = []
324
+ const rawQuestions = input?.questions
325
+ if (Array.isArray(rawQuestions) && rawQuestions.length > 0) {
326
+ questions = (rawQuestions as Array<Record<string, unknown> | string>)
327
+ .map((q) => {
328
+ if (typeof q === 'string') return { question: q.trim() }
329
+ const questionText = String(q.question || q.query || q.title || '').trim()
330
+ const answerText = typeof q.answer === 'string' ? q.answer.trim() : undefined
331
+ return { question: questionText, answer: answerText }
332
+ })
333
+ .filter((q) => q.question.length > 0)
334
+ } else if (query) {
335
+ questions = [{ question: query }]
336
+ }
337
+ if (questions.length > 0) {
338
+ const headerText = typeof input?.header === 'string' ? input.header.trim() : undefined
339
+ askUserData = {
340
+ header: headerText || undefined,
341
+ questions,
342
+ }
222
343
  }
223
344
  }
224
345
 
@@ -230,7 +351,348 @@ export function describeTool(action: string, input: Record<string, unknown>): Ag
230
351
  kind: activityKind(action, target),
231
352
  state: 'active',
232
353
  action,
354
+ semanticVerb,
355
+ intentTitle,
356
+ lineRange,
357
+ diffStats,
358
+ askUserData,
359
+ }
360
+ }
361
+
362
+ export function formatDurationText(durationSecs?: number): string {
363
+ if (durationSecs === undefined || durationSecs === null || durationSecs <= 0) return ''
364
+ const secs = Math.round(durationSecs)
365
+ if (secs < 60) return `${secs}s`
366
+ const mins = Math.floor(secs / 60)
367
+ const remSecs = secs % 60
368
+ if (remSecs === 0) return `${mins}m`
369
+ return `${mins}m ${remSecs}s`
370
+ }
371
+
372
+ export function isMarkdownContent(action?: string, target?: string, output?: string): boolean {
373
+ if (!output || !output.trim()) return false
374
+ if (action === 'run_shell' || action === 'run_command' || action === 'bash') return false
375
+ const cleanTarget = (target || '').toLowerCase()
376
+ if (cleanTarget.endsWith('.md') || cleanTarget.endsWith('.markdown') || cleanTarget.endsWith('.txt')) {
377
+ return true
378
+ }
379
+ if (cleanTarget.includes('skill') || output.startsWith('[Loaded skill:')) {
380
+ return true
381
+ }
382
+ if (action === 'note_read' || action === 'web_search') {
383
+ return true
384
+ }
385
+ if (/^#{1,6}\s+/m.test(output) && (output.includes('\n\n') || output.includes('\n- '))) {
386
+ return true
233
387
  }
388
+ return false
389
+ }
390
+
391
+ export function cleanMarkdownOutput(text: string): string {
392
+ let cleaned = text.trim()
393
+ cleaned = cleaned.replace(/^\[Loaded skill:\s*([^\]]+)\][\s\n]*/i, '**Skill: $1**\n\n')
394
+ const frontmatterMatch = cleaned.match(/^(\*\*Skill:[^\n]+\*\*[\s\n]*)?---\s*\n([\s\S]*?)\n---\s*\n/)
395
+ if (frontmatterMatch) {
396
+ const prefix = frontmatterMatch[1] ? frontmatterMatch[1].trim() + '\n\n' : ''
397
+ const yaml = frontmatterMatch[2]
398
+ const descMatch = yaml.match(/description:\s*([^\n]+(?:\n\s+[^\n]+)*)/)
399
+ const desc = descMatch ? descMatch[1].replace(/\n\s+/g, ' ').trim() : ''
400
+ const remaining = cleaned.slice(frontmatterMatch[0].length)
401
+ if (desc) {
402
+ cleaned = `${prefix}> ${desc}\n\n${remaining}`
403
+ } else {
404
+ cleaned = `${prefix}${remaining}`
405
+ }
406
+ }
407
+ return cleaned
408
+ }
409
+
410
+ export interface DirectoryEntryItem {
411
+ name?: string
412
+ path?: string
413
+ kind?: 'directory' | 'file' | 'symlink' | string
414
+ size?: number | null
415
+ }
416
+
417
+ export function formatFileSize(bytes?: number | null): string {
418
+ if (bytes === null || bytes === undefined || isNaN(bytes)) return ''
419
+ if (bytes < 0) return '0 B'
420
+ if (bytes < 1024) return `${bytes} B`
421
+ const kb = bytes / 1024
422
+ if (kb < 1024) return `${Number(kb.toFixed(1))} KB`
423
+ const mb = kb / 1024
424
+ if (mb < 1024) return `${Number(mb.toFixed(1))} MB`
425
+ const gb = mb / 1024
426
+ return `${Number(gb.toFixed(1))} GB`
427
+ }
428
+
429
+ export function parseDirectoryEntries(text: string): DirectoryEntryItem[] | null {
430
+ if (!text || typeof text !== 'string') return null
431
+ let trimmed = text.trim()
432
+
433
+ if (!trimmed.startsWith('[') && trimmed.includes('stdout:')) {
434
+ const stdoutMatch = trimmed.match(/stdout:\s*\n([\s\S]*?)(?:\n\s*stderr:|$)/)
435
+ if (stdoutMatch && stdoutMatch[1]) {
436
+ trimmed = stdoutMatch[1].trim()
437
+ }
438
+ }
439
+
440
+ if (!trimmed.startsWith('[') || !trimmed.endsWith(']')) return null
441
+
442
+ let parsed: unknown
443
+ try {
444
+ parsed = JSON.parse(trimmed)
445
+ } catch {
446
+ return null
447
+ }
448
+
449
+ if (!Array.isArray(parsed)) return null
450
+ if (parsed.length === 0) return []
451
+
452
+ const isDirEntryArray = parsed.every(
453
+ (item) =>
454
+ item &&
455
+ typeof item === 'object' &&
456
+ (typeof item.name === 'string' || typeof item.path === 'string') &&
457
+ ('kind' in item || 'size' in item)
458
+ )
459
+
460
+ if (!isDirEntryArray) return null
461
+
462
+ const entries = parsed as DirectoryEntryItem[]
463
+ const dirs: DirectoryEntryItem[] = []
464
+ const files: DirectoryEntryItem[] = []
465
+ const others: DirectoryEntryItem[] = []
466
+
467
+ for (const entry of entries) {
468
+ const kind = (entry.kind || '').toLowerCase()
469
+ if (kind === 'directory' || kind === 'dir' || kind === 'folder') {
470
+ dirs.push(entry)
471
+ } else if (kind === 'file') {
472
+ files.push(entry)
473
+ } else {
474
+ others.push(entry)
475
+ }
476
+ }
477
+
478
+ const getEntryName = (e: DirectoryEntryItem): string => {
479
+ if (e.name) return e.name
480
+ if (e.path) {
481
+ const parts = e.path.split(/[/\\]/)
482
+ return parts[parts.length - 1] || e.path
483
+ }
484
+ return ''
485
+ }
486
+
487
+ dirs.sort((a, b) => getEntryName(a).localeCompare(getEntryName(b), undefined, { sensitivity: 'base' }))
488
+ files.sort((a, b) => getEntryName(a).localeCompare(getEntryName(b), undefined, { sensitivity: 'base' }))
489
+ others.sort((a, b) => getEntryName(a).localeCompare(getEntryName(b), undefined, { sensitivity: 'base' }))
490
+
491
+ return [...dirs, ...others, ...files]
492
+ }
493
+
494
+ export function formatDirectoryListing(text: string): string | null {
495
+ const entries = parseDirectoryEntries(text)
496
+ if (!entries) return null
497
+ if (entries.length === 0) return '(empty directory)'
498
+
499
+ const getEntryName = (e: DirectoryEntryItem): string => {
500
+ if (e.name) return e.name
501
+ if (e.path) {
502
+ const parts = e.path.split(/[/\\]/)
503
+ return parts[parts.length - 1] || e.path
504
+ }
505
+ return ''
506
+ }
507
+
508
+ const lines: string[] = []
509
+
510
+ for (const entry of entries) {
511
+ const kind = (entry.kind || '').toLowerCase()
512
+ const name = getEntryName(entry)
513
+ if (kind === 'directory' || kind === 'dir' || kind === 'folder') {
514
+ const displayName = name.endsWith('/') ? name : `${name}/`
515
+ lines.push(`📁 ${displayName}`)
516
+ } else if (kind === 'symlink') {
517
+ const sizeStr = formatFileSize(entry.size)
518
+ const suffix = sizeStr ? ` (${sizeStr})` : ''
519
+ lines.push(`🔗 ${name}${suffix}`)
520
+ } else {
521
+ const sizeStr = formatFileSize(entry.size)
522
+ const suffix = sizeStr ? ` (${sizeStr})` : ''
523
+ lines.push(`📄 ${name}${suffix}`)
524
+ }
525
+ }
526
+
527
+ return lines.join('\n')
528
+ }
529
+
530
+ export function summarizeDirectoryListing(text: string): string | null {
531
+ if (!text || typeof text !== 'string') return null
532
+ const trimmed = text.trim()
533
+
534
+ const entries = parseDirectoryEntries(trimmed)
535
+ if (entries) {
536
+ if (entries.length === 0) return 'empty folder'
537
+ let dirCount = 0
538
+ let fileCount = 0
539
+ let otherCount = 0
540
+
541
+ for (const item of entries) {
542
+ const kind = String(item.kind || '').toLowerCase()
543
+ if (kind === 'directory' || kind === 'dir' || kind === 'folder') {
544
+ dirCount++
545
+ } else if (kind === 'file') {
546
+ fileCount++
547
+ } else {
548
+ otherCount++
549
+ }
550
+ }
551
+
552
+ const total = dirCount + fileCount + otherCount
553
+ if (total === 0) return 'empty folder'
554
+
555
+ if (dirCount > 0 && fileCount > 0) {
556
+ const folderStr = `${dirCount} folder${dirCount === 1 ? '' : 's'}`
557
+ const fileStr = `${fileCount} file${fileCount === 1 ? '' : 's'}`
558
+ const detailed = `${total} items (${folderStr}, ${fileStr})`
559
+ if (detailed.length <= 35) return detailed
560
+ return `${total} items`
561
+ }
562
+
563
+ if (dirCount > 0 && fileCount === 0) {
564
+ return `${dirCount} folder${dirCount === 1 ? '' : 's'}`
565
+ }
566
+
567
+ if (fileCount > 0 && dirCount === 0) {
568
+ return `${fileCount} file${fileCount === 1 ? '' : 's'}`
569
+ }
570
+
571
+ return `${total} items`
572
+ }
573
+
574
+ // Check if already formatted lines
575
+ if (!trimmed.startsWith('[') && (trimmed.includes('📁 ') || trimmed.includes('📄 ') || trimmed.includes('🔗 '))) {
576
+ const lines = trimmed.split('\n').map((l) => l.trim()).filter(Boolean)
577
+ const dirCount = lines.filter((l) => l.startsWith('📁')).length
578
+ const fileCount = lines.filter((l) => l.startsWith('📄') || l.startsWith('🔗')).length
579
+ const total = dirCount + fileCount
580
+ if (total === 0) return 'empty folder'
581
+ if (dirCount > 0 && fileCount > 0) {
582
+ const detailed = `${total} items (${dirCount} folders, ${fileCount} files)`
583
+ return detailed.length <= 35 ? detailed : `${total} items`
584
+ }
585
+ if (dirCount > 0) return `${dirCount} folder${dirCount === 1 ? '' : 's'}`
586
+ if (fileCount > 0) return `${fileCount} file${fileCount === 1 ? '' : 's'}`
587
+ return `${total} items`
588
+ }
589
+
590
+ return null
591
+ }
592
+
593
+ /**
594
+ * Strips internal machine/executor envelope lines (`exit:`, `mode:`, `sandboxed:`, empty `stderr:`)
595
+ * from raw command execution output, returning clean stdout/stderr for human viewing.
596
+ */
597
+ export function cleanCommandOutput(text: string): string {
598
+ if (!text || typeof text !== 'string') return ''
599
+ const trimmed = text.trim()
600
+ if (!trimmed) return ''
601
+
602
+ // If text is serialized directory listing JSON, format it cleanly into readable entries
603
+ const formattedDir = formatDirectoryListing(trimmed)
604
+ if (formattedDir) return formattedDir
605
+
606
+ const lines = text.split('\n')
607
+ const hasEnvelope = lines.some((l) => {
608
+ const t = l.trim()
609
+ return t.startsWith('mode: ') || t.startsWith('sandboxed: ') || (t.startsWith('exit: ') && lines.some(x => x.trim() === 'stdout:'))
610
+ })
611
+
612
+ if (hasEnvelope) {
613
+ let section: 'meta' | 'stdout' | 'stderr' = 'meta'
614
+ const stdoutLines: string[] = []
615
+ const stderrLines: string[] = []
616
+
617
+ for (const line of lines) {
618
+ const t = line.trim()
619
+ if (section === 'meta' && (t.startsWith('exit: ') || t.startsWith('mode: ') || t.startsWith('sandboxed: '))) {
620
+ continue
621
+ }
622
+ if (t === 'stdout:') {
623
+ section = 'stdout'
624
+ continue
625
+ }
626
+ if (t === 'stderr:') {
627
+ section = 'stderr'
628
+ continue
629
+ }
630
+
631
+ if (section === 'stdout') {
632
+ stdoutLines.push(line)
633
+ } else if (section === 'stderr') {
634
+ stderrLines.push(line)
635
+ } else {
636
+ stdoutLines.push(line)
637
+ }
638
+ }
639
+
640
+ const cleanStdout = stdoutLines.join('\n').trim()
641
+ const cleanStderr = stderrLines.join('\n').trim()
642
+
643
+ if (cleanStdout && cleanStderr) {
644
+ const formattedStdout = formatDirectoryListing(cleanStdout) || cleanStdout
645
+ return `${formattedStdout}\n\n[stderr]\n${cleanStderr}`
646
+ }
647
+ if (cleanStderr) {
648
+ return cleanStderr
649
+ }
650
+ if (cleanStdout) {
651
+ return formatDirectoryListing(cleanStdout) || cleanStdout
652
+ }
653
+ return '(no output)'
654
+ }
655
+
656
+ // If there's no full envelope, strip empty trailing `stderr:` or `stderr:\n`
657
+ const cleaned = trimmed.replace(/\n\s*stderr:\s*$/i, '').trim()
658
+ return formatDirectoryListing(cleaned) || cleaned
659
+ }
660
+
661
+ export function generateNaturalActivitySummary(items: AgentActivity[], timeline?: TimelineItem[]): string {
662
+ if (!items || items.length === 0) {
663
+ if (timeline && timeline.some(t => t.kind === 'extendedThinking' || t.kind === 'thought')) {
664
+ return 'Thinking'
665
+ }
666
+ return 'Working'
667
+ }
668
+
669
+ let hasRead = false
670
+ let hasWrite = false
671
+ let hasCommand = false
672
+ let hasSearch = false
673
+ let hasExplore = false
674
+ let hasThought = Boolean(timeline && timeline.some(t => t.kind === 'extendedThinking'))
675
+
676
+ for (const item of items) {
677
+ if (item.action === 'read_file') hasRead = true
678
+ else if (item.action === 'write_file' || item.action === 'edit_file' || item.action === 'apply_patch') hasWrite = true
679
+ else if (item.action === 'run_shell' || item.action === 'run_command' || item.action === 'bash') hasCommand = true
680
+ else if (item.action?.includes('search')) hasSearch = true
681
+ else if (item.action === 'list_files' || item.action === 'list_directory' || item.action === 'list_dir' || item.kind === 'folder') hasExplore = true
682
+ }
683
+
684
+ const parts: string[] = []
685
+ if (hasExplore && !hasRead) parts.push('Explored workspace')
686
+ if (hasRead) parts.push('Explored files')
687
+ if (hasCommand) parts.push('ran commands')
688
+ if (hasWrite) parts.push('edited files')
689
+ if (hasSearch && !hasRead && !hasExplore) parts.push('searched codebase')
690
+ if (parts.length === 0) {
691
+ if (hasThought) return 'Analyzed task'
692
+ return 'Working through task'
693
+ }
694
+
695
+ return parts.join(', ')
234
696
  }
235
697
 
236
698
  export function activitySummary(items: AgentActivity[]): string {
@@ -296,6 +758,7 @@ function finalizeGroup(group: AgentActivityGroup): AgentActivityGroup {
296
758
  if (count === 1) {
297
759
  group.title = group.items[0].target
298
760
  group.state = group.items[0].state
761
+ group.semanticVerb = group.items[0].semanticVerb
299
762
  return group
300
763
  }
301
764
 
@@ -319,25 +782,28 @@ function finalizeGroup(group: AgentActivityGroup): AgentActivityGroup {
319
782
 
320
783
  const action = group.action
321
784
  const doneCount = group.items.filter((it) => it.state === 'done').length
322
- if (action === 'calculation') {
323
- group.title = group.state === 'active'
324
- ? `Calculating expressions (${doneCount}/${count})`
325
- : `Calculated ${count} expressions`
785
+ if (action === 'calculation' || action === 'calculator') {
786
+ group.semanticVerb = 'Calculated'
787
+ group.title = `${count} expressions`
326
788
  } else if (action === 'read_file') {
327
- group.title = group.state === 'active'
328
- ? `Reading files (${doneCount}/${count})`
329
- : `Read ${count} files`
330
- } else if (action === 'write_file') {
331
- group.title = group.state === 'active'
332
- ? `Writing files (${doneCount}/${count})`
333
- : `Wrote ${count} files`
334
- } else if (action === 'search_code' || action === 'semantic_search') {
335
- group.title = `Searched codebase (${count} queries)`
336
- } else if (action === 'web_search') {
337
- group.title = `Searched web (${count} queries)`
789
+ group.semanticVerb = 'Analyzed'
790
+ group.title = group.state === 'active' ? `reading files (${doneCount}/${count})` : `${count} files`
791
+ } else if (action === 'write_file' || action === 'edit_file' || action === 'apply_patch') {
792
+ group.semanticVerb = 'Edited'
793
+ group.title = group.state === 'active' ? `writing files (${doneCount}/${count})` : `${count} files`
794
+ } else if (action === 'run_shell' || action === 'run_command' || action === 'bash') {
795
+ group.semanticVerb = 'Ran'
796
+ group.title = `${count} commands`
797
+ } else if (action === 'list_files' || action === 'list_directory' || action === 'list_dir') {
798
+ group.semanticVerb = 'Explored'
799
+ group.title = `${count} directories`
800
+ } else if (action === 'search_code' || action === 'semantic_search' || action === 'web_search') {
801
+ group.semanticVerb = 'Searched'
802
+ group.title = `${count} queries`
338
803
  } else {
804
+ group.semanticVerb = 'Executed'
339
805
  const readableAction = action.replaceAll('_', ' ')
340
- group.title = `Executed ${count} ${readableAction}`
806
+ group.title = `${count} ${readableAction}`
341
807
  }
342
808
 
343
809
  return group
@@ -410,8 +876,21 @@ function deriveActivitiesFrom(progress: AgentProgress[]): AgentActivityView {
410
876
  for (let index = activities.length - 1; index >= 0; index -= 1) {
411
877
  if (activities[index].state !== 'active') continue
412
878
  if (event.data.callId && activities[index].callId !== event.data.callId) continue
413
- if (activities[index].action !== finished.action || activities[index].target !== finished.target) continue
879
+ if (activities[index].action !== finished.action) continue
880
+ if (event.data.input && activities[index].target !== finished.target) continue
414
881
  activities[index].state = event.data.status ? (event.data.status === 'success' ? 'done' : 'error') : /^(Error:|Blocked|User denied)/.test(event.data.result) ? 'error' : 'done'
882
+ const elapsed = event.data.elapsed_ms ?? event.data.elapsedMs
883
+ if (typeof elapsed === 'number') {
884
+ activities[index].elapsedMs = elapsed
885
+ }
886
+ const lineMatch = typeof event.data.result === 'string' && event.data.result.match(/\+(\d+)\/-(\d+) lines/)
887
+ if (lineMatch) {
888
+ const add = parseInt(lineMatch[1], 10)
889
+ const del = parseInt(lineMatch[2], 10)
890
+ if (!isNaN(add) && !isNaN(del)) {
891
+ activities[index].diffStats = { additions: add, deletions: del }
892
+ }
893
+ }
415
894
  const viewedImage = event.data.action === 'view_image' ? viewedImageDataUri(event.data.result) : null
416
895
  activities[index].result = viewedImage ? event.data.result.replace(viewedImage, '[Image displayed below]') : event.data.result
417
896
  if (event.data.action === 'read_file') {
@@ -423,9 +902,46 @@ function deriveActivitiesFrom(progress: AgentProgress[]): AgentActivityView {
423
902
  : activities[index].label.replace('Reading skill:', 'Read skill preview:')
424
903
  }
425
904
  }
426
- if (activities[index].action === 'ask_user' && event.data.result.startsWith('User answered:')) {
427
- const answer = event.data.result.replace('User answered:', '').trim()
428
- activities[index].target = `(Answered: "${answer}") ${activities[index].target}`
905
+ if (activities[index].action === 'ask_user') {
906
+ let answer: string | undefined
907
+ if (typeof event.data.result === 'string') {
908
+ if (event.data.result.startsWith('User answered:')) {
909
+ answer = event.data.result.replace(/^User answered:\s*/, '').trim()
910
+ activities[index].target = `(Answered: "${answer}") ${activities[index].target}`
911
+ } else if (event.data.result.startsWith('User approved')) {
912
+ answer = 'Approved'
913
+ } else if (event.data.result.startsWith('User declined')) {
914
+ answer = 'Declined'
915
+ }
916
+ }
917
+
918
+ if (answer) {
919
+ if (!activities[index].askUserData) {
920
+ const cleanQuestion = activities[index].target.replace(/^\(Answered:\s*".*?"\)\s*/s, '').trim()
921
+ activities[index].askUserData = {
922
+ questions: [{ question: cleanQuestion, answer }],
923
+ }
924
+ } else if (activities[index].askUserData.questions.length > 0) {
925
+ let assigned = false
926
+ if (answer.startsWith('[') && answer.endsWith(']')) {
927
+ try {
928
+ const parsed = JSON.parse(answer)
929
+ if (Array.isArray(parsed) && parsed.length === activities[index].askUserData.questions.length) {
930
+ parsed.forEach((ans, i) => {
931
+ activities[index].askUserData!.questions[i].answer =
932
+ typeof ans === 'string' ? ans : JSON.stringify(ans)
933
+ })
934
+ assigned = true
935
+ }
936
+ } catch {
937
+ // ignore
938
+ }
939
+ }
940
+ if (!assigned) {
941
+ activities[index].askUserData.questions[0].answer = answer
942
+ }
943
+ }
944
+ }
429
945
  }
430
946
  break
431
947
  }
@@ -540,8 +1056,25 @@ function deriveActivitiesFrom(progress: AgentProgress[]): AgentActivityView {
540
1056
  }
541
1057
  }
542
1058
 
543
- const items = activities.slice(-25)
544
- return { summary: activitySummary(activities), items, groups: groupActivities(items), timeline: timeline.slice(-60) }
1059
+ let totalDurationSecs: number | undefined
1060
+ for (const event of progress) {
1061
+ if (event.type === 'RunCompleted' && typeof event.data?.summary?.durationSecs === 'number') {
1062
+ totalDurationSecs = event.data.summary.durationSecs
1063
+ }
1064
+ }
1065
+
1066
+ const naturalSummary = generateNaturalActivitySummary(activities, timeline)
1067
+ const durationText = formatDurationText(totalDurationSecs)
1068
+
1069
+ const items = activities.slice(-50)
1070
+ return {
1071
+ summary: activitySummary(activities),
1072
+ naturalSummary,
1073
+ durationText,
1074
+ items,
1075
+ groups: groupActivities(items),
1076
+ timeline: timeline.slice(-80),
1077
+ }
545
1078
  }
546
1079
 
547
1080
  export interface WebSearchSource {
@@ -645,3 +1178,37 @@ export function hasSubagentActivity(progress: AgentProgress[]): boolean {
645
1178
  (e.data.subagent || e.data.action === 'dispatch_agents' || e.data.action === 'dispatch_subagent')),
646
1179
  )
647
1180
  }
1181
+
1182
+ export function isAskUserActivity(activity: AgentActivity): boolean {
1183
+ return (
1184
+ activity.action === 'ask_user' ||
1185
+ activity.label === 'ask_user' ||
1186
+ Boolean(activity.askUserData) ||
1187
+ activity.target.startsWith('(Answered:')
1188
+ )
1189
+ }
1190
+
1191
+ export function extractAskUserQuestions(activity: AgentActivity): AskUserItem[] {
1192
+ if (activity.askUserData?.questions && activity.askUserData.questions.length > 0) {
1193
+ return activity.askUserData.questions
1194
+ }
1195
+ const match = activity.target.match(/^\(Answered:\s*"(.*?)"\)\s*(.*)$/s)
1196
+ if (match) {
1197
+ return [{ question: match[2].trim(), answer: match[1].trim() }]
1198
+ }
1199
+ let answer: string | undefined
1200
+ if (typeof activity.result === 'string') {
1201
+ if (activity.result.startsWith('User answered:')) {
1202
+ answer = activity.result.replace(/^User answered:\s*/, '').trim()
1203
+ } else if (activity.result.startsWith('User approved')) {
1204
+ answer = 'Approved'
1205
+ } else if (activity.result.startsWith('User declined')) {
1206
+ answer = 'Declined'
1207
+ }
1208
+ }
1209
+ const cleanQuestion = activity.target.replace(/^\(Answered:\s*".*?"\)\s*/s, '').trim()
1210
+ if (cleanQuestion) {
1211
+ return [{ question: cleanQuestion, answer }]
1212
+ }
1213
+ return []
1214
+ }