@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
@@ -218,6 +218,7 @@ pub(in crate::orchestration) async fn execute(
218
218
  #[cfg(test)]
219
219
  mod tests {
220
220
  use super::*;
221
+ use crate::CodePatchHunk;
221
222
 
222
223
  #[tokio::test]
223
224
  async fn skill_writes_require_separate_approval_even_when_file_edits_are_allowed() {
@@ -1,5 +1,4 @@
1
1
  pub(super) mod avatar;
2
- pub(super) mod browser;
3
2
  pub(super) mod code_search;
4
3
  pub(super) mod files;
5
4
  pub(super) mod git;
@@ -12,3 +11,8 @@ pub(super) mod shell;
12
11
  pub(super) mod web;
13
12
 
14
13
  pub(super) mod mods;
14
+
15
+ pub(super) mod dispatch;
16
+ pub use dispatch::execute_mod_requests;
17
+ pub(crate) use dispatch::execute_tool_from_json;
18
+ pub(super) use dispatch::*;
@@ -14,6 +14,79 @@ fn persist_plan_to_workspace(root: &Path, plan: &ActivePlan) {
14
14
  }
15
15
  }
16
16
 
17
+ fn load_plan_from_workspace(root: &Path) -> Option<ActivePlan> {
18
+ let plan_file = root.join(".agents").join("plans").join("active_plan.json");
19
+ let content = std::fs::read_to_string(plan_file).ok()?;
20
+ serde_json::from_str(&content).ok()
21
+ }
22
+
23
+ fn parse_plan_step_update(raw: &str) -> (&'static str, String) {
24
+ let trimmed = raw.trim();
25
+ let lower = trimmed.to_ascii_lowercase();
26
+
27
+ const PREFIXES: &[(&str, &str)] = &[
28
+ ("[x]", "completed"),
29
+ ("[✓]", "completed"),
30
+ ("done:", "completed"),
31
+ ("completed:", "completed"),
32
+ ("[>]", "in_progress"),
33
+ ("in_progress:", "in_progress"),
34
+ ("in-progress:", "in_progress"),
35
+ ("current:", "in_progress"),
36
+ ("[!]", "failed"),
37
+ ("[✗]", "failed"),
38
+ ("failed:", "failed"),
39
+ ("blocked:", "failed"),
40
+ ("[ ]", "pending"),
41
+ ("[]", "pending"),
42
+ ("pending:", "pending"),
43
+ ("todo:", "pending"),
44
+ ];
45
+
46
+ for &(prefix, status) in PREFIXES {
47
+ if lower.starts_with(prefix) {
48
+ let rest = trimmed[prefix.len()..]
49
+ .trim_start_matches(|c: char| c == '-' || c == ':' || c.is_whitespace())
50
+ .trim();
51
+ return (
52
+ status,
53
+ if rest.is_empty() {
54
+ trimmed.to_string()
55
+ } else {
56
+ rest.to_string()
57
+ },
58
+ );
59
+ }
60
+ }
61
+
62
+ // Check trailing parenthesized status markers like "Step 1 (completed)"
63
+ const SUFFIXES: &[(&str, &str)] = &[
64
+ ("(completed)", "completed"),
65
+ ("(done)", "completed"),
66
+ ("(in_progress)", "in_progress"),
67
+ ("(in-progress)", "in_progress"),
68
+ ("(current)", "in_progress"),
69
+ ("(failed)", "failed"),
70
+ ("(blocked)", "failed"),
71
+ ("(pending)", "pending"),
72
+ ];
73
+ for &(suffix, status) in SUFFIXES {
74
+ if lower.ends_with(suffix) {
75
+ let rest = trimmed[..trimmed.len() - suffix.len()].trim();
76
+ return (
77
+ status,
78
+ if rest.is_empty() {
79
+ trimmed.to_string()
80
+ } else {
81
+ rest.to_string()
82
+ },
83
+ );
84
+ }
85
+ }
86
+
87
+ ("pending", trimmed.to_string())
88
+ }
89
+
17
90
  pub(in crate::orchestration) async fn execute(
18
91
  action: &str,
19
92
  input: &AgentInput,
@@ -71,47 +144,22 @@ pub(in crate::orchestration) async fn execute(
71
144
  .iter()
72
145
  .enumerate()
73
146
  .map(|(idx, step)| {
74
- let lower = step.to_ascii_lowercase();
75
- let status = if lower.starts_with("[x]")
76
- || lower.starts_with("[✓]")
77
- || lower.contains("completed")
78
- || lower.contains("done")
79
- {
80
- "completed".to_string()
81
- } else if lower.starts_with("[>]")
82
- || lower.contains("in_progress")
83
- || lower.contains("current")
84
- {
85
- "in_progress".to_string()
86
- } else {
87
- "pending".to_string()
88
- };
89
- let title = step
90
- .trim_start_matches(|c| {
91
- c == '['
92
- || c == ']'
93
- || c == 'x'
94
- || c == 'X'
95
- || c == '✓'
96
- || c == ' '
97
- || c == '>'
98
- })
99
- .trim()
100
- .to_string();
147
+ let (status, title) = parse_plan_step_update(step);
101
148
  PlanTaskItem {
102
149
  id: format!("step-{}", idx + 1),
103
- title: if title.is_empty() {
104
- step.clone()
105
- } else {
106
- title
107
- },
108
- status,
150
+ title,
151
+ status: status.to_string(),
109
152
  }
110
153
  })
111
154
  .collect();
112
155
 
113
156
  let objective = if !input.summary.trim().is_empty() {
114
157
  input.summary.trim().to_string()
158
+ } else if let Some(existing) = load_plan_from_workspace(root)
159
+ .map(|p| p.objective)
160
+ .filter(|obj| !obj.trim().is_empty())
161
+ {
162
+ existing
115
163
  } else {
116
164
  "Implementation Plan".to_string()
117
165
  };
@@ -125,12 +173,11 @@ pub(in crate::orchestration) async fn execute(
125
173
 
126
174
  let mut formatted = String::from("Plan Updated:\n");
127
175
  for task in &tasks {
128
- let mark = if task.status == "completed" {
129
- "[✓]"
130
- } else if task.status == "in_progress" {
131
- "[>]"
132
- } else {
133
- "[ ]"
176
+ let mark = match task.status.as_str() {
177
+ "completed" => "[✓]",
178
+ "in_progress" => "[>]",
179
+ "failed" => "[✗]",
180
+ _ => "[ ]",
134
181
  };
135
182
  formatted.push_str(&format!("{} {} ({})\n", mark, task.title, task.status));
136
183
  }
@@ -252,10 +299,16 @@ mod tests {
252
299
  assert_eq!(plan.tasks.len(), 2);
253
300
  assert_eq!(plan.tasks[0].status, "in_progress");
254
301
 
255
- // Now test update_plan
302
+ // Now test update_plan without summary (matching tool_catalog schema) and with
303
+ // colon-prefixed statuses ("done: ...", "in_progress: ...", "pending: ...")
256
304
  let update_input = AgentInput {
257
- summary: "Test Feature".to_string(),
258
- steps: vec!["[x] Step 1".to_string(), "[>] Step 2".to_string()],
305
+ summary: String::new(),
306
+ steps: vec![
307
+ "done: Step 1".to_string(),
308
+ "in_progress: Inspect current state".to_string(),
309
+ "[ ] Ensure streaming is done".to_string(),
310
+ "failed: Broken step".to_string(),
311
+ ],
259
312
  ..AgentInput::default()
260
313
  };
261
314
  let update_res = execute(
@@ -273,8 +326,15 @@ mod tests {
273
326
  assert!(update_res.contains("[✓] Step 1"));
274
327
  let updated_content = std::fs::read_to_string(&plan_file).unwrap();
275
328
  let updated_plan: ActivePlan = serde_json::from_str(&updated_content).unwrap();
329
+ assert_eq!(updated_plan.objective, "Test Feature");
276
330
  assert_eq!(updated_plan.tasks[0].status, "completed");
331
+ assert_eq!(updated_plan.tasks[0].title, "Step 1");
277
332
  assert_eq!(updated_plan.tasks[1].status, "in_progress");
333
+ assert_eq!(updated_plan.tasks[1].title, "Inspect current state");
334
+ assert_eq!(updated_plan.tasks[2].status, "pending");
335
+ assert_eq!(updated_plan.tasks[2].title, "Ensure streaming is done");
336
+ assert_eq!(updated_plan.tasks[3].status, "failed");
337
+ assert_eq!(updated_plan.tasks[3].title, "Broken step");
278
338
 
279
339
  let _ = std::fs::remove_dir_all(&temp);
280
340
  }
@@ -2,38 +2,62 @@ use std::path::Path;
2
2
 
3
3
  use super::super::*;
4
4
 
5
- /// Catch ordinary URL launcher commands, not arbitrary shell programs. This is
6
- /// a workflow guard, not a shell security boundary.
7
- fn launches_external_url(command: &str) -> bool {
8
- let Some(tokens) = shlex::split(command) else {
9
- return false;
10
- };
11
- let Some(program) = tokens.first() else {
12
- return false;
13
- };
14
- let program = program.rsplit(['/', '\\']).next().unwrap_or(program);
15
- let launcher = match program.to_ascii_lowercase().as_str() {
16
- "xdg-open" | "open" | "wslview" | "sensible-browser" | "start-process" | "start" => true,
17
- "gio" => tokens.get(1).is_some_and(|arg| arg == "open"),
18
- _ => false,
19
- };
20
- launcher
21
- && tokens.iter().skip(1).any(|arg| {
22
- reqwest::Url::parse(arg).is_ok_and(|url| {
23
- matches!(url.scheme(), "http" | "https") && url.host_str().is_some()
24
- })
25
- })
5
+ /// Deletes a Chrome run's throw-away profile once the tool call is over, however
6
+ /// it ends (success, denial, repeat refusal, error). Background jobs outlive the
7
+ /// call, so they disarm it and rely on the stale-profile sweep instead.
8
+ struct ProfileCleanup<'a>(Option<&'a crate::browser::headless::ChromeRun>);
9
+
10
+ impl ProfileCleanup<'_> {
11
+ fn disarm(&mut self) {
12
+ self.0 = None;
13
+ }
26
14
  }
27
15
 
28
- pub(in crate::orchestration) async fn prevent_duplicate_browser_launch(
29
- command: &str,
30
- ) -> Result<(), OrchestrationError> {
31
- if launches_external_url(command) && crate::browser::session_finish_evidence().await {
32
- return Err(OrchestrationError::Agent(
33
- "External URL launch skipped: this task already uses the automation browser. Use browser_observe/browser_read to inspect its current page, browser_screenshot for visual inspection, or browser_open if navigation is still needed. Do not open the same URL in another browser.".into(),
34
- ));
16
+ impl Drop for ProfileCleanup<'_> {
17
+ fn drop(&mut self) {
18
+ if let Some(run) = self.0 {
19
+ run.discard_profile();
20
+ }
21
+ }
22
+ }
23
+
24
+ /// Refuses to capture the same page with the same options twice in one turn:
25
+ /// that is the screenshot → view_image → recapture loop. A `retryReason` is the
26
+ /// model's explicit statement that the page changed, and a deleted capture file
27
+ /// may always be retaken.
28
+ fn repeat_capture_notice(
29
+ run: &crate::browser::headless::ChromeRun,
30
+ root: &Path,
31
+ retry_reason: &str,
32
+ ) -> Option<String> {
33
+ if !retry_reason.trim().is_empty() {
34
+ return None;
35
+ }
36
+ let previous = reliability::recorded_capture(run.key.as_ref()?.as_str())?;
37
+ match previous {
38
+ Some(file) if root.join(&file).exists() => Some(format!(
39
+ "Already captured: this page was captured with the same options earlier in this turn at {file}. \
40
+ Call view_image on that file once (or read it) instead of capturing again, or pass \
41
+ retryReason if the page must be fetched again."
42
+ )),
43
+ Some(_) => None,
44
+ None => Some(
45
+ "Already read: this page was read with the same options earlier in this turn. Reuse that \
46
+ output, change the options (for example capture a screenshot), or pass retryReason if the \
47
+ page must be fetched again."
48
+ .into(),
49
+ ),
50
+ }
51
+ }
52
+
53
+ /// Remembers a successful capture so `repeat_capture_notice` can spot repeats.
54
+ fn record_capture_result(run: &crate::browser::headless::ChromeRun, result: &str) {
55
+ if let Some(key) = &run.key
56
+ && !result.starts_with("Skipped")
57
+ && !shell_result_failed(result)
58
+ {
59
+ reliability::record_capture(key.as_str(), run.output.clone());
35
60
  }
36
- Ok(())
37
61
  }
38
62
 
39
63
  /// Handles the subset of `execute_tool` actions related to shell.
@@ -50,7 +74,16 @@ pub(in crate::orchestration) async fn execute(
50
74
  match action {
51
75
  "run_shell" => {
52
76
  let command = required(&input.command, "command")?;
53
- prevent_duplicate_browser_launch(command).await?;
77
+ // Headless Chrome is made reliable (flags, throw-away profile, timeout) and
78
+ // de-duplicated here, so the model only has to write the plain command.
79
+ let chrome = crate::browser::headless::prepare(command, config);
80
+ let mut cleanup = ProfileCleanup(chrome.as_ref());
81
+ let command = chrome.as_ref().map_or(command, |run| run.command.as_str());
82
+ if let Some(run) = &chrome
83
+ && let Some(notice) = repeat_capture_notice(run, root, &input.retry_reason)
84
+ {
85
+ return Ok(notice);
86
+ }
54
87
  let mode = classify_shell_command(command).mode.as_str().to_owned();
55
88
  let approved = approve_cb(&AgentApproval::RunShell {
56
89
  command: command.to_owned(),
@@ -69,6 +102,7 @@ pub(in crate::orchestration) async fn execute(
69
102
  || crate::bg_shell::start_background(root, config, command),
70
103
  )
71
104
  .map_err(|e| OrchestrationError::Agent(e.to_string()))?;
105
+ cleanup.disarm();
72
106
  reliability::record(
73
107
  command,
74
108
  reliability::ExecutionState::Running,
@@ -85,7 +119,12 @@ pub(in crate::orchestration) async fn execute(
85
119
  ))
86
120
  }
87
121
  ApprovalOutcome::Approved => {
88
- run_shell(root, config, chat_id, command, &input.retry_reason).await
122
+ let out =
123
+ run_shell(root, config, chat_id, command, &input.retry_reason).await?;
124
+ if let Some(run) = &chrome {
125
+ record_capture_result(run, &out);
126
+ }
127
+ Ok(out)
89
128
  }
90
129
  ApprovalOutcome::Denied => Ok(format!("User denied shell command: {}", command)),
91
130
  ApprovalOutcome::Intercepted(obs) => Ok(obs),
@@ -245,38 +284,70 @@ pub(in crate::orchestration) async fn execute(
245
284
  #[cfg(test)]
246
285
  mod tests {
247
286
  use super::*;
287
+ use crate::browser::headless::{HostEnv, prepare_with};
248
288
 
249
- #[test]
250
- fn external_url_launch_detection_preserves_other_shell_work() {
251
- for command in [
252
- "xdg-open 'https://example.com/watch?v=1&list=2'",
253
- "/usr/bin/xdg-open https://example.com &",
254
- "open -a Firefox https://example.com",
255
- "gio open https://example.com",
256
- "wslview https://example.com",
257
- "Start-Process https://example.com",
258
- ] {
259
- assert!(launches_external_url(command), "{command}");
260
- }
261
- for command in [
262
- "xdg-open /tmp/report.pdf",
263
- "open /tmp",
264
- "curl https://example.com",
265
- "echo xdg-open https://example.com",
266
- "gio info https://example.com",
267
- "cargo test",
268
- ] {
269
- assert!(!launches_external_url(command), "{command}");
270
- }
289
+ fn chrome(command: &str) -> crate::browser::headless::ChromeRun {
290
+ let host = HostEnv {
291
+ no_sandbox: false,
292
+ linux_timeout: false,
293
+ profile_root: std::env::temp_dir().join("mint-headless-shell-test"),
294
+ };
295
+ prepare_with(command, &MintConfig::default(), &host).unwrap()
296
+ }
297
+
298
+ #[tokio::test]
299
+ async fn a_repeated_capture_of_the_same_page_is_refused_until_a_reason_is_given() {
300
+ let dir = std::env::temp_dir().join(format!("mint-capture-{}", uuid::Uuid::new_v4()));
301
+ std::fs::create_dir_all(&dir).unwrap();
302
+ reliability::scope(async {
303
+ let first = chrome("google-chrome --screenshot=a.png https://example.com/shop");
304
+ assert!(
305
+ repeat_capture_notice(&first, &dir, "").is_none(),
306
+ "first capture runs"
307
+ );
308
+ std::fs::write(dir.join("a.png"), b"png").unwrap();
309
+ record_capture_result(&first, "");
310
+
311
+ let again = chrome("google-chrome --screenshot=b.png https://example.com/shop/");
312
+ let notice = repeat_capture_notice(&again, &dir, "").expect("repeat refused");
313
+ assert!(
314
+ notice.contains("a.png") && notice.contains("view_image"),
315
+ "{notice}"
316
+ );
317
+ assert!(
318
+ repeat_capture_notice(&again, &dir, "page changed after login").is_none(),
319
+ "a retryReason allows a deliberate recapture"
320
+ );
321
+ std::fs::remove_file(dir.join("a.png")).unwrap();
322
+ assert!(
323
+ repeat_capture_notice(&again, &dir, "").is_none(),
324
+ "a deleted capture may be retaken"
325
+ );
326
+ })
327
+ .await;
328
+ let _ = std::fs::remove_dir_all(&dir);
271
329
  }
272
330
 
273
331
  #[tokio::test]
274
- async fn external_url_launch_allowed_without_browser_use() {
275
- crate::browser::run_session(async {
276
- prevent_duplicate_browser_launch("xdg-open https://example.com")
277
- .await
278
- .unwrap();
332
+ async fn dom_reads_repeat_detection_and_failed_runs_are_not_recorded() {
333
+ let dir = std::env::temp_dir();
334
+ reliability::scope(async {
335
+ let dom = chrome("google-chrome --dump-dom https://example.com");
336
+ record_capture_result(&dom, "Error: Chrome failed\nexit: 1\n");
337
+ assert!(
338
+ repeat_capture_notice(&dom, &dir, "").is_none(),
339
+ "failures can be retried"
340
+ );
341
+ record_capture_result(&dom, "<html></html>");
342
+ let notice = repeat_capture_notice(&dom, &dir, "").expect("repeat refused");
343
+ assert!(notice.starts_with("Already read"), "{notice}");
279
344
  })
280
345
  .await;
281
346
  }
347
+
348
+ #[test]
349
+ fn commands_without_a_single_url_are_never_deduplicated() {
350
+ let run = chrome("google-chrome --dump-dom");
351
+ assert!(repeat_capture_notice(&run, &std::env::temp_dir(), "").is_none());
352
+ }
282
353
  }