@pheem49/mint 1.15.0 โ†’ 1.16.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 (644) hide show
  1. package/Cargo.lock +1012 -69
  2. package/Cargo.toml +5 -2
  3. package/LICENSE +21 -654
  4. package/README.md +92 -817
  5. package/Release_Note.md +53 -560
  6. package/apps/mint-companion/src-tauri/Cargo.toml +20 -0
  7. package/apps/mint-companion/src-tauri/build.rs +3 -0
  8. package/apps/mint-companion/src-tauri/src/main.rs +468 -0
  9. package/apps/mint-companion/tsconfig.json +20 -0
  10. package/apps/mint-companion/vite.config.ts +3 -0
  11. package/components.json +19 -0
  12. package/crates/mint-cli/Cargo.toml +6 -0
  13. package/crates/mint-cli/src/agent/live_status.rs +187 -39
  14. package/crates/mint-cli/src/agent.rs +323 -95
  15. package/crates/mint-cli/src/commands/agent.rs +69 -55
  16. package/crates/mint-cli/src/commands/integrations.rs +7 -0
  17. package/crates/mint-cli/src/commands/knowledge.rs +12 -32
  18. package/crates/mint-cli/src/commands/mod.rs +44 -0
  19. package/crates/mint-cli/src/image.rs +31 -33
  20. package/crates/mint-cli/src/interactive/chat_tui.rs +3594 -483
  21. package/crates/mint-cli/src/interactive/input_box.rs +56 -6
  22. package/crates/mint-cli/src/interactive/slash_commands.rs +208 -56
  23. package/crates/mint-cli/src/interactive/tool_activity.rs +328 -0
  24. package/crates/mint-cli/src/interactive.rs +64 -40
  25. package/crates/mint-cli/src/main.rs +13 -7
  26. package/crates/mint-cli/src/mcp.rs +5 -0
  27. package/crates/mint-cli/src/mod_host.rs +6 -0
  28. package/crates/mint-cli/src/mods_cli.rs +62 -0
  29. package/crates/mint-cli/src/mods_ui.rs +187 -0
  30. package/crates/mint-cli/src/onboard.rs +5 -31
  31. package/crates/mint-cli/src/terminal_theme.rs +3 -1
  32. package/crates/mint-cli/src/updater.rs +56 -2
  33. package/crates/mint-cli/tests/library_intake.rs +77 -0
  34. package/crates/mint-cli/tests/mods_testing.rs +133 -0
  35. package/crates/mint-companion-protocol/Cargo.toml +10 -0
  36. package/crates/mint-companion-protocol/src/lib.rs +163 -0
  37. package/crates/mint-core/Cargo.toml +6 -3
  38. package/crates/mint-core/build.rs +26 -0
  39. package/crates/mint-core/src/agent/chat.rs +167 -39
  40. package/crates/mint-core/src/agent/code_tools.rs +15 -0
  41. package/crates/mint-core/src/agent/memory/sources.rs +240 -0
  42. package/crates/mint-core/src/agent/memory.rs +147 -13
  43. package/crates/mint-core/src/agent/skill_inventory.rs +325 -0
  44. package/crates/mint-core/src/agent/skills.rs +437 -90
  45. package/crates/mint-core/src/agent.rs +1 -0
  46. package/crates/mint-core/src/api_server/routes/background_jobs.rs +310 -0
  47. package/crates/mint-core/src/api_server/routes/chat.rs +216 -199
  48. package/crates/mint-core/src/api_server/routes/html_preview.rs +77 -0
  49. package/crates/mint-core/src/api_server/routes/library.rs +97 -0
  50. package/crates/mint-core/src/api_server/routes/media_gen.rs +10 -57
  51. package/crates/mint-core/src/api_server/routes/misc.rs +104 -107
  52. package/crates/mint-core/src/api_server/routes/mod.rs +6 -0
  53. package/crates/mint-core/src/api_server/routes/mods.rs +31 -0
  54. package/crates/mint-core/src/api_server/routes/sessions.rs +41 -0
  55. package/crates/mint-core/src/api_server/routes/skills_subagents.rs +12 -42
  56. package/crates/mint-core/src/api_server/routes/slash.rs +50 -1
  57. package/crates/mint-core/src/api_server/routes/sources.rs +101 -0
  58. package/crates/mint-core/src/api_server.rs +118 -9
  59. package/crates/mint-core/src/avatar_bridge.rs +11 -2
  60. package/crates/mint-core/src/browser/session.rs +52 -0
  61. package/crates/mint-core/src/companion.rs +716 -0
  62. package/crates/mint-core/src/cron/scheduler.rs +34 -2
  63. package/crates/mint-core/src/integrations/channels.rs +87 -345
  64. package/crates/mint-core/src/integrations/mcp.rs +397 -50
  65. package/crates/mint-core/src/integrations/mcp_calls.rs +823 -0
  66. package/crates/mint-core/src/integrations/mcp_catalog.rs +267 -0
  67. package/crates/mint-core/src/integrations/mcp_device.rs +319 -0
  68. package/crates/mint-core/src/integrations/mcp_result.rs +356 -0
  69. package/crates/mint-core/src/integrations/webhooks.rs +539 -0
  70. package/crates/mint-core/src/integrations.rs +2 -0
  71. package/crates/mint-core/src/lib.rs +85 -26
  72. package/crates/mint-core/src/media/gemini_live.rs +134 -14
  73. package/crates/mint-core/src/media/image_models.rs +3 -27
  74. package/crates/mint-core/src/media/image_workflow.rs +216 -0
  75. package/crates/mint-core/src/media/library/intake.rs +144 -0
  76. package/crates/mint-core/src/media/library/migration.rs +56 -0
  77. package/crates/mint-core/src/media/library/removal.rs +55 -0
  78. package/crates/mint-core/src/media/library/tests.rs +730 -0
  79. package/crates/mint-core/src/media/library/thumbnails.rs +72 -0
  80. package/crates/mint-core/src/media/library.rs +545 -0
  81. package/crates/mint-core/src/media/model_catalog.rs +375 -0
  82. package/crates/mint-core/src/media/pictures.rs +79 -222
  83. package/crates/mint-core/src/media/video_models.rs +3 -22
  84. package/crates/mint-core/src/media.rs +3 -0
  85. package/crates/mint-core/src/mods/manager.rs +1124 -0
  86. package/crates/mint-core/src/mods/mod.rs +569 -0
  87. package/crates/mint-core/src/mods/operations.rs +204 -0
  88. package/crates/mint-core/src/mods/session.rs +146 -0
  89. package/crates/mint-core/src/mods/slash.rs +59 -0
  90. package/crates/mint-core/src/mods/telemetry.rs +306 -0
  91. package/crates/mint-core/src/orchestration/approvals.rs +344 -0
  92. package/crates/mint-core/src/orchestration/context_render.rs +104 -0
  93. package/crates/mint-core/src/orchestration/mcp_review.rs +153 -0
  94. package/crates/mint-core/src/orchestration/memory_skill.rs +104 -32
  95. package/crates/mint-core/src/orchestration/mod.rs +2139 -244
  96. package/crates/mint-core/src/orchestration/mod_authoring_regressions.rs +209 -0
  97. package/crates/mint-core/src/orchestration/progress_clock.rs +80 -0
  98. package/crates/mint-core/src/orchestration/repeated_failures.rs +116 -0
  99. package/crates/mint-core/src/orchestration/review_regressions.rs +449 -0
  100. package/crates/mint-core/src/orchestration/run_control.rs +196 -0
  101. package/crates/mint-core/src/orchestration/tools/files.rs +161 -14
  102. package/crates/mint-core/src/orchestration/tools/media.rs +37 -41
  103. package/crates/mint-core/src/orchestration/tools/misc.rs +3 -2
  104. package/crates/mint-core/src/orchestration/tools/mod.rs +2 -0
  105. package/crates/mint-core/src/orchestration/tools/mods.rs +161 -0
  106. package/crates/mint-core/src/orchestration/tools/plugins_mcp.rs +303 -71
  107. package/crates/mint-core/src/orchestration/tools/shell.rs +95 -2
  108. package/crates/mint-core/src/orchestration/workspace_helpers.rs +4 -1
  109. package/crates/mint-core/src/prompts/agent.rs +17 -2
  110. package/crates/mint-core/src/prompts/tool_catalog.rs +28 -2
  111. package/crates/mint-core/src/search/knowledge.rs +7 -1
  112. package/crates/mint-core/src/search/linked_folders/index.rs +177 -13
  113. package/crates/mint-core/src/search/linked_folders.rs +83 -2
  114. package/crates/mint-core/src/slash/catalog.rs +1 -1
  115. package/crates/mint-core/src/slash/mod.rs +174 -11
  116. package/crates/mint-core/src/slash/mods.rs +155 -0
  117. package/crates/mint-core/src/system/bg_shell.rs +1129 -95
  118. package/crates/mint-core/src/system/config.rs +5 -10
  119. package/crates/mint-core/src/system/html_preview.rs +398 -0
  120. package/crates/mint-core/src/system/mint_web.rs +164 -0
  121. package/crates/mint-core/src/system/release_notes.rs +18 -0
  122. package/crates/mint-core/src/system/shell.rs +21 -6
  123. package/crates/mint-core/src/system/workspace.rs +72 -8
  124. package/crates/mint-core/src/system/workspace_history.rs +604 -0
  125. package/crates/mint-core/src/system.rs +4 -0
  126. package/crates/mint-core/tests/background_retention.rs +34 -0
  127. package/crates/mint-core/tests/background_shutdown.rs +24 -0
  128. package/crates/mint-core/tests/library_http.rs +209 -0
  129. package/crates/mint-core/tests/mcp_contract.rs +283 -0
  130. package/crates/mint-core/tests/mcp_stdio.rs +11 -10
  131. package/crates/mint-core/tests/media_model_catalog_http.rs +403 -0
  132. package/crates/mint-core/tests/mods_http.rs +215 -0
  133. package/crates/mint-core/tests/terminal_shutdown.rs +109 -0
  134. package/crates/mint-mod-runtime/Cargo.toml +18 -0
  135. package/crates/mint-mod-runtime/src/lib.rs +507 -0
  136. package/crates/mint-mod-runtime/src/sdk.js +56 -0
  137. package/crates/mint-mod-runtime/src/test_sdk.js +32 -0
  138. package/crates/mint-mod-runtime/src/testing.rs +94 -0
  139. package/crates/mint-mod-runtime/tests/runtime.rs +156 -0
  140. package/docs/mods/AUTHORING.md +61 -0
  141. package/docs/mods/README.md +139 -0
  142. package/docs/mods/TESTING.md +56 -0
  143. package/docs/mods/mint-mods.d.ts +66 -0
  144. package/examples/device-mcp/README.md +22 -0
  145. package/examples/device-mcp/contract.md +49 -0
  146. package/examples/device-mcp/mock-device.mjs +66 -0
  147. package/examples/device-mcp/server.mjs +48 -0
  148. package/examples/device-mcp/server.test.mjs +78 -0
  149. package/examples/mods/task-checklist/README.md +1 -0
  150. package/examples/mods/task-checklist/main.ts +17 -0
  151. package/examples/mods/task-checklist/manifest.json +1 -0
  152. package/examples/mods/task-checklist/tests/checklist.test.ts +15 -0
  153. package/examples/mods/token-meter/README.md +1 -0
  154. package/examples/mods/token-meter/main.ts +8 -0
  155. package/examples/mods/token-meter/manifest.json +1 -0
  156. package/examples/mods/tool-guard/README.md +1 -0
  157. package/examples/mods/tool-guard/main.ts +9 -0
  158. package/examples/mods/tool-guard/manifest.json +1 -0
  159. package/out/web/assets/CliSessionsView-CtzUJpK_.css +1 -0
  160. package/out/web/assets/CliSessionsView-D7hw0Mgj.js +1 -0
  161. package/out/web/assets/CodeReviewPage-C7bEOJfd.js +4 -0
  162. package/out/web/assets/CodeReviewPage-Dte9uLiL.css +1 -0
  163. package/out/web/assets/CommandPalette-0RrF-AOT.js +1 -0
  164. package/out/web/assets/ImageStudioPanel-CdxkAf_r.js +1 -0
  165. package/out/web/assets/LibraryPage--Qfisp6D.js +2 -0
  166. package/out/web/assets/LibraryPage-DAxUTAg7.css +1 -0
  167. package/out/web/assets/LinkedFoldersView-2nfZbDgN.js +1 -0
  168. package/out/web/assets/McpRegistryPicker-h9uS8_wT.js +1 -0
  169. package/out/web/assets/McpServersView-slfQJzNB.js +1 -0
  170. package/out/web/assets/MintDashboard-BSR5XpXh.js +25 -0
  171. package/out/web/assets/MintDashboard-CWUcCGhd.css +1 -0
  172. package/out/web/assets/PluginsView-l7LSm503.js +1 -0
  173. package/out/web/assets/ScheduledTasksView-CttmLbq1.js +1 -0
  174. package/out/web/assets/SettingsWindow-BCMLYvEV.js +4 -0
  175. package/out/web/assets/SettingsWindow-DcMVmU9u.css +1 -0
  176. package/out/web/assets/SkillsView-rNs1qr_U.js +2 -0
  177. package/out/web/assets/ToolSurfacePage-B9DzEPWN.js +37 -0
  178. package/out/web/assets/ToolSurfacePage-CTfgggF2.css +1 -0
  179. package/out/web/assets/VeoStudioPanel-DiJs_Cg2.js +4 -0
  180. package/out/web/assets/WorkspaceSelector-B2xIp04D.js +1 -0
  181. package/out/web/assets/WorkspaceSelector-Ca8uJOsS.css +1 -0
  182. package/out/web/assets/{abnfDiagram-VCTEODGH-ByMyfanq.js โ†’ abnfDiagram-VCTEODGH-CD0CjoGl.js} +1 -1
  183. package/out/web/assets/{architecture-7GRP2DOG-CLq2kEOB.js โ†’ architecture-7GRP2DOG-RKV83OeZ.js} +1 -1
  184. package/out/web/assets/{architectureDiagram-5GKGNRK7-6PoVSmNG.js โ†’ architectureDiagram-5GKGNRK7-nN_HAFct.js} +1 -1
  185. package/out/web/assets/{blockDiagram-NRAW4CY4-cKw9mAKi.js โ†’ blockDiagram-NRAW4CY4-BrrqzHW5.js} +1 -1
  186. package/out/web/assets/{c4Diagram-UCG6FXSJ-Cqy8N5Ge.js โ†’ c4Diagram-UCG6FXSJ-DTdV8_zE.js} +1 -1
  187. package/out/web/assets/{chunk-75Z2AOVW-3qybijF5.js โ†’ chunk-75Z2AOVW-_M0z5olV.js} +1 -1
  188. package/out/web/assets/chunk-CKBBP62Z-BpQEgeHF.js +2 -0
  189. package/out/web/assets/{chunk-CLGD4ZFX-CndDQkww.js โ†’ chunk-CLGD4ZFX-D54yGvGS.js} +1 -1
  190. package/out/web/assets/{chunk-DSNCTWBM-Cc4NxvgD.js โ†’ chunk-DSNCTWBM-C-gvT2ru.js} +1 -1
  191. package/out/web/assets/{chunk-DU6HZSFF-drzh6GsU.js โ†’ chunk-DU6HZSFF-CnFngIhW.js} +1 -1
  192. package/out/web/assets/{chunk-F27PBJKO-FiY35l4v.js โ†’ chunk-F27PBJKO-BqtWrnEO.js} +1 -1
  193. package/out/web/assets/{chunk-G27WJ6UU-Bjt9Il4A.js โ†’ chunk-G27WJ6UU-Ckoq5mi2.js} +1 -1
  194. package/out/web/assets/{chunk-GMAD6QVW-Bppi3Ctd.js โ†’ chunk-GMAD6QVW-BuhEcgHq.js} +1 -1
  195. package/out/web/assets/{chunk-JQ64N6SF-DGg5kcEV.js โ†’ chunk-JQ64N6SF-CF8T7GiZ.js} +1 -1
  196. package/out/web/assets/{chunk-LCL6LL3I-CCYcjd7p.js โ†’ chunk-LCL6LL3I-BhXV9zyH.js} +1 -1
  197. package/out/web/assets/{chunk-OBVCFTLP-DJeNil5M.js โ†’ chunk-OBVCFTLP-CRIkunUB.js} +1 -1
  198. package/out/web/assets/{chunk-P2QGCYS3-lRzkRjBB.js โ†’ chunk-P2QGCYS3-UFEU6BxZ.js} +1 -1
  199. package/out/web/assets/{chunk-POPQ4Y6H-DouASMBW.js โ†’ chunk-POPQ4Y6H-CQcrNbjr.js} +1 -1
  200. package/out/web/assets/{chunk-PWAF6VOD-DMmD1vVf.js โ†’ chunk-PWAF6VOD-Cc2uVDsG.js} +1 -1
  201. package/out/web/assets/{chunk-R7TYR2AO-CYLA5UC5.js โ†’ chunk-R7TYR2AO-DiCxzdAY.js} +1 -1
  202. package/out/web/assets/{chunk-RHFEMEQ7-BiXwEcUT.js โ†’ chunk-RHFEMEQ7-b7hnPmrR.js} +1 -1
  203. package/out/web/assets/{chunk-SVP7TREG-BV-zPslF.js โ†’ chunk-SVP7TREG-Bu_yYb-V.js} +1 -1
  204. package/out/web/assets/{chunk-TEH6E4GO-B2RGEohZ.js โ†’ chunk-TEH6E4GO-G4Cdlyrt.js} +1 -1
  205. package/out/web/assets/classDiagram-DTDB5LWJ-RnC2mYO4.js +1 -0
  206. package/out/web/assets/classDiagram-v2-JRS7N3AN-RnC2mYO4.js +1 -0
  207. package/out/web/assets/config-CnvCChUM.js +2 -0
  208. package/out/web/assets/config-DM6oIVO3.css +1 -0
  209. package/out/web/assets/core-DsKkQ7ig.js +1 -0
  210. package/out/web/assets/{cynefin-OW5HDTMX-Cas4csHM.js โ†’ cynefin-OW5HDTMX-CnzQfOGp.js} +1 -1
  211. package/out/web/assets/{cynefinDiagram-5FMLGOSQ-Bbpj66Y3.js โ†’ cynefinDiagram-5FMLGOSQ-DVtQnfpm.js} +1 -1
  212. package/out/web/assets/{dagre-3AP2YEHR-B0-2RDEd.js โ†’ dagre-3AP2YEHR-DG76_k5_.js} +1 -1
  213. package/out/web/assets/{diagram-S7CK7UJ4-BdzEjUyH.js โ†’ diagram-S7CK7UJ4-CaouUoze.js} +1 -1
  214. package/out/web/assets/{diagram-UQ7AKVKN-CJDwyPxF.js โ†’ diagram-UQ7AKVKN-B4bEc26B.js} +1 -1
  215. package/out/web/assets/{diagram-VSXAHHWV-BxaG1r_B.js โ†’ diagram-VSXAHHWV-BJ86Ov2Y.js} +1 -1
  216. package/out/web/assets/{diagram-VX7I27RA-BMmUqm8f.js โ†’ diagram-VX7I27RA-7hH5IPYf.js} +1 -1
  217. package/out/web/assets/{diagram-Z3DM3KII-CFtd03eE.js โ†’ diagram-Z3DM3KII-CCM4clo8.js} +1 -1
  218. package/out/web/assets/dist-js-BkYNznHX.js +1 -0
  219. package/out/web/assets/{ebnfDiagram-PWID7BFC-DwAen1hO.js โ†’ ebnfDiagram-PWID7BFC-Da7WAM3k.js} +1 -1
  220. package/out/web/assets/{erDiagram-SSCWMZ5O-BCZSHACj.js โ†’ erDiagram-SSCWMZ5O-CzzmnZWT.js} +1 -1
  221. package/out/web/assets/{event-ChBVpRy0.js โ†’ event-Cdt7Byyj.js} +1 -1
  222. package/out/web/assets/{eventmodeling-NTZA5JFV-XHStE4E_.js โ†’ eventmodeling-NTZA5JFV-xcfsA0XD.js} +1 -1
  223. package/out/web/assets/flowDiagram-A5DVABFB-JdtH1673.js +1 -0
  224. package/out/web/assets/{ganttDiagram-EL5Y4UJY-BAEu1Cql.js โ†’ ganttDiagram-EL5Y4UJY-B3ClFGmL.js} +1 -1
  225. package/out/web/assets/{gitGraph-4MIJSDKK-r32Vyper.js โ†’ gitGraph-4MIJSDKK-BZ_y_cDU.js} +1 -1
  226. package/out/web/assets/{gitGraphDiagram-WWUBYQGX-z9o9qt6V.js โ†’ gitGraphDiagram-WWUBYQGX-drhRSYm3.js} +1 -1
  227. package/out/web/assets/index-BZ9xgmMP.css +1 -0
  228. package/out/web/assets/index-Ca8cCS4h.js +168 -0
  229. package/out/web/assets/{info-A6RAGUB7-BvLaC2cw.js โ†’ info-A6RAGUB7-B7I3K0F1.js} +1 -1
  230. package/out/web/assets/{infoDiagram-RXCK75RN-Be6WAXXw.js โ†’ infoDiagram-RXCK75RN-deX_1MPh.js} +1 -1
  231. package/out/web/assets/{ishikawaDiagram-5VMMS53U-DZjoHJHC.js โ†’ ishikawaDiagram-5VMMS53U-s38yo28y.js} +1 -1
  232. package/out/web/assets/{journeyDiagram-EYS64GPL-CtVkjkGm.js โ†’ journeyDiagram-EYS64GPL-CZn2kB8b.js} +1 -1
  233. package/out/web/assets/jsx-runtime-DN4NhQUK.js +1 -0
  234. package/out/web/assets/{kanban-definition-3QL26DDD-CW5FMv1w.js โ†’ kanban-definition-3QL26DDD-DodjgySL.js} +1 -1
  235. package/out/web/assets/markdown-CMhYIcWB.js +39 -0
  236. package/out/web/assets/{mermaid-parser.core-aUgCqsZ5.js โ†’ mermaid-parser.core-Whu2rlJC.js} +3 -3
  237. package/out/web/assets/{mermaid.core-D57sEiSL.js โ†’ mermaid.core-CKyPo8VH.js} +4 -4
  238. package/out/web/assets/{mindmap-definition-FBJOCRG2-DAmSpM3a.js โ†’ mindmap-definition-FBJOCRG2-260WBDxZ.js} +1 -1
  239. package/out/web/assets/modelManager-CUaGWMWu.js +2 -0
  240. package/out/web/assets/{oauthManager-9jxicDWJ.js โ†’ oauthManager-DlNhXJr6.js} +1 -1
  241. package/out/web/assets/{packet-AYTQ26CC-DJ27iIoJ.js โ†’ packet-AYTQ26CC-CEeIRKRG.js} +1 -1
  242. package/out/web/assets/{pegDiagram-XKGWAZYB-Bj3ii-gS.js โ†’ pegDiagram-XKGWAZYB-BYadI-KO.js} +1 -1
  243. package/out/web/assets/{pie-WAS4IAKB-DG5mCUbc.js โ†’ pie-WAS4IAKB-D8uf4dh5.js} +1 -1
  244. package/out/web/assets/{pieDiagram-E7YTZNPT-BrJLPnDA.js โ†’ pieDiagram-E7YTZNPT-CjSDy4hO.js} +1 -1
  245. package/out/web/assets/{quadrantDiagram-AXDQQJYC-3x_TmF3j.js โ†’ quadrantDiagram-AXDQQJYC-B8D3gU5d.js} +1 -1
  246. package/out/web/assets/{radar-RG4KPBEZ-CrXaTTuV.js โ†’ radar-RG4KPBEZ-DZJxnokY.js} +1 -1
  247. package/out/web/assets/{railroad-74A4TZTK-BpqMH9BE.js โ†’ railroad-74A4TZTK-DBysUiUi.js} +1 -1
  248. package/out/web/assets/{railroad-abnf-HS5TGJTU-DHI0uLlV.js โ†’ railroad-abnf-HS5TGJTU-CHWxoJYg.js} +1 -1
  249. package/out/web/assets/{railroad-ebnf-LZEXJU2U-CQCb316y.js โ†’ railroad-ebnf-LZEXJU2U-DDv7X6bY.js} +1 -1
  250. package/out/web/assets/{railroad-peg-WCYAUIDC-Ph1giKkH.js โ†’ railroad-peg-WCYAUIDC-DpfcYYNJ.js} +1 -1
  251. package/out/web/assets/{railroadDiagram-O6MQD6OU-BRq6wIxx.js โ†’ railroadDiagram-O6MQD6OU-CYtyv0t3.js} +1 -1
  252. package/out/web/assets/{requirementDiagram-EFPCY7ZU-DIJj6G1g.js โ†’ requirementDiagram-EFPCY7ZU-D2X-Ewik.js} +1 -1
  253. package/out/web/assets/{sankeyDiagram-P5KCCOFB-DZajSryq.js โ†’ sankeyDiagram-P5KCCOFB-Bn2x6C4Y.js} +1 -1
  254. package/out/web/assets/{sequenceDiagram-WJ2MYXX4-DxJCWyZS.js โ†’ sequenceDiagram-WJ2MYXX4-BvFLKtPd.js} +1 -1
  255. package/out/web/assets/{stateDiagram-HBIQ2CUA-k6voi8Vf.js โ†’ stateDiagram-HBIQ2CUA-Bx0GWeaY.js} +1 -1
  256. package/out/web/assets/stateDiagram-v2-4QOOHH4V--hol1yg_.js +1 -0
  257. package/out/web/assets/{swimlanes-XN3QIQJK-igYhTjXB.js โ†’ swimlanes-XN3QIQJK-CCma0T7L.js} +1 -1
  258. package/out/web/assets/swimlanesDiagram-VK2B7HYN-BkohoVsU.js +8 -0
  259. package/out/web/assets/{timeline-definition-24CTP7MA-Dke2p93p.js โ†’ timeline-definition-24CTP7MA-iCRhhJ7C.js} +1 -1
  260. package/out/web/assets/{treeView-Q6P3EWNA-hNELnXUs.js โ†’ treeView-Q6P3EWNA-DZ-y08qp.js} +1 -1
  261. package/out/web/assets/{treemap-WGGIJYW6-D2B6wDsx.js โ†’ treemap-WGGIJYW6-De0CxzMx.js} +1 -1
  262. package/out/web/assets/{vennDiagram-4TSXK5OY-CDkW7rUP.js โ†’ vennDiagram-4TSXK5OY-w_7aZ6xe.js} +1 -1
  263. package/out/web/assets/{wardley-WFR3VGLG-BECdXETi.js โ†’ wardley-WFR3VGLG-DT0y0xJ4.js} +1 -1
  264. package/out/web/assets/{wardleyDiagram-VM6X3IG4-B5BycpQ4.js โ†’ wardleyDiagram-VM6X3IG4-DAdXELbQ.js} +1 -1
  265. package/out/web/assets/window-BJCVtyET.js +1 -0
  266. package/out/web/assets/{xychartDiagram-S5SC5T6Z-CtJS3b--.js โ†’ xychartDiagram-S5SC5T6Z-IZIXf3Vo.js} +1 -1
  267. package/out/web/index-web.html +3 -2
  268. package/out/web/index.html +3 -2
  269. package/package.json +71 -22
  270. package/shared-modules/.github/workflows/ci.yml +120 -0
  271. package/shared-modules/.github/workflows/update.yaml +28 -0
  272. package/slash-commands.json +7 -2
  273. package/src/bin/auto-build.js +66 -0
  274. package/src/bin/desktop.js +113 -0
  275. package/src/bin/desktop.test.js +113 -0
  276. package/src/bin/index.js +18 -25
  277. package/src/bin/index.test.js +89 -0
  278. package/src/bin/measure-effects.mjs +53 -0
  279. package/src/bin/package.test.js +28 -0
  280. package/src/bin/release-notes.test.js +84 -0
  281. package/src/bin/styles.test.js +20 -0
  282. package/src/bin/web.js +5 -0
  283. package/src/bin/web.test.js +39 -0
  284. package/src/renderer/index.html +0 -2
  285. package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  286. package/src/renderer/shared/agentProgress.ts +33 -0
  287. package/src/renderer/shared/components/AgentActivityDrawer.tsx +3 -2
  288. package/src/renderer/shared/components/AgentActivityTable.tsx +1 -1
  289. package/src/renderer/shared/components/ApprovalCard.test.tsx +24 -0
  290. package/src/renderer/shared/components/ApprovalCard.tsx +10 -3
  291. package/src/renderer/shared/components/ArtifactPreviewPanel.tsx +88 -37
  292. package/src/renderer/shared/components/AuthGate.tsx +75 -39
  293. package/src/renderer/shared/components/BackgroundTerminals.tsx +181 -0
  294. package/src/renderer/shared/components/ChatCodeBlock.tsx +8 -4
  295. package/src/renderer/shared/components/ChatMessageItem.test.tsx +68 -0
  296. package/src/renderer/shared/components/ChatMessageItem.tsx +36 -16
  297. package/src/renderer/shared/components/ChatPanel.test.tsx +21 -0
  298. package/src/renderer/shared/components/ChatPanel.tsx +294 -272
  299. package/src/renderer/shared/components/ChunkErrorBoundary.tsx +4 -102
  300. package/src/renderer/shared/components/CliSessionsView.test.tsx +125 -0
  301. package/src/renderer/shared/components/CliSessionsView.tsx +61 -13
  302. package/src/renderer/shared/components/CodeReviewPage.tsx +111 -8
  303. package/src/renderer/shared/components/CollapsiblePasteInput.tsx +90 -0
  304. package/src/renderer/shared/components/ContextCompactionStatus.tsx +17 -0
  305. package/src/renderer/shared/components/DashboardSidebar.tsx +15 -128
  306. package/src/renderer/shared/components/DesktopTitlebar.tsx +45 -19
  307. package/src/renderer/shared/components/ErrorNotice.tsx +29 -0
  308. package/src/renderer/shared/components/FormattedMessage.tsx +11 -0
  309. package/src/renderer/shared/components/GeminiLiveOverlay.css +86 -0
  310. package/src/renderer/shared/components/GeminiLiveOverlay.test.tsx +24 -0
  311. package/src/renderer/shared/components/GeminiLiveOverlay.tsx +85 -101
  312. package/src/renderer/shared/components/GlideSelect.css +138 -0
  313. package/src/renderer/shared/components/GlideSelect.d.ts +31 -0
  314. package/src/renderer/shared/components/GlideSelect.jsx +266 -0
  315. package/src/renderer/shared/components/ImageSearchCard.tsx +46 -56
  316. package/src/renderer/shared/components/ImageStudioPanel.tsx +9 -8
  317. package/src/renderer/shared/components/LibraryItemCard.tsx +72 -0
  318. package/src/renderer/shared/components/LibraryPage.tsx +165 -0
  319. package/src/renderer/shared/components/LibraryPreview.tsx +59 -0
  320. package/src/renderer/shared/components/McpServersView.tsx +27 -8
  321. package/src/renderer/shared/components/McpToolAllowlist.tsx +2 -4
  322. package/src/renderer/shared/components/McpToolResults.tsx +99 -0
  323. package/src/renderer/shared/components/MintDashboard.tsx +308 -770
  324. package/src/renderer/shared/components/ModSessionControls.tsx +8 -0
  325. package/src/renderer/shared/components/ModUi.test.tsx +65 -0
  326. package/src/renderer/shared/components/ModUi.tsx +26 -0
  327. package/src/renderer/shared/components/ModelSelectorPopover.tsx +14 -7
  328. package/src/renderer/shared/components/ModsPanel.tsx +156 -0
  329. package/src/renderer/shared/components/ProgressActivity.tsx +17 -0
  330. package/src/renderer/shared/components/ReviewHistory.tsx +42 -0
  331. package/src/renderer/shared/components/RewindModal.tsx +5 -12
  332. package/src/renderer/shared/components/RunSummaryDashboard.tsx +6 -12
  333. package/src/renderer/shared/components/ScheduledTasksView.tsx +7 -6
  334. package/src/renderer/shared/components/SelectField.test.tsx +27 -0
  335. package/src/renderer/shared/components/SelectField.tsx +38 -0
  336. package/src/renderer/shared/components/SessionSources.tsx +48 -0
  337. package/src/renderer/shared/components/SessionSourcesPopover.tsx +61 -0
  338. package/src/renderer/shared/components/Settings/AgentsTab.tsx +7 -6
  339. package/src/renderer/shared/components/Settings/AudioTab.tsx +8 -7
  340. package/src/renderer/shared/components/Settings/AutomationTab.tsx +2 -2
  341. package/src/renderer/shared/components/Settings/GeneralTab.tsx +33 -32
  342. package/src/renderer/shared/components/Settings/MemoryTab.test.tsx +37 -0
  343. package/src/renderer/shared/components/Settings/MemoryTab.tsx +71 -31
  344. package/src/renderer/shared/components/Settings/NotificationsTab.test.tsx +15 -0
  345. package/src/renderer/shared/components/Settings/NotificationsTab.tsx +84 -0
  346. package/src/renderer/shared/components/Settings/ThemeTab.tsx +19 -11
  347. package/src/renderer/shared/components/SlashChoicePanel.tsx +88 -0
  348. package/src/renderer/shared/components/SlashSuggestions.test.tsx +33 -0
  349. package/src/renderer/shared/components/SlashSuggestions.tsx +38 -10
  350. package/src/renderer/shared/components/SourcesBlock.tsx +12 -20
  351. package/src/renderer/shared/components/ToolSurfacePage.test.tsx +32 -0
  352. package/src/renderer/shared/components/ToolSurfacePage.tsx +59 -11
  353. package/src/renderer/shared/components/ToolsPanelToggle.tsx +13 -0
  354. package/src/renderer/shared/components/VeoStudioPanel.tsx +9 -8
  355. package/src/renderer/shared/components/ViewedImages.tsx +36 -0
  356. package/src/renderer/shared/components/VirtualChatHistory.tsx +82 -0
  357. package/src/renderer/shared/components/VoicePill.css +85 -0
  358. package/src/renderer/shared/components/VoicePill.d.ts +12 -0
  359. package/src/renderer/shared/components/VoicePill.jsx +190 -0
  360. package/src/renderer/shared/components/VoicePill.test.tsx +22 -0
  361. package/src/renderer/shared/components/WorkspaceContextPopover.tsx +20 -15
  362. package/src/renderer/shared/components/WorkspacePaneResizer.tsx +92 -0
  363. package/src/renderer/shared/components/WorkspacePreviewActions.test.tsx +44 -0
  364. package/src/renderer/shared/components/WorkspacePreviewActions.tsx +19 -0
  365. package/src/renderer/shared/components/WorkspaceSelector.tsx +13 -8
  366. package/src/renderer/shared/components/background-terminals.css +292 -0
  367. package/src/renderer/shared/components/ui/README.md +52 -0
  368. package/src/renderer/shared/components/ui/button.tsx +37 -0
  369. package/src/renderer/shared/components/ui/demo.tsx +26 -0
  370. package/src/renderer/shared/components/ui/error-recovery.css +35 -0
  371. package/src/renderer/shared/components/ui/error-recovery.tsx +35 -0
  372. package/src/renderer/shared/components/ui/voice-powered-orb.css +6 -0
  373. package/src/renderer/shared/components/ui/voice-powered-orb.tsx +374 -0
  374. package/src/renderer/shared/components/workspace-selector.css +248 -0
  375. package/src/renderer/shared/constants/config.ts +30 -1
  376. package/src/renderer/shared/constants/slashCommands.ts +1 -1
  377. package/src/renderer/shared/conversation/lifecycle.test.ts +362 -0
  378. package/src/renderer/shared/conversation/lifecycle.ts +418 -0
  379. package/src/renderer/shared/conversation/projectNavigation.test.ts +105 -0
  380. package/src/renderer/shared/conversation/projectNavigation.ts +39 -0
  381. package/src/renderer/shared/conversation/sourcesTransport.ts +27 -0
  382. package/src/renderer/shared/conversation/syncView.test.ts +1 -34
  383. package/src/renderer/shared/conversation/syncView.ts +0 -37
  384. package/src/renderer/shared/conversation/transport.test.ts +163 -0
  385. package/src/renderer/shared/conversation/transport.ts +159 -0
  386. package/src/renderer/shared/conversation/useConversationCoordinator.ts +30 -72
  387. package/src/renderer/shared/css/agent-observability.css +20 -21
  388. package/src/renderer/shared/css/auth-gate.css +112 -13
  389. package/src/renderer/shared/css/code-review.css +33 -0
  390. package/src/renderer/shared/css/code-sessions.css +27 -0
  391. package/src/renderer/shared/css/collapsed-paste.css +35 -0
  392. package/src/renderer/shared/css/effects.css +15 -0
  393. package/src/renderer/shared/css/error-notice.css +60 -0
  394. package/src/renderer/shared/css/library.css +476 -0
  395. package/src/renderer/shared/css/management-views.css +138 -0
  396. package/src/renderer/shared/css/mcp-results.css +11 -0
  397. package/src/renderer/shared/css/model-selector.css +163 -105
  398. package/src/renderer/shared/css/mods.css +77 -0
  399. package/src/renderer/shared/css/notification-notice.css +28 -0
  400. package/src/renderer/shared/css/session-sources-popover.css +9 -0
  401. package/src/renderer/shared/css/session-sources.css +23 -0
  402. package/src/renderer/shared/css/settings/memory.css +154 -0
  403. package/src/renderer/shared/css/settings/notifications.css +11 -0
  404. package/src/renderer/shared/css/settings/theme.css +5 -0
  405. package/src/renderer/shared/css/slash-choice.css +95 -0
  406. package/src/renderer/shared/css/slash-suggestions.css +68 -0
  407. package/src/renderer/shared/css/sources-block.css +91 -0
  408. package/src/renderer/shared/css/thinking-status.css +46 -0
  409. package/src/renderer/shared/css/tools-panel.css +7 -0
  410. package/src/renderer/shared/css/ui-tailwind.css +19 -0
  411. package/src/renderer/shared/css/viewed-images.css +15 -0
  412. package/src/renderer/shared/fonts.css +46 -0
  413. package/src/renderer/shared/fonts.ts +3 -12
  414. package/src/renderer/shared/lib/utils.ts +6 -0
  415. package/src/renderer/shared/library/controller.test.ts +61 -0
  416. package/src/renderer/shared/library/controller.ts +57 -0
  417. package/src/renderer/shared/library/transport.test.ts +26 -0
  418. package/src/renderer/shared/library/transport.ts +60 -0
  419. package/src/renderer/shared/library/useLibraryFile.ts +38 -0
  420. package/src/renderer/shared/mods.ts +58 -0
  421. package/src/renderer/shared/platform.test.ts +57 -1
  422. package/src/renderer/shared/platform.ts +46 -1
  423. package/src/renderer/shared/theme/themeManager.test.ts +43 -0
  424. package/src/renderer/shared/theme/themeManager.ts +17 -4
  425. package/src/renderer/shared/types.ts +56 -2
  426. package/src/renderer/shared/utils/agentActivity.test.ts +75 -0
  427. package/src/renderer/shared/utils/agentActivity.ts +42 -3
  428. package/src/renderer/shared/utils/approval.tsx +7 -0
  429. package/src/renderer/shared/utils/artifactBrowser.test.ts +80 -0
  430. package/src/renderer/shared/utils/artifactBrowser.ts +41 -0
  431. package/src/renderer/shared/utils/contextCompaction.test.tsx +48 -0
  432. package/src/renderer/shared/utils/contextCompaction.ts +21 -0
  433. package/src/renderer/shared/utils/effectVisibility.ts +9 -0
  434. package/src/renderer/shared/utils/markdown.tsx +3 -3
  435. package/src/renderer/shared/utils/mcpResults.test.tsx +91 -0
  436. package/src/renderer/shared/utils/modViewport.ts +7 -0
  437. package/src/renderer/shared/utils/nativeRecordingSession.test.ts +61 -0
  438. package/src/renderer/shared/utils/nativeRecordingSession.ts +46 -0
  439. package/src/renderer/shared/utils/notificationSettings.test.ts +14 -0
  440. package/src/renderer/shared/utils/notificationSettings.ts +23 -0
  441. package/src/renderer/shared/utils/notifications.test.ts +51 -0
  442. package/src/renderer/shared/utils/notifications.ts +54 -0
  443. package/src/renderer/shared/utils/pastedText.test.ts +20 -0
  444. package/src/renderer/shared/utils/pastedText.ts +13 -0
  445. package/src/renderer/shared/utils/progressCache.test.ts +32 -0
  446. package/src/renderer/shared/utils/sessionSources.test.ts +31 -0
  447. package/src/renderer/shared/utils/sessionSources.ts +34 -0
  448. package/src/renderer/shared/utils/slashCommandProcessor.ts +1 -1
  449. package/src/renderer/shared/utils/sourceAttachments.ts +28 -0
  450. package/src/renderer/shared/utils/speech.ts +51 -9
  451. package/src/renderer/shared/utils/syntaxHighlight.test.tsx +22 -0
  452. package/src/renderer/shared/utils/syntaxHighlight.tsx +26 -0
  453. package/src/renderer/shared/utils/useGeminiLiveVoice.ts +49 -5
  454. package/src/renderer/shared/utils/useLatestCallback.ts +9 -0
  455. package/src/renderer/shared/utils/useNativeVoiceInput.ts +80 -72
  456. package/src/renderer/shared/utils/useSessionSources.ts +33 -0
  457. package/src/renderer/shared/utils/viewedImages.ts +26 -0
  458. package/src/renderer/shared/utils/workspacePane.test.ts +33 -0
  459. package/src/renderer/shared/utils/workspacePane.ts +20 -0
  460. package/src/renderer/shared/utils/workspacePreview.test.ts +24 -0
  461. package/src/renderer/shared/utils/workspacePreview.ts +16 -0
  462. package/src/renderer/shared/utils/workspaceSnapshot.test.ts +49 -0
  463. package/src/renderer/shared/utils/workspaceSnapshot.ts +26 -0
  464. package/src/renderer/shared/utils/workspaces.ts +10 -0
  465. package/src/renderer/shared/workspaceUndo.ts +88 -0
  466. package/src/renderer/src/App.tsx +9 -1
  467. package/src/renderer/src/components/LiveTranslateControls.tsx +65 -0
  468. package/src/renderer/src/components/PicturesLibrary.tsx +2 -288
  469. package/src/renderer/src/components/ScreenPicker.css +67 -30
  470. package/src/renderer/src/components/ScreenPicker.tsx +201 -60
  471. package/src/renderer/src/components/SettingsWindow.tsx +10 -5
  472. package/src/renderer/src/components/WorkspacePanel.tsx +222 -32
  473. package/src/renderer/src/components/browser-shell.css +1 -1
  474. package/src/renderer/src/components/browser-suggestions-popup.css +1 -1
  475. package/src/renderer/src/components/liveTranslateFrames.test.ts +26 -5
  476. package/src/renderer/src/components/liveTranslateFrames.ts +18 -7
  477. package/src/renderer/src/components/liveTranslateLanguage.ts +6 -0
  478. package/src/renderer/src/css/app-shell.css +52 -137
  479. package/src/renderer/src/css/chat.css +21 -495
  480. package/src/renderer/src/css/desktop-tools.css +199 -1
  481. package/src/renderer/src/css/misc.css +0 -52
  482. package/src/renderer/src/css/pictures.css +2 -415
  483. package/src/renderer/src/css/settings/base.css +50 -47
  484. package/src/renderer/src/css/sidebar-workspace.css +151 -94
  485. package/src/renderer/src/css/tool-surfaces.css +65 -9
  486. package/src/renderer/src/css/widget.css +1 -1
  487. package/src/renderer/src/env.d.ts +1 -9
  488. package/src/renderer/src/index.css +1 -1
  489. package/src/renderer/src/main.tsx +2 -1
  490. package/src/renderer/src/proactiveSuggestions.ts +29 -0
  491. package/src/renderer/src/tauri.ts +122 -330
  492. package/src/renderer/src/voiceInput.ts +2 -1
  493. package/src/renderer/src-web/components/PicturesLibrary.tsx +2 -285
  494. package/src/renderer/src-web/components/PullToRefresh.tsx +1 -1
  495. package/src/renderer/src-web/components/SettingsWindow.tsx +10 -5
  496. package/src/renderer/src-web/css/app-shell.css +24 -121
  497. package/src/renderer/src-web/css/chat.css +21 -495
  498. package/src/renderer/src-web/css/misc.css +0 -52
  499. package/src/renderer/src-web/css/mobile.css +15 -14
  500. package/src/renderer/src-web/css/pictures.css +2 -462
  501. package/src/renderer/src-web/css/settings/base.css +50 -47
  502. package/src/renderer/src-web/css/sidebar-workspace.css +9 -95
  503. package/src/renderer/src-web/css/widget.css +1 -1
  504. package/src/renderer/src-web/env.d.ts +1 -12
  505. package/src/renderer/src-web/index.css +1 -1
  506. package/src/renderer/src-web/main.tsx +2 -1
  507. package/src/renderer/src-web/proactiveSuggestions.ts +3 -0
  508. package/src/renderer/src-web/tauri.ts +129 -354
  509. package/src/renderer/tests/backgroundTerminals.browser.html +81 -0
  510. package/src/renderer/tests/chatPanelFixture.ts +23 -0
  511. package/src/renderer/tests/chatPerformance.browser.html +71 -0
  512. package/src/renderer/tests/cliSessions.browser.html +82 -0
  513. package/src/renderer/tests/collapsedPaste.browser.html +70 -0
  514. package/src/renderer/tests/composerMenus.browser.html +116 -0
  515. package/src/renderer/tests/contextCompaction.browser.html +17 -0
  516. package/src/renderer/tests/conversationLifecycle.browser.html +70 -0
  517. package/src/renderer/tests/effectsPerformance.browser.html +13 -0
  518. package/src/renderer/tests/errorRecovery.browser.html +21 -0
  519. package/src/renderer/tests/glideSelect.browser.html +71 -0
  520. package/src/renderer/tests/glideSelect.preview.html +27 -0
  521. package/src/renderer/tests/library.browser.html +1 -0
  522. package/src/renderer/tests/library.browser.tsx +78 -0
  523. package/src/renderer/tests/liveOrb.browser.html +49 -0
  524. package/src/renderer/tests/liveVoice.browser.html +29 -0
  525. package/src/renderer/tests/modInput.browser.html +22 -0
  526. package/src/renderer/tests/sessionSources.browser.html +1 -0
  527. package/src/renderer/tests/sessionSources.browser.tsx +107 -0
  528. package/src/renderer/tests/skills.browser.html +24 -0
  529. package/src/renderer/tests/toolsPanel.browser.html +1 -0
  530. package/src/renderer/tests/toolsPanel.browser.tsx +113 -0
  531. package/src/renderer/tests/viewedImages.browser.html +1 -0
  532. package/src/renderer/tests/viewedImages.browser.tsx +93 -0
  533. package/src/renderer/tests/voicePill.browser.html +80 -0
  534. package/src/renderer/tests/voicePill.preview.html +34 -0
  535. package/src/renderer/tests/webSources.browser.html +1 -0
  536. package/src/renderer/tests/webSources.browser.tsx +98 -0
  537. package/src/renderer/tests/workspaceUndo.browser.html +92 -0
  538. package/src-tauri/Cargo.toml +40 -0
  539. package/src-tauri/build.rs +3 -0
  540. package/src-tauri/src/app/lifecycle.rs +277 -0
  541. package/src-tauri/src/app/mod.rs +4 -0
  542. package/src-tauri/src/app/shortcuts.rs +40 -0
  543. package/src-tauri/src/app/tray.rs +52 -0
  544. package/src-tauri/src/background/events.rs +70 -0
  545. package/src-tauri/src/background/headless.rs +230 -0
  546. package/src-tauri/src/background/mod.rs +4 -0
  547. package/src-tauri/src/background/proactive.rs +262 -0
  548. package/src-tauri/src/commands/approvals.rs +20 -0
  549. package/src-tauri/src/commands/auth.rs +127 -0
  550. package/src-tauri/src/commands/conversation.rs +642 -0
  551. package/src-tauri/src/commands/integrations.rs +157 -0
  552. package/src-tauri/src/commands/media.rs +179 -0
  553. package/src-tauri/src/commands/mod.rs +11 -0
  554. package/src-tauri/src/commands/mods.rs +6 -0
  555. package/src-tauri/src/commands/settings.rs +141 -0
  556. package/src-tauri/src/commands/voice.rs +153 -0
  557. package/src-tauri/src/commands/workspace.rs +501 -0
  558. package/src-tauri/src/lib.rs +10 -0
  559. package/src-tauri/src/main.rs +3 -0
  560. package/src-tauri/src/native/actions.rs +206 -0
  561. package/src-tauri/src/native/browser.rs +252 -0
  562. package/src-tauri/src/native/capture.rs +695 -0
  563. package/src-tauri/src/native/discord_rpc.rs +99 -0
  564. package/src-tauri/src/native/integrations.rs +126 -0
  565. package/src-tauri/src/native/mod.rs +12 -0
  566. package/src-tauri/src/native/notifications.rs +51 -0
  567. package/src-tauri/src/native/plugins.rs +14 -0
  568. package/src-tauri/src/native/system.rs +250 -0
  569. package/src-tauri/src/native/terminal.rs +201 -0
  570. package/src-tauri/src/native/updater.rs +148 -0
  571. package/src-tauri/src/native/windows.rs +143 -0
  572. package/tsconfig.json +2 -0
  573. package/vite.config.ts +4 -1
  574. package/vite.config.web.ts +4 -0
  575. package/.cargo/config.toml +0 -13
  576. package/.claude/settings.local.json +0 -182
  577. package/.codex +0 -0
  578. package/CLAUDE.md +0 -102
  579. package/ROADMAP.md +0 -30
  580. package/mint-presentation-demo.mp4 +0 -0
  581. package/out/web/Live2DCubismCore.js +0 -9
  582. package/out/web/assets/CliSessionsView-BxUVWEEL.js +0 -1
  583. package/out/web/assets/CliSessionsView-CBDTXuQv.css +0 -1
  584. package/out/web/assets/CodeReviewPage-DkMgfcos.css +0 -1
  585. package/out/web/assets/CodeReviewPage-jKlmLJwT.js +0 -3
  586. package/out/web/assets/CommandPalette-DnwPA5hy.js +0 -1
  587. package/out/web/assets/ImageStudioPanel-BR1XFwhR.js +0 -1
  588. package/out/web/assets/LinkedFoldersView-bi-CWQ-I.js +0 -1
  589. package/out/web/assets/McpRegistryPicker-D5tEFnyq.js +0 -1
  590. package/out/web/assets/McpServersView-BGG1dhFM.js +0 -1
  591. package/out/web/assets/MintDashboard-BIglDEWX.js +0 -20
  592. package/out/web/assets/MintDashboard-DSMywSgs.css +0 -1
  593. package/out/web/assets/ModelPanel-CbJ1PFoZ.js +0 -1
  594. package/out/web/assets/PicturesLibrary-CQP0fLP1.css +0 -1
  595. package/out/web/assets/PicturesLibrary-DoYN7T1p.js +0 -1
  596. package/out/web/assets/PluginsView-CU1HWXGk.js +0 -1
  597. package/out/web/assets/ScheduledTasksView-Bnl36y2N.js +0 -1
  598. package/out/web/assets/SettingsWindow-DhWLAn9T.css +0 -1
  599. package/out/web/assets/SettingsWindow-Dw8L01pw.js +0 -2
  600. package/out/web/assets/SkillsView-cnl8aY5p.js +0 -2
  601. package/out/web/assets/ToolSurfacePage-CgsQw6l5.css +0 -1
  602. package/out/web/assets/ToolSurfacePage-Cz2VJAPZ.js +0 -37
  603. package/out/web/assets/VeoStudioPanel-D4JnERWR.js +0 -4
  604. package/out/web/assets/chunk-CKBBP62Z-cIrXq6jN.js +0 -2
  605. package/out/web/assets/classDiagram-DTDB5LWJ-Dq0smRWk.js +0 -1
  606. package/out/web/assets/classDiagram-v2-JRS7N3AN-Dq0smRWk.js +0 -1
  607. package/out/web/assets/config-C-ZFtipU.css +0 -1
  608. package/out/web/assets/config-eEDngJXO.js +0 -1
  609. package/out/web/assets/core-hQ2VA9Ti.js +0 -1
  610. package/out/web/assets/dist-js-Bf2bhoku.js +0 -1
  611. package/out/web/assets/flowDiagram-A5DVABFB-BLg7nEcd.js +0 -1
  612. package/out/web/assets/index-Btsf4L8M.css +0 -1
  613. package/out/web/assets/index-D8D9MeRA.js +0 -11
  614. package/out/web/assets/lucide-react-Ip3L6Jtn.js +0 -1
  615. package/out/web/assets/markdown-BSeeUbJH.js +0 -38
  616. package/out/web/assets/modelManager-tMVmJVAW.js +0 -2
  617. package/out/web/assets/prompt-latin-400-normal-BQ9zjSN8.woff2 +0 -0
  618. package/out/web/assets/prompt-latin-400-normal-CxU4ec_r.woff +0 -0
  619. package/out/web/assets/prompt-latin-500-normal-B1edBDQ9.woff +0 -0
  620. package/out/web/assets/prompt-latin-500-normal-CxzxEHZc.woff2 +0 -0
  621. package/out/web/assets/prompt-latin-600-normal-CSDDLorv.woff +0 -0
  622. package/out/web/assets/prompt-latin-600-normal-hKZWXsc1.woff2 +0 -0
  623. package/out/web/assets/prompt-latin-700-normal-BfbftDSq.woff +0 -0
  624. package/out/web/assets/prompt-latin-700-normal-I2gc831J.woff2 +0 -0
  625. package/out/web/assets/prompt-thai-400-normal-BrkKv8cO.woff2 +0 -0
  626. package/out/web/assets/prompt-thai-400-normal-DJypnfr3.woff +0 -0
  627. package/out/web/assets/prompt-thai-500-normal-C18pDUoL.woff2 +0 -0
  628. package/out/web/assets/prompt-thai-500-normal-D7YNqUJD.woff +0 -0
  629. package/out/web/assets/prompt-thai-600-normal-CYPViXds.woff +0 -0
  630. package/out/web/assets/prompt-thai-600-normal-MrdfU7zR.woff2 +0 -0
  631. package/out/web/assets/prompt-thai-700-normal-Cb5ilR-c.woff +0 -0
  632. package/out/web/assets/prompt-thai-700-normal-Cg4aQ0Nn.woff2 +0 -0
  633. package/out/web/assets/stateDiagram-v2-4QOOHH4V-DeEc0lGZ.js +0 -1
  634. package/out/web/assets/swimlanesDiagram-VK2B7HYN-DpVik9v7.js +0 -8
  635. package/out/web/assets/window-5qYXKoW7.js +0 -1
  636. package/skills-lock.json +0 -77
  637. package/src/renderer/public/Live2DCubismCore.js +0 -9
  638. package/src/renderer/src/companionWidget.ts +0 -95
  639. package/src/renderer/src/components/Live2DStage.tsx +0 -412
  640. package/src/renderer/src/components/ModelPanel.tsx +0 -155
  641. package/src/renderer/src/css/model.css +0 -628
  642. package/src/renderer/src-web/companionWidget.ts +0 -37
  643. package/src/renderer/src-web/components/ModelPanel.tsx +0 -12
  644. package/src/renderer/src-web/css/model.css +0 -628
@@ -0,0 +1,91 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import React from 'react'
3
+ import { renderToStaticMarkup } from 'react-dom/server'
4
+ import McpToolResults from '../components/McpToolResults'
5
+ import { reduceLiveAgentProgress, compactAgentProgressForPersistence } from '../agentProgress'
6
+
7
+ describe('MCP progress retention', () => {
8
+ it('keeps artifacts visible after later tools and folds progress by call', () => {
9
+ let events: any[] = [{ type: 'ToolArtifacts', data: { callId: 'a', artifacts: [{ id: 'image' }], warnings: [] } }]
10
+ for (let i = 0; i < 30; i++) events = reduceLiveAgentProgress(events, { type: 'Thought', data: { thought: 'working' } })
11
+ for (let i = 0; i < 30; i++) events = reduceLiveAgentProgress(events, { type: 'ToolEnd', data: { action: 'read_file', input: {}, result: 'ok' } })
12
+ for (let i = 1; i < 4; i++) events = reduceLiveAgentProgress(events, { type: 'ToolProgress', data: { callId: 'a', server: 's', tool: 't', update: { progress: i, total: 4, elapsedSecs: i } } } as any)
13
+ expect(events.filter(e => e.type === 'ToolArtifacts')).toHaveLength(1)
14
+ expect(events.filter(e => e.type === 'ToolProgress')).toHaveLength(1)
15
+ expect((events.find(e => e.type === 'ToolProgress') as any).data.update.progress).toBe(3)
16
+ })
17
+ it('persists only the last progress per call without losing attachments', () => {
18
+ const events: any[] = [1, 2].map(progress => ({ type: 'ToolProgress', data: { callId: 'a', server: 's', tool: 't', update: { progress } } }))
19
+ events.push({ type: 'ToolArtifacts', data: { callId: 'a', artifacts: [], warnings: ['file unavailable'] } })
20
+ const persisted = compactAgentProgressForPersistence(events)
21
+ expect(persisted.filter(e => e.type === 'ToolProgress')).toHaveLength(1)
22
+ expect(persisted.filter(e => e.type === 'ToolArtifacts')).toHaveLength(1)
23
+ })
24
+ })
25
+
26
+ describe('MCP result display', () => {
27
+ it('shows blocked legacy calls as failed instead of completed', () => {
28
+ const html = renderToStaticMarkup(<McpToolResults progress={[{ type: 'ToolEnd', data: { callId: 'blocked', action: 'mcp_tool', input: {server: 's', tool: 'render'}, result: 'Blocked by hook: denied' } }]} />)
29
+ expect(html).toContain('Failed')
30
+ expect(html).not.toContain('Completed')
31
+ })
32
+ it('shows attachments and terminal status without an assistant summary', () => {
33
+ const progress: any[] = [
34
+ { type: 'ToolStart', data: { callId: 'a', action: 'mcp_tool', input: { server: 'blender', tool: 'render' } } },
35
+ { type: 'ToolArtifacts', data: { callId: 'a', artifacts: [{ id: 'link', kind: 'link', name: 'model.obj', uri: 'https://example.test/model.obj' }], warnings: [] } },
36
+ { type: 'ToolEnd', data: { callId: 'a', action: 'mcp_tool', input: { server: 'blender', tool: 'render' }, status: 'timed_out', result: 'unconfirmed' } },
37
+ ]
38
+ const html = renderToStaticMarkup(<McpToolResults progress={progress} />)
39
+ expect(html).toContain('model.obj')
40
+ expect(html).toContain('href="https://example.test/model.obj"')
41
+ expect(html).toContain('remote completion is unconfirmed')
42
+ expect(html).not.toContain('Completed')
43
+ })
44
+ it('does not turn custom or executable resource URIs into browser links', () => {
45
+ const progress: any[] = [{ type: 'ToolArtifacts', data: { callId: 'a', warnings: [], artifacts: [{ id: 'x', kind: 'link', name: 'resource', uri: 'javascript:alert(1)' }] } }]
46
+ const html = renderToStaticMarkup(<McpToolResults progress={progress} />)
47
+ expect(html).toContain('resource')
48
+ expect(html).not.toContain('href=')
49
+ })
50
+ it('shows real progress only for an active call', () => {
51
+ const progress: any[] = [{ type: 'ToolProgress', data: { callId: 'a', server: 's', tool: 'render', update: { progress: 25, total: 100, message: 'Rendering', elapsedSecs: 3 } } }]
52
+ expect(renderToStaticMarkup(<McpToolResults progress={progress} running />)).toContain('value="25"')
53
+ progress.push({ type: 'ToolEnd', data: { callId: 'a', action: 'mcp_tool', input: {}, result: 'stopped', status: 'cancelled' } })
54
+ const html = renderToStaticMarkup(<McpToolResults progress={progress} />)
55
+ expect(html).toContain('remote work may still be running')
56
+ expect(html).not.toContain('<progress')
57
+ })
58
+ })
59
+
60
+ describe('Device verification display', () => {
61
+ const event = (state: string, seq = 1): any => ({type:'DeviceState', data:{callId:'motor',server:'device',tool:'set_speed',update:{state,deviceId:'motor',operationId:'op',target:{rpm:100},actual:{rpm:state==='verified'?100:0},observationSeq:seq,elapsedSecs:2,message:'Observed device state'}}})
62
+ it('distinguishes receipt, running, and verified completion', () => {
63
+ expect(renderToStaticMarkup(<McpToolResults progress={[event('accepted')]} running />)).toContain('Command accepted')
64
+ expect(renderToStaticMarkup(<McpToolResults progress={[event('running')]} running />)).toContain('Awaiting device completion')
65
+ const html=renderToStaticMarkup(<McpToolResults progress={[event('verified',3)]} />)
66
+ expect(html).toContain('Device completion verified')
67
+ expect(html).toContain('rpm')
68
+ expect(html).toContain('100')
69
+ })
70
+ it('retains acceptance and the latest observation in saved history', () => {
71
+ let events: any[]=[]
72
+ for(const state of ['accepted','running','running','verified']) events=reduceLiveAgentProgress(events,event(state))
73
+ for(let i=0;i<40;i++) events=reduceLiveAgentProgress(events,{type:'ToolEnd',data:{action:'read_file',input:{},result:'ok'}})
74
+ const states=compactAgentProgressForPersistence(events).filter(e=>e.type==='DeviceState')
75
+ expect(states).toHaveLength(2)
76
+ expect((states[0] as any).data.update.state).toBe('accepted')
77
+ expect((states[1] as any).data.update.state).toBe('verified')
78
+ })
79
+ it('shows discovery without claiming execution', () => {
80
+ const html=renderToStaticMarkup(<McpToolResults progress={[{type:'ToolDiscovery',data:{server:'device',tool:'move',message:'Reading definition'}} as any]} />)
81
+ expect(html).toContain('Reading definition')
82
+ expect(html).not.toContain('Completed')
83
+ })
84
+ })
85
+
86
+ it('shows stopped waiting when interrupted during a device operation', () => {
87
+ const progress: any[]=[{type:'DeviceState',data:{callId:'a',server:'device',tool:'move',update:{state:'running',deviceId:'motor',operationId:'op',target:{position:10},actual:{position:3},observationSeq:2,elapsedSecs:1,message:'Moving'}}}]
88
+ const html=renderToStaticMarkup(<McpToolResults progress={progress} interrupted />)
89
+ expect(html).toContain('Stopped waiting')
90
+ expect(html).not.toContain('Awaiting device completion')
91
+ })
@@ -0,0 +1,7 @@
1
+ import type { ModViewport } from '../mods'
2
+ export function modViewport(width: number, height: number, surface: 'web' | 'desktop', open: boolean): ModViewport | null {
3
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 24 || height < 1) return null
4
+ const widthPx = Math.min(100000, Math.floor(width - 24))
5
+ const heightPx = Math.min(100000, Math.floor(height))
6
+ return { surface, widthPx, heightPx, columns: Math.min(10000, Math.max(1, Math.floor(widthPx / 8))), rows: Math.min(10000, Math.max(1, Math.floor(heightPx / 20))), sidePanelOpen: open }
7
+ }
@@ -0,0 +1,61 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { createNativeRecordingSession as create } from './nativeRecordingSession'
4
+
5
+ function deferred() {
6
+ let resolve!: () => void
7
+ const promise = new Promise<void>(done => { resolve = done })
8
+ return { promise, resolve }
9
+ }
10
+
11
+ describe('native microphone recording lifecycle', () => {
12
+ it('discards a canceled recording even when device startup finishes later', async () => {
13
+ const ready = deferred(); const calls: string[] = []
14
+ const session = create({
15
+ startRecording: async () => { calls.push('start'); await ready.promise },
16
+ stopRecordingAndTranscribe: async () => { calls.push('transcribe'); return 'should not send' },
17
+ cancelRecording: async () => { calls.push('discard') }
18
+ })
19
+ const start = session.start()
20
+ const cancel = session.cancel()
21
+ ready.resolve()
22
+ expect(await start).toBe(false)
23
+ await cancel
24
+ expect(calls).toEqual(['start', 'discard'])
25
+ })
26
+ it('waits for startup before stopping, transcribes once, and permits another recording', async () => {
27
+ const ready = deferred(); const calls: string[] = []
28
+ const session = create({
29
+ startRecording: async () => { calls.push('start'); await ready.promise },
30
+ stopRecordingAndTranscribe: async () => { calls.push('transcribe'); return 'Hello Mint' },
31
+ cancelRecording: async () => { calls.push('discard') }
32
+ })
33
+ const start = session.start(); const stop = session.stop(); const duplicate = session.stop()
34
+ ready.resolve(); await start
35
+ expect(await stop).toBe('Hello Mint'); expect(await duplicate).toBe('Hello Mint')
36
+ expect(calls).toEqual(['start', 'transcribe'])
37
+ expect(await session.start()).toBe(true); await session.cancel()
38
+ expect(calls).toEqual(['start', 'transcribe', 'start', 'discard'])
39
+ })
40
+ it('does not transcribe or discard when opening the microphone is denied', async () => {
41
+ const calls: string[] = []
42
+ const session = create({
43
+ startRecording: async () => { throw new Error('Microphone denied') },
44
+ stopRecordingAndTranscribe: async () => { calls.push('transcribe'); return 'wrong' },
45
+ cancelRecording: async () => { calls.push('discard') }
46
+ })
47
+ await expect(session.start()).rejects.toThrow('Microphone denied')
48
+ await session.cancel(); expect(calls).toEqual([])
49
+ })
50
+ it('suppresses a transcript when canceled during transcription', async () => {
51
+ const ready = deferred()
52
+ const session = create({
53
+ startRecording: async () => {},
54
+ stopRecordingAndTranscribe: async () => { await ready.promise; return 'late transcript' },
55
+ cancelRecording: async () => { throw new Error('already stopped') }
56
+ })
57
+ await session.start(); const stop = session.stop(); await Promise.resolve()
58
+ const cancel = session.cancel(); ready.resolve()
59
+ expect(await stop).toBeNull(); await cancel
60
+ })
61
+ })
@@ -0,0 +1,46 @@
1
+ export interface NativeRecordingBackend {
2
+ startRecording: () => Promise<void>
3
+ stopRecordingAndTranscribe: () => Promise<string>
4
+ cancelRecording: () => Promise<void>
5
+ }
6
+
7
+ /** One recorder lifecycle, including stop/cancel requests made during device startup. */
8
+ export function createNativeRecordingSession(backend: NativeRecordingBackend) {
9
+ type Run = { phase: 'starting' | 'recording' | 'ending'; cancelled: boolean; ready: Promise<void>; result?: Promise<string | null> }
10
+ let run: Run | null = null
11
+
12
+ const start = async () => {
13
+ if (run) return false
14
+ const current: Run = { phase: 'starting', cancelled: false, ready: Promise.resolve() }
15
+ run = current
16
+ current.ready = backend.startRecording()
17
+ try {
18
+ await current.ready
19
+ if (current.phase !== 'starting') return false
20
+ current.phase = 'recording'
21
+ return true
22
+ } catch (error) {
23
+ if (run === current) run = null
24
+ throw error
25
+ }
26
+ }
27
+ const finish = (cancelled: boolean): Promise<string | null> => {
28
+ const current = run
29
+ if (!current) return Promise.resolve(null)
30
+ current.cancelled ||= cancelled
31
+ if (current.result) return current.result
32
+ current.phase = 'ending'
33
+ current.result = (async () => {
34
+ try {
35
+ try { await current.ready } catch { return null }
36
+ if (current.cancelled) { await backend.cancelRecording(); return null }
37
+ const text = await backend.stopRecordingAndTranscribe()
38
+ return current.cancelled ? null : text
39
+ } finally {
40
+ if (run === current) run = null
41
+ }
42
+ })()
43
+ return current.result
44
+ }
45
+ return { start, stop: () => finish(false), cancel: async () => { await finish(true) } }
46
+ }
@@ -0,0 +1,14 @@
1
+ import { expect, it } from 'vitest'
2
+ import { shouldNotify } from './notificationSettings'
3
+
4
+ it('preserves existing notifications when settings have not been saved yet', () => {
5
+ expect(shouldNotify(null, 'replies', 'os')).toBe(true)
6
+ })
7
+ it('applies master, category, and delivery switches independently', () => {
8
+ expect(shouldNotify({ enabled: false }, 'approvals', 'inApp')).toBe(false)
9
+ expect(shouldNotify({ replies: false }, 'replies', 'os')).toBe(false)
10
+ expect(shouldNotify({ replies: false }, 'approvals', 'os')).toBe(true)
11
+ expect(shouldNotify({ os: false }, 'system', 'os')).toBe(false)
12
+ expect(shouldNotify({ os: false }, 'system', 'inApp')).toBe(true)
13
+ expect(shouldNotify({ inApp: false }, 'system', 'inApp')).toBe(false)
14
+ })
@@ -0,0 +1,23 @@
1
+ export const DEFAULT_NOTIFICATION_SETTINGS = {
2
+ enabled: true, replies: true, approvals: true, system: true, os: true, inApp: true,
3
+ }
4
+ export type NotificationSettings = typeof DEFAULT_NOTIFICATION_SETTINGS
5
+ export function readNotificationSettings(settings: unknown): NotificationSettings {
6
+ const data = settings && typeof settings === 'object' ? settings as Record<string, unknown> : {}
7
+ return Object.fromEntries(Object.entries(DEFAULT_NOTIFICATION_SETTINGS).map(([key, fallback]) =>
8
+ [key, typeof data[key] === 'boolean' ? data[key] : fallback])) as NotificationSettings
9
+ }
10
+ export function shouldNotify(settings: unknown, kind: 'replies' | 'approvals' | 'system', channel: 'os' | 'inApp'): boolean {
11
+ const preferences = readNotificationSettings(settings)
12
+ return preferences.enabled && preferences[kind] && preferences[channel]
13
+ }
14
+
15
+ export async function notificationPermission(request = false): Promise<NotificationPermission | 'unsupported'> {
16
+ if ((window as any).__TAURI_INTERNALS__) {
17
+ const api = await import('@tauri-apps/plugin-notification')
18
+ if (await api.isPermissionGranted()) return 'granted'
19
+ return request ? api.requestPermission() : 'default'
20
+ }
21
+ if (typeof Notification === 'undefined') return 'unsupported'
22
+ return request ? Notification.requestPermission() : Notification.permission
23
+ }
@@ -0,0 +1,51 @@
1
+ import { afterEach, expect, it, vi } from 'vitest'
2
+ import { sendBrowserNotification, clearBrowserNotifications, deliverNotification, receiveProactiveNotification } from './notifications'
3
+
4
+ const shown: any[] = []
5
+ class BrowserNotification {
6
+ static permission = 'granted'
7
+ close = vi.fn()
8
+ onclick?: () => void
9
+ onclose?: () => void
10
+ constructor(public title: string, public options: any) { shown.push(this) }
11
+ }
12
+ afterEach(() => { clearBrowserNotifications(); shown.length = 0; vi.unstubAllGlobals() })
13
+ it('closes every outstanding notification when returning to Mint', async () => {
14
+ vi.stubGlobal('Notification', BrowserNotification)
15
+ await sendBrowserNotification('One')
16
+ await sendBrowserNotification('Two')
17
+ expect(shown).toHaveLength(2)
18
+ clearBrowserNotifications()
19
+ expect(shown.every(n => n.close.mock.calls.length === 1)).toBe(true)
20
+ })
21
+ it('opens the originating chat on click and dismisses that notification', async () => {
22
+ vi.stubGlobal('Notification', BrowserNotification)
23
+ const opened: string[] = []
24
+ await sendBrowserNotification('Done', 'chat-one', id => opened.push(id))
25
+ shown[0].onclick()
26
+ expect(opened).toEqual(['chat-one'])
27
+ expect(shown[0].close).toHaveBeenCalledOnce()
28
+ })
29
+ it('reports a rejected delivery without propagating it into the chat run', async () => {
30
+ const errors: string[] = []
31
+ await expect(deliverNotification(() => Promise.reject(new Error('No service')), message => errors.push(message))).resolves.toBeUndefined()
32
+ expect(errors).toEqual(['Could not send notification: No service'])
33
+ })
34
+ it('shows a background event in-app and sends its originating chat to the OS', async () => {
35
+ const notices: unknown[] = []
36
+ const deliveries: unknown[] = []
37
+ await receiveProactiveNotification({ message: 'Task completed', type: 'info', chatId: 'task-chat' }, true,
38
+ notice => notices.push(notice), async (body, chatId) => { deliveries.push({ body, chatId }) })
39
+ expect(notices).toEqual([{ message: 'Task completed', type: 'info', chatId: 'task-chat' }])
40
+ expect(deliveries).toEqual([{ body: 'Task completed', chatId: 'task-chat' }])
41
+ })
42
+ it('keeps foreground system events in-app and ignores malformed events', async () => {
43
+ const notices: unknown[] = []
44
+ const deliveries: unknown[] = []
45
+ const show = (notice: unknown) => { notices.push(notice) }
46
+ const send = async (body: string) => { deliveries.push(body) }
47
+ await receiveProactiveNotification({ message: 'Battery low', type: 'warning' }, false, show, send)
48
+ await receiveProactiveNotification({ message: '' }, true, show, send)
49
+ expect(notices).toEqual([{ message: 'Battery low', type: 'warning', chatId: undefined }])
50
+ expect(deliveries).toEqual([])
51
+ })
@@ -0,0 +1,54 @@
1
+ const active = new Set<Notification>()
2
+ let generation = 0
3
+
4
+ export async function sendBrowserNotification(body: string, chatId?: string, openChat?: (id: string) => void): Promise<void> {
5
+ if (typeof Notification === 'undefined') throw new Error('Notifications are not supported here.')
6
+ const started = generation
7
+ let permission = Notification.permission
8
+ if (permission === 'default') permission = await Notification.requestPermission()
9
+ if (permission !== 'granted') throw new Error('Notification permission is not granted.')
10
+ // Returning to Mint while a permission prompt was pending cancels this delivery.
11
+ if (started !== generation) return
12
+ const notification = new Notification('Mint Agent', { body })
13
+ active.add(notification)
14
+ notification.onclose = () => active.delete(notification)
15
+ notification.onclick = () => {
16
+ notification.close()
17
+ active.delete(notification)
18
+ if (chatId) openChat?.(chatId)
19
+ else if (typeof window !== 'undefined') window.focus()
20
+ }
21
+ }
22
+
23
+ export function clearBrowserNotifications(): void {
24
+ generation++
25
+ for (const notification of active) notification.close()
26
+ active.clear()
27
+ }
28
+
29
+ export async function deliverNotification(send: () => unknown, report: (message: string) => void): Promise<void> {
30
+ try { await send() }
31
+ catch (error) { report('Could not send notification: ' + (error instanceof Error ? error.message : String(error))) }
32
+ }
33
+
34
+ export function openNotificationChat(chatId: string): void {
35
+ window.focus()
36
+ window.dispatchEvent(new CustomEvent('mint:notification-click', { detail: { chatId } }))
37
+ }
38
+
39
+ export type NotificationNotice = { message: string; chatId?: string; type?: string }
40
+ export async function receiveProactiveNotification(
41
+ payload: unknown, background: boolean, show: (notice: NotificationNotice) => void,
42
+ send: (body: string, chatId?: string) => Promise<void>,
43
+ ): Promise<void> {
44
+ if (!payload || typeof payload !== 'object') return
45
+ const data = payload as Record<string, unknown>
46
+ if (typeof data.message !== 'string' || !data.message.trim()) return
47
+ const notice = {
48
+ message: data.message,
49
+ chatId: typeof data.chatId === 'string' ? data.chatId : undefined,
50
+ type: typeof data.type === 'string' ? data.type : undefined,
51
+ }
52
+ show(notice)
53
+ if (background) await send(notice.message, notice.chatId)
54
+ }
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { insertPastedText, pastedTextLabel } from './pastedText'
3
+
4
+ describe('long clipboard text on Desktop and Web', () => {
5
+ it('collapses seven lines while retaining the complete submitted value', () => {
6
+ const result = insertPastedText('before selection after', 7, 16, 'a\rb\rc\rd\re\rf\rg')
7
+ expect(result.collapsed).toBe(true)
8
+ expect(result.value).toBe('before a\nb\nc\nd\ne\nf\ng after')
9
+ expect(result.text).toBe('a\nb\nc\nd\ne\nf\ng')
10
+ expect(result.caret).toBe(20)
11
+ })
12
+ it('keeps six lines inline and normalizes mixed clipboard line endings', () => {
13
+ const result = insertPastedText('', 0, 0, 'a\r\nb\rc\nd\ne\nf\r\n')
14
+ expect(result.collapsed).toBe(false)
15
+ expect(result.value).toBe('a\nb\nc\nd\ne\nf\n')
16
+ })
17
+ it('counts Unicode characters rather than UTF-16 units in the badge', () => {
18
+ expect(pastedTextLabel('๐Ÿ™‚\na\nb\nc\nd\ne\nf')).toBe('[Pasted Content 13 chars]')
19
+ })
20
+ })
@@ -0,0 +1,13 @@
1
+ export function insertPastedText(value: string, start: number, end: number, text: string) {
2
+ const normalized = text.replace(/\r\n?/g, '\n')
3
+ return {
4
+ value: value.slice(0, start) + normalized + value.slice(end),
5
+ text: normalized,
6
+ caret: start + normalized.length,
7
+ collapsed: normalized.replace(/\n$/, '').split('\n').length > 6,
8
+ }
9
+ }
10
+
11
+ export function pastedTextLabel(text: string) {
12
+ return `[Pasted Content ${Array.from(text).length} chars]`
13
+ }
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { parseFileChangesFromProgress } from '../agentProgress'
3
+ import { activitiesFrom } from './agentActivity'
4
+ import type { AgentProgress } from '../types'
5
+
6
+ describe('immutable progress summaries', () => {
7
+ it('reuses file summaries while a turn has no new events', () => {
8
+ const progress: AgentProgress[] = []
9
+ expect(parseFileChangesFromProgress(progress)).toBe(parseFileChangesFromProgress(progress))
10
+ })
11
+ it('reuses activity views across unrelated UI updates', () => {
12
+ const progress: AgentProgress[] = [{ type: 'ToolStart', data: { action: 'read_file', input: { path: '/a.ts' } } }]
13
+ expect(activitiesFrom(progress)).toBe(activitiesFrom(progress))
14
+ })
15
+ it('adds a completed file edit without changing the cached earlier snapshot', () => {
16
+ const progress: AgentProgress[] = [{ type: 'ToolStart', data: { action: 'write_file', input: { path: '/a.ts', content: 'first\nsecond' } } }]
17
+ const before = parseFileChangesFromProgress(progress)
18
+ const next: AgentProgress[] = [...progress, { type: 'ToolEnd', data: { action: 'write_file', input: {}, result: 'ok' } }]
19
+ expect(parseFileChangesFromProgress(next)).toEqual([{ path: '/a.ts', created: true, additions: 2, deletions: 0, hunks: [{ oldText: '', newText: 'first\nsecond' }] }])
20
+ expect(before).toEqual([])
21
+ expect(parseFileChangesFromProgress(progress)).toBe(before)
22
+ })
23
+ it('updates activity when a new progress snapshot arrives', () => {
24
+ const progress: AgentProgress[] = [{ type: 'ToolStart', data: { action: 'read_file', input: { path: '/a.ts' } } }]
25
+ const before = activitiesFrom(progress)
26
+ const next: AgentProgress[] = [...progress, { type: 'ToolEnd', data: { action: 'read_file', input: { path: '/a.ts' }, result: 'content' } }]
27
+ const after = activitiesFrom(next)
28
+ expect(after).not.toBe(before)
29
+ expect(before.items[0].state).not.toBe(after.items[0].state)
30
+ expect(activitiesFrom(progress)).toBe(before)
31
+ })
32
+ })
@@ -0,0 +1,31 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { collectSessionSources } from './sessionSources'
3
+ import type { AgentProgress } from '../types'
4
+ import { createElement } from 'react'
5
+ import { renderToStaticMarkup } from 'react-dom/server'
6
+ import SessionSources from '../components/SessionSources'
7
+
8
+ const search = (url: string, title = 'Reference'): AgentProgress => ({ type: 'ToolEnd', data: { action: 'web_search', input: { query: 'example' }, result: `1. ${title}\nURL: ${url}\nA useful reference` } })
9
+ describe('session sources', () => {
10
+ it('includes sent images and files alongside historical and live searches, deduplicating pages', () => {
11
+ const result = collectSessionSources({ attachments: [
12
+ { id: 1, interactionId: 3, name: 'photo.png', kind: 'image', mimeType: 'image/png', thumbnailDataUri: 'data:image/png;base64,aGVsbG8=' },
13
+ { id: 2, interactionId: 7, name: 'notes.pdf', kind: 'file', mimeType: 'application/pdf', thumbnailDataUri: null },
14
+ ], webSearchProgress: [search('https://example.com/docs')] }, [search('https://example.com/docs'), search('https://other.example/guide')])
15
+ expect(result.map(source => source.name)).toEqual(['photo.png', 'notes.pdf', 'Reference', 'Reference'])
16
+ expect(result[0]).toMatchObject({ kind: 'image', interactionId: 3, thumbnailDataUri: 'data:image/png;base64,aGVsbG8=' })
17
+ expect(result[1]).toMatchObject({ kind: 'file', detail: 'PDF' })
18
+ expect(result[2]).toMatchObject({ kind: 'web', url: 'https://example.com/docs', detail: 'example.com' })
19
+ })
20
+ it('does not turn non-web URLs or failed searches into source links', () => {
21
+ expect(collectSessionSources({ attachments: [], webSearchProgress: [search('javascript:alert(1)'), search('file:///etc/passwd')] })).toEqual([])
22
+ expect(collectSessionSources({ attachments: [], webSearchProgress: [search('https://example.com')] })).toHaveLength(1)
23
+ })
24
+ it('shows website favicons from the existing web-search source metadata', () => {
25
+ const sources = collectSessionSources({ attachments: [], webSearchProgress: [search('https://open.spotify.com/track/example?secret=private', 'Song')] })
26
+ expect(sources[0]).toMatchObject({ faviconUrl: 'https://www.google.com/s2/favicons?sz=32&domain=open.spotify.com' })
27
+ const html = renderToStaticMarkup(createElement(SessionSources, { sources, loading: false, error: '', onRetry: () => {}, onOpenLibrary: () => {} }))
28
+ expect(html).toContain('src="https://www.google.com/s2/favicons?sz=32&amp;domain=open.spotify.com"')
29
+ expect(html).toContain('referrerPolicy="no-referrer"')
30
+ })
31
+ })
@@ -0,0 +1,34 @@
1
+ import type { AgentProgress, ConversationSources } from '../types'
2
+ import { parseWebSearchSources } from './agentActivity'
3
+
4
+ export interface SessionSource {
5
+ id: string
6
+ kind: 'image' | 'file' | 'video' | 'web'
7
+ name: string
8
+ detail: string
9
+ interactionId?: number
10
+ thumbnailDataUri?: string
11
+ faviconUrl?: string
12
+ url?: string
13
+ }
14
+
15
+ export function collectSessionSources(persisted: ConversationSources, liveProgress: AgentProgress[] = []): SessionSource[] {
16
+ const attachments: SessionSource[] = persisted.attachments.map(attachment => ({
17
+ id: `attachment:${attachment.id}`, kind: attachment.kind, name: attachment.name,
18
+ detail: attachment.trashed ? 'In Trash ยท Restore in Library' : attachment.availability === 'missing' ? 'Original file missing' : attachment.kind === 'image' ? 'Image' : attachment.kind === 'video' ? 'Video' : (attachment.name.split('.').length > 1 ? attachment.name.split('.').at(-1)!.toUpperCase() : 'File'),
19
+ url: attachment.libraryItemId ? `/library?${new URLSearchParams({ item: attachment.libraryItemId, ...(attachment.trashed ? { trash: '1' } : {}) })}` : undefined,
20
+ interactionId: attachment.interactionId ?? undefined, thumbnailDataUri: attachment.thumbnailDataUri || undefined,
21
+ }))
22
+ const seen = new Set<string>()
23
+ const web: SessionSource[] = []
24
+ const progress = [...persisted.webSearchProgress, ...liveProgress].filter(event => event.type !== 'ToolEnd' || !event.data.status || event.data.status === 'success')
25
+ for (const source of parseWebSearchSources(progress)) {
26
+ const url = new URL(source.url)
27
+ if (!['http:', 'https:'].includes(url.protocol)) continue
28
+ url.hash = ''
29
+ if (seen.has(url.href)) continue
30
+ seen.add(url.href)
31
+ web.push({ id: `web:${url.href}`, kind: 'web', name: source.title || source.domain, detail: source.domain, url: url.href, faviconUrl: source.faviconUrl })
32
+ }
33
+ return [...attachments, ...web]
34
+ }
@@ -74,7 +74,7 @@ export function executeSlashCommand(input: string): SlashCommandExecutionResult
74
74
  handled: true,
75
75
  action: 'system_message',
76
76
  systemText:
77
- '๐Ÿง **Project Avatar** โ€” the desktop app renders the live companion automatically. Use the CLI `/avatar link` to pair an external viewer.',
77
+ '๐Ÿง **Project Avatar** โ€” use the CLI `/avatar link` to pair a Project Avatar viewer. The separate Mint Companion app connects locally to Mint for Live2D.',
78
78
  }
79
79
 
80
80
  default:
@@ -0,0 +1,28 @@
1
+ import type { SourceAttachment } from '../types'
2
+ import type { PromptOptions } from '../conversation/lifecycle'
3
+
4
+ async function thumbnail(dataUri: string): Promise<string | null> {
5
+ try {
6
+ const image = new Image()
7
+ image.src = dataUri
8
+ await image.decode()
9
+ const ratio = Math.min(1, 160 / Math.max(image.naturalWidth, image.naturalHeight))
10
+ const canvas = document.createElement('canvas')
11
+ canvas.width = Math.max(1, Math.round(image.naturalWidth * ratio))
12
+ canvas.height = Math.max(1, Math.round(image.naturalHeight * ratio))
13
+ const context = canvas.getContext('2d')
14
+ if (!context) return null
15
+ context.drawImage(image, 0, 0, canvas.width, canvas.height)
16
+ return canvas.toDataURL('image/webp', .75)
17
+ } catch { return null }
18
+ }
19
+
20
+ export async function sourceAttachments(options: PromptOptions): Promise<SourceAttachment[]> {
21
+ const mime = (uri: string) => /^data:([^;,]+)/.exec(uri)?.[1] || 'application/octet-stream'
22
+ const images = await Promise.all((options.imageAttachments || []).map(async image => ({
23
+ name: image.name, kind: 'image' as const, mimeType: mime(image.dataUri), thumbnailDataUri: await thumbnail(image.dataUri),
24
+ })))
25
+ const videos = (options.videoAttachments || []).map(video => ({ name: video.name, kind: 'video' as const, mimeType: mime(video.dataUri), thumbnailDataUri: null }))
26
+ const document = options.documentAttachment
27
+ return [...images, ...videos, ...(document ? [{ name: document.filename, kind: 'file' as const, mimeType: mime(document.dataUri), thumbnailDataUri: null }] : [])]
28
+ }