@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.
- package/Cargo.lock +1012 -69
- package/Cargo.toml +5 -2
- package/LICENSE +21 -654
- package/README.md +92 -817
- package/Release_Note.md +53 -560
- package/apps/mint-companion/src-tauri/Cargo.toml +20 -0
- package/apps/mint-companion/src-tauri/build.rs +3 -0
- package/apps/mint-companion/src-tauri/src/main.rs +468 -0
- package/apps/mint-companion/tsconfig.json +20 -0
- package/apps/mint-companion/vite.config.ts +3 -0
- package/components.json +19 -0
- package/crates/mint-cli/Cargo.toml +6 -0
- package/crates/mint-cli/src/agent/live_status.rs +187 -39
- package/crates/mint-cli/src/agent.rs +323 -95
- package/crates/mint-cli/src/commands/agent.rs +69 -55
- package/crates/mint-cli/src/commands/integrations.rs +7 -0
- package/crates/mint-cli/src/commands/knowledge.rs +12 -32
- package/crates/mint-cli/src/commands/mod.rs +44 -0
- package/crates/mint-cli/src/image.rs +31 -33
- package/crates/mint-cli/src/interactive/chat_tui.rs +3594 -483
- package/crates/mint-cli/src/interactive/input_box.rs +56 -6
- package/crates/mint-cli/src/interactive/slash_commands.rs +208 -56
- package/crates/mint-cli/src/interactive/tool_activity.rs +328 -0
- package/crates/mint-cli/src/interactive.rs +64 -40
- package/crates/mint-cli/src/main.rs +13 -7
- package/crates/mint-cli/src/mcp.rs +5 -0
- package/crates/mint-cli/src/mod_host.rs +6 -0
- package/crates/mint-cli/src/mods_cli.rs +62 -0
- package/crates/mint-cli/src/mods_ui.rs +187 -0
- package/crates/mint-cli/src/onboard.rs +5 -31
- package/crates/mint-cli/src/terminal_theme.rs +3 -1
- package/crates/mint-cli/src/updater.rs +56 -2
- package/crates/mint-cli/tests/library_intake.rs +77 -0
- package/crates/mint-cli/tests/mods_testing.rs +133 -0
- package/crates/mint-companion-protocol/Cargo.toml +10 -0
- package/crates/mint-companion-protocol/src/lib.rs +163 -0
- package/crates/mint-core/Cargo.toml +6 -3
- package/crates/mint-core/build.rs +26 -0
- package/crates/mint-core/src/agent/chat.rs +167 -39
- package/crates/mint-core/src/agent/code_tools.rs +15 -0
- package/crates/mint-core/src/agent/memory/sources.rs +240 -0
- package/crates/mint-core/src/agent/memory.rs +147 -13
- package/crates/mint-core/src/agent/skill_inventory.rs +325 -0
- package/crates/mint-core/src/agent/skills.rs +437 -90
- package/crates/mint-core/src/agent.rs +1 -0
- package/crates/mint-core/src/api_server/routes/background_jobs.rs +310 -0
- package/crates/mint-core/src/api_server/routes/chat.rs +216 -199
- package/crates/mint-core/src/api_server/routes/html_preview.rs +77 -0
- package/crates/mint-core/src/api_server/routes/library.rs +97 -0
- package/crates/mint-core/src/api_server/routes/media_gen.rs +10 -57
- package/crates/mint-core/src/api_server/routes/misc.rs +104 -107
- package/crates/mint-core/src/api_server/routes/mod.rs +6 -0
- package/crates/mint-core/src/api_server/routes/mods.rs +31 -0
- package/crates/mint-core/src/api_server/routes/sessions.rs +41 -0
- package/crates/mint-core/src/api_server/routes/skills_subagents.rs +12 -42
- package/crates/mint-core/src/api_server/routes/slash.rs +50 -1
- package/crates/mint-core/src/api_server/routes/sources.rs +101 -0
- package/crates/mint-core/src/api_server.rs +118 -9
- package/crates/mint-core/src/avatar_bridge.rs +11 -2
- package/crates/mint-core/src/browser/session.rs +52 -0
- package/crates/mint-core/src/companion.rs +716 -0
- package/crates/mint-core/src/cron/scheduler.rs +34 -2
- package/crates/mint-core/src/integrations/channels.rs +87 -345
- package/crates/mint-core/src/integrations/mcp.rs +397 -50
- package/crates/mint-core/src/integrations/mcp_calls.rs +823 -0
- package/crates/mint-core/src/integrations/mcp_catalog.rs +267 -0
- package/crates/mint-core/src/integrations/mcp_device.rs +319 -0
- package/crates/mint-core/src/integrations/mcp_result.rs +356 -0
- package/crates/mint-core/src/integrations/webhooks.rs +539 -0
- package/crates/mint-core/src/integrations.rs +2 -0
- package/crates/mint-core/src/lib.rs +85 -26
- package/crates/mint-core/src/media/gemini_live.rs +134 -14
- package/crates/mint-core/src/media/image_models.rs +3 -27
- package/crates/mint-core/src/media/image_workflow.rs +216 -0
- package/crates/mint-core/src/media/library/intake.rs +144 -0
- package/crates/mint-core/src/media/library/migration.rs +56 -0
- package/crates/mint-core/src/media/library/removal.rs +55 -0
- package/crates/mint-core/src/media/library/tests.rs +730 -0
- package/crates/mint-core/src/media/library/thumbnails.rs +72 -0
- package/crates/mint-core/src/media/library.rs +545 -0
- package/crates/mint-core/src/media/model_catalog.rs +375 -0
- package/crates/mint-core/src/media/pictures.rs +79 -222
- package/crates/mint-core/src/media/video_models.rs +3 -22
- package/crates/mint-core/src/media.rs +3 -0
- package/crates/mint-core/src/mods/manager.rs +1124 -0
- package/crates/mint-core/src/mods/mod.rs +569 -0
- package/crates/mint-core/src/mods/operations.rs +204 -0
- package/crates/mint-core/src/mods/session.rs +146 -0
- package/crates/mint-core/src/mods/slash.rs +59 -0
- package/crates/mint-core/src/mods/telemetry.rs +306 -0
- package/crates/mint-core/src/orchestration/approvals.rs +344 -0
- package/crates/mint-core/src/orchestration/context_render.rs +104 -0
- package/crates/mint-core/src/orchestration/mcp_review.rs +153 -0
- package/crates/mint-core/src/orchestration/memory_skill.rs +104 -32
- package/crates/mint-core/src/orchestration/mod.rs +2139 -244
- package/crates/mint-core/src/orchestration/mod_authoring_regressions.rs +209 -0
- package/crates/mint-core/src/orchestration/progress_clock.rs +80 -0
- package/crates/mint-core/src/orchestration/repeated_failures.rs +116 -0
- package/crates/mint-core/src/orchestration/review_regressions.rs +449 -0
- package/crates/mint-core/src/orchestration/run_control.rs +196 -0
- package/crates/mint-core/src/orchestration/tools/files.rs +161 -14
- package/crates/mint-core/src/orchestration/tools/media.rs +37 -41
- package/crates/mint-core/src/orchestration/tools/misc.rs +3 -2
- package/crates/mint-core/src/orchestration/tools/mod.rs +2 -0
- package/crates/mint-core/src/orchestration/tools/mods.rs +161 -0
- package/crates/mint-core/src/orchestration/tools/plugins_mcp.rs +303 -71
- package/crates/mint-core/src/orchestration/tools/shell.rs +95 -2
- package/crates/mint-core/src/orchestration/workspace_helpers.rs +4 -1
- package/crates/mint-core/src/prompts/agent.rs +17 -2
- package/crates/mint-core/src/prompts/tool_catalog.rs +28 -2
- package/crates/mint-core/src/search/knowledge.rs +7 -1
- package/crates/mint-core/src/search/linked_folders/index.rs +177 -13
- package/crates/mint-core/src/search/linked_folders.rs +83 -2
- package/crates/mint-core/src/slash/catalog.rs +1 -1
- package/crates/mint-core/src/slash/mod.rs +174 -11
- package/crates/mint-core/src/slash/mods.rs +155 -0
- package/crates/mint-core/src/system/bg_shell.rs +1129 -95
- package/crates/mint-core/src/system/config.rs +5 -10
- package/crates/mint-core/src/system/html_preview.rs +398 -0
- package/crates/mint-core/src/system/mint_web.rs +164 -0
- package/crates/mint-core/src/system/release_notes.rs +18 -0
- package/crates/mint-core/src/system/shell.rs +21 -6
- package/crates/mint-core/src/system/workspace.rs +72 -8
- package/crates/mint-core/src/system/workspace_history.rs +604 -0
- package/crates/mint-core/src/system.rs +4 -0
- package/crates/mint-core/tests/background_retention.rs +34 -0
- package/crates/mint-core/tests/background_shutdown.rs +24 -0
- package/crates/mint-core/tests/library_http.rs +209 -0
- package/crates/mint-core/tests/mcp_contract.rs +283 -0
- package/crates/mint-core/tests/mcp_stdio.rs +11 -10
- package/crates/mint-core/tests/media_model_catalog_http.rs +403 -0
- package/crates/mint-core/tests/mods_http.rs +215 -0
- package/crates/mint-core/tests/terminal_shutdown.rs +109 -0
- package/crates/mint-mod-runtime/Cargo.toml +18 -0
- package/crates/mint-mod-runtime/src/lib.rs +507 -0
- package/crates/mint-mod-runtime/src/sdk.js +56 -0
- package/crates/mint-mod-runtime/src/test_sdk.js +32 -0
- package/crates/mint-mod-runtime/src/testing.rs +94 -0
- package/crates/mint-mod-runtime/tests/runtime.rs +156 -0
- package/docs/mods/AUTHORING.md +61 -0
- package/docs/mods/README.md +139 -0
- package/docs/mods/TESTING.md +56 -0
- package/docs/mods/mint-mods.d.ts +66 -0
- package/examples/device-mcp/README.md +22 -0
- package/examples/device-mcp/contract.md +49 -0
- package/examples/device-mcp/mock-device.mjs +66 -0
- package/examples/device-mcp/server.mjs +48 -0
- package/examples/device-mcp/server.test.mjs +78 -0
- package/examples/mods/task-checklist/README.md +1 -0
- package/examples/mods/task-checklist/main.ts +17 -0
- package/examples/mods/task-checklist/manifest.json +1 -0
- package/examples/mods/task-checklist/tests/checklist.test.ts +15 -0
- package/examples/mods/token-meter/README.md +1 -0
- package/examples/mods/token-meter/main.ts +8 -0
- package/examples/mods/token-meter/manifest.json +1 -0
- package/examples/mods/tool-guard/README.md +1 -0
- package/examples/mods/tool-guard/main.ts +9 -0
- package/examples/mods/tool-guard/manifest.json +1 -0
- package/out/web/assets/CliSessionsView-CtzUJpK_.css +1 -0
- package/out/web/assets/CliSessionsView-D7hw0Mgj.js +1 -0
- package/out/web/assets/CodeReviewPage-C7bEOJfd.js +4 -0
- package/out/web/assets/CodeReviewPage-Dte9uLiL.css +1 -0
- package/out/web/assets/CommandPalette-0RrF-AOT.js +1 -0
- package/out/web/assets/ImageStudioPanel-CdxkAf_r.js +1 -0
- package/out/web/assets/LibraryPage--Qfisp6D.js +2 -0
- package/out/web/assets/LibraryPage-DAxUTAg7.css +1 -0
- package/out/web/assets/LinkedFoldersView-2nfZbDgN.js +1 -0
- package/out/web/assets/McpRegistryPicker-h9uS8_wT.js +1 -0
- package/out/web/assets/McpServersView-slfQJzNB.js +1 -0
- package/out/web/assets/MintDashboard-BSR5XpXh.js +25 -0
- package/out/web/assets/MintDashboard-CWUcCGhd.css +1 -0
- package/out/web/assets/PluginsView-l7LSm503.js +1 -0
- package/out/web/assets/ScheduledTasksView-CttmLbq1.js +1 -0
- package/out/web/assets/SettingsWindow-BCMLYvEV.js +4 -0
- package/out/web/assets/SettingsWindow-DcMVmU9u.css +1 -0
- package/out/web/assets/SkillsView-rNs1qr_U.js +2 -0
- package/out/web/assets/ToolSurfacePage-B9DzEPWN.js +37 -0
- package/out/web/assets/ToolSurfacePage-CTfgggF2.css +1 -0
- package/out/web/assets/VeoStudioPanel-DiJs_Cg2.js +4 -0
- package/out/web/assets/WorkspaceSelector-B2xIp04D.js +1 -0
- package/out/web/assets/WorkspaceSelector-Ca8uJOsS.css +1 -0
- package/out/web/assets/{abnfDiagram-VCTEODGH-ByMyfanq.js → abnfDiagram-VCTEODGH-CD0CjoGl.js} +1 -1
- package/out/web/assets/{architecture-7GRP2DOG-CLq2kEOB.js → architecture-7GRP2DOG-RKV83OeZ.js} +1 -1
- package/out/web/assets/{architectureDiagram-5GKGNRK7-6PoVSmNG.js → architectureDiagram-5GKGNRK7-nN_HAFct.js} +1 -1
- package/out/web/assets/{blockDiagram-NRAW4CY4-cKw9mAKi.js → blockDiagram-NRAW4CY4-BrrqzHW5.js} +1 -1
- package/out/web/assets/{c4Diagram-UCG6FXSJ-Cqy8N5Ge.js → c4Diagram-UCG6FXSJ-DTdV8_zE.js} +1 -1
- package/out/web/assets/{chunk-75Z2AOVW-3qybijF5.js → chunk-75Z2AOVW-_M0z5olV.js} +1 -1
- package/out/web/assets/chunk-CKBBP62Z-BpQEgeHF.js +2 -0
- package/out/web/assets/{chunk-CLGD4ZFX-CndDQkww.js → chunk-CLGD4ZFX-D54yGvGS.js} +1 -1
- package/out/web/assets/{chunk-DSNCTWBM-Cc4NxvgD.js → chunk-DSNCTWBM-C-gvT2ru.js} +1 -1
- package/out/web/assets/{chunk-DU6HZSFF-drzh6GsU.js → chunk-DU6HZSFF-CnFngIhW.js} +1 -1
- package/out/web/assets/{chunk-F27PBJKO-FiY35l4v.js → chunk-F27PBJKO-BqtWrnEO.js} +1 -1
- package/out/web/assets/{chunk-G27WJ6UU-Bjt9Il4A.js → chunk-G27WJ6UU-Ckoq5mi2.js} +1 -1
- package/out/web/assets/{chunk-GMAD6QVW-Bppi3Ctd.js → chunk-GMAD6QVW-BuhEcgHq.js} +1 -1
- package/out/web/assets/{chunk-JQ64N6SF-DGg5kcEV.js → chunk-JQ64N6SF-CF8T7GiZ.js} +1 -1
- package/out/web/assets/{chunk-LCL6LL3I-CCYcjd7p.js → chunk-LCL6LL3I-BhXV9zyH.js} +1 -1
- package/out/web/assets/{chunk-OBVCFTLP-DJeNil5M.js → chunk-OBVCFTLP-CRIkunUB.js} +1 -1
- package/out/web/assets/{chunk-P2QGCYS3-lRzkRjBB.js → chunk-P2QGCYS3-UFEU6BxZ.js} +1 -1
- package/out/web/assets/{chunk-POPQ4Y6H-DouASMBW.js → chunk-POPQ4Y6H-CQcrNbjr.js} +1 -1
- package/out/web/assets/{chunk-PWAF6VOD-DMmD1vVf.js → chunk-PWAF6VOD-Cc2uVDsG.js} +1 -1
- package/out/web/assets/{chunk-R7TYR2AO-CYLA5UC5.js → chunk-R7TYR2AO-DiCxzdAY.js} +1 -1
- package/out/web/assets/{chunk-RHFEMEQ7-BiXwEcUT.js → chunk-RHFEMEQ7-b7hnPmrR.js} +1 -1
- package/out/web/assets/{chunk-SVP7TREG-BV-zPslF.js → chunk-SVP7TREG-Bu_yYb-V.js} +1 -1
- package/out/web/assets/{chunk-TEH6E4GO-B2RGEohZ.js → chunk-TEH6E4GO-G4Cdlyrt.js} +1 -1
- package/out/web/assets/classDiagram-DTDB5LWJ-RnC2mYO4.js +1 -0
- package/out/web/assets/classDiagram-v2-JRS7N3AN-RnC2mYO4.js +1 -0
- package/out/web/assets/config-CnvCChUM.js +2 -0
- package/out/web/assets/config-DM6oIVO3.css +1 -0
- package/out/web/assets/core-DsKkQ7ig.js +1 -0
- package/out/web/assets/{cynefin-OW5HDTMX-Cas4csHM.js → cynefin-OW5HDTMX-CnzQfOGp.js} +1 -1
- package/out/web/assets/{cynefinDiagram-5FMLGOSQ-Bbpj66Y3.js → cynefinDiagram-5FMLGOSQ-DVtQnfpm.js} +1 -1
- package/out/web/assets/{dagre-3AP2YEHR-B0-2RDEd.js → dagre-3AP2YEHR-DG76_k5_.js} +1 -1
- package/out/web/assets/{diagram-S7CK7UJ4-BdzEjUyH.js → diagram-S7CK7UJ4-CaouUoze.js} +1 -1
- package/out/web/assets/{diagram-UQ7AKVKN-CJDwyPxF.js → diagram-UQ7AKVKN-B4bEc26B.js} +1 -1
- package/out/web/assets/{diagram-VSXAHHWV-BxaG1r_B.js → diagram-VSXAHHWV-BJ86Ov2Y.js} +1 -1
- package/out/web/assets/{diagram-VX7I27RA-BMmUqm8f.js → diagram-VX7I27RA-7hH5IPYf.js} +1 -1
- package/out/web/assets/{diagram-Z3DM3KII-CFtd03eE.js → diagram-Z3DM3KII-CCM4clo8.js} +1 -1
- package/out/web/assets/dist-js-BkYNznHX.js +1 -0
- package/out/web/assets/{ebnfDiagram-PWID7BFC-DwAen1hO.js → ebnfDiagram-PWID7BFC-Da7WAM3k.js} +1 -1
- package/out/web/assets/{erDiagram-SSCWMZ5O-BCZSHACj.js → erDiagram-SSCWMZ5O-CzzmnZWT.js} +1 -1
- package/out/web/assets/{event-ChBVpRy0.js → event-Cdt7Byyj.js} +1 -1
- package/out/web/assets/{eventmodeling-NTZA5JFV-XHStE4E_.js → eventmodeling-NTZA5JFV-xcfsA0XD.js} +1 -1
- package/out/web/assets/flowDiagram-A5DVABFB-JdtH1673.js +1 -0
- package/out/web/assets/{ganttDiagram-EL5Y4UJY-BAEu1Cql.js → ganttDiagram-EL5Y4UJY-B3ClFGmL.js} +1 -1
- package/out/web/assets/{gitGraph-4MIJSDKK-r32Vyper.js → gitGraph-4MIJSDKK-BZ_y_cDU.js} +1 -1
- package/out/web/assets/{gitGraphDiagram-WWUBYQGX-z9o9qt6V.js → gitGraphDiagram-WWUBYQGX-drhRSYm3.js} +1 -1
- package/out/web/assets/index-BZ9xgmMP.css +1 -0
- package/out/web/assets/index-Ca8cCS4h.js +168 -0
- package/out/web/assets/{info-A6RAGUB7-BvLaC2cw.js → info-A6RAGUB7-B7I3K0F1.js} +1 -1
- package/out/web/assets/{infoDiagram-RXCK75RN-Be6WAXXw.js → infoDiagram-RXCK75RN-deX_1MPh.js} +1 -1
- package/out/web/assets/{ishikawaDiagram-5VMMS53U-DZjoHJHC.js → ishikawaDiagram-5VMMS53U-s38yo28y.js} +1 -1
- package/out/web/assets/{journeyDiagram-EYS64GPL-CtVkjkGm.js → journeyDiagram-EYS64GPL-CZn2kB8b.js} +1 -1
- package/out/web/assets/jsx-runtime-DN4NhQUK.js +1 -0
- package/out/web/assets/{kanban-definition-3QL26DDD-CW5FMv1w.js → kanban-definition-3QL26DDD-DodjgySL.js} +1 -1
- package/out/web/assets/markdown-CMhYIcWB.js +39 -0
- package/out/web/assets/{mermaid-parser.core-aUgCqsZ5.js → mermaid-parser.core-Whu2rlJC.js} +3 -3
- package/out/web/assets/{mermaid.core-D57sEiSL.js → mermaid.core-CKyPo8VH.js} +4 -4
- package/out/web/assets/{mindmap-definition-FBJOCRG2-DAmSpM3a.js → mindmap-definition-FBJOCRG2-260WBDxZ.js} +1 -1
- package/out/web/assets/modelManager-CUaGWMWu.js +2 -0
- package/out/web/assets/{oauthManager-9jxicDWJ.js → oauthManager-DlNhXJr6.js} +1 -1
- package/out/web/assets/{packet-AYTQ26CC-DJ27iIoJ.js → packet-AYTQ26CC-CEeIRKRG.js} +1 -1
- package/out/web/assets/{pegDiagram-XKGWAZYB-Bj3ii-gS.js → pegDiagram-XKGWAZYB-BYadI-KO.js} +1 -1
- package/out/web/assets/{pie-WAS4IAKB-DG5mCUbc.js → pie-WAS4IAKB-D8uf4dh5.js} +1 -1
- package/out/web/assets/{pieDiagram-E7YTZNPT-BrJLPnDA.js → pieDiagram-E7YTZNPT-CjSDy4hO.js} +1 -1
- package/out/web/assets/{quadrantDiagram-AXDQQJYC-3x_TmF3j.js → quadrantDiagram-AXDQQJYC-B8D3gU5d.js} +1 -1
- package/out/web/assets/{radar-RG4KPBEZ-CrXaTTuV.js → radar-RG4KPBEZ-DZJxnokY.js} +1 -1
- package/out/web/assets/{railroad-74A4TZTK-BpqMH9BE.js → railroad-74A4TZTK-DBysUiUi.js} +1 -1
- package/out/web/assets/{railroad-abnf-HS5TGJTU-DHI0uLlV.js → railroad-abnf-HS5TGJTU-CHWxoJYg.js} +1 -1
- package/out/web/assets/{railroad-ebnf-LZEXJU2U-CQCb316y.js → railroad-ebnf-LZEXJU2U-DDv7X6bY.js} +1 -1
- package/out/web/assets/{railroad-peg-WCYAUIDC-Ph1giKkH.js → railroad-peg-WCYAUIDC-DpfcYYNJ.js} +1 -1
- package/out/web/assets/{railroadDiagram-O6MQD6OU-BRq6wIxx.js → railroadDiagram-O6MQD6OU-CYtyv0t3.js} +1 -1
- package/out/web/assets/{requirementDiagram-EFPCY7ZU-DIJj6G1g.js → requirementDiagram-EFPCY7ZU-D2X-Ewik.js} +1 -1
- package/out/web/assets/{sankeyDiagram-P5KCCOFB-DZajSryq.js → sankeyDiagram-P5KCCOFB-Bn2x6C4Y.js} +1 -1
- package/out/web/assets/{sequenceDiagram-WJ2MYXX4-DxJCWyZS.js → sequenceDiagram-WJ2MYXX4-BvFLKtPd.js} +1 -1
- package/out/web/assets/{stateDiagram-HBIQ2CUA-k6voi8Vf.js → stateDiagram-HBIQ2CUA-Bx0GWeaY.js} +1 -1
- package/out/web/assets/stateDiagram-v2-4QOOHH4V--hol1yg_.js +1 -0
- package/out/web/assets/{swimlanes-XN3QIQJK-igYhTjXB.js → swimlanes-XN3QIQJK-CCma0T7L.js} +1 -1
- package/out/web/assets/swimlanesDiagram-VK2B7HYN-BkohoVsU.js +8 -0
- package/out/web/assets/{timeline-definition-24CTP7MA-Dke2p93p.js → timeline-definition-24CTP7MA-iCRhhJ7C.js} +1 -1
- package/out/web/assets/{treeView-Q6P3EWNA-hNELnXUs.js → treeView-Q6P3EWNA-DZ-y08qp.js} +1 -1
- package/out/web/assets/{treemap-WGGIJYW6-D2B6wDsx.js → treemap-WGGIJYW6-De0CxzMx.js} +1 -1
- package/out/web/assets/{vennDiagram-4TSXK5OY-CDkW7rUP.js → vennDiagram-4TSXK5OY-w_7aZ6xe.js} +1 -1
- package/out/web/assets/{wardley-WFR3VGLG-BECdXETi.js → wardley-WFR3VGLG-DT0y0xJ4.js} +1 -1
- package/out/web/assets/{wardleyDiagram-VM6X3IG4-B5BycpQ4.js → wardleyDiagram-VM6X3IG4-DAdXELbQ.js} +1 -1
- package/out/web/assets/window-BJCVtyET.js +1 -0
- package/out/web/assets/{xychartDiagram-S5SC5T6Z-CtJS3b--.js → xychartDiagram-S5SC5T6Z-IZIXf3Vo.js} +1 -1
- package/out/web/index-web.html +3 -2
- package/out/web/index.html +3 -2
- package/package.json +71 -22
- package/shared-modules/.github/workflows/ci.yml +120 -0
- package/shared-modules/.github/workflows/update.yaml +28 -0
- package/slash-commands.json +7 -2
- package/src/bin/auto-build.js +66 -0
- package/src/bin/desktop.js +113 -0
- package/src/bin/desktop.test.js +113 -0
- package/src/bin/index.js +18 -25
- package/src/bin/index.test.js +89 -0
- package/src/bin/measure-effects.mjs +53 -0
- package/src/bin/package.test.js +28 -0
- package/src/bin/release-notes.test.js +84 -0
- package/src/bin/styles.test.js +20 -0
- package/src/bin/web.js +5 -0
- package/src/bin/web.test.js +39 -0
- package/src/renderer/index.html +0 -2
- package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/src/renderer/shared/agentProgress.ts +33 -0
- package/src/renderer/shared/components/AgentActivityDrawer.tsx +3 -2
- package/src/renderer/shared/components/AgentActivityTable.tsx +1 -1
- package/src/renderer/shared/components/ApprovalCard.test.tsx +24 -0
- package/src/renderer/shared/components/ApprovalCard.tsx +10 -3
- package/src/renderer/shared/components/ArtifactPreviewPanel.tsx +88 -37
- package/src/renderer/shared/components/AuthGate.tsx +75 -39
- package/src/renderer/shared/components/BackgroundTerminals.tsx +181 -0
- package/src/renderer/shared/components/ChatCodeBlock.tsx +8 -4
- package/src/renderer/shared/components/ChatMessageItem.test.tsx +68 -0
- package/src/renderer/shared/components/ChatMessageItem.tsx +36 -16
- package/src/renderer/shared/components/ChatPanel.test.tsx +21 -0
- package/src/renderer/shared/components/ChatPanel.tsx +294 -272
- package/src/renderer/shared/components/ChunkErrorBoundary.tsx +4 -102
- package/src/renderer/shared/components/CliSessionsView.test.tsx +125 -0
- package/src/renderer/shared/components/CliSessionsView.tsx +61 -13
- package/src/renderer/shared/components/CodeReviewPage.tsx +111 -8
- package/src/renderer/shared/components/CollapsiblePasteInput.tsx +90 -0
- package/src/renderer/shared/components/ContextCompactionStatus.tsx +17 -0
- package/src/renderer/shared/components/DashboardSidebar.tsx +15 -128
- package/src/renderer/shared/components/DesktopTitlebar.tsx +45 -19
- package/src/renderer/shared/components/ErrorNotice.tsx +29 -0
- package/src/renderer/shared/components/FormattedMessage.tsx +11 -0
- package/src/renderer/shared/components/GeminiLiveOverlay.css +86 -0
- package/src/renderer/shared/components/GeminiLiveOverlay.test.tsx +24 -0
- package/src/renderer/shared/components/GeminiLiveOverlay.tsx +85 -101
- package/src/renderer/shared/components/GlideSelect.css +138 -0
- package/src/renderer/shared/components/GlideSelect.d.ts +31 -0
- package/src/renderer/shared/components/GlideSelect.jsx +266 -0
- package/src/renderer/shared/components/ImageSearchCard.tsx +46 -56
- package/src/renderer/shared/components/ImageStudioPanel.tsx +9 -8
- package/src/renderer/shared/components/LibraryItemCard.tsx +72 -0
- package/src/renderer/shared/components/LibraryPage.tsx +165 -0
- package/src/renderer/shared/components/LibraryPreview.tsx +59 -0
- package/src/renderer/shared/components/McpServersView.tsx +27 -8
- package/src/renderer/shared/components/McpToolAllowlist.tsx +2 -4
- package/src/renderer/shared/components/McpToolResults.tsx +99 -0
- package/src/renderer/shared/components/MintDashboard.tsx +308 -770
- package/src/renderer/shared/components/ModSessionControls.tsx +8 -0
- package/src/renderer/shared/components/ModUi.test.tsx +65 -0
- package/src/renderer/shared/components/ModUi.tsx +26 -0
- package/src/renderer/shared/components/ModelSelectorPopover.tsx +14 -7
- package/src/renderer/shared/components/ModsPanel.tsx +156 -0
- package/src/renderer/shared/components/ProgressActivity.tsx +17 -0
- package/src/renderer/shared/components/ReviewHistory.tsx +42 -0
- package/src/renderer/shared/components/RewindModal.tsx +5 -12
- package/src/renderer/shared/components/RunSummaryDashboard.tsx +6 -12
- package/src/renderer/shared/components/ScheduledTasksView.tsx +7 -6
- package/src/renderer/shared/components/SelectField.test.tsx +27 -0
- package/src/renderer/shared/components/SelectField.tsx +38 -0
- package/src/renderer/shared/components/SessionSources.tsx +48 -0
- package/src/renderer/shared/components/SessionSourcesPopover.tsx +61 -0
- package/src/renderer/shared/components/Settings/AgentsTab.tsx +7 -6
- package/src/renderer/shared/components/Settings/AudioTab.tsx +8 -7
- package/src/renderer/shared/components/Settings/AutomationTab.tsx +2 -2
- package/src/renderer/shared/components/Settings/GeneralTab.tsx +33 -32
- package/src/renderer/shared/components/Settings/MemoryTab.test.tsx +37 -0
- package/src/renderer/shared/components/Settings/MemoryTab.tsx +71 -31
- package/src/renderer/shared/components/Settings/NotificationsTab.test.tsx +15 -0
- package/src/renderer/shared/components/Settings/NotificationsTab.tsx +84 -0
- package/src/renderer/shared/components/Settings/ThemeTab.tsx +19 -11
- package/src/renderer/shared/components/SlashChoicePanel.tsx +88 -0
- package/src/renderer/shared/components/SlashSuggestions.test.tsx +33 -0
- package/src/renderer/shared/components/SlashSuggestions.tsx +38 -10
- package/src/renderer/shared/components/SourcesBlock.tsx +12 -20
- package/src/renderer/shared/components/ToolSurfacePage.test.tsx +32 -0
- package/src/renderer/shared/components/ToolSurfacePage.tsx +59 -11
- package/src/renderer/shared/components/ToolsPanelToggle.tsx +13 -0
- package/src/renderer/shared/components/VeoStudioPanel.tsx +9 -8
- package/src/renderer/shared/components/ViewedImages.tsx +36 -0
- package/src/renderer/shared/components/VirtualChatHistory.tsx +82 -0
- package/src/renderer/shared/components/VoicePill.css +85 -0
- package/src/renderer/shared/components/VoicePill.d.ts +12 -0
- package/src/renderer/shared/components/VoicePill.jsx +190 -0
- package/src/renderer/shared/components/VoicePill.test.tsx +22 -0
- package/src/renderer/shared/components/WorkspaceContextPopover.tsx +20 -15
- package/src/renderer/shared/components/WorkspacePaneResizer.tsx +92 -0
- package/src/renderer/shared/components/WorkspacePreviewActions.test.tsx +44 -0
- package/src/renderer/shared/components/WorkspacePreviewActions.tsx +19 -0
- package/src/renderer/shared/components/WorkspaceSelector.tsx +13 -8
- package/src/renderer/shared/components/background-terminals.css +292 -0
- package/src/renderer/shared/components/ui/README.md +52 -0
- package/src/renderer/shared/components/ui/button.tsx +37 -0
- package/src/renderer/shared/components/ui/demo.tsx +26 -0
- package/src/renderer/shared/components/ui/error-recovery.css +35 -0
- package/src/renderer/shared/components/ui/error-recovery.tsx +35 -0
- package/src/renderer/shared/components/ui/voice-powered-orb.css +6 -0
- package/src/renderer/shared/components/ui/voice-powered-orb.tsx +374 -0
- package/src/renderer/shared/components/workspace-selector.css +248 -0
- package/src/renderer/shared/constants/config.ts +30 -1
- package/src/renderer/shared/constants/slashCommands.ts +1 -1
- package/src/renderer/shared/conversation/lifecycle.test.ts +362 -0
- package/src/renderer/shared/conversation/lifecycle.ts +418 -0
- package/src/renderer/shared/conversation/projectNavigation.test.ts +105 -0
- package/src/renderer/shared/conversation/projectNavigation.ts +39 -0
- package/src/renderer/shared/conversation/sourcesTransport.ts +27 -0
- package/src/renderer/shared/conversation/syncView.test.ts +1 -34
- package/src/renderer/shared/conversation/syncView.ts +0 -37
- package/src/renderer/shared/conversation/transport.test.ts +163 -0
- package/src/renderer/shared/conversation/transport.ts +159 -0
- package/src/renderer/shared/conversation/useConversationCoordinator.ts +30 -72
- package/src/renderer/shared/css/agent-observability.css +20 -21
- package/src/renderer/shared/css/auth-gate.css +112 -13
- package/src/renderer/shared/css/code-review.css +33 -0
- package/src/renderer/shared/css/code-sessions.css +27 -0
- package/src/renderer/shared/css/collapsed-paste.css +35 -0
- package/src/renderer/shared/css/effects.css +15 -0
- package/src/renderer/shared/css/error-notice.css +60 -0
- package/src/renderer/shared/css/library.css +476 -0
- package/src/renderer/shared/css/management-views.css +138 -0
- package/src/renderer/shared/css/mcp-results.css +11 -0
- package/src/renderer/shared/css/model-selector.css +163 -105
- package/src/renderer/shared/css/mods.css +77 -0
- package/src/renderer/shared/css/notification-notice.css +28 -0
- package/src/renderer/shared/css/session-sources-popover.css +9 -0
- package/src/renderer/shared/css/session-sources.css +23 -0
- package/src/renderer/shared/css/settings/memory.css +154 -0
- package/src/renderer/shared/css/settings/notifications.css +11 -0
- package/src/renderer/shared/css/settings/theme.css +5 -0
- package/src/renderer/shared/css/slash-choice.css +95 -0
- package/src/renderer/shared/css/slash-suggestions.css +68 -0
- package/src/renderer/shared/css/sources-block.css +91 -0
- package/src/renderer/shared/css/thinking-status.css +46 -0
- package/src/renderer/shared/css/tools-panel.css +7 -0
- package/src/renderer/shared/css/ui-tailwind.css +19 -0
- package/src/renderer/shared/css/viewed-images.css +15 -0
- package/src/renderer/shared/fonts.css +46 -0
- package/src/renderer/shared/fonts.ts +3 -12
- package/src/renderer/shared/lib/utils.ts +6 -0
- package/src/renderer/shared/library/controller.test.ts +61 -0
- package/src/renderer/shared/library/controller.ts +57 -0
- package/src/renderer/shared/library/transport.test.ts +26 -0
- package/src/renderer/shared/library/transport.ts +60 -0
- package/src/renderer/shared/library/useLibraryFile.ts +38 -0
- package/src/renderer/shared/mods.ts +58 -0
- package/src/renderer/shared/platform.test.ts +57 -1
- package/src/renderer/shared/platform.ts +46 -1
- package/src/renderer/shared/theme/themeManager.test.ts +43 -0
- package/src/renderer/shared/theme/themeManager.ts +17 -4
- package/src/renderer/shared/types.ts +56 -2
- package/src/renderer/shared/utils/agentActivity.test.ts +75 -0
- package/src/renderer/shared/utils/agentActivity.ts +42 -3
- package/src/renderer/shared/utils/approval.tsx +7 -0
- package/src/renderer/shared/utils/artifactBrowser.test.ts +80 -0
- package/src/renderer/shared/utils/artifactBrowser.ts +41 -0
- package/src/renderer/shared/utils/contextCompaction.test.tsx +48 -0
- package/src/renderer/shared/utils/contextCompaction.ts +21 -0
- package/src/renderer/shared/utils/effectVisibility.ts +9 -0
- package/src/renderer/shared/utils/markdown.tsx +3 -3
- package/src/renderer/shared/utils/mcpResults.test.tsx +91 -0
- package/src/renderer/shared/utils/modViewport.ts +7 -0
- package/src/renderer/shared/utils/nativeRecordingSession.test.ts +61 -0
- package/src/renderer/shared/utils/nativeRecordingSession.ts +46 -0
- package/src/renderer/shared/utils/notificationSettings.test.ts +14 -0
- package/src/renderer/shared/utils/notificationSettings.ts +23 -0
- package/src/renderer/shared/utils/notifications.test.ts +51 -0
- package/src/renderer/shared/utils/notifications.ts +54 -0
- package/src/renderer/shared/utils/pastedText.test.ts +20 -0
- package/src/renderer/shared/utils/pastedText.ts +13 -0
- package/src/renderer/shared/utils/progressCache.test.ts +32 -0
- package/src/renderer/shared/utils/sessionSources.test.ts +31 -0
- package/src/renderer/shared/utils/sessionSources.ts +34 -0
- package/src/renderer/shared/utils/slashCommandProcessor.ts +1 -1
- package/src/renderer/shared/utils/sourceAttachments.ts +28 -0
- package/src/renderer/shared/utils/speech.ts +51 -9
- package/src/renderer/shared/utils/syntaxHighlight.test.tsx +22 -0
- package/src/renderer/shared/utils/syntaxHighlight.tsx +26 -0
- package/src/renderer/shared/utils/useGeminiLiveVoice.ts +49 -5
- package/src/renderer/shared/utils/useLatestCallback.ts +9 -0
- package/src/renderer/shared/utils/useNativeVoiceInput.ts +80 -72
- package/src/renderer/shared/utils/useSessionSources.ts +33 -0
- package/src/renderer/shared/utils/viewedImages.ts +26 -0
- package/src/renderer/shared/utils/workspacePane.test.ts +33 -0
- package/src/renderer/shared/utils/workspacePane.ts +20 -0
- package/src/renderer/shared/utils/workspacePreview.test.ts +24 -0
- package/src/renderer/shared/utils/workspacePreview.ts +16 -0
- package/src/renderer/shared/utils/workspaceSnapshot.test.ts +49 -0
- package/src/renderer/shared/utils/workspaceSnapshot.ts +26 -0
- package/src/renderer/shared/utils/workspaces.ts +10 -0
- package/src/renderer/shared/workspaceUndo.ts +88 -0
- package/src/renderer/src/App.tsx +9 -1
- package/src/renderer/src/components/LiveTranslateControls.tsx +65 -0
- package/src/renderer/src/components/PicturesLibrary.tsx +2 -288
- package/src/renderer/src/components/ScreenPicker.css +67 -30
- package/src/renderer/src/components/ScreenPicker.tsx +201 -60
- package/src/renderer/src/components/SettingsWindow.tsx +10 -5
- package/src/renderer/src/components/WorkspacePanel.tsx +222 -32
- package/src/renderer/src/components/browser-shell.css +1 -1
- package/src/renderer/src/components/browser-suggestions-popup.css +1 -1
- package/src/renderer/src/components/liveTranslateFrames.test.ts +26 -5
- package/src/renderer/src/components/liveTranslateFrames.ts +18 -7
- package/src/renderer/src/components/liveTranslateLanguage.ts +6 -0
- package/src/renderer/src/css/app-shell.css +52 -137
- package/src/renderer/src/css/chat.css +21 -495
- package/src/renderer/src/css/desktop-tools.css +199 -1
- package/src/renderer/src/css/misc.css +0 -52
- package/src/renderer/src/css/pictures.css +2 -415
- package/src/renderer/src/css/settings/base.css +50 -47
- package/src/renderer/src/css/sidebar-workspace.css +151 -94
- package/src/renderer/src/css/tool-surfaces.css +65 -9
- package/src/renderer/src/css/widget.css +1 -1
- package/src/renderer/src/env.d.ts +1 -9
- package/src/renderer/src/index.css +1 -1
- package/src/renderer/src/main.tsx +2 -1
- package/src/renderer/src/proactiveSuggestions.ts +29 -0
- package/src/renderer/src/tauri.ts +122 -330
- package/src/renderer/src/voiceInput.ts +2 -1
- package/src/renderer/src-web/components/PicturesLibrary.tsx +2 -285
- package/src/renderer/src-web/components/PullToRefresh.tsx +1 -1
- package/src/renderer/src-web/components/SettingsWindow.tsx +10 -5
- package/src/renderer/src-web/css/app-shell.css +24 -121
- package/src/renderer/src-web/css/chat.css +21 -495
- package/src/renderer/src-web/css/misc.css +0 -52
- package/src/renderer/src-web/css/mobile.css +15 -14
- package/src/renderer/src-web/css/pictures.css +2 -462
- package/src/renderer/src-web/css/settings/base.css +50 -47
- package/src/renderer/src-web/css/sidebar-workspace.css +9 -95
- package/src/renderer/src-web/css/widget.css +1 -1
- package/src/renderer/src-web/env.d.ts +1 -12
- package/src/renderer/src-web/index.css +1 -1
- package/src/renderer/src-web/main.tsx +2 -1
- package/src/renderer/src-web/proactiveSuggestions.ts +3 -0
- package/src/renderer/src-web/tauri.ts +129 -354
- package/src/renderer/tests/backgroundTerminals.browser.html +81 -0
- package/src/renderer/tests/chatPanelFixture.ts +23 -0
- package/src/renderer/tests/chatPerformance.browser.html +71 -0
- package/src/renderer/tests/cliSessions.browser.html +82 -0
- package/src/renderer/tests/collapsedPaste.browser.html +70 -0
- package/src/renderer/tests/composerMenus.browser.html +116 -0
- package/src/renderer/tests/contextCompaction.browser.html +17 -0
- package/src/renderer/tests/conversationLifecycle.browser.html +70 -0
- package/src/renderer/tests/effectsPerformance.browser.html +13 -0
- package/src/renderer/tests/errorRecovery.browser.html +21 -0
- package/src/renderer/tests/glideSelect.browser.html +71 -0
- package/src/renderer/tests/glideSelect.preview.html +27 -0
- package/src/renderer/tests/library.browser.html +1 -0
- package/src/renderer/tests/library.browser.tsx +78 -0
- package/src/renderer/tests/liveOrb.browser.html +49 -0
- package/src/renderer/tests/liveVoice.browser.html +29 -0
- package/src/renderer/tests/modInput.browser.html +22 -0
- package/src/renderer/tests/sessionSources.browser.html +1 -0
- package/src/renderer/tests/sessionSources.browser.tsx +107 -0
- package/src/renderer/tests/skills.browser.html +24 -0
- package/src/renderer/tests/toolsPanel.browser.html +1 -0
- package/src/renderer/tests/toolsPanel.browser.tsx +113 -0
- package/src/renderer/tests/viewedImages.browser.html +1 -0
- package/src/renderer/tests/viewedImages.browser.tsx +93 -0
- package/src/renderer/tests/voicePill.browser.html +80 -0
- package/src/renderer/tests/voicePill.preview.html +34 -0
- package/src/renderer/tests/webSources.browser.html +1 -0
- package/src/renderer/tests/webSources.browser.tsx +98 -0
- package/src/renderer/tests/workspaceUndo.browser.html +92 -0
- package/src-tauri/Cargo.toml +40 -0
- package/src-tauri/build.rs +3 -0
- package/src-tauri/src/app/lifecycle.rs +277 -0
- package/src-tauri/src/app/mod.rs +4 -0
- package/src-tauri/src/app/shortcuts.rs +40 -0
- package/src-tauri/src/app/tray.rs +52 -0
- package/src-tauri/src/background/events.rs +70 -0
- package/src-tauri/src/background/headless.rs +230 -0
- package/src-tauri/src/background/mod.rs +4 -0
- package/src-tauri/src/background/proactive.rs +262 -0
- package/src-tauri/src/commands/approvals.rs +20 -0
- package/src-tauri/src/commands/auth.rs +127 -0
- package/src-tauri/src/commands/conversation.rs +642 -0
- package/src-tauri/src/commands/integrations.rs +157 -0
- package/src-tauri/src/commands/media.rs +179 -0
- package/src-tauri/src/commands/mod.rs +11 -0
- package/src-tauri/src/commands/mods.rs +6 -0
- package/src-tauri/src/commands/settings.rs +141 -0
- package/src-tauri/src/commands/voice.rs +153 -0
- package/src-tauri/src/commands/workspace.rs +501 -0
- package/src-tauri/src/lib.rs +10 -0
- package/src-tauri/src/main.rs +3 -0
- package/src-tauri/src/native/actions.rs +206 -0
- package/src-tauri/src/native/browser.rs +252 -0
- package/src-tauri/src/native/capture.rs +695 -0
- package/src-tauri/src/native/discord_rpc.rs +99 -0
- package/src-tauri/src/native/integrations.rs +126 -0
- package/src-tauri/src/native/mod.rs +12 -0
- package/src-tauri/src/native/notifications.rs +51 -0
- package/src-tauri/src/native/plugins.rs +14 -0
- package/src-tauri/src/native/system.rs +250 -0
- package/src-tauri/src/native/terminal.rs +201 -0
- package/src-tauri/src/native/updater.rs +148 -0
- package/src-tauri/src/native/windows.rs +143 -0
- package/tsconfig.json +2 -0
- package/vite.config.ts +4 -1
- package/vite.config.web.ts +4 -0
- package/.cargo/config.toml +0 -13
- package/.claude/settings.local.json +0 -182
- package/.codex +0 -0
- package/CLAUDE.md +0 -102
- package/ROADMAP.md +0 -30
- package/mint-presentation-demo.mp4 +0 -0
- package/out/web/Live2DCubismCore.js +0 -9
- package/out/web/assets/CliSessionsView-BxUVWEEL.js +0 -1
- package/out/web/assets/CliSessionsView-CBDTXuQv.css +0 -1
- package/out/web/assets/CodeReviewPage-DkMgfcos.css +0 -1
- package/out/web/assets/CodeReviewPage-jKlmLJwT.js +0 -3
- package/out/web/assets/CommandPalette-DnwPA5hy.js +0 -1
- package/out/web/assets/ImageStudioPanel-BR1XFwhR.js +0 -1
- package/out/web/assets/LinkedFoldersView-bi-CWQ-I.js +0 -1
- package/out/web/assets/McpRegistryPicker-D5tEFnyq.js +0 -1
- package/out/web/assets/McpServersView-BGG1dhFM.js +0 -1
- package/out/web/assets/MintDashboard-BIglDEWX.js +0 -20
- package/out/web/assets/MintDashboard-DSMywSgs.css +0 -1
- package/out/web/assets/ModelPanel-CbJ1PFoZ.js +0 -1
- package/out/web/assets/PicturesLibrary-CQP0fLP1.css +0 -1
- package/out/web/assets/PicturesLibrary-DoYN7T1p.js +0 -1
- package/out/web/assets/PluginsView-CU1HWXGk.js +0 -1
- package/out/web/assets/ScheduledTasksView-Bnl36y2N.js +0 -1
- package/out/web/assets/SettingsWindow-DhWLAn9T.css +0 -1
- package/out/web/assets/SettingsWindow-Dw8L01pw.js +0 -2
- package/out/web/assets/SkillsView-cnl8aY5p.js +0 -2
- package/out/web/assets/ToolSurfacePage-CgsQw6l5.css +0 -1
- package/out/web/assets/ToolSurfacePage-Cz2VJAPZ.js +0 -37
- package/out/web/assets/VeoStudioPanel-D4JnERWR.js +0 -4
- package/out/web/assets/chunk-CKBBP62Z-cIrXq6jN.js +0 -2
- package/out/web/assets/classDiagram-DTDB5LWJ-Dq0smRWk.js +0 -1
- package/out/web/assets/classDiagram-v2-JRS7N3AN-Dq0smRWk.js +0 -1
- package/out/web/assets/config-C-ZFtipU.css +0 -1
- package/out/web/assets/config-eEDngJXO.js +0 -1
- package/out/web/assets/core-hQ2VA9Ti.js +0 -1
- package/out/web/assets/dist-js-Bf2bhoku.js +0 -1
- package/out/web/assets/flowDiagram-A5DVABFB-BLg7nEcd.js +0 -1
- package/out/web/assets/index-Btsf4L8M.css +0 -1
- package/out/web/assets/index-D8D9MeRA.js +0 -11
- package/out/web/assets/lucide-react-Ip3L6Jtn.js +0 -1
- package/out/web/assets/markdown-BSeeUbJH.js +0 -38
- package/out/web/assets/modelManager-tMVmJVAW.js +0 -2
- package/out/web/assets/prompt-latin-400-normal-BQ9zjSN8.woff2 +0 -0
- package/out/web/assets/prompt-latin-400-normal-CxU4ec_r.woff +0 -0
- package/out/web/assets/prompt-latin-500-normal-B1edBDQ9.woff +0 -0
- package/out/web/assets/prompt-latin-500-normal-CxzxEHZc.woff2 +0 -0
- package/out/web/assets/prompt-latin-600-normal-CSDDLorv.woff +0 -0
- package/out/web/assets/prompt-latin-600-normal-hKZWXsc1.woff2 +0 -0
- package/out/web/assets/prompt-latin-700-normal-BfbftDSq.woff +0 -0
- package/out/web/assets/prompt-latin-700-normal-I2gc831J.woff2 +0 -0
- package/out/web/assets/prompt-thai-400-normal-BrkKv8cO.woff2 +0 -0
- package/out/web/assets/prompt-thai-400-normal-DJypnfr3.woff +0 -0
- package/out/web/assets/prompt-thai-500-normal-C18pDUoL.woff2 +0 -0
- package/out/web/assets/prompt-thai-500-normal-D7YNqUJD.woff +0 -0
- package/out/web/assets/prompt-thai-600-normal-CYPViXds.woff +0 -0
- package/out/web/assets/prompt-thai-600-normal-MrdfU7zR.woff2 +0 -0
- package/out/web/assets/prompt-thai-700-normal-Cb5ilR-c.woff +0 -0
- package/out/web/assets/prompt-thai-700-normal-Cg4aQ0Nn.woff2 +0 -0
- package/out/web/assets/stateDiagram-v2-4QOOHH4V-DeEc0lGZ.js +0 -1
- package/out/web/assets/swimlanesDiagram-VK2B7HYN-DpVik9v7.js +0 -8
- package/out/web/assets/window-5qYXKoW7.js +0 -1
- package/skills-lock.json +0 -77
- package/src/renderer/public/Live2DCubismCore.js +0 -9
- package/src/renderer/src/companionWidget.ts +0 -95
- package/src/renderer/src/components/Live2DStage.tsx +0 -412
- package/src/renderer/src/components/ModelPanel.tsx +0 -155
- package/src/renderer/src/css/model.css +0 -628
- package/src/renderer/src-web/companionWidget.ts +0 -37
- package/src/renderer/src-web/components/ModelPanel.tsx +0 -12
- package/src/renderer/src-web/css/model.css +0 -628
|
@@ -17,6 +17,16 @@ use thiserror::Error;
|
|
|
17
17
|
|
|
18
18
|
use crate::{ConfigError, MintConfig, load_config, save_config};
|
|
19
19
|
|
|
20
|
+
#[path = "mcp_calls.rs"]
|
|
21
|
+
mod calls;
|
|
22
|
+
#[path = "mcp_catalog.rs"]
|
|
23
|
+
pub mod catalog;
|
|
24
|
+
#[path = "mcp_device.rs"]
|
|
25
|
+
pub mod device;
|
|
26
|
+
use calls::PROGRESS_SUBSCRIBERS;
|
|
27
|
+
pub(crate) use calls::call_mcp_tool_reviewed;
|
|
28
|
+
pub use calls::{McpProgress, call_mcp_tool_async, cancel_mcp_calls};
|
|
29
|
+
|
|
20
30
|
const MCP_TIMEOUT: Duration = Duration::from_secs(30);
|
|
21
31
|
|
|
22
32
|
/// Extended per-request timeout used only while a session is mid-OAuth (its
|
|
@@ -55,6 +65,10 @@ fn block_on_mcp<F: std::future::Future<Output = T> + Send + 'static, T: Send + '
|
|
|
55
65
|
/// constraint of one stdio pipe pair per child process.
|
|
56
66
|
static SESSIONS: LazyLock<Mutex<HashMap<String, Arc<Mutex<McpSession>>>>> =
|
|
57
67
|
LazyLock::new(|| Mutex::new(HashMap::new()));
|
|
68
|
+
// Startup can involve a slow initialize/OAuth exchange. Serialize only callers
|
|
69
|
+
// starting the same server, never unrelated sessions.
|
|
70
|
+
static SESSION_STARTS: LazyLock<Mutex<HashMap<String, Arc<Mutex<()>>>>> =
|
|
71
|
+
LazyLock::new(|| Mutex::new(HashMap::new()));
|
|
58
72
|
|
|
59
73
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
60
74
|
pub struct McpServer {
|
|
@@ -78,6 +92,12 @@ pub struct McpServer {
|
|
|
78
92
|
pub headers: Option<BTreeMap<String, String>>,
|
|
79
93
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
80
94
|
pub transport: Option<String>,
|
|
95
|
+
#[serde(
|
|
96
|
+
default,
|
|
97
|
+
rename = "timeoutSecs",
|
|
98
|
+
skip_serializing_if = "Option::is_none"
|
|
99
|
+
)]
|
|
100
|
+
pub timeout_secs: Option<u64>,
|
|
81
101
|
}
|
|
82
102
|
|
|
83
103
|
impl McpServer {
|
|
@@ -128,20 +148,76 @@ pub enum McpError {
|
|
|
128
148
|
Write(std::io::Error),
|
|
129
149
|
#[error("MCP server response timed out")]
|
|
130
150
|
Timeout,
|
|
151
|
+
#[error("Stopped waiting for MCP; cancellation requested. Remote completion is unconfirmed")]
|
|
152
|
+
Cancelled,
|
|
153
|
+
#[error(
|
|
154
|
+
"Stopped waiting for MCP; cancellation could not be delivered. Remote completion is unconfirmed"
|
|
155
|
+
)]
|
|
156
|
+
CancelledUndelivered,
|
|
157
|
+
#[error(
|
|
158
|
+
"MCP server response timed out; cancellation could not be delivered. Remote completion is unconfirmed"
|
|
159
|
+
)]
|
|
160
|
+
TimeoutUndelivered,
|
|
161
|
+
#[error("invalid MCP timeout: expected 1–3600 seconds")]
|
|
162
|
+
InvalidTimeout,
|
|
131
163
|
#[error("MCP tool call failed: {0}")]
|
|
132
164
|
Tool(Value),
|
|
165
|
+
#[error("MCP preflight failed; no tool was executed: {0}")]
|
|
166
|
+
Preflight(String),
|
|
133
167
|
}
|
|
134
168
|
|
|
135
169
|
pub fn configured_mcp_servers(
|
|
136
170
|
config: &MintConfig,
|
|
137
171
|
) -> Result<BTreeMap<String, McpServer>, McpError> {
|
|
138
|
-
|
|
172
|
+
let servers: BTreeMap<String, McpServer> = config
|
|
139
173
|
.extra
|
|
140
174
|
.get("mcpServers")
|
|
141
175
|
.cloned()
|
|
142
176
|
.map(serde_json::from_value)
|
|
143
177
|
.transpose()?
|
|
144
|
-
.unwrap_or_default()
|
|
178
|
+
.unwrap_or_default();
|
|
179
|
+
if servers
|
|
180
|
+
.values()
|
|
181
|
+
.any(|s| s.timeout_secs.is_some_and(|t| !(1..=3600).contains(&t)))
|
|
182
|
+
{
|
|
183
|
+
return Err(McpError::InvalidTimeout);
|
|
184
|
+
}
|
|
185
|
+
Ok(servers)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
pub fn set_mcp_timeout(name: &str, timeout_secs: u64) -> Result<bool, McpError> {
|
|
189
|
+
let mut config = load_config()?;
|
|
190
|
+
let changed = set_server_timeout_in(&mut config, name, timeout_secs)?;
|
|
191
|
+
if changed {
|
|
192
|
+
save_config(&config)?;
|
|
193
|
+
}
|
|
194
|
+
Ok(changed)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
pub fn parse_mcp_timeout(value: &str) -> Result<u64, McpError> {
|
|
198
|
+
value
|
|
199
|
+
.trim()
|
|
200
|
+
.parse::<u64>()
|
|
201
|
+
.ok()
|
|
202
|
+
.filter(|v| (1..=3600).contains(v))
|
|
203
|
+
.ok_or(McpError::InvalidTimeout)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
pub fn set_server_timeout_in(
|
|
207
|
+
config: &mut MintConfig,
|
|
208
|
+
name: &str,
|
|
209
|
+
timeout_secs: u64,
|
|
210
|
+
) -> Result<bool, McpError> {
|
|
211
|
+
if !(1..=3600).contains(&timeout_secs) {
|
|
212
|
+
return Err(McpError::InvalidTimeout);
|
|
213
|
+
}
|
|
214
|
+
let mut servers = configured_mcp_servers(config)?;
|
|
215
|
+
let Some(server) = servers.get_mut(name) else {
|
|
216
|
+
return Ok(false);
|
|
217
|
+
};
|
|
218
|
+
server.timeout_secs = Some(timeout_secs);
|
|
219
|
+
write_servers(config, servers)?;
|
|
220
|
+
Ok(true)
|
|
145
221
|
}
|
|
146
222
|
|
|
147
223
|
pub fn list_mcp_servers() -> Result<BTreeMap<String, McpServer>, McpError> {
|
|
@@ -249,6 +325,7 @@ pub fn add_mcp_server(
|
|
|
249
325
|
url: None,
|
|
250
326
|
headers: None,
|
|
251
327
|
transport: None,
|
|
328
|
+
timeout_secs: None,
|
|
252
329
|
},
|
|
253
330
|
)?;
|
|
254
331
|
Ok(save_config(&config)?)
|
|
@@ -272,6 +349,7 @@ pub fn add_remote_mcp_server(
|
|
|
272
349
|
url: Some(url.to_string()),
|
|
273
350
|
headers,
|
|
274
351
|
transport: Some("sse".to_string()),
|
|
352
|
+
timeout_secs: None,
|
|
275
353
|
},
|
|
276
354
|
)?;
|
|
277
355
|
Ok(save_config(&config)?)
|
|
@@ -331,11 +409,11 @@ pub fn call_mcp_tool(
|
|
|
331
409
|
tool: tool_name.into(),
|
|
332
410
|
});
|
|
333
411
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
)
|
|
412
|
+
let config = config.clone();
|
|
413
|
+
let server_name = server_name.to_owned();
|
|
414
|
+
let tool_name = tool_name.to_owned();
|
|
415
|
+
block_on_mcp(async move {
|
|
416
|
+
call_mcp_tool_async(&config, &server_name, &tool_name, arguments, "", |_| {}).await
|
|
339
417
|
})
|
|
340
418
|
}
|
|
341
419
|
|
|
@@ -383,8 +461,10 @@ pub fn get_server_prompt(
|
|
|
383
461
|
/// server's session since the last call — e.g. `notifications/tools/list_changed`.
|
|
384
462
|
/// Returns an empty `Vec` if there's no live session for `server_name`.
|
|
385
463
|
pub fn drain_mcp_notifications(server_name: &str) -> Vec<Value> {
|
|
386
|
-
|
|
387
|
-
|
|
464
|
+
// Never keep the registry locked while waiting for a session: raw
|
|
465
|
+
// requests can hold that session while a checked writer needs the registry.
|
|
466
|
+
let session = SESSIONS.lock().unwrap().get(server_name).cloned();
|
|
467
|
+
match session {
|
|
388
468
|
Some(session) => session
|
|
389
469
|
.lock()
|
|
390
470
|
.unwrap()
|
|
@@ -478,15 +558,23 @@ fn stream_and_watch_for_oauth_url(pipe: impl std::io::Read) {
|
|
|
478
558
|
|
|
479
559
|
/// Closes and removes one server's persistent session, if one is running.
|
|
480
560
|
pub fn close_mcp_session(server_name: &str) {
|
|
481
|
-
|
|
561
|
+
// Release the registry before taking a session lock. Dispatch checks the
|
|
562
|
+
// registry while holding stdin; legacy resource reads can hold both locks.
|
|
563
|
+
let session = SESSIONS.lock().unwrap().remove(server_name);
|
|
564
|
+
if let Some(session) = session {
|
|
482
565
|
session.lock().unwrap().close();
|
|
483
566
|
}
|
|
484
567
|
}
|
|
485
568
|
|
|
486
569
|
/// Closes and removes every server's persistent session.
|
|
487
570
|
pub fn close_all_mcp_sessions() {
|
|
488
|
-
let
|
|
489
|
-
|
|
571
|
+
let sessions = SESSIONS
|
|
572
|
+
.lock()
|
|
573
|
+
.unwrap()
|
|
574
|
+
.drain()
|
|
575
|
+
.map(|(_, s)| s)
|
|
576
|
+
.collect::<Vec<_>>();
|
|
577
|
+
for session in sessions {
|
|
490
578
|
session.lock().unwrap().close();
|
|
491
579
|
}
|
|
492
580
|
}
|
|
@@ -734,10 +822,17 @@ pub fn expand_registry_entry(
|
|
|
734
822
|
url: None,
|
|
735
823
|
headers: None,
|
|
736
824
|
transport: None,
|
|
825
|
+
timeout_secs: None,
|
|
737
826
|
}
|
|
738
827
|
}
|
|
739
828
|
|
|
740
829
|
fn find_url(line: &str) -> Option<String> {
|
|
830
|
+
if serde_json::from_str::<Value>(line)
|
|
831
|
+
.ok()
|
|
832
|
+
.is_some_and(|value| value.get("result").is_some() && value.get("id").is_some())
|
|
833
|
+
{
|
|
834
|
+
return None;
|
|
835
|
+
}
|
|
741
836
|
let start_idx = line.find("http://").or_else(|| line.find("https://"))?;
|
|
742
837
|
let rest = &line[start_idx..];
|
|
743
838
|
let end_idx = rest
|
|
@@ -804,8 +899,12 @@ fn open_url_in_browser(url: &str) -> std::io::Result<()> {
|
|
|
804
899
|
}
|
|
805
900
|
|
|
806
901
|
pub fn list_server_tools(config: &MintConfig, server_name: &str) -> Result<Value, McpError> {
|
|
807
|
-
|
|
808
|
-
|
|
902
|
+
let config = config.clone();
|
|
903
|
+
let server = server_name.to_owned();
|
|
904
|
+
block_on_mcp(async move {
|
|
905
|
+
calls::McpScope::new("")
|
|
906
|
+
.run(catalog::list_tools(&config, &server, ""), MCP_TIMEOUT)
|
|
907
|
+
.await
|
|
809
908
|
})
|
|
810
909
|
}
|
|
811
910
|
|
|
@@ -833,7 +932,9 @@ pub fn mcp_server_tool_names(server_name: &str) -> Result<Vec<String>, McpError>
|
|
|
833
932
|
/// each incoming line to whichever in-flight request it answers.
|
|
834
933
|
struct McpStdioSession {
|
|
835
934
|
process: Child,
|
|
836
|
-
stdin: ChildStdin
|
|
935
|
+
stdin: Arc<Mutex<ChildStdin>>,
|
|
936
|
+
connected: Arc<AtomicBool>,
|
|
937
|
+
catalog_generation: Arc<AtomicU64>,
|
|
837
938
|
next_id: AtomicU64,
|
|
838
939
|
pending: Arc<Mutex<HashMap<u64, mpsc::Sender<Value>>>>,
|
|
839
940
|
/// Server-initiated messages (a `method` but no `id`), e.g.
|
|
@@ -858,6 +959,7 @@ struct McpRemoteSession {
|
|
|
858
959
|
pending: Arc<Mutex<HashMap<u64, mpsc::Sender<Value>>>>,
|
|
859
960
|
notifications: Arc<Mutex<VecDeque<Value>>>,
|
|
860
961
|
is_active: Arc<AtomicBool>,
|
|
962
|
+
catalog_generation: Arc<AtomicU64>,
|
|
861
963
|
server_info: Option<Value>,
|
|
862
964
|
abort_handle: Option<tokio::task::AbortHandle>,
|
|
863
965
|
}
|
|
@@ -893,7 +995,7 @@ impl McpRemoteSession {
|
|
|
893
995
|
.ok_or_else(|| McpError::Remote("missing remote URL".into()))?
|
|
894
996
|
.to_string();
|
|
895
997
|
|
|
896
|
-
let mut client_builder = reqwest::Client::builder().
|
|
998
|
+
let mut client_builder = reqwest::Client::builder().connect_timeout(MCP_TIMEOUT);
|
|
897
999
|
let mut header_map = reqwest::header::HeaderMap::new();
|
|
898
1000
|
let headers = server.headers.clone().unwrap_or_default();
|
|
899
1001
|
for (k, v) in &headers {
|
|
@@ -914,6 +1016,7 @@ impl McpRemoteSession {
|
|
|
914
1016
|
Arc::new(Mutex::new(HashMap::new()));
|
|
915
1017
|
let notifications: Arc<Mutex<VecDeque<Value>>> = Arc::new(Mutex::new(VecDeque::new()));
|
|
916
1018
|
let is_active = Arc::new(AtomicBool::new(true));
|
|
1019
|
+
let catalog_generation = Arc::new(AtomicU64::new(0));
|
|
917
1020
|
|
|
918
1021
|
let sse_url = url.clone();
|
|
919
1022
|
let sse_client = client.clone();
|
|
@@ -926,22 +1029,28 @@ impl McpRemoteSession {
|
|
|
926
1029
|
let notify_endpoint_tx = Arc::new(Mutex::new(Some(notify_endpoint_tx)));
|
|
927
1030
|
|
|
928
1031
|
let connect_res = block_on_mcp(async move {
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1032
|
+
tokio::time::timeout(
|
|
1033
|
+
MCP_TIMEOUT,
|
|
1034
|
+
sse_client
|
|
1035
|
+
.get(&sse_url)
|
|
1036
|
+
.header(reqwest::header::ACCEPT, "text/event-stream")
|
|
1037
|
+
.send(),
|
|
1038
|
+
)
|
|
1039
|
+
.await
|
|
1040
|
+
.ok()
|
|
1041
|
+
.and_then(Result::ok)
|
|
934
1042
|
});
|
|
935
1043
|
|
|
936
1044
|
let mut abort_handle = None;
|
|
937
1045
|
|
|
938
|
-
if let
|
|
1046
|
+
if let Some(resp) = connect_res {
|
|
939
1047
|
if resp.status().is_success() {
|
|
940
1048
|
let base_url = url.clone();
|
|
941
1049
|
let post_ep = Arc::clone(&sse_post_endpoint);
|
|
942
1050
|
let reader_pending = Arc::clone(&sse_pending);
|
|
943
1051
|
let reader_notifications = Arc::clone(&sse_notifications);
|
|
944
1052
|
let stream_active = Arc::clone(&sse_is_active);
|
|
1053
|
+
let reader_generation = Arc::clone(&catalog_generation);
|
|
945
1054
|
let ep_tx = Arc::clone(¬ify_endpoint_tx);
|
|
946
1055
|
|
|
947
1056
|
let handle = MCP_ASYNC_RUNTIME.spawn(async move {
|
|
@@ -984,7 +1093,8 @@ impl McpRemoteSession {
|
|
|
984
1093
|
}
|
|
985
1094
|
}
|
|
986
1095
|
McpLine::Notification(notification) => {
|
|
987
|
-
|
|
1096
|
+
dispatch_notification(
|
|
1097
|
+
&reader_generation,
|
|
988
1098
|
&reader_notifications,
|
|
989
1099
|
notification,
|
|
990
1100
|
);
|
|
@@ -1019,6 +1129,7 @@ impl McpRemoteSession {
|
|
|
1019
1129
|
pending,
|
|
1020
1130
|
notifications,
|
|
1021
1131
|
is_active,
|
|
1132
|
+
catalog_generation,
|
|
1022
1133
|
server_info: None,
|
|
1023
1134
|
abort_handle,
|
|
1024
1135
|
};
|
|
@@ -1063,7 +1174,7 @@ impl McpRemoteSession {
|
|
|
1063
1174
|
let headers = self.headers.clone();
|
|
1064
1175
|
|
|
1065
1176
|
let _ = block_on_mcp(async move {
|
|
1066
|
-
let mut req = client.post(&post_url).json(&payload);
|
|
1177
|
+
let mut req = client.post(&post_url).timeout(MCP_TIMEOUT).json(&payload);
|
|
1067
1178
|
for (k, v) in &headers {
|
|
1068
1179
|
req = req.header(k, v);
|
|
1069
1180
|
}
|
|
@@ -1094,7 +1205,7 @@ impl McpRemoteSession {
|
|
|
1094
1205
|
let headers = self.headers.clone();
|
|
1095
1206
|
|
|
1096
1207
|
let post_result = block_on_mcp(async move {
|
|
1097
|
-
let mut req = client.post(&post_url).json(&payload);
|
|
1208
|
+
let mut req = client.post(&post_url).timeout(MCP_TIMEOUT).json(&payload);
|
|
1098
1209
|
for (k, v) in &headers {
|
|
1099
1210
|
req = req.header(k, v);
|
|
1100
1211
|
}
|
|
@@ -1119,10 +1230,7 @@ impl McpRemoteSession {
|
|
|
1119
1230
|
let body_text = block_on_mcp(async move { response.text().await.unwrap_or_default() });
|
|
1120
1231
|
if !body_text.trim().is_empty() {
|
|
1121
1232
|
if let Ok(val) = serde_json::from_str::<Value>(&body_text) {
|
|
1122
|
-
if val.get("id").and_then(Value::as_u64) == Some(id)
|
|
1123
|
-
|| val.get("result").is_some()
|
|
1124
|
-
|| val.get("error").is_some()
|
|
1125
|
-
{
|
|
1233
|
+
if val.get("id").and_then(Value::as_u64) == Some(id) {
|
|
1126
1234
|
self.pending.lock().unwrap().remove(&id);
|
|
1127
1235
|
if let Some(err) = val.get("error") {
|
|
1128
1236
|
return Err(McpError::Tool(err.clone()));
|
|
@@ -1170,7 +1278,26 @@ fn classify_mcp_line(value: &Value) -> McpLine {
|
|
|
1170
1278
|
/// Appends a server notification, evicting the oldest so the queue never grows
|
|
1171
1279
|
/// past `MAX_BUFFERED_NOTIFICATIONS`. Free function so the eviction is testable
|
|
1172
1280
|
/// without a live subprocess.
|
|
1281
|
+
fn dispatch_notification(
|
|
1282
|
+
generation: &AtomicU64,
|
|
1283
|
+
queue: &Mutex<VecDeque<Value>>,
|
|
1284
|
+
notification: Value,
|
|
1285
|
+
) {
|
|
1286
|
+
if notification["method"] == "notifications/tools/list_changed" {
|
|
1287
|
+
generation.fetch_add(1, Ordering::AcqRel);
|
|
1288
|
+
}
|
|
1289
|
+
buffer_notification(queue, notification);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1173
1292
|
fn buffer_notification(queue: &Mutex<VecDeque<Value>>, notification: Value) {
|
|
1293
|
+
if notification.get("method").and_then(Value::as_str) == Some("notifications/progress")
|
|
1294
|
+
&& let Some(token) = notification
|
|
1295
|
+
.pointer("/params/progressToken")
|
|
1296
|
+
.and_then(Value::as_str)
|
|
1297
|
+
&& let Some(sender) = PROGRESS_SUBSCRIBERS.lock().unwrap().get(token)
|
|
1298
|
+
{
|
|
1299
|
+
let _ = sender.try_send(notification.clone());
|
|
1300
|
+
}
|
|
1174
1301
|
let mut queue = queue.lock().unwrap();
|
|
1175
1302
|
while queue.len() >= MAX_BUFFERED_NOTIFICATIONS {
|
|
1176
1303
|
queue.pop_front();
|
|
@@ -1221,6 +1348,10 @@ impl McpStdioSession {
|
|
|
1221
1348
|
let reader_pending = Arc::clone(&pending);
|
|
1222
1349
|
let reader_notifications = Arc::clone(¬ifications);
|
|
1223
1350
|
let reader_oauth = Arc::clone(&oauth_pending);
|
|
1351
|
+
let connected = Arc::new(AtomicBool::new(true));
|
|
1352
|
+
let reader_connected = Arc::clone(&connected);
|
|
1353
|
+
let catalog_generation = Arc::new(AtomicU64::new(0));
|
|
1354
|
+
let reader_generation = Arc::clone(&catalog_generation);
|
|
1224
1355
|
std::thread::spawn(move || {
|
|
1225
1356
|
// Isolates a panic in the read loop (e.g. a poisoned `pending`/
|
|
1226
1357
|
// `notifications` lock from some other unrelated failure) so it's
|
|
@@ -1248,12 +1379,17 @@ impl McpStdioSession {
|
|
|
1248
1379
|
}
|
|
1249
1380
|
}
|
|
1250
1381
|
McpLine::Notification(notification) => {
|
|
1251
|
-
|
|
1382
|
+
dispatch_notification(
|
|
1383
|
+
&reader_generation,
|
|
1384
|
+
&reader_notifications,
|
|
1385
|
+
notification,
|
|
1386
|
+
);
|
|
1252
1387
|
}
|
|
1253
1388
|
McpLine::Other => {}
|
|
1254
1389
|
}
|
|
1255
1390
|
}
|
|
1256
1391
|
}));
|
|
1392
|
+
reader_connected.store(false, Ordering::Release);
|
|
1257
1393
|
if let Err(payload) = result {
|
|
1258
1394
|
let message = crate::channels::panic_payload_message(&payload);
|
|
1259
1395
|
eprintln!("[mint] MCP stdout reader thread panicked: {message}");
|
|
@@ -1262,7 +1398,9 @@ impl McpStdioSession {
|
|
|
1262
1398
|
|
|
1263
1399
|
let mut session = McpStdioSession {
|
|
1264
1400
|
process,
|
|
1265
|
-
stdin,
|
|
1401
|
+
stdin: Arc::new(Mutex::new(stdin)),
|
|
1402
|
+
connected,
|
|
1403
|
+
catalog_generation,
|
|
1266
1404
|
next_id: AtomicU64::new(2), // 1 is reserved for `initialize` below.
|
|
1267
1405
|
pending,
|
|
1268
1406
|
notifications,
|
|
@@ -1302,8 +1440,9 @@ impl McpStdioSession {
|
|
|
1302
1440
|
}
|
|
1303
1441
|
|
|
1304
1442
|
fn write(&mut self, message: &Value) -> Result<(), McpError> {
|
|
1305
|
-
|
|
1306
|
-
|
|
1443
|
+
let mut stdin = self.stdin.lock().unwrap();
|
|
1444
|
+
writeln!(stdin, "{message}").map_err(McpError::Write)?;
|
|
1445
|
+
stdin.flush().map_err(McpError::Write)
|
|
1307
1446
|
}
|
|
1308
1447
|
|
|
1309
1448
|
fn request(&mut self, method: &str, params: Value) -> Result<Value, McpError> {
|
|
@@ -1346,6 +1485,46 @@ enum McpSessionBackend {
|
|
|
1346
1485
|
struct McpSession {
|
|
1347
1486
|
backend: McpSessionBackend,
|
|
1348
1487
|
notifications: Arc<Mutex<VecDeque<Value>>>,
|
|
1488
|
+
config_key: String,
|
|
1489
|
+
catalog: Arc<tokio::sync::Mutex<Option<catalog::Catalog>>>,
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
/// Pins both discovery and dispatch to one live catalog generation.
|
|
1493
|
+
#[derive(Clone)]
|
|
1494
|
+
pub(crate) struct SessionBinding {
|
|
1495
|
+
server: String,
|
|
1496
|
+
session: Arc<Mutex<McpSession>>,
|
|
1497
|
+
generation: u64,
|
|
1498
|
+
generation_counter: Arc<AtomicU64>,
|
|
1499
|
+
}
|
|
1500
|
+
impl SessionBinding {
|
|
1501
|
+
pub(crate) fn check(&self) -> Result<(), McpError> {
|
|
1502
|
+
self.check_fast()?;
|
|
1503
|
+
let mut session = self.session.lock().unwrap();
|
|
1504
|
+
if !session.is_alive() {
|
|
1505
|
+
return Err(McpError::Preflight(
|
|
1506
|
+
"Session disconnected; rediscover tools before dispatch".into(),
|
|
1507
|
+
));
|
|
1508
|
+
}
|
|
1509
|
+
Ok(())
|
|
1510
|
+
}
|
|
1511
|
+
fn check_fast(&self) -> Result<(), McpError> {
|
|
1512
|
+
let registered = SESSIONS.lock().unwrap().get(&self.server).cloned();
|
|
1513
|
+
if !registered.is_some_and(|s| Arc::ptr_eq(&s, &self.session)) {
|
|
1514
|
+
return Err(McpError::Preflight(
|
|
1515
|
+
"Session was replaced; rediscover tools before dispatch".into(),
|
|
1516
|
+
));
|
|
1517
|
+
}
|
|
1518
|
+
if self.generation_counter.load(Ordering::Acquire) != self.generation {
|
|
1519
|
+
return Err(McpError::Preflight(
|
|
1520
|
+
"Session or catalog changed; rediscover tools before dispatch".into(),
|
|
1521
|
+
));
|
|
1522
|
+
}
|
|
1523
|
+
Ok(())
|
|
1524
|
+
}
|
|
1525
|
+
pub(crate) fn same_session(&self, other: &Self) -> bool {
|
|
1526
|
+
Arc::ptr_eq(&self.session, &other.session) && self.generation == other.generation
|
|
1527
|
+
}
|
|
1349
1528
|
}
|
|
1350
1529
|
|
|
1351
1530
|
impl McpSession {
|
|
@@ -1428,6 +1607,7 @@ pub fn test_remote_mcp_connection(
|
|
|
1428
1607
|
url: Some(url.to_string()),
|
|
1429
1608
|
headers,
|
|
1430
1609
|
transport: Some("sse".to_string()),
|
|
1610
|
+
timeout_secs: None,
|
|
1431
1611
|
};
|
|
1432
1612
|
|
|
1433
1613
|
let mut session = McpRemoteSession::start(&server)?;
|
|
@@ -1468,21 +1648,41 @@ fn get_or_start_session(
|
|
|
1468
1648
|
.get(server_name)
|
|
1469
1649
|
.ok_or_else(|| McpError::MissingServer(server_name.into()))?;
|
|
1470
1650
|
|
|
1471
|
-
let
|
|
1651
|
+
let config_key = serde_json::to_string(&json!([
|
|
1652
|
+
server.command,
|
|
1653
|
+
server.args,
|
|
1654
|
+
server.env,
|
|
1655
|
+
server.url,
|
|
1656
|
+
server.headers,
|
|
1657
|
+
server.transport
|
|
1658
|
+
]))
|
|
1659
|
+
.unwrap();
|
|
1660
|
+
let start_lock = SESSION_STARTS
|
|
1661
|
+
.lock()
|
|
1662
|
+
.unwrap()
|
|
1663
|
+
.entry(server_name.into())
|
|
1664
|
+
.or_default()
|
|
1665
|
+
.clone();
|
|
1666
|
+
let _starting = start_lock.lock().unwrap();
|
|
1472
1667
|
|
|
1473
1668
|
if server.disabled {
|
|
1474
1669
|
// Turned off in Settings after a session was already running — kill it
|
|
1475
1670
|
// so a stale process doesn't linger past the toggle.
|
|
1476
|
-
|
|
1671
|
+
let session = SESSIONS.lock().unwrap().remove(server_name);
|
|
1672
|
+
if let Some(session) = session {
|
|
1477
1673
|
session.lock().unwrap().close();
|
|
1478
1674
|
}
|
|
1479
1675
|
return Err(McpError::Disabled(server_name.into()));
|
|
1480
1676
|
}
|
|
1481
1677
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1678
|
+
let existing = SESSIONS.lock().unwrap().get(server_name).cloned();
|
|
1679
|
+
if let Some(session) = existing {
|
|
1680
|
+
let mut current = session.lock().unwrap();
|
|
1681
|
+
if current.config_key == config_key && current.is_alive() {
|
|
1682
|
+
drop(current);
|
|
1683
|
+
return Ok(session);
|
|
1485
1684
|
}
|
|
1685
|
+
current.close();
|
|
1486
1686
|
}
|
|
1487
1687
|
|
|
1488
1688
|
let session = if server.is_remote() {
|
|
@@ -1491,6 +1691,8 @@ fn get_or_start_session(
|
|
|
1491
1691
|
Arc::new(Mutex::new(McpSession {
|
|
1492
1692
|
backend: McpSessionBackend::Remote(remote),
|
|
1493
1693
|
notifications,
|
|
1694
|
+
config_key,
|
|
1695
|
+
catalog: Arc::new(tokio::sync::Mutex::new(None)),
|
|
1494
1696
|
}))
|
|
1495
1697
|
} else {
|
|
1496
1698
|
let stdio = McpStdioSession::start(server)?;
|
|
@@ -1498,9 +1700,14 @@ fn get_or_start_session(
|
|
|
1498
1700
|
Arc::new(Mutex::new(McpSession {
|
|
1499
1701
|
backend: McpSessionBackend::Stdio(stdio),
|
|
1500
1702
|
notifications,
|
|
1703
|
+
config_key,
|
|
1704
|
+
catalog: Arc::new(tokio::sync::Mutex::new(None)),
|
|
1501
1705
|
}))
|
|
1502
1706
|
};
|
|
1503
|
-
|
|
1707
|
+
SESSIONS
|
|
1708
|
+
.lock()
|
|
1709
|
+
.unwrap()
|
|
1710
|
+
.insert(server_name.to_string(), Arc::clone(&session));
|
|
1504
1711
|
Ok(session)
|
|
1505
1712
|
}
|
|
1506
1713
|
|
|
@@ -1521,6 +1728,83 @@ fn with_session<T>(
|
|
|
1521
1728
|
mod tests {
|
|
1522
1729
|
use super::*;
|
|
1523
1730
|
|
|
1731
|
+
#[cfg(unix)]
|
|
1732
|
+
#[test]
|
|
1733
|
+
fn invalid_device_arguments_are_blocked_before_tools_call() {
|
|
1734
|
+
let marker =
|
|
1735
|
+
std::env::temp_dir().join(format!("mint-invalid-call-{}", uuid::Uuid::new_v4()));
|
|
1736
|
+
let name = format!("validation-{}", uuid::Uuid::new_v4());
|
|
1737
|
+
let script = format!(
|
|
1738
|
+
r#"
|
|
1739
|
+
import sys,json
|
|
1740
|
+
for line in sys.stdin:
|
|
1741
|
+
r=json.loads(line); m=r.get('method')
|
|
1742
|
+
if m=='initialize': result={{}}
|
|
1743
|
+
elif m=='tools/list': result={{'tools':[{{'name':'set_speed','description':'Set RPM','inputSchema':{{'type':'object','properties':{{'rpm':{{'type':'integer','minimum':0,'maximum':3000}}}},'required':['rpm'],'additionalProperties':False}}}}]}}
|
|
1744
|
+
elif m=='tools/call':
|
|
1745
|
+
open({:?},'w').close();result={{'content':[{{'type':'text','text':'accepted'}}]}}
|
|
1746
|
+
else: continue
|
|
1747
|
+
print(json.dumps({{'jsonrpc':'2.0','id':r['id'],'result':result}}),flush=True)
|
|
1748
|
+
"#,
|
|
1749
|
+
marker.to_string_lossy()
|
|
1750
|
+
);
|
|
1751
|
+
let mut config = MintConfig::default();
|
|
1752
|
+
config.extra.insert(
|
|
1753
|
+
"mcpServers".into(),
|
|
1754
|
+
json!({name.clone():{"command":"python3","args":["-u","-c",script]}}),
|
|
1755
|
+
);
|
|
1756
|
+
config
|
|
1757
|
+
.extra
|
|
1758
|
+
.insert("allowedMcpTools".into(), json!({name.clone():["*"]}));
|
|
1759
|
+
let result = call_mcp_tool(&config, &name, "set_speed", json!({"rpm":"fast"}));
|
|
1760
|
+
close_mcp_session(&name);
|
|
1761
|
+
let executed = marker.exists();
|
|
1762
|
+
if executed {
|
|
1763
|
+
std::fs::remove_file(marker).unwrap();
|
|
1764
|
+
}
|
|
1765
|
+
assert!(
|
|
1766
|
+
result.is_err(),
|
|
1767
|
+
"invalid arguments must fail locally: {result:?}"
|
|
1768
|
+
);
|
|
1769
|
+
assert!(!executed, "invalid arguments reached the device");
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
#[test]
|
|
1773
|
+
fn server_timeout_survives_config_round_trip() {
|
|
1774
|
+
let server: McpServer =
|
|
1775
|
+
serde_json::from_value(json!({"command":"node","timeoutSecs":75})).unwrap();
|
|
1776
|
+
assert_eq!(serde_json::to_value(server).unwrap()["timeoutSecs"], 75);
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
#[test]
|
|
1780
|
+
fn rejects_timeouts_outside_the_supported_range() {
|
|
1781
|
+
for value in [0, 3601] {
|
|
1782
|
+
let mut cfg = MintConfig::default();
|
|
1783
|
+
cfg.extra.insert(
|
|
1784
|
+
"mcpServers".into(),
|
|
1785
|
+
json!({"server":{"command":"node","timeoutSecs":value}}),
|
|
1786
|
+
);
|
|
1787
|
+
assert!(matches!(
|
|
1788
|
+
configured_mcp_servers(&cfg),
|
|
1789
|
+
Err(McpError::InvalidTimeout)
|
|
1790
|
+
));
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
#[test]
|
|
1795
|
+
fn result_links_do_not_trigger_the_oauth_browser_watcher() {
|
|
1796
|
+
assert_eq!(
|
|
1797
|
+
find_url(
|
|
1798
|
+
r#"{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"resource_link","uri":"https://example.test/authors/model"}]}}"#
|
|
1799
|
+
),
|
|
1800
|
+
None
|
|
1801
|
+
);
|
|
1802
|
+
assert_eq!(
|
|
1803
|
+
find_url("Authorize at https://example.test/oauth"),
|
|
1804
|
+
Some("https://example.test/oauth".into())
|
|
1805
|
+
);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1524
1808
|
#[test]
|
|
1525
1809
|
fn rejects_environment_without_equals_separator() {
|
|
1526
1810
|
assert!(matches!(
|
|
@@ -1571,23 +1855,23 @@ mod tests {
|
|
|
1571
1855
|
/// reply, matching real JSON-RPC notification semantics.
|
|
1572
1856
|
fn mock_echo_server() -> McpServer {
|
|
1573
1857
|
McpServer {
|
|
1574
|
-
command: "
|
|
1575
|
-
args:
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
],
|
|
1858
|
+
command: "python3".into(),
|
|
1859
|
+
args:vec!["-u".into(),"-c".into(),r#"
|
|
1860
|
+
import sys,json
|
|
1861
|
+
for line in sys.stdin:
|
|
1862
|
+
r=json.loads(line)
|
|
1863
|
+
if 'id' not in r: continue
|
|
1864
|
+
if r.get('method')=='tools/list': result={'tools':[{'name':n,'inputSchema':{'type':'object'}} for n in ['anything','x','ping','one','two']]}
|
|
1865
|
+
else: result={'echo':True}
|
|
1866
|
+
print(json.dumps({'jsonrpc':'2.0','id':r['id'],'result':result}),flush=True)
|
|
1867
|
+
"#.into()],
|
|
1585
1868
|
env: BTreeMap::new(),
|
|
1586
1869
|
icon: None,
|
|
1587
1870
|
disabled: false,
|
|
1588
1871
|
url: None,
|
|
1589
1872
|
headers: None,
|
|
1590
1873
|
transport: None,
|
|
1874
|
+
timeout_secs: None,
|
|
1591
1875
|
}
|
|
1592
1876
|
}
|
|
1593
1877
|
|
|
@@ -1602,6 +1886,66 @@ mod tests {
|
|
|
1602
1886
|
config
|
|
1603
1887
|
}
|
|
1604
1888
|
|
|
1889
|
+
#[cfg(unix)]
|
|
1890
|
+
#[test]
|
|
1891
|
+
fn review_notification_drain_does_not_block_unrelated_tool_calls() {
|
|
1892
|
+
let blocked_name = format!("drain-blocked-{}", uuid::Uuid::new_v4());
|
|
1893
|
+
let healthy_name = format!("drain-healthy-{}", uuid::Uuid::new_v4());
|
|
1894
|
+
let blocked_config = config_with_mock_echo_server(&blocked_name);
|
|
1895
|
+
let healthy_config = config_with_mock_echo_server(&healthy_name);
|
|
1896
|
+
let blocked = get_or_start_session(&blocked_config, &blocked_name).unwrap();
|
|
1897
|
+
call_mcp_tool(&healthy_config, &healthy_name, "ping", json!({})).unwrap();
|
|
1898
|
+
|
|
1899
|
+
let notification = json!({"jsonrpc":"2.0","method":"notifications/tools/list_changed"});
|
|
1900
|
+
// A raw resource/prompt request keeps this mutex while awaiting its
|
|
1901
|
+
// response. Hold it here so the scheduling does not depend on a server.
|
|
1902
|
+
let session = blocked.lock().unwrap();
|
|
1903
|
+
session
|
|
1904
|
+
.notifications
|
|
1905
|
+
.lock()
|
|
1906
|
+
.unwrap()
|
|
1907
|
+
.push_back(notification.clone());
|
|
1908
|
+
let completed = std::thread::scope(|threads| {
|
|
1909
|
+
let started = Arc::new(std::sync::Barrier::new(2));
|
|
1910
|
+
let ready = Arc::clone(&started);
|
|
1911
|
+
let drain_name = &blocked_name;
|
|
1912
|
+
let drain = threads.spawn(move || {
|
|
1913
|
+
ready.wait();
|
|
1914
|
+
drain_mcp_notifications(drain_name)
|
|
1915
|
+
});
|
|
1916
|
+
started.wait();
|
|
1917
|
+
// Let the drainer reach the contended session. Old code keeps the
|
|
1918
|
+
// registry locked there; fixed code releases it immediately.
|
|
1919
|
+
let deadline = std::time::Instant::now() + Duration::from_millis(100);
|
|
1920
|
+
while std::time::Instant::now() < deadline {
|
|
1921
|
+
if SESSIONS.try_lock().is_err() {
|
|
1922
|
+
break;
|
|
1923
|
+
}
|
|
1924
|
+
std::thread::sleep(Duration::from_millis(1));
|
|
1925
|
+
}
|
|
1926
|
+
let (tx, rx) = mpsc::channel();
|
|
1927
|
+
let healthy_config = &healthy_config;
|
|
1928
|
+
let healthy_name = &healthy_name;
|
|
1929
|
+
threads.spawn(move || {
|
|
1930
|
+
let result = call_mcp_tool(healthy_config, healthy_name, "ping", json!({}));
|
|
1931
|
+
let _ = tx.send(result);
|
|
1932
|
+
});
|
|
1933
|
+
let completed = rx.recv_timeout(Duration::from_secs(2));
|
|
1934
|
+
// Always unblock and join both workers before asserting, even on
|
|
1935
|
+
// the regression path, so this test cannot deadlock the test suite.
|
|
1936
|
+
drop(session);
|
|
1937
|
+
assert_eq!(drain.join().unwrap(), vec![notification]);
|
|
1938
|
+
completed
|
|
1939
|
+
});
|
|
1940
|
+
close_mcp_session(&blocked_name);
|
|
1941
|
+
close_mcp_session(&healthy_name);
|
|
1942
|
+
assert!(
|
|
1943
|
+
completed.is_ok_and(|result| result.is_ok()),
|
|
1944
|
+
"notification drain blocked an unrelated server's tool call"
|
|
1945
|
+
);
|
|
1946
|
+
assert!(drain_mcp_notifications(&blocked_name).is_empty());
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1605
1949
|
#[test]
|
|
1606
1950
|
fn persistent_session_is_reused_across_calls_and_respawned_after_exit() {
|
|
1607
1951
|
let name = "mock-echo-reuse-test";
|
|
@@ -1875,6 +2219,7 @@ mod tests {
|
|
|
1875
2219
|
url: Some("https://example.com/sse".into()),
|
|
1876
2220
|
headers: Some(headers.clone()),
|
|
1877
2221
|
transport: Some("sse".into()),
|
|
2222
|
+
timeout_secs: None,
|
|
1878
2223
|
};
|
|
1879
2224
|
|
|
1880
2225
|
assert!(remote.is_remote());
|
|
@@ -1904,6 +2249,7 @@ mod tests {
|
|
|
1904
2249
|
url: None,
|
|
1905
2250
|
headers: None,
|
|
1906
2251
|
transport: None,
|
|
2252
|
+
timeout_secs: None,
|
|
1907
2253
|
};
|
|
1908
2254
|
assert!(!stdio.is_remote());
|
|
1909
2255
|
assert_eq!(stdio.remote_url(), None);
|
|
@@ -1927,6 +2273,7 @@ mod tests {
|
|
|
1927
2273
|
url: Some("https://docs.example.com/mcp".into()),
|
|
1928
2274
|
headers: Some(headers),
|
|
1929
2275
|
transport: Some("sse".into()),
|
|
2276
|
+
timeout_secs: None,
|
|
1930
2277
|
},
|
|
1931
2278
|
)
|
|
1932
2279
|
.expect("upserts remote mcp server");
|