@pheem49/mint 1.17.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +6 -6
- package/Cargo.toml +2 -2
- package/README.md +8 -2
- package/Release_Note.md +222 -71
- package/crates/mint-cli/src/agent/diff_render.rs +66 -44
- package/crates/mint-cli/src/agent/live_status.rs +21 -4
- package/crates/mint-cli/src/agent/markdown_render.rs +342 -5
- package/crates/mint-cli/src/agent.rs +25 -10
- package/crates/mint-cli/src/commands/agent.rs +4 -92
- package/crates/mint-cli/src/commands/browser.rs +2 -15
- package/crates/mint-cli/src/commands/integrations.rs +3 -3
- package/crates/mint-cli/src/commands/mod.rs +0 -3
- package/crates/mint-cli/src/commands/tailnet_web.rs +1 -0
- package/crates/mint-cli/src/interactive/chat_tui/dialog.rs +572 -0
- package/crates/mint-cli/src/interactive/chat_tui/mod.rs +1427 -0
- package/crates/mint-cli/src/interactive/chat_tui/render.rs +1524 -0
- package/crates/mint-cli/src/interactive/chat_tui/selection.rs +401 -0
- package/crates/mint-cli/src/interactive/chat_tui/state.rs +1833 -0
- package/crates/mint-cli/src/interactive/chat_tui/tests.rs +3414 -0
- package/crates/mint-cli/src/interactive/chat_tui/thought.rs +202 -0
- package/crates/mint-cli/src/interactive/chat_tui/transcript.rs +728 -0
- package/crates/mint-cli/src/interactive/chat_tui/watermark.rs +148 -0
- package/crates/mint-cli/src/interactive/command_ui.rs +53 -5
- package/crates/mint-cli/src/interactive/composer.rs +186 -0
- package/crates/mint-cli/src/interactive/format.rs +78 -3
- package/crates/mint-cli/src/interactive/input_box.rs +61 -1
- package/crates/mint-cli/src/interactive/resume_picker.rs +367 -25
- package/crates/mint-cli/src/interactive/slash_commands.rs +648 -371
- package/crates/mint-cli/src/interactive/tool_activity.rs +93 -20
- package/crates/mint-cli/src/interactive.rs +37 -0
- package/crates/mint-cli/src/main.rs +6 -3
- package/crates/mint-cli/src/markdown.rs +2 -37
- package/crates/mint-cli/src/onboard.rs +700 -282
- package/crates/mint-cli/src/setup.rs +1342 -624
- package/crates/mint-cli/src/terminal_theme.rs +159 -35
- package/crates/mint-cli/tests/library_intake.rs +1 -0
- package/crates/mint-core/src/agent/chat/anthropic.rs +318 -0
- package/crates/mint-core/src/agent/chat/custom.rs +149 -0
- package/crates/mint-core/src/agent/chat/gemini.rs +518 -0
- package/crates/mint-core/src/agent/chat/mod.rs +857 -0
- package/crates/mint-core/src/agent/chat/ollama.rs +321 -0
- package/crates/mint-core/src/agent/chat/openai.rs +582 -0
- package/crates/mint-core/src/agent/chat/streaming.rs +469 -0
- package/crates/mint-core/src/agent/chat/tests.rs +1479 -0
- package/crates/mint-core/src/agent/memory.rs +56 -2
- package/crates/mint-core/src/agent/tasks.rs +2 -2
- package/crates/mint-core/src/api_server/routes/chat.rs +24 -2
- package/crates/mint-core/src/api_server/routes/gemini_live.rs +14 -1
- package/crates/mint-core/src/api_server/routes/html_preview.rs +1 -8
- package/crates/mint-core/src/api_server/routes/sessions.rs +21 -0
- package/crates/mint-core/src/avatar_bridge.rs +0 -22
- package/crates/mint-core/src/browser/cdp.rs +10 -4
- package/crates/mint-core/src/browser/headless.rs +582 -0
- package/crates/mint-core/src/browser/input.rs +7 -62
- package/crates/mint-core/src/browser/interact.rs +0 -14
- package/crates/mint-core/src/browser/mod.rs +8 -29
- package/crates/mint-core/src/browser/navigate.rs +2 -36
- package/crates/mint-core/src/browser/runtime.rs +2 -4
- package/crates/mint-core/src/browser/session.rs +50 -466
- package/crates/mint-core/src/browser/workflow.rs +142 -32
- package/crates/mint-core/src/cron/store.rs +2 -2
- package/crates/mint-core/src/integrations/mcp.rs +6 -8
- package/crates/mint-core/src/lib.rs +0 -5
- package/crates/mint-core/src/media/gemini_live.rs +636 -86
- package/crates/mint-core/src/media/library/migration.rs +26 -0
- package/crates/mint-core/src/media/library/tests.rs +1 -1
- package/crates/mint-core/src/media/library/thumbnails.rs +8 -2
- package/crates/mint-core/src/media/pictures.rs +1 -1
- package/crates/mint-core/src/media/tool_images.rs +92 -1
- package/crates/mint-core/src/mods/manager.rs +101 -35
- package/crates/mint-core/src/orchestration/context_render.rs +441 -24
- package/crates/mint-core/src/orchestration/decision_parsing.rs +531 -16
- package/crates/mint-core/src/orchestration/full_skill_result_tests.rs +12 -0
- package/crates/mint-core/src/orchestration/loop_guard.rs +145 -0
- package/crates/mint-core/src/orchestration/mcp_outcome_tests.rs +864 -0
- package/crates/mint-core/src/orchestration/memory_skill.rs +88 -28
- package/crates/mint-core/src/orchestration/mod.rs +320 -4249
- package/crates/mint-core/src/orchestration/reliability.rs +67 -27
- package/crates/mint-core/src/orchestration/review_regressions.rs +5 -0
- package/crates/mint-core/src/orchestration/tests.rs +1665 -0
- package/crates/mint-core/src/orchestration/tools/dispatch.rs +967 -0
- package/crates/mint-core/src/orchestration/tools/files.rs +1 -0
- package/crates/mint-core/src/orchestration/tools/mod.rs +5 -1
- package/crates/mint-core/src/orchestration/tools/planning.rs +102 -42
- package/crates/mint-core/src/orchestration/tools/shell.rs +129 -58
- package/crates/mint-core/src/orchestration/truncation.rs +170 -13
- package/crates/mint-core/src/orchestration/types.rs +648 -0
- package/crates/mint-core/src/orchestration/verification.rs +0 -3
- package/crates/mint-core/src/orchestration/workspace_helpers.rs +24 -6
- package/crates/mint-core/src/prompts/agent.rs +52 -76
- package/crates/mint-core/src/prompts/tool_catalog.rs +8 -151
- package/crates/mint-core/src/search/files.rs +20 -0
- package/crates/mint-core/src/slash/mod.rs +228 -265
- package/crates/mint-core/src/slash/models.rs +6 -1
- package/crates/mint-core/src/system/bg_shell.rs +1 -0
- package/crates/mint-core/src/system/config.rs +176 -34
- package/crates/mint-mod-runtime/src/lib.rs +37 -23
- package/out/web/assets/ArtifactPreviewPanel-Cx6BVOOF.js +2 -0
- package/out/web/assets/CalculationCard-D2E74u5q.js +1 -0
- package/out/web/assets/CliSessionsView-ZbdtSjvK.css +1 -0
- package/out/web/assets/{CliSessionsView-MI8kXMAb.js → CliSessionsView-z5i6qGyb.js} +1 -1
- package/out/web/assets/CodeReviewPage-BK2bOScd.css +1 -0
- package/out/web/assets/CodeReviewPage-Dp6zHuHu.js +2 -0
- package/out/web/assets/{CommandPalette-BD8SlTE5.js → CommandPalette-DaKlzbKs.js} +1 -1
- package/out/web/assets/ImageGenCard-BMCJHP9O.js +1 -0
- package/out/web/assets/ImageSearchCard-Cld_SOUg.js +1 -0
- package/out/web/assets/{ImageStudioPanel-BP7N4rLr.js → ImageStudioPanel-B5N7TjeB.js} +1 -1
- package/out/web/assets/LibraryPage-CMB89rJ0.css +1 -0
- package/out/web/assets/LibraryPage-DbZEi4W_.js +2 -0
- package/out/web/assets/LinkedFoldersView-9B9DIPoZ.js +1 -0
- package/out/web/assets/{McpRegistryPicker-CoS83vtc.js → McpRegistryPicker-DnXPlzI7.js} +1 -1
- package/out/web/assets/McpServersView-CiNBNUl4.js +1 -0
- package/out/web/assets/MermaidCard-BBDNEXbY.js +2 -0
- package/out/web/assets/MintDashboard-C_UYPoNV.js +119 -0
- package/out/web/assets/MintDashboard-DTd8C_c8.css +1 -0
- package/out/web/assets/PlanCard-C2x5pajq.js +1 -0
- package/out/web/assets/{PluginsView-jBYmcKh4.js → PluginsView-DoTg9MVa.js} +1 -1
- package/out/web/assets/RewindModal-B5oowrR0.js +1 -0
- package/out/web/assets/{ScheduledTasksView-5uw9-V9H.js → ScheduledTasksView-BD5DVPkZ.js} +1 -1
- package/out/web/assets/{SelectField-DXBQtrUK.js → SelectField-Cy6CghrU.js} +1 -1
- package/out/web/assets/SettingsWindow-BYl_oCFh.js +4 -0
- package/out/web/assets/SettingsWindow-Ckcgnjt8.css +1 -0
- package/out/web/assets/{SkillsView-C-gp9J7m.js → SkillsView-CIeWwq5z.js} +1 -1
- package/out/web/assets/StockCard-DxUciKyp.js +1 -0
- package/out/web/assets/ToolSurfacePage-qBgs_VHq.css +1 -0
- package/out/web/assets/ToolSurfacePage-saVbVrkE.js +38 -0
- package/out/web/assets/{UiFeatureCard-6BwxTOCe.js → UiFeatureCard-CdY0hGWy.js} +1 -1
- package/out/web/assets/{UiGridCard-DvZcLNxb.js → UiGridCard-BBK3I5hC.js} +1 -1
- package/out/web/assets/UiMockupWidget-CpR9EDuc.js +1 -0
- package/out/web/assets/{VeoStudioPanel-DOj7_WR3.css → VeoStudioPanel-9kU9YLEN.css} +1 -1
- package/out/web/assets/{VeoStudioPanel-DfsOJKv0.js → VeoStudioPanel-Co6-wEWM.js} +2 -2
- package/out/web/assets/WeatherCard-Buzi7Vfu.js +1 -0
- package/out/web/assets/WorkspacePdfPreview-Bi8Zzyir.js +2 -0
- package/out/web/assets/{abnfDiagram-VCTEODGH-BOo3Ckng.js → abnfDiagram-VCTEODGH-CMfR95N2.js} +1 -1
- package/out/web/assets/{architecture-7GRP2DOG-ZOwheW3n.js → architecture-7GRP2DOG-DhAM4HyO.js} +1 -1
- package/out/web/assets/{architectureDiagram-5GKGNRK7-DC2FDzVd.js → architectureDiagram-5GKGNRK7-Yk3TUJ_F.js} +1 -1
- package/out/web/assets/{blockDiagram-I7D4REHJ-BrKFWdAH.js → blockDiagram-I7D4REHJ-X9fBgJik.js} +1 -1
- package/out/web/assets/{c4Diagram-7LVT6UL2-BHTHK1Yd.js → c4Diagram-7LVT6UL2-Bk9x7N-8.js} +1 -1
- package/out/web/assets/{chunk-4HAMMTFA-BYFkZwNj.js → chunk-4HAMMTFA-Dx5fk1_0.js} +1 -1
- package/out/web/assets/{chunk-75Z2AOVW-BSKfSKSA.js → chunk-75Z2AOVW-BszTQoM4.js} +1 -1
- package/out/web/assets/{chunk-DU6HZSFF-vIhsiW6n.js → chunk-DU6HZSFF-DK-As3rk.js} +1 -1
- package/out/web/assets/{chunk-F27PBJKO-BlYpW9xq.js → chunk-F27PBJKO-LmncZZIS.js} +1 -1
- package/out/web/assets/{chunk-GMAD6QVW-BIBz_Ug5.js → chunk-GMAD6QVW-DSQ_sDRJ.js} +1 -1
- package/out/web/assets/{chunk-GVQU2GXP-NPSHxeqC.js → chunk-GVQU2GXP-Cno3ulje.js} +1 -1
- package/out/web/assets/{chunk-IMKFNOWR-YlJK8_Fp.js → chunk-IMKFNOWR-bIzfY9Jc.js} +1 -1
- package/out/web/assets/{chunk-L3NEJ4N5-DMYi4-af.js → chunk-L3NEJ4N5-Dl6eOa0D.js} +1 -1
- package/out/web/assets/{chunk-OSK3NFVY-Begd3-jA.js → chunk-OSK3NFVY-BaUGJ-cM.js} +1 -1
- package/out/web/assets/{chunk-P2QGCYS3-D1I4xFHN.js → chunk-P2QGCYS3-_066ndhH.js} +1 -1
- package/out/web/assets/{chunk-POPQ4Y6H-CafZZzNE.js → chunk-POPQ4Y6H-YT1AKhTv.js} +1 -1
- package/out/web/assets/{chunk-PWAF6VOD-Dwh6_Lnt.js → chunk-PWAF6VOD-CrEgDsxh.js} +1 -1
- package/out/web/assets/{chunk-SHT3W25Y-B0XrCSEb.js → chunk-SHT3W25Y-_KwaRLOl.js} +1 -1
- package/out/web/assets/{chunk-SVP7TREG-CxLsddxC.js → chunk-SVP7TREG-BQ1qb32L.js} +1 -1
- package/out/web/assets/{chunk-TICWLB2K-y156QInb.js → chunk-TICWLB2K-BDyehCtg.js} +1 -1
- package/out/web/assets/classDiagram-ZZMXUADV-BoSpzRCJ.js +1 -0
- package/out/web/assets/classDiagram-v2-VYDZK3BY-BoSpzRCJ.js +1 -0
- package/out/web/assets/{config-DqXJXeZM.css → config-2suVFPZg.css} +1 -1
- package/out/web/assets/config-CkRCdkxn.js +2 -0
- package/out/web/assets/{cynefin-OW5HDTMX-SMVvrLf-.js → cynefin-OW5HDTMX-Dl_31aaN.js} +1 -1
- package/out/web/assets/{cynefinDiagram-5FMLGOSQ-rZznN24T.js → cynefinDiagram-5FMLGOSQ-Bpir8jha.js} +1 -1
- package/out/web/assets/{dagre-GXQ25YYZ-BOToydFu.js → dagre-GXQ25YYZ-D9xu3nsq.js} +1 -1
- package/out/web/assets/{diagram-S7CK7UJ4-TMVaGQEJ.js → diagram-S7CK7UJ4-DeveoEeB.js} +1 -1
- package/out/web/assets/{diagram-UQ7AKVKN-C2JK-GP2.js → diagram-UQ7AKVKN-p4xWRESV.js} +1 -1
- package/out/web/assets/{diagram-VSXAHHWV-Cw8VSi2K.js → diagram-VSXAHHWV-C7qz9NDF.js} +1 -1
- package/out/web/assets/{diagram-VX7I27RA-DXp4fKk9.js → diagram-VX7I27RA-DRiCa3Vd.js} +1 -1
- package/out/web/assets/{diagram-Z3DM3KII-CzSicYiW.js → diagram-Z3DM3KII-Bt7zduQX.js} +1 -1
- package/out/web/assets/{ebnfDiagram-PWID7BFC-Dqj-zqPu.js → ebnfDiagram-PWID7BFC-DV9WpwdR.js} +1 -1
- package/out/web/assets/{erDiagram-RLTQ6QDP-DZwFZAuD.js → erDiagram-RLTQ6QDP-BJ_nwG2m.js} +1 -1
- package/out/web/assets/{eventmodeling-NTZA5JFV-C0O-Lvgp.js → eventmodeling-NTZA5JFV-BTBMrfVa.js} +1 -1
- package/out/web/assets/flowDiagram-HODETNUW-Cdlq3cJL.js +1 -0
- package/out/web/assets/{ganttDiagram-EL5Y4UJY-Da4ULXTl.js → ganttDiagram-EL5Y4UJY-DQLdsXrC.js} +1 -1
- package/out/web/assets/{gitGraph-4MIJSDKK-B9wNFZIP.js → gitGraph-4MIJSDKK-CL1OEOQ_.js} +1 -1
- package/out/web/assets/{gitGraphDiagram-WWUBYQGX-RjrhB0MP.js → gitGraphDiagram-WWUBYQGX-8cSi42H7.js} +1 -1
- package/out/web/assets/index-BocxnrDG.js +168 -0
- package/out/web/assets/index-yeh_r55a.css +1 -0
- package/out/web/assets/{info-A6RAGUB7-B3dfCpeY.js → info-A6RAGUB7-C1zsX5sc.js} +1 -1
- package/out/web/assets/{infoDiagram-27XIBGKW-QVWkLVx7.js → infoDiagram-27XIBGKW-DsRYtIyI.js} +1 -1
- package/out/web/assets/{ishikawaDiagram-5VMMS53U-CLFNIRI9.js → ishikawaDiagram-5VMMS53U-B2crwIXj.js} +1 -1
- package/out/web/assets/{journeyDiagram-3NMN7TZE-fgOQadYF.js → journeyDiagram-3NMN7TZE-BTH-GyVR.js} +1 -1
- package/out/web/assets/{kanban-definition-UXKFOSKX-G3Q_9g9O.js → kanban-definition-UXKFOSKX-BeRfCnuA.js} +1 -1
- package/out/web/assets/{mermaid-parser.core-v3Jm7nco.js → mermaid-parser.core-BJwgCBBc.js} +3 -3
- package/out/web/assets/{mermaid.core-DTEjLUA1.js → mermaid.core-CsDDodN8.js} +4 -4
- package/out/web/assets/{mindmap-definition-YA3MSWOX-C_D42_fF.js → mindmap-definition-YA3MSWOX-B42efOQ1.js} +1 -1
- package/out/web/assets/{modelManager-BPLycQrN.js → modelManager-B3ZFMh8v.js} +2 -2
- package/out/web/assets/{oauthManager-BUK7ic7q.js → oauthManager-Ch5Du2fF.js} +1 -1
- package/out/web/assets/{packet-AYTQ26CC-B-R-SHlF.js → packet-AYTQ26CC-B6xUyE6r.js} +1 -1
- package/out/web/assets/{pegDiagram-XKGWAZYB-Da2q_giz.js → pegDiagram-XKGWAZYB-1LabVycL.js} +1 -1
- package/out/web/assets/{pie-WAS4IAKB-BC8F0N7-.js → pie-WAS4IAKB-DJwo-nf_.js} +1 -1
- package/out/web/assets/{pieDiagram-E7YTZNPT-Iy9REJNC.js → pieDiagram-E7YTZNPT-icmAqzy4.js} +1 -1
- package/out/web/assets/{quadrantDiagram-AXDQQJYC-ztsiYw7h.js → quadrantDiagram-AXDQQJYC-CZ-wEx41.js} +1 -1
- package/out/web/assets/{radar-RG4KPBEZ-B_1P8Z_z.js → radar-RG4KPBEZ-D7d-Xriw.js} +1 -1
- package/out/web/assets/{railroad-74A4TZTK-DuTqczSo.js → railroad-74A4TZTK-viuhdYbs.js} +1 -1
- package/out/web/assets/{railroad-abnf-HS5TGJTU-Df29WLmp.js → railroad-abnf-HS5TGJTU-DNXiCNea.js} +1 -1
- package/out/web/assets/{railroad-ebnf-LZEXJU2U-ePFMo-qp.js → railroad-ebnf-LZEXJU2U-nkSu1-0C.js} +1 -1
- package/out/web/assets/{railroad-peg-WCYAUIDC-DYXLW4_E.js → railroad-peg-WCYAUIDC-BhbMeQzz.js} +1 -1
- package/out/web/assets/{railroadDiagram-O6MQD6OU-LStnZf2S.js → railroadDiagram-O6MQD6OU-CLpG1jUX.js} +1 -1
- package/out/web/assets/{requirementDiagram-BXWQKSXE-DmzonMH-.js → requirementDiagram-BXWQKSXE-kRDrvWrc.js} +1 -1
- package/out/web/assets/{sankeyDiagram-P5KCCOFB-B8vjmUU1.js → sankeyDiagram-P5KCCOFB-DxAwmq4x.js} +1 -1
- package/out/web/assets/{sequenceDiagram-WJ2MYXX4-CTnplOAX.js → sequenceDiagram-WJ2MYXX4-aLgY-6Wz.js} +1 -1
- package/out/web/assets/{stateDiagram-D77RDMKH-BFrIaLdI.js → stateDiagram-D77RDMKH-D5QWfOhp.js} +1 -1
- package/out/web/assets/stateDiagram-v2-MP3YSRHH-D6OiZoAP.js +1 -0
- package/out/web/assets/{swimlanes-42K2YHIH-BXGngUNU.js → swimlanes-42K2YHIH-CRs5UyA-.js} +1 -1
- package/out/web/assets/swimlanesDiagram-VR7AAH4N-UgEXIsV0.js +8 -0
- package/out/web/assets/syntaxHighlight-C-wAGT84.js +3 -0
- package/out/web/assets/{timeline-definition-24CTP7MA-CegH7n43.js → timeline-definition-24CTP7MA-Denqd9qb.js} +1 -1
- package/out/web/assets/{treeView-Q6P3EWNA-DTpJv2Tw.js → treeView-Q6P3EWNA-0my3Z3s4.js} +1 -1
- package/out/web/assets/{treemap-WGGIJYW6-i8M9iJU6.js → treemap-WGGIJYW6-B_EjkLYq.js} +1 -1
- package/out/web/assets/{vennDiagram-4TSXK5OY-Dpzo1S8U.js → vennDiagram-4TSXK5OY-DxVg3Ewr.js} +1 -1
- package/out/web/assets/{wardley-WFR3VGLG-BrNsgtfw.js → wardley-WFR3VGLG-CGD-Tj4p.js} +1 -1
- package/out/web/assets/{wardleyDiagram-VM6X3IG4-lQ92LFE1.js → wardleyDiagram-VM6X3IG4-CmfS39Xm.js} +1 -1
- package/out/web/assets/{xychartDiagram-S5SC5T6Z-BaOaEoBd.js → xychartDiagram-S5SC5T6Z-BVM9bKis.js} +1 -1
- package/out/web/index-web.html +2 -2
- package/out/web/index.html +2 -2
- package/package.json +2 -2
- package/shared-modules/CODEOWNERS +3 -3
- package/shared-modules/SDL2/SDL2_mixer.json +27 -0
- package/shared-modules/appstream/README.md +13 -0
- package/shared-modules/appstream/appstream-compose.json +111 -0
- package/shared-modules/dbus-glib/dbus-glib.json +2 -2
- package/shared-modules/libportal/libportal.json +23 -0
- package/shared-modules/linux-audio/fluidsynth2-static.json +2 -2
- package/shared-modules/linux-audio/fluidsynth2.json +2 -2
- package/shared-modules/linux-audio/libinstpatch.json +2 -2
- package/shared-modules/linux-audio/liblo-static.json +3 -0
- package/shared-modules/luajit/luajit.json +2 -2
- package/shared-modules/squashfs-tools/squashfs-tools.json +0 -3
- package/slash-commands.json +7 -2
- package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/src/renderer/shared/agentProgress.ts +51 -3
- package/src/renderer/shared/components/Settings/AutomationTab.tsx +2 -56
- package/src/renderer/shared/components/Settings/AvatarCropModal.tsx +15 -1
- package/src/renderer/shared/components/Settings/GeneralTab.tsx +25 -12
- package/src/renderer/shared/components/Settings/ThemeTab.test.tsx +45 -0
- package/src/renderer/shared/components/Settings/ThemeTab.tsx +106 -3
- package/src/renderer/shared/components/activity/AgentActivityDrawer.tsx +67 -17
- package/src/renderer/shared/components/activity/AgentActivityTable.tsx +376 -134
- package/src/renderer/shared/components/activity/AskUserActivityCard.test.tsx +122 -0
- package/src/renderer/shared/components/activity/AskUserActivityCard.tsx +70 -0
- package/src/renderer/shared/components/activity/McpToolResults.tsx +39 -2
- package/src/renderer/shared/components/activity/SubagentDagView.tsx +7 -7
- package/src/renderer/shared/components/activity/ViewedImages.tsx +41 -2
- package/src/renderer/shared/components/app/ChunkErrorBoundary.tsx +3 -3
- package/src/renderer/shared/components/app/CommandPalette.tsx +7 -7
- package/src/renderer/shared/components/app/DesktopTitlebar.tsx +2 -2
- package/src/renderer/shared/components/app/MintDashboard.tsx +27 -17
- package/src/renderer/shared/components/app/ModalErrorBoundary.tsx +3 -3
- package/src/renderer/shared/components/cards/CalculationCard.tsx +3 -3
- package/src/renderer/shared/components/cards/ImageGenCard.tsx +2 -2
- package/src/renderer/shared/components/cards/ImageSearchCard.tsx +2 -2
- package/src/renderer/shared/components/cards/MermaidCard.tsx +1 -1
- package/src/renderer/shared/components/cards/PlanCard.test.tsx +41 -0
- package/src/renderer/shared/components/cards/PlanCard.tsx +32 -0
- package/src/renderer/shared/components/cards/ReleaseNotesCard.test.tsx +29 -0
- package/src/renderer/shared/components/cards/ReleaseNotesCard.tsx +137 -0
- package/src/renderer/shared/components/cards/StockCard.tsx +7 -7
- package/src/renderer/shared/components/cards/ToolResultCards.tsx +11 -3
- package/src/renderer/shared/components/cards/UiMockupWidget.tsx +4 -4
- package/src/renderer/shared/components/cards/WeatherCard.tsx +5 -5
- package/src/renderer/shared/components/chat/ApprovalCard.test.tsx +11 -0
- package/src/renderer/shared/components/chat/ApprovalCard.tsx +21 -2
- package/src/renderer/shared/components/chat/ArtifactCards.test.tsx +69 -0
- package/src/renderer/shared/components/chat/ArtifactCards.tsx +238 -0
- package/src/renderer/shared/components/chat/AttachedDocumentCard.test.tsx +39 -0
- package/src/renderer/shared/components/chat/AttachedDocumentCard.tsx +220 -0
- package/src/renderer/shared/components/chat/ChatCodeBlock.test.tsx +33 -0
- package/src/renderer/shared/components/chat/ChatCodeBlock.tsx +15 -1
- package/src/renderer/shared/components/chat/ChatMessageItem.test.tsx +40 -2
- package/src/renderer/shared/components/chat/ChatMessageItem.tsx +180 -34
- package/src/renderer/shared/components/chat/ChatModeSelector.css +227 -0
- package/src/renderer/shared/components/chat/ChatModeSelector.test.tsx +45 -0
- package/src/renderer/shared/components/chat/ChatModeSelector.tsx +198 -0
- package/src/renderer/shared/components/chat/ChatPanel.test.tsx +22 -0
- package/src/renderer/shared/components/chat/ChatPanel.tsx +227 -53
- package/src/renderer/shared/components/chat/DiffReviewModal.tsx +6 -6
- package/src/renderer/shared/components/chat/GeminiLiveOverlay.test.tsx +142 -1
- package/src/renderer/shared/components/chat/GeminiLiveOverlay.tsx +45 -3
- package/src/renderer/shared/components/chat/ModelSelectorPopover.tsx +42 -14
- package/src/renderer/shared/components/chat/RewindModal.tsx +10 -10
- package/src/renderer/shared/components/chat/SlashSuggestions.test.tsx +14 -0
- package/src/renderer/shared/components/chat/SourcesBlock.test.tsx +53 -0
- package/src/renderer/shared/components/chat/SourcesBlock.tsx +236 -90
- package/src/renderer/shared/components/chat/TurnFileChanges.test.tsx +45 -1
- package/src/renderer/shared/components/chat/TurnFileChanges.tsx +202 -169
- package/src/renderer/shared/components/extensions/CliSessionsView.tsx +5 -4
- package/src/renderer/shared/components/extensions/McpRegistryPicker.tsx +3 -3
- package/src/renderer/shared/components/studio/LibraryPage.tsx +48 -5
- package/src/renderer/shared/components/workspace/ArtifactPreviewPanel.tsx +125 -13
- package/src/renderer/shared/components/workspace/CodeReviewPage.test.tsx +50 -0
- package/src/renderer/shared/components/workspace/CodeReviewPage.tsx +77 -16
- package/src/renderer/shared/components/workspace/GitBranchSelector.tsx +78 -22
- package/src/renderer/shared/components/workspace/ToolSurfacePage.test.tsx +13 -0
- package/src/renderer/shared/components/workspace/ToolSurfacePage.tsx +48 -28
- package/src/renderer/shared/components/workspace/WorkspacePdfPreview.tsx +266 -47
- package/src/renderer/shared/components/workspace/WorkspacePreviewActions.test.tsx +18 -6
- package/src/renderer/shared/components/workspace/background-terminals.css +11 -11
- package/src/renderer/shared/constants/config.ts +1 -5
- package/src/renderer/shared/constants/models.test.ts +51 -0
- package/src/renderer/shared/constants/models.ts +139 -32
- package/src/renderer/shared/conversation/lifecycle.test.ts +91 -3
- package/src/renderer/shared/conversation/lifecycle.ts +104 -5
- package/src/renderer/shared/conversation/progressiveStream.test.ts +83 -0
- package/src/renderer/shared/conversation/progressiveStream.ts +219 -0
- package/src/renderer/shared/css/agent-observability.css +133 -0
- package/src/renderer/shared/css/chat-ui-cards.css +13 -13
- package/src/renderer/shared/css/code-review.css +63 -34
- package/src/renderer/shared/css/code-sessions.css +47 -18
- package/src/renderer/shared/css/collapsed-paste.css +1 -1
- package/src/renderer/shared/css/error-notice.css +1 -1
- package/src/renderer/shared/css/file-changes.css +165 -80
- package/src/renderer/shared/css/library.css +105 -10
- package/src/renderer/shared/css/management-views.css +1 -1
- package/src/renderer/shared/css/mcp-results.css +5 -5
- package/src/renderer/shared/css/model-selector.css +18 -18
- package/src/renderer/shared/css/mods.css +7 -7
- package/src/renderer/shared/css/notification-notice.css +2 -2
- package/src/renderer/shared/css/queued-messages.css +5 -5
- package/src/renderer/shared/css/settings/general.css +18 -13
- package/src/renderer/shared/css/settings/profile.css +5 -5
- package/src/renderer/shared/css/settings/theme.css +5 -5
- package/src/renderer/shared/css/slash-choice.css +2 -2
- package/src/renderer/shared/css/slash-suggestions.css +3 -3
- package/src/renderer/shared/css/sources-block.css +41 -2
- package/src/renderer/shared/css/tools-panel.css +2 -2
- package/src/renderer/shared/css/ui-tailwind.css +8 -0
- package/src/renderer/shared/css/veo-studio.css +2 -2
- package/src/renderer/shared/css/viewed-images.css +7 -7
- package/src/renderer/shared/platform.ts +2 -1
- package/src/renderer/shared/theme/themeManager.test.ts +17 -0
- package/src/renderer/shared/theme/themeManager.ts +21 -4
- package/src/renderer/shared/theme/themes.ts +54 -0
- package/src/renderer/shared/theme/tokens.css +6 -4
- package/src/renderer/shared/types.ts +1 -1
- package/src/renderer/shared/utils/agentActivity.test.ts +468 -2
- package/src/renderer/shared/utils/agentActivity.ts +602 -35
- package/src/renderer/shared/utils/artifactBrowser.test.ts +37 -32
- package/src/renderer/shared/utils/artifactBrowser.ts +17 -19
- package/src/renderer/shared/utils/attachedDocument.test.ts +73 -0
- package/src/renderer/shared/utils/attachedDocument.ts +49 -0
- package/src/renderer/shared/utils/fileIcons.test.ts +59 -0
- package/src/renderer/shared/utils/fileIcons.ts +149 -16
- package/src/renderer/shared/utils/markdown.tsx +72 -19
- package/src/renderer/shared/utils/markdown.webImages.test.tsx +16 -0
- package/src/renderer/shared/utils/mcpResults.test.tsx +29 -0
- package/src/renderer/shared/utils/slashCommandProcessor.ts +4 -1
- package/src/renderer/shared/utils/syntaxHighlight.test.tsx +11 -1
- package/src/renderer/shared/utils/syntaxHighlight.tsx +51 -3
- package/src/renderer/shared/utils/useGeminiLiveVoice.ts +324 -51
- package/src/renderer/shared/utils/workspaceDrag.test.ts +153 -0
- package/src/renderer/shared/utils/workspaceDrag.ts +142 -0
- package/src/renderer/shared/utils/workspaceMedia.ts +10 -2
- package/src/renderer/src/App.tsx +14 -8
- package/src/renderer/src/agentProgress.ts +1 -0
- package/src/renderer/src/components/ScreenPicker.css +1 -1
- package/src/renderer/src/components/WorkspacePanel.tsx +6 -5
- package/src/renderer/src/css/app-shell.css +4 -4
- package/src/renderer/src/css/chat.css +1261 -241
- package/src/renderer/src/css/desktop-tools.css +11 -11
- package/src/renderer/src/css/sidebar-workspace.css +2 -2
- package/src/renderer/src/css/tool-surfaces.css +12 -12
- package/src/renderer/src/index.css +38 -20
- package/src/renderer/src/tauri.ts +22 -2
- package/src/renderer/src-web/App.tsx +14 -0
- package/src/renderer/src-web/agentProgress.ts +1 -0
- package/src/renderer/src-web/css/app-shell.css +4 -4
- package/src/renderer/src-web/css/chat.css +1278 -190
- package/src/renderer/src-web/css/mobile.css +29 -13
- package/src/renderer/src-web/css/settings/mobile.css +2 -2
- package/src/renderer/src-web/css/sidebar-workspace.css +1 -1
- package/src/renderer/src-web/index.css +38 -20
- package/src/renderer/src-web/tauri.ts +22 -2
- package/src/renderer/tests/browserArtifacts.browser.tsx +1 -1
- package/src/renderer/tests/library.browser.tsx +3 -0
- package/src/renderer/tests/liveVoice.browser.html +1 -0
- package/src/renderer/tests/workspaceDrop.browser.tsx +1 -1
- package/src-tauri/src/app/lifecycle.rs +1 -14
- package/src-tauri/src/background/headless.rs +14 -8
- package/src-tauri/src/commands/conversation.rs +38 -0
- package/src-tauri/src/native/mod.rs +0 -1
- package/src-tauri/src/native/system.rs +4 -47
- package/crates/mint-cli/src/interactive/chat_tui.rs +0 -8855
- package/crates/mint-core/src/agent/chat.rs +0 -4394
- package/crates/mint-core/src/browser/lifecycle.rs +0 -291
- package/crates/mint-core/src/browser/logging.rs +0 -20
- package/crates/mint-core/src/orchestration/tools/browser.rs +0 -31
- package/out/web/assets/ArtifactPreviewPanel-S0dPfYFx.js +0 -2
- package/out/web/assets/CalculationCard-CRjP3E1j.js +0 -1
- package/out/web/assets/CliSessionsView-C3Br2v5Y.css +0 -1
- package/out/web/assets/CodeReviewPage-CUJceQme.css +0 -1
- package/out/web/assets/CodeReviewPage-VCUgPla_.js +0 -2
- package/out/web/assets/ImageGenCard-BCXxkVJX.js +0 -1
- package/out/web/assets/ImageSearchCard-N3yQOr8Z.js +0 -1
- package/out/web/assets/LibraryPage-DAxUTAg7.css +0 -1
- package/out/web/assets/LibraryPage-WlviRdR8.js +0 -2
- package/out/web/assets/LinkedFoldersView-CKRgroWK.js +0 -1
- package/out/web/assets/McpServersView-D3wHnuHg.js +0 -1
- package/out/web/assets/MermaidCard-NqrHdyJW.js +0 -2
- package/out/web/assets/MintDashboard-B2DJsDDO.css +0 -1
- package/out/web/assets/MintDashboard-COw_xol7.js +0 -63
- package/out/web/assets/RewindModal-DempuDod.js +0 -1
- package/out/web/assets/SettingsWindow-2YP3yj7s.js +0 -4
- package/out/web/assets/SettingsWindow-Dz8LQVgD.css +0 -1
- package/out/web/assets/StockCard-BDMCe7mx.js +0 -1
- package/out/web/assets/ToolSurfacePage-B0J_q0Jl.js +0 -38
- package/out/web/assets/ToolSurfacePage-CRIQ1owD.css +0 -1
- package/out/web/assets/UiMockupWidget-DD73QWg1.js +0 -1
- package/out/web/assets/WeatherCard-CPucIfq8.js +0 -1
- package/out/web/assets/WorkspacePdfPreview-Dx_lkjNu.js +0 -2
- package/out/web/assets/classDiagram-ZZMXUADV-BgJ5WtSp.js +0 -1
- package/out/web/assets/classDiagram-v2-VYDZK3BY-BgJ5WtSp.js +0 -1
- package/out/web/assets/config-CvIO5hkn.js +0 -2
- package/out/web/assets/flowDiagram-HODETNUW-uqCHJond.js +0 -1
- package/out/web/assets/index-BmpulIkg.js +0 -168
- package/out/web/assets/index-CkvBwhX_.css +0 -1
- package/out/web/assets/stateDiagram-v2-MP3YSRHH-uf3kdNW4.js +0 -1
- package/out/web/assets/swimlanesDiagram-VR7AAH4N-Y8q5bQce.js +0 -8
- package/shared-modules/libsecret/libsecret.json +0 -23
- package/shared-modules/libusb/libusb.json +0 -27
- package/shared-modules/lzo/lzo.json +0 -27
- package/src/renderer/src/components/BrowserShell.tsx +0 -108
- package/src/renderer/src/components/BrowserSuggestionsPopup.tsx +0 -91
- package/src/renderer/src/components/browser-shell.css +0 -185
- package/src-tauri/src/native/browser.rs +0 -252
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
getModelMetadata,
|
|
4
|
+
hasNativeThinkingSupport,
|
|
5
|
+
isNonTextModel,
|
|
6
|
+
} from './models'
|
|
7
|
+
|
|
8
|
+
describe('models thinking classification', () => {
|
|
9
|
+
it('excludes only non-text media and embedding models from thinking', () => {
|
|
10
|
+
expect(isNonTextModel('nano-banana-pro-preview')).toBe(true)
|
|
11
|
+
expect(isNonTextModel('gemini-nano-banana-2.1')).toBe(true)
|
|
12
|
+
expect(isNonTextModel('gemini-3.1-flash-image')).toBe(true)
|
|
13
|
+
expect(isNonTextModel('dall-e-3')).toBe(true)
|
|
14
|
+
expect(isNonTextModel('text-embedding-3-large')).toBe(true)
|
|
15
|
+
expect(isNonTextModel('whisper-1')).toBe(true)
|
|
16
|
+
expect(isNonTextModel('gemini-2.5-flash-native-audio-preview-12-2025')).toBe(true)
|
|
17
|
+
|
|
18
|
+
expect(getModelMetadata('nano-banana-pro-preview', 'gemini').supportsThinking).toBe(false)
|
|
19
|
+
expect(getModelMetadata('gemini-nano-banana-2.1', 'gemini').supportsThinking).toBe(false)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('allows thinking on all text and chat models across providers', () => {
|
|
23
|
+
expect(getModelMetadata('deepseek-flash', 'deepseek').supportsThinking).toBe(true)
|
|
24
|
+
expect(getModelMetadata('deepseek-v4-pro', 'deepseek').supportsThinking).toBe(true)
|
|
25
|
+
expect(getModelMetadata('deepseek-chat', 'deepseek').supportsThinking).toBe(true)
|
|
26
|
+
expect(getModelMetadata('gemini-2.5-flash', 'gemini').supportsThinking).toBe(true)
|
|
27
|
+
expect(getModelMetadata('gemini-1.5-flash', 'gemini').supportsThinking).toBe(true)
|
|
28
|
+
expect(getModelMetadata('gpt-4o', 'openai').supportsThinking).toBe(true)
|
|
29
|
+
expect(getModelMetadata('claude-haiku-4.5', 'anthropic').supportsThinking).toBe(true)
|
|
30
|
+
expect(getModelMetadata('meta-llama/llama-3.3-70b-instruct', 'openrouter').supportsThinking).toBe(true)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('distinguishes native reasoning API models from prompted CoT models', () => {
|
|
34
|
+
expect(hasNativeThinkingSupport('deepseek-flash', 'deepseek')).toBe(true)
|
|
35
|
+
expect(hasNativeThinkingSupport('deepseek-v4-pro', 'deepseek')).toBe(true)
|
|
36
|
+
expect(hasNativeThinkingSupport('deepseek-reasoner', 'deepseek')).toBe(true)
|
|
37
|
+
expect(hasNativeThinkingSupport('deepseek-chat', 'deepseek')).toBe(false)
|
|
38
|
+
|
|
39
|
+
expect(hasNativeThinkingSupport('gemini-2.5-flash', 'gemini')).toBe(true)
|
|
40
|
+
expect(hasNativeThinkingSupport('gemini-3.6-flash', 'gemini')).toBe(true)
|
|
41
|
+
expect(hasNativeThinkingSupport('gemini-1.5-flash', 'gemini')).toBe(false)
|
|
42
|
+
|
|
43
|
+
expect(hasNativeThinkingSupport('gpt-5.6-terra', 'openai')).toBe(true)
|
|
44
|
+
expect(hasNativeThinkingSupport('o3-mini', 'openai')).toBe(true)
|
|
45
|
+
expect(hasNativeThinkingSupport('gpt-4o', 'openai')).toBe(false)
|
|
46
|
+
|
|
47
|
+
expect(hasNativeThinkingSupport('claude-sonnet-5', 'anthropic')).toBe(true)
|
|
48
|
+
expect(hasNativeThinkingSupport('claude-haiku-4.5', 'anthropic')).toBe(true)
|
|
49
|
+
expect(hasNativeThinkingSupport('claude-3-5-sonnet', 'anthropic')).toBe(false)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -82,6 +82,8 @@ export function isFreeModel(modelId: string): boolean {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export const DEEPSEEK_MODELS = [
|
|
85
|
+
'deepseek-flash',
|
|
86
|
+
'deepseek-v4-pro',
|
|
85
87
|
'deepseek-chat',
|
|
86
88
|
'deepseek-reasoner',
|
|
87
89
|
] as const
|
|
@@ -227,6 +229,18 @@ export const MODEL_METADATA: Record<string, ModelMeta> = {
|
|
|
227
229
|
supportsThinking: true,
|
|
228
230
|
},
|
|
229
231
|
|
|
232
|
+
// Gemini Image Generation (Nano Banana)
|
|
233
|
+
'nano-banana-pro-preview': {
|
|
234
|
+
description: 'Google Gemini Nano Banana Pro model for AI image generation and photo editing.',
|
|
235
|
+
contextWindow: '32K Context',
|
|
236
|
+
supportsThinking: false,
|
|
237
|
+
},
|
|
238
|
+
'gemini-nano-banana-2.1': {
|
|
239
|
+
description: 'Google Gemini Nano Banana 2.1 model for fast image generation and visual editing.',
|
|
240
|
+
contextWindow: '32K Context',
|
|
241
|
+
supportsThinking: false,
|
|
242
|
+
},
|
|
243
|
+
|
|
230
244
|
// Anthropic Claude
|
|
231
245
|
'claude-sonnet-5': {
|
|
232
246
|
description: 'Anthropic flagship hybrid model with extended thinking and deep coding comprehension.',
|
|
@@ -251,7 +265,7 @@ export const MODEL_METADATA: Record<string, ModelMeta> = {
|
|
|
251
265
|
'claude-haiku-4.5': {
|
|
252
266
|
description: 'Fast and responsive Claude model for lightweight conversational turns.',
|
|
253
267
|
contextWindow: '200K Context',
|
|
254
|
-
supportsThinking:
|
|
268
|
+
supportsThinking: true,
|
|
255
269
|
},
|
|
256
270
|
|
|
257
271
|
// OpenAI
|
|
@@ -283,37 +297,139 @@ export const MODEL_METADATA: Record<string, ModelMeta> = {
|
|
|
283
297
|
'gpt-4o': {
|
|
284
298
|
description: 'OpenAI versatile multimodal model for text, vision, and high-speed chat.',
|
|
285
299
|
contextWindow: '128K Context',
|
|
286
|
-
supportsThinking:
|
|
300
|
+
supportsThinking: true,
|
|
287
301
|
},
|
|
288
302
|
'gpt-4o-mini': {
|
|
289
303
|
description: 'Compact and fast OpenAI model for everyday conversational tasks.',
|
|
290
304
|
contextWindow: '128K Context',
|
|
291
|
-
supportsThinking:
|
|
305
|
+
supportsThinking: true,
|
|
292
306
|
},
|
|
293
307
|
|
|
294
308
|
// DeepSeek
|
|
309
|
+
'deepseek-flash': {
|
|
310
|
+
description: 'DeepSeek V4.1 Flash (1M context) supporting both thinking and fast non-thinking modes.',
|
|
311
|
+
contextWindow: '1M Context',
|
|
312
|
+
supportsThinking: true,
|
|
313
|
+
},
|
|
295
314
|
'deepseek-v4-flash': {
|
|
296
|
-
description: 'High-throughput DeepSeek model
|
|
297
|
-
contextWindow: '
|
|
298
|
-
supportsThinking:
|
|
315
|
+
description: 'High-throughput DeepSeek V4 Flash model supporting both thinking and non-thinking modes.',
|
|
316
|
+
contextWindow: '1M Context',
|
|
317
|
+
supportsThinking: true,
|
|
299
318
|
},
|
|
300
319
|
'deepseek-v4-pro': {
|
|
301
|
-
description: 'Advanced DeepSeek model featuring deep multilingual and coding reasoning.',
|
|
302
|
-
contextWindow: '
|
|
320
|
+
description: 'Advanced DeepSeek V4 Pro model (1M context) featuring deep multilingual and coding reasoning.',
|
|
321
|
+
contextWindow: '1M Context',
|
|
303
322
|
supportsThinking: true,
|
|
304
323
|
},
|
|
305
324
|
'deepseek-reasoner': {
|
|
306
|
-
description: 'DeepSeek native reasoning model (R1
|
|
307
|
-
contextWindow: '
|
|
325
|
+
description: 'DeepSeek native reasoning model (R1 / V3.2 thinking) with comprehensive chain-of-thought.',
|
|
326
|
+
contextWindow: '128K Context',
|
|
308
327
|
supportsThinking: true,
|
|
309
328
|
},
|
|
310
329
|
'deepseek-chat': {
|
|
311
|
-
description: 'General-purpose DeepSeek
|
|
312
|
-
contextWindow: '
|
|
313
|
-
supportsThinking:
|
|
330
|
+
description: 'General-purpose DeepSeek conversational and coding model.',
|
|
331
|
+
contextWindow: '128K Context',
|
|
332
|
+
supportsThinking: true,
|
|
314
333
|
},
|
|
315
334
|
}
|
|
316
335
|
|
|
336
|
+
/**
|
|
337
|
+
* Returns true if the model is strictly a non-text/media/embedding model
|
|
338
|
+
* (image generation, TTS, audio transcription, embeddings) that cannot use
|
|
339
|
+
* Chain-of-Thought reasoning.
|
|
340
|
+
*/
|
|
341
|
+
export function isNonTextModel(modelId: string): boolean {
|
|
342
|
+
const m = modelId.trim().toLowerCase()
|
|
343
|
+
const clean = m.split('/').pop() || m
|
|
344
|
+
return (
|
|
345
|
+
clean.includes('banana') ||
|
|
346
|
+
clean.includes('imagen') ||
|
|
347
|
+
clean.includes('image') ||
|
|
348
|
+
clean.includes('dall-e') ||
|
|
349
|
+
clean.includes('flux') ||
|
|
350
|
+
clean.includes('sdxl') ||
|
|
351
|
+
clean.includes('sd3') ||
|
|
352
|
+
clean.includes('ideogram') ||
|
|
353
|
+
clean.includes('veo-') ||
|
|
354
|
+
clean.includes('tts') ||
|
|
355
|
+
clean.includes('whisper') ||
|
|
356
|
+
clean.includes('embed') ||
|
|
357
|
+
clean.includes('aqa') ||
|
|
358
|
+
clean.includes('moderation') ||
|
|
359
|
+
clean.includes('native-audio') ||
|
|
360
|
+
clean.includes('-live-') ||
|
|
361
|
+
clean.endsWith('-live')
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Returns true if the provider & model expose a native API parameter for visible
|
|
367
|
+
* reasoning tokens (e.g. Gemini 2.5+/3 thinkingConfig, DeepSeek V4/R1 thinking,
|
|
368
|
+
* OpenAI o1/o3/o4/gpt-5 reasoning_effort, Claude 3.7/4/5 extended thinking).
|
|
369
|
+
* Models where this returns false can still use Thinking via Prompt-based CoT (<think>).
|
|
370
|
+
*/
|
|
371
|
+
export function hasNativeThinkingSupport(modelId: string, provider?: string): boolean {
|
|
372
|
+
if (isNonTextModel(modelId)) return false
|
|
373
|
+
const p = (provider || '').trim().toLowerCase()
|
|
374
|
+
const m = modelId.trim().toLowerCase()
|
|
375
|
+
const clean = m.split('/').pop() || m
|
|
376
|
+
|
|
377
|
+
if (clean.includes('gemini') || p === 'gemini') {
|
|
378
|
+
return (
|
|
379
|
+
clean.startsWith('gemini-2.5') ||
|
|
380
|
+
clean.startsWith('gemini-3') ||
|
|
381
|
+
clean.includes('thinking')
|
|
382
|
+
)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (clean.includes('claude') || p === 'anthropic') {
|
|
386
|
+
return (
|
|
387
|
+
clean.includes('3-7') ||
|
|
388
|
+
clean.includes('3.7') ||
|
|
389
|
+
clean.includes('sonnet-4') ||
|
|
390
|
+
clean.includes('opus-4') ||
|
|
391
|
+
clean.includes('haiku-4') ||
|
|
392
|
+
clean.includes('claude-4') ||
|
|
393
|
+
clean.includes('sonnet-5') ||
|
|
394
|
+
clean.includes('opus-5') ||
|
|
395
|
+
clean.includes('haiku-5') ||
|
|
396
|
+
clean.includes('thinking')
|
|
397
|
+
)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (clean.includes('deepseek') || p === 'deepseek') {
|
|
401
|
+
return (
|
|
402
|
+
clean.includes('reasoner') ||
|
|
403
|
+
clean.includes('r1') ||
|
|
404
|
+
clean.includes('v4') ||
|
|
405
|
+
clean.includes('flash') ||
|
|
406
|
+
clean.includes('pro') ||
|
|
407
|
+
clean.includes('thinking')
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (
|
|
412
|
+
clean.startsWith('o1') ||
|
|
413
|
+
clean.startsWith('o3') ||
|
|
414
|
+
clean.startsWith('o4') ||
|
|
415
|
+
clean.startsWith('gpt-5')
|
|
416
|
+
) {
|
|
417
|
+
return true
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return (
|
|
421
|
+
clean.includes('thinking') ||
|
|
422
|
+
clean.includes('reason') ||
|
|
423
|
+
clean.includes('qwq') ||
|
|
424
|
+
clean.includes('r1') ||
|
|
425
|
+
clean.includes('qwen3') ||
|
|
426
|
+
clean.includes('glm-5') ||
|
|
427
|
+
clean.includes('glm-4.5') ||
|
|
428
|
+
clean.includes('grok-3-mini') ||
|
|
429
|
+
clean.includes('grok-4')
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
|
|
317
433
|
export function getModelMetadata(modelId: string, provider?: string): ModelMeta {
|
|
318
434
|
const clean = modelId.trim()
|
|
319
435
|
const lower = clean.toLowerCase()
|
|
@@ -331,29 +447,20 @@ export function getModelMetadata(modelId: string, provider?: string): ModelMeta
|
|
|
331
447
|
contextWindow = lower.includes('pro') ? '2M Context' : '1M Context'
|
|
332
448
|
} else if (lower.includes('claude') || provider === 'anthropic') {
|
|
333
449
|
contextWindow = '200K Context'
|
|
450
|
+
} else if (lower.includes('deepseek-flash') || lower.includes('deepseek-v4')) {
|
|
451
|
+
contextWindow = '1M Context'
|
|
334
452
|
} else if (lower.includes('llama-3') || lower.includes('qwen')) {
|
|
335
453
|
contextWindow = '128K Context'
|
|
336
454
|
}
|
|
337
455
|
|
|
338
|
-
const supportsThinking =
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
lower.includes('opus-5') ||
|
|
347
|
-
lower.includes('3-7') ||
|
|
348
|
-
lower.includes('2.5') ||
|
|
349
|
-
lower.includes('glm') ||
|
|
350
|
-
lower.includes('deepseek') ||
|
|
351
|
-
lower.includes('qwen') ||
|
|
352
|
-
(provider === 'gemini')
|
|
353
|
-
|
|
354
|
-
const description = supportsThinking
|
|
355
|
-
? `${clean} supports advanced multi-step reasoning and dynamic thinking effort.`
|
|
356
|
-
: `${clean} is an optimized model for responsive conversational interactions.`
|
|
456
|
+
const supportsThinking = !isNonTextModel(clean)
|
|
457
|
+
const hasNative = hasNativeThinkingSupport(clean, provider)
|
|
458
|
+
|
|
459
|
+
const description = !supportsThinking
|
|
460
|
+
? `${clean} is a specialized media or embedding model.`
|
|
461
|
+
: hasNative
|
|
462
|
+
? `${clean} supports native multi-step reasoning and dynamic thinking effort.`
|
|
463
|
+
: `${clean} is an optimized conversational model supporting prompted chain-of-thought reasoning.`
|
|
357
464
|
|
|
358
465
|
return { description, contextWindow, supportsThinking }
|
|
359
466
|
}
|
|
@@ -18,7 +18,7 @@ const reply: ChatResponse = { provider: 'system', model: 'test', text: 'complete
|
|
|
18
18
|
const activity: AgentProgress = { type: 'ToolStart', data: { action: 'read_file', input: { path: 'file.txt' } } }
|
|
19
19
|
|
|
20
20
|
function setup(overrides: Partial<ConversationTransport> = {}, callbacks: ConversationEffects = {}, drafts?: { read(id: string): string; write(id: string, text: string): void }) {
|
|
21
|
-
const streams: Array<{ chatId: string; onProgress: (p: AgentProgress) => void; onApproval: (p: PendingApproval) => void; onStarted: (id: number) => void; result: ReturnType<typeof deferred<ChatResponse>> }> = []
|
|
21
|
+
const streams: Array<{ chatId: string; onChunk: (chunk: string) => void; onProgress: (p: AgentProgress) => void; onApproval: (p: PendingApproval) => void; onStarted: (id: number) => void; result: ReturnType<typeof deferred<ChatResponse>> }> = []
|
|
22
22
|
const saved: Array<{ id: number; progress: AgentProgress[] }> = []
|
|
23
23
|
const submitted: Array<{ token: string; approved: boolean; answer?: string }> = []
|
|
24
24
|
const transport: ConversationTransport = {
|
|
@@ -28,10 +28,11 @@ function setup(overrides: Partial<ConversationTransport> = {}, callbacks: Conver
|
|
|
28
28
|
saveInteractionAgentActivity: async (id: number, progress: AgentProgress[]) => { saved.push({ id, progress }) },
|
|
29
29
|
cancelChatMessage: async () => {},
|
|
30
30
|
clearChatHistory: async () => 1,
|
|
31
|
+
truncateInteractionsFrom: async () => 1,
|
|
31
32
|
submitToolApproval: async (token: string, approved: boolean, answer?: string) => { submitted.push({ token, approved, answer }) },
|
|
32
|
-
streamChatMessage: (_message: string,
|
|
33
|
+
streamChatMessage: (_message: string, onChunk: (chunk: string) => void, _image: unknown, _audio: unknown, _video: unknown, _instruction: unknown, onProgress: (progress: AgentProgress) => void, _document: unknown, _workspace: unknown, chatId: string, _agent: unknown, _plan: unknown, _mcp: unknown, onApproval: (approval: PendingApproval) => void, onStarted: (id: number) => void) => {
|
|
33
34
|
const result = deferred<ChatResponse>()
|
|
34
|
-
streams.push({ chatId, onProgress, onApproval, onStarted, result })
|
|
35
|
+
streams.push({ chatId, onChunk, onProgress, onApproval, onStarted, result })
|
|
35
36
|
return result.promise
|
|
36
37
|
},
|
|
37
38
|
...overrides,
|
|
@@ -521,4 +522,91 @@ describe('Queued messages lifecycle and steering', () => {
|
|
|
521
522
|
expect(lifecycle.getSnapshot().sendingMessage).toBe('Important Urgent Turn')
|
|
522
523
|
expect(streams).toHaveLength(2)
|
|
523
524
|
})
|
|
525
|
+
|
|
526
|
+
it('progressively reveals streamed lines while holding incomplete tables and unclosed UI cards until completed', async () => {
|
|
527
|
+
const { lifecycle, streams } = setup()
|
|
528
|
+
const run = lifecycle.sendPrompt('Show card and table')
|
|
529
|
+
|
|
530
|
+
streams[0].onChunk('Line 1\n```ui-card\n[{"title":"Box"}]\n')
|
|
531
|
+
expect(lifecycle.getSnapshot().streamedReply).toBe('Line 1')
|
|
532
|
+
|
|
533
|
+
streams[0].onChunk('```\n| A | B |\n| - | - |\n')
|
|
534
|
+
expect(lifecycle.getSnapshot().streamedReply).toBe('Line 1\n```ui-card\n[{"title":"Box"}]\n```')
|
|
535
|
+
|
|
536
|
+
streams[0].onChunk('| 1 | 2 |\nAfter table\n')
|
|
537
|
+
expect(lifecycle.getSnapshot().streamedReply).toBe(
|
|
538
|
+
'Line 1\n```ui-card\n[{"title":"Box"}]\n```\n| A | B |\n| - | - |\n| 1 | 2 |\nAfter table',
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
streams[0].result.resolve({
|
|
542
|
+
provider: 'system',
|
|
543
|
+
model: 'test',
|
|
544
|
+
text: 'Line 1\n```ui-card\n[{"title":"Box"}]\n```\n| A | B |\n| - | - |\n| 1 | 2 |\nAfter table',
|
|
545
|
+
})
|
|
546
|
+
await run
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
it('drains a single-burst DirectText response progressively across multiple frames instead of dumping all at once', async () => {
|
|
550
|
+
const { lifecycle, streams } = setup()
|
|
551
|
+
const observedReplies: string[] = []
|
|
552
|
+
const unsubscribe = lifecycle.subscribe(() => {
|
|
553
|
+
const current = lifecycle.getSnapshot().streamedReply
|
|
554
|
+
if (current && observedReplies[observedReplies.length - 1] !== current) {
|
|
555
|
+
observedReplies.push(current)
|
|
556
|
+
}
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
const fullText = [
|
|
560
|
+
'Line 1',
|
|
561
|
+
'Line 2',
|
|
562
|
+
'| H1 | H2 |',
|
|
563
|
+
'| - | - |',
|
|
564
|
+
'| V1 | V2 |',
|
|
565
|
+
'```ui-card',
|
|
566
|
+
'[{"title":"Card"}]',
|
|
567
|
+
'```',
|
|
568
|
+
'Final line',
|
|
569
|
+
].join('\n')
|
|
570
|
+
|
|
571
|
+
const run = lifecycle.sendPrompt('Direct text burst')
|
|
572
|
+
streams[0].onChunk(fullText)
|
|
573
|
+
streams[0].result.resolve({ provider: 'system', model: 'test', text: fullText })
|
|
574
|
+
await run
|
|
575
|
+
unsubscribe()
|
|
576
|
+
|
|
577
|
+
expect(observedReplies).toEqual([
|
|
578
|
+
'Line 1\nLine 2',
|
|
579
|
+
'Line 1\nLine 2\n| H1 | H2 |\n| - | - |\n| V1 | V2 |',
|
|
580
|
+
'Line 1\nLine 2\n| H1 | H2 |\n| - | - |\n| V1 | V2 |\n```ui-card\n[{"title":"Card"}]\n```',
|
|
581
|
+
fullText,
|
|
582
|
+
])
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
it('truncates interactions from a given id and resends prompt', async () => {
|
|
586
|
+
let truncatedFrom: { chatId: string; fromId: number } | null = null
|
|
587
|
+
const { lifecycle, streams } = setup({
|
|
588
|
+
truncateInteractionsFrom: async (chatId, fromId) => {
|
|
589
|
+
truncatedFrom = { chatId, fromId }
|
|
590
|
+
return 1
|
|
591
|
+
},
|
|
592
|
+
})
|
|
593
|
+
|
|
594
|
+
lifecycle.appendInteraction({ id: 1, userText: 'first', aiText: 'reply 1', createdAt: '2026-01-01T00:00:00Z', provider: 'test', model: 'test' })
|
|
595
|
+
lifecycle.appendInteraction({ id: 2, userText: 'second', aiText: 'reply 2', createdAt: '2026-01-01T00:01:00Z', provider: 'test', model: 'test' })
|
|
596
|
+
lifecycle.appendInteraction({ id: 3, userText: 'third', aiText: 'reply 3', createdAt: '2026-01-01T00:02:00Z', provider: 'test', model: 'test' })
|
|
597
|
+
|
|
598
|
+
expect(lifecycle.getSnapshot().interactions).toHaveLength(3)
|
|
599
|
+
|
|
600
|
+
const resendPromise = lifecycle.resendFromInteraction(2, 'edited second')
|
|
601
|
+
expect(truncatedFrom).toEqual({ chatId: 'A', fromId: 2 })
|
|
602
|
+
expect(lifecycle.getSnapshot().interactions).toEqual([{ id: 1, userText: 'first', aiText: 'reply 1', createdAt: '2026-01-01T00:00:00Z', provider: 'test', model: 'test', chatId: 'A', status: 'completed' }])
|
|
603
|
+
|
|
604
|
+
await Promise.resolve()
|
|
605
|
+
await Promise.resolve()
|
|
606
|
+
|
|
607
|
+
streams[0].onChunk('new reply')
|
|
608
|
+
streams[0].result.resolve({ provider: 'test', model: 'test', text: 'new reply' })
|
|
609
|
+
await resendPromise
|
|
610
|
+
expect(lifecycle.getSnapshot().sending).toBe(false)
|
|
611
|
+
})
|
|
524
612
|
})
|
|
@@ -2,13 +2,14 @@ import { initialConversationState, transitionConversation, type ConversationEven
|
|
|
2
2
|
import { compactAgentProgressForPersistence, mergeActivitySnapshots } from '../agentProgress'
|
|
3
3
|
import { requiresExplicitApproval } from '../utils/approval'
|
|
4
4
|
import { navigateToProject } from './projectNavigation'
|
|
5
|
+
import { createProgressiveStreamPacer, type ProgressiveStreamPacer } from './progressiveStream'
|
|
5
6
|
import type { MintPlatformApi } from '../platform'
|
|
6
7
|
import type { AgentProgress, ChatResponse, ChatSession, InteractionMemory, QueuedPrompt } from '../types'
|
|
7
8
|
|
|
8
9
|
export type ConversationTransport = Pick<MintPlatformApi,
|
|
9
10
|
| 'getConversationSnapshot' | 'getConversationChanges' | 'listChatSessions'
|
|
10
11
|
| 'streamChatMessage' | 'cancelChatMessage' | 'submitToolApproval'
|
|
11
|
-
| 'saveInteractionAgentActivity' | 'clearChatHistory'>
|
|
12
|
+
| 'saveInteractionAgentActivity' | 'clearChatHistory' | 'truncateInteractionsFrom'>
|
|
12
13
|
|
|
13
14
|
export interface PendingApproval { token: string; approval: Record<string, unknown> }
|
|
14
15
|
export interface ConversationView extends ConversationState {
|
|
@@ -53,7 +54,9 @@ interface Run {
|
|
|
53
54
|
autoApprove: boolean
|
|
54
55
|
progress: AgentProgress[]
|
|
55
56
|
queuedProgress: AgentProgress[]
|
|
57
|
+
pacer: ProgressiveStreamPacer
|
|
56
58
|
timer?: ReturnType<typeof setTimeout>
|
|
59
|
+
streamTimer?: ReturnType<typeof setTimeout>
|
|
57
60
|
}
|
|
58
61
|
export interface ConversationBrowser {
|
|
59
62
|
window: Pick<Window, 'addEventListener' | 'removeEventListener' | 'setInterval' | 'clearInterval'>
|
|
@@ -100,6 +103,21 @@ export function createConversationLifecycle(
|
|
|
100
103
|
if (run.timer !== undefined) clearTimeout(run.timer)
|
|
101
104
|
run.timer = undefined
|
|
102
105
|
}
|
|
106
|
+
const clearStreamTimer = (run: Run) => {
|
|
107
|
+
if (run.streamTimer !== undefined) clearTimeout(run.streamTimer)
|
|
108
|
+
run.streamTimer = undefined
|
|
109
|
+
}
|
|
110
|
+
const stepStreamPacer = (run: Run) => {
|
|
111
|
+
clearStreamTimer(run)
|
|
112
|
+
if (!currentRun(run)) return
|
|
113
|
+
const revealed = run.pacer.step()
|
|
114
|
+
if (revealed !== null) {
|
|
115
|
+
update({ streamedReply: revealed })
|
|
116
|
+
if (!run.pacer.isDone()) {
|
|
117
|
+
run.streamTimer = setTimeout(() => stepStreamPacer(run), 18)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
103
121
|
const flushProgress = (run: Run) => {
|
|
104
122
|
clearProgressTimer(run)
|
|
105
123
|
const batch = run.queuedProgress
|
|
@@ -256,6 +274,7 @@ export function createConversationLifecycle(
|
|
|
256
274
|
if (!state.sending || !run || !currentRun(run)) return
|
|
257
275
|
runs.delete(origin.chatId)
|
|
258
276
|
clearProgressTimer(run)
|
|
277
|
+
clearStreamTimer(run)
|
|
259
278
|
const approvalToken = state.pendingApprovals[origin.chatId]?.token
|
|
260
279
|
try {
|
|
261
280
|
await transport.cancelChatMessage(origin.chatId)
|
|
@@ -275,7 +294,7 @@ export function createConversationLifecycle(
|
|
|
275
294
|
async function sendPrompt(prompt: string, options: PromptOptions = {}) {
|
|
276
295
|
if (state.sending) return
|
|
277
296
|
const origin = session
|
|
278
|
-
const run: Run = { session: origin, turnId: null, autoApprove: false, progress: [], queuedProgress: [] }
|
|
297
|
+
const run: Run = { session: origin, turnId: null, autoApprove: false, progress: [], queuedProgress: [], pacer: createProgressiveStreamPacer() }
|
|
279
298
|
runs.set(origin.chatId, run)
|
|
280
299
|
const images = options.imageAttachments ?? []
|
|
281
300
|
const videos = options.videoAttachments ?? []
|
|
@@ -305,7 +324,27 @@ export function createConversationLifecycle(
|
|
|
305
324
|
}
|
|
306
325
|
try {
|
|
307
326
|
const response = await transport.streamChatMessage(
|
|
308
|
-
agent ? prompt : `/chat ${prompt}`,
|
|
327
|
+
agent ? prompt : `/chat ${prompt}`,
|
|
328
|
+
chunk => {
|
|
329
|
+
if (runs.get(origin.chatId) !== run) return
|
|
330
|
+
run.pacer.pushDelta(chunk)
|
|
331
|
+
if (!currentRun(run)) return
|
|
332
|
+
// Immediately reveal up to 2 ready units (e.g. a completed table + following line);
|
|
333
|
+
// if a larger multi-line burst arrived at once, pace the remaining units smoothly.
|
|
334
|
+
let latest: string | null = null
|
|
335
|
+
for (let i = 0; i < 2; i++) {
|
|
336
|
+
const next = run.pacer.step()
|
|
337
|
+
if (next === null) break
|
|
338
|
+
latest = next
|
|
339
|
+
}
|
|
340
|
+
if (latest !== null) {
|
|
341
|
+
update({ streamedReply: latest })
|
|
342
|
+
if (run.streamTimer === undefined) {
|
|
343
|
+
run.streamTimer = setTimeout(() => stepStreamPacer(run), 18)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
images.map(img => img.dataUri).join(' '),
|
|
309
348
|
options.audioDataUri ?? null, videos.map(video => video.dataUri).join(' '), options.systemInstruction ?? '',
|
|
310
349
|
progress => {
|
|
311
350
|
if (runs.get(origin.chatId) !== run) return
|
|
@@ -325,8 +364,36 @@ export function createConversationLifecycle(
|
|
|
325
364
|
)
|
|
326
365
|
if (response.provider !== 'error') accepted()
|
|
327
366
|
flushProgress(run)
|
|
328
|
-
|
|
329
|
-
|
|
367
|
+
clearStreamTimer(run)
|
|
368
|
+
run.pacer.finish(response.text)
|
|
369
|
+
if (currentRun(run)) {
|
|
370
|
+
const firstStep = run.pacer.step()
|
|
371
|
+
if (firstStep !== null) {
|
|
372
|
+
update({ streamedReply: firstStep, streamedResponse: response })
|
|
373
|
+
}
|
|
374
|
+
const usingFakeTimers = typeof (globalThis as any).vi?.isFakeTimers === 'function'
|
|
375
|
+
&& (globalThis as any).vi.isFakeTimers()
|
|
376
|
+
if (!usingFakeTimers) {
|
|
377
|
+
while (!run.pacer.isDone() && runs.get(origin.chatId) === run && currentRun(run)) {
|
|
378
|
+
await new Promise<void>(resolve => {
|
|
379
|
+
run.streamTimer = setTimeout(() => {
|
|
380
|
+
run.streamTimer = undefined
|
|
381
|
+
resolve()
|
|
382
|
+
}, 18)
|
|
383
|
+
})
|
|
384
|
+
if (runs.get(origin.chatId) !== run || !currentRun(run)) break
|
|
385
|
+
const next = run.pacer.step()
|
|
386
|
+
if (next !== null) {
|
|
387
|
+
update({ streamedReply: next, streamedResponse: response })
|
|
388
|
+
} else {
|
|
389
|
+
break
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (runs.get(origin.chatId) === run && currentRun(run)) {
|
|
394
|
+
update({ streamedReply: run.pacer.flushAll(), streamedResponse: response })
|
|
395
|
+
}
|
|
396
|
+
}
|
|
330
397
|
await effects.onReply?.(response, origin.chatId)
|
|
331
398
|
const persisted = compactAgentProgressForPersistence(run.progress)
|
|
332
399
|
if (persisted.length && run.turnId !== null) {
|
|
@@ -349,6 +416,7 @@ export function createConversationLifecycle(
|
|
|
349
416
|
if (currentRun(run)) effects.onError?.(error)
|
|
350
417
|
} finally {
|
|
351
418
|
flushProgress(run)
|
|
419
|
+
clearStreamTimer(run)
|
|
352
420
|
if (runs.get(origin.chatId) === run) {
|
|
353
421
|
const isCurrent = currentRun(run)
|
|
354
422
|
removeApproval(origin.chatId)
|
|
@@ -399,6 +467,36 @@ export function createConversationLifecycle(
|
|
|
399
467
|
}
|
|
400
468
|
}
|
|
401
469
|
|
|
470
|
+
async function truncateFrom(fromId: number) {
|
|
471
|
+
const origin = session
|
|
472
|
+
try {
|
|
473
|
+
if (state.sending && runs.has(origin.chatId)) {
|
|
474
|
+
await cancel()
|
|
475
|
+
}
|
|
476
|
+
// Optimistically update local state immediately so UI updates with zero latency
|
|
477
|
+
const filtered = state.interactions.filter(i => i.id < fromId)
|
|
478
|
+
const snapshots = { ...state.agentActivitySnapshots }
|
|
479
|
+
for (const interaction of state.interactions) {
|
|
480
|
+
if (interaction.id >= fromId) {
|
|
481
|
+
delete snapshots[String(interaction.id)]
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
update({
|
|
485
|
+
interactions: filtered,
|
|
486
|
+
agentActivitySnapshots: snapshots,
|
|
487
|
+
activeTurnId: null,
|
|
488
|
+
})
|
|
489
|
+
await transport.truncateInteractionsFrom(origin.chatId, fromId)
|
|
490
|
+
} catch (error) {
|
|
491
|
+
if (origin === session) throw error
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
async function resendFromInteraction(fromId: number, promptText: string, options: PromptOptions = {}) {
|
|
496
|
+
await truncateFrom(fromId)
|
|
497
|
+
await sendPrompt(promptText, options)
|
|
498
|
+
}
|
|
499
|
+
|
|
402
500
|
async function selectProject(
|
|
403
501
|
choosePath: () => Promise<string | null>,
|
|
404
502
|
navigation: {
|
|
@@ -560,6 +658,7 @@ export function createConversationLifecycle(
|
|
|
560
658
|
subscribe: (notify: () => void) => { subscribers.add(notify); return () => { subscribers.delete(notify) } },
|
|
561
659
|
activate, refreshHistory, refreshSessions, syncHistory, loadOlder, sendPrompt, cancel,
|
|
562
660
|
receiveApproval, resolveApproval, clearHistory, selectProject, watch,
|
|
661
|
+
truncateFrom, resendFromInteraction,
|
|
563
662
|
queuePrompt, removeQueuedPrompt, editQueuedPrompt, clearQueuedPrompts, setQueuePaused, resumeQueue, steerQueuedPrompt,
|
|
564
663
|
appendInteraction: (row: Omit<InteractionMemory, 'chatId' | 'status'> & Partial<Pick<InteractionMemory, 'chatId' | 'status'>>) =>
|
|
565
664
|
update({ interactions: [...state.interactions, { chatId: session.chatId, status: 'completed', ...row }] }),
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
extractRenderableStreamPrefix,
|
|
4
|
+
splitProgressiveStreamUnits,
|
|
5
|
+
createProgressiveStreamPacer,
|
|
6
|
+
} from './progressiveStream'
|
|
7
|
+
|
|
8
|
+
describe('progressiveStream (Line-by-Line + Block-Aware)', () => {
|
|
9
|
+
it('releases normal paragraphs and regular code blocks line by line while holding incomplete tables and unclosed UI cards', () => {
|
|
10
|
+
const partialTable = 'Intro paragraph\n| Name | Role |\n| --- | --- |\n| Alice | Admin |\n'
|
|
11
|
+
expect(extractRenderableStreamPrefix(partialTable, false)).toBe('Intro paragraph')
|
|
12
|
+
expect(extractRenderableStreamPrefix(partialTable, true)).toBe(partialTable.trimEnd())
|
|
13
|
+
|
|
14
|
+
const completedTable = `${partialTable}Summary after table\n`
|
|
15
|
+
expect(extractRenderableStreamPrefix(completedTable, false)).toBe(completedTable.trimEnd())
|
|
16
|
+
|
|
17
|
+
const partialUiCard = 'Before card\n```ui-card\n[{"title": "Fast", "subtitle": "Card"}]\n'
|
|
18
|
+
expect(extractRenderableStreamPrefix(partialUiCard, false)).toBe('Before card')
|
|
19
|
+
|
|
20
|
+
const closedUiCard = `${partialUiCard}\`\`\`\nAfter card\n`
|
|
21
|
+
expect(extractRenderableStreamPrefix(closedUiCard, false)).toBe(closedUiCard.trimEnd())
|
|
22
|
+
|
|
23
|
+
const bareFence = 'Before code\n```ts\n'
|
|
24
|
+
expect(extractRenderableStreamPrefix(bareFence, false)).toBe('Before code')
|
|
25
|
+
|
|
26
|
+
const partialCode = 'Before code\n```ts\nconst x = 1\n'
|
|
27
|
+
expect(extractRenderableStreamPrefix(partialCode, false)).toBe('Before code\n```ts\nconst x = 1')
|
|
28
|
+
|
|
29
|
+
const unlabeledUiCard = 'Before card\n```\nui-card [\n {"title": "Fast"}\n]\n'
|
|
30
|
+
expect(extractRenderableStreamPrefix(unlabeledUiCard, false)).toBe('Before card')
|
|
31
|
+
expect(extractRenderableStreamPrefix(`${unlabeledUiCard}\`\`\`\n`, false)).toBe(
|
|
32
|
+
`${unlabeledUiCard}\`\`\``,
|
|
33
|
+
)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('splits complete markdown into line units for text/code and atomic block units for tables and JSON/Mermaid cards', () => {
|
|
37
|
+
const doc = [
|
|
38
|
+
'# Title',
|
|
39
|
+
'First line',
|
|
40
|
+
'| A | B |',
|
|
41
|
+
'| - | - |',
|
|
42
|
+
'| 1 | 2 |',
|
|
43
|
+
'```python',
|
|
44
|
+
'print(42)',
|
|
45
|
+
'```',
|
|
46
|
+
'```ui-grid',
|
|
47
|
+
'[{"title": "Item"}]',
|
|
48
|
+
'```',
|
|
49
|
+
'Done',
|
|
50
|
+
].join('\n')
|
|
51
|
+
|
|
52
|
+
expect(splitProgressiveStreamUnits(doc, true)).toEqual([
|
|
53
|
+
'# Title',
|
|
54
|
+
'First line',
|
|
55
|
+
'| A | B |\n| - | - |\n| 1 | 2 |',
|
|
56
|
+
'```python\nprint(42)',
|
|
57
|
+
'```',
|
|
58
|
+
'```ui-grid\n[{"title": "Item"}]\n```',
|
|
59
|
+
'Done',
|
|
60
|
+
])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('paces deltas and authoritative final text smoothly in order without leaking raw JSON mid-card', () => {
|
|
64
|
+
const revealed: string[] = []
|
|
65
|
+
const pacer = createProgressiveStreamPacer((text) => revealed.push(text))
|
|
66
|
+
|
|
67
|
+
pacer.pushDelta('Line 1\n```ui-card\n[{"title":"Box"}]\n')
|
|
68
|
+
expect(pacer.step()).toBe('Line 1')
|
|
69
|
+
expect(pacer.step()).toBeNull()
|
|
70
|
+
|
|
71
|
+
pacer.pushDelta('```\nLine 2\n')
|
|
72
|
+
expect(pacer.step()).toBe('Line 1\n```ui-card\n[{"title":"Box"}]\n```')
|
|
73
|
+
expect(pacer.step()).toBe('Line 1\n```ui-card\n[{"title":"Box"}]\n```\nLine 2')
|
|
74
|
+
expect(pacer.isDone()).toBe(false)
|
|
75
|
+
|
|
76
|
+
pacer.finish('Line 1\n```ui-card\n[{"title":"Box"}]\n```\nLine 2\nFinal tail')
|
|
77
|
+
expect(pacer.flushAll()).toBe('Line 1\n```ui-card\n[{"title":"Box"}]\n```\nLine 2\nFinal tail')
|
|
78
|
+
expect(pacer.isDone()).toBe(true)
|
|
79
|
+
expect(revealed[revealed.length - 1]).toBe(
|
|
80
|
+
'Line 1\n```ui-card\n[{"title":"Box"}]\n```\nLine 2\nFinal tail',
|
|
81
|
+
)
|
|
82
|
+
})
|
|
83
|
+
})
|