@pheem49/mint 1.14.0 → 1.15.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/CLAUDE.md +1 -1
- package/Cargo.lock +291 -11
- package/Cargo.toml +1 -1
- package/README.md +71 -9
- package/ROADMAP.md +30 -0
- package/Release_Note.md +579 -465
- package/crates/mint-cli/Cargo.toml +3 -1
- package/crates/mint-cli/src/agent/approval_prompts.rs +16 -3
- package/crates/mint-cli/src/agent/live_status.rs +367 -118
- package/crates/mint-cli/src/agent/markdown_render.rs +601 -61
- package/crates/mint-cli/src/agent.rs +452 -215
- package/crates/mint-cli/src/commands/agent.rs +16 -21
- package/crates/mint-cli/src/commands/eval.rs +9 -5
- package/crates/mint-cli/src/commands/git.rs +204 -0
- package/crates/mint-cli/src/commands/integrations.rs +28 -0
- package/crates/mint-cli/src/commands/knowledge.rs +1 -0
- package/crates/mint-cli/src/commands/mod.rs +37 -2
- package/crates/mint-cli/src/commands/tailnet_web.rs +235 -0
- package/crates/mint-cli/src/image.rs +26 -3
- package/crates/mint-cli/src/interactive/chat_tui.rs +4742 -0
- package/crates/mint-cli/src/interactive/command_ui.rs +477 -0
- package/crates/mint-cli/src/interactive/format.rs +8 -0
- package/crates/mint-cli/src/interactive/input_box.rs +38 -2
- package/crates/mint-cli/src/interactive/picker.rs +180 -37
- package/crates/mint-cli/src/interactive/resume_picker.rs +686 -0
- package/crates/mint-cli/src/interactive/slash_commands.rs +868 -450
- package/crates/mint-cli/src/interactive/thought_viewer.rs +286 -0
- package/crates/mint-cli/src/interactive.rs +400 -136
- package/crates/mint-cli/src/main.rs +84 -23
- package/crates/mint-cli/src/markdown.rs +12 -5
- package/crates/mint-cli/src/onboard.rs +32 -30
- package/crates/mint-cli/src/plugins_cli.rs +8 -5
- package/crates/mint-cli/src/setup.rs +10 -8
- package/crates/mint-cli/src/terminal_theme.rs +187 -0
- package/crates/mint-cli/src/updater.rs +56 -21
- package/crates/mint-core/src/agent/agent_loop.rs +1 -0
- package/crates/mint-core/src/agent/chat.rs +843 -177
- package/crates/mint-core/src/agent/code_tools.rs +83 -2
- package/crates/mint-core/src/agent/memory.rs +658 -20
- package/crates/mint-core/src/agent/skills.rs +1 -1
- package/crates/mint-core/src/api_server/routes/chat.rs +31 -5
- package/crates/mint-core/src/api_server/routes/linked_folders.rs +118 -3
- package/crates/mint-core/src/api_server/routes/misc.rs +10 -14
- package/crates/mint-core/src/api_server/routes/sessions.rs +44 -0
- package/crates/mint-core/src/api_server.rs +22 -3
- package/crates/mint-core/src/avatar_bridge.rs +2 -0
- package/crates/mint-core/src/browser/cdp.rs +74 -83
- package/crates/mint-core/src/browser/evidence.rs +260 -0
- package/crates/mint-core/src/browser/input.rs +1 -1
- package/crates/mint-core/src/browser/interact.rs +98 -124
- package/crates/mint-core/src/browser/lifecycle.rs +75 -4
- package/crates/mint-core/src/browser/mod.rs +16 -2
- package/crates/mint-core/src/browser/navigate.rs +27 -19
- package/crates/mint-core/src/browser/observe.js +40 -0
- package/crates/mint-core/src/browser/session.rs +1558 -0
- package/crates/mint-core/src/git/branches.rs +348 -0
- package/crates/mint-core/src/git/checkpoint.rs +40 -0
- package/crates/mint-core/src/git/diff.rs +206 -0
- package/crates/mint-core/src/git/mod.rs +10 -2
- package/crates/mint-core/src/integrations/channels.rs +1 -0
- package/crates/mint-core/src/lib.rs +22 -14
- package/crates/mint-core/src/live_sync.rs +28 -9
- package/crates/mint-core/src/media/auto_shorts.rs +1 -0
- package/crates/mint-core/src/media/gemini_live.rs +12 -1
- package/crates/mint-core/src/media/mic_transcribe.rs +1 -0
- package/crates/mint-core/src/media/subtitle.rs +1 -0
- package/crates/mint-core/src/orchestration/context_render.rs +1 -0
- package/crates/mint-core/src/orchestration/memory_skill.rs +6 -1
- package/crates/mint-core/src/orchestration/mod.rs +1024 -271
- package/crates/mint-core/src/orchestration/tools/browser.rs +16 -139
- package/crates/mint-core/src/orchestration/tools/misc.rs +2 -1
- package/crates/mint-core/src/orchestration/tools/web.rs +1 -1
- package/crates/mint-core/src/prompts/agent.rs +58 -29
- package/crates/mint-core/src/prompts/chat.rs +1 -1
- package/crates/mint-core/src/prompts/persona.rs +1 -1
- package/crates/mint-core/src/prompts/tool_catalog.rs +93 -25
- package/crates/mint-core/src/search/linked_folders/index.rs +732 -0
- package/crates/mint-core/src/search/linked_folders.rs +391 -130
- package/crates/mint-core/src/slash/mod.rs +470 -22
- package/crates/mint-core/src/slash/model_fetcher.rs +12 -3
- package/crates/mint-core/src/slash/models.rs +66 -10
- package/crates/mint-core/src/system/config.rs +230 -0
- package/crates/mint-core/src/system/workspace.rs +228 -0
- package/crates/mint-core/src/system.rs +1 -0
- package/crates/mint-core/tests/memory_persistence.rs +39 -0
- package/mint-presentation-demo.mp4 +0 -0
- package/out/web/assets/CliSessionsView-BxUVWEEL.js +1 -0
- package/out/web/assets/CliSessionsView-CBDTXuQv.css +1 -0
- package/out/web/assets/CodeReviewPage-DkMgfcos.css +1 -0
- package/out/web/assets/CodeReviewPage-jKlmLJwT.js +3 -0
- package/out/web/assets/CommandPalette-DnwPA5hy.js +1 -0
- package/out/web/assets/ImageStudioPanel-BR1XFwhR.js +1 -0
- package/out/web/assets/ImageStudioPanel-D0lnhQV5.css +1 -0
- package/out/web/assets/LinkedFoldersView-bi-CWQ-I.js +1 -0
- package/out/web/assets/McpRegistryPicker-D5tEFnyq.js +1 -0
- package/out/web/assets/McpServersView-BGG1dhFM.js +1 -0
- package/out/web/assets/MintDashboard-BIglDEWX.js +20 -0
- package/out/web/assets/MintDashboard-DSMywSgs.css +1 -0
- package/out/web/assets/ModelPanel-CbJ1PFoZ.js +1 -0
- package/out/web/assets/PicturesLibrary-CQP0fLP1.css +1 -0
- package/out/web/assets/PicturesLibrary-DoYN7T1p.js +1 -0
- package/out/web/assets/PluginsView-CU1HWXGk.js +1 -0
- package/out/web/assets/ScheduledTasksView-Bnl36y2N.js +1 -0
- package/out/web/assets/SettingsWindow-DhWLAn9T.css +1 -0
- package/out/web/assets/SettingsWindow-Dw8L01pw.js +2 -0
- package/out/web/assets/SkillsView-cnl8aY5p.js +2 -0
- package/out/web/assets/ToolSurfacePage-CgsQw6l5.css +1 -0
- package/out/web/assets/ToolSurfacePage-Cz2VJAPZ.js +37 -0
- package/out/web/assets/VeoStudioPanel-D4JnERWR.js +4 -0
- package/out/web/assets/VeoStudioPanel-DOj7_WR3.css +1 -0
- package/out/web/assets/WorkspacePanel-CbJ1PFoZ.js +1 -0
- package/out/web/assets/{abnfDiagram-VCTEODGH-DAOBA7ZG.js → abnfDiagram-VCTEODGH-ByMyfanq.js} +1 -1
- package/out/web/assets/{architecture-7GRP2DOG-CgSwQOPQ.js → architecture-7GRP2DOG-CLq2kEOB.js} +1 -1
- package/out/web/assets/{architectureDiagram-5GKGNRK7-CYp3yupP.js → architectureDiagram-5GKGNRK7-6PoVSmNG.js} +1 -1
- package/out/web/assets/{blockDiagram-NRAW4CY4-CyWybDWr.js → blockDiagram-NRAW4CY4-cKw9mAKi.js} +1 -1
- package/out/web/assets/{c4Diagram-UCG6FXSJ-BvY3U709.js → c4Diagram-UCG6FXSJ-Cqy8N5Ge.js} +1 -1
- package/out/web/assets/{chunk-75Z2AOVW-TLXCoZYv.js → chunk-75Z2AOVW-3qybijF5.js} +1 -1
- package/out/web/assets/chunk-CKBBP62Z-cIrXq6jN.js +2 -0
- package/out/web/assets/{chunk-CLGD4ZFX-xjsKtNS7.js → chunk-CLGD4ZFX-CndDQkww.js} +1 -1
- package/out/web/assets/chunk-DSNCTWBM-Cc4NxvgD.js +2 -0
- package/out/web/assets/chunk-DU6HZSFF-drzh6GsU.js +128 -0
- package/out/web/assets/{chunk-F27PBJKO-BFpgF55A.js → chunk-F27PBJKO-FiY35l4v.js} +1 -1
- package/out/web/assets/{chunk-G27WJ6UU-DfF7qW-C.js → chunk-G27WJ6UU-Bjt9Il4A.js} +1 -1
- package/out/web/assets/{chunk-GMAD6QVW-C-558kh_.js → chunk-GMAD6QVW-Bppi3Ctd.js} +1 -1
- package/out/web/assets/{chunk-JQ64N6SF-wJmeGKAw.js → chunk-JQ64N6SF-DGg5kcEV.js} +1 -1
- package/out/web/assets/{chunk-LCL6LL3I-C1wHD8Ie.js → chunk-LCL6LL3I-CCYcjd7p.js} +1 -1
- package/out/web/assets/{chunk-OBVCFTLP-BsRTPBqs.js → chunk-OBVCFTLP-DJeNil5M.js} +1 -1
- package/out/web/assets/{chunk-P2QGCYS3-HZIiM7Ni.js → chunk-P2QGCYS3-lRzkRjBB.js} +1 -1
- package/out/web/assets/{chunk-POPQ4Y6H-Dk9wvVb0.js → chunk-POPQ4Y6H-DouASMBW.js} +1 -1
- package/out/web/assets/{chunk-PWAF6VOD-BmjYLNbq.js → chunk-PWAF6VOD-DMmD1vVf.js} +1 -1
- package/out/web/assets/{chunk-R7TYR2AO-CaHIf-lK.js → chunk-R7TYR2AO-CYLA5UC5.js} +1 -1
- package/out/web/assets/{chunk-RHFEMEQ7-_T561hOU.js → chunk-RHFEMEQ7-BiXwEcUT.js} +1 -1
- package/out/web/assets/{chunk-SVP7TREG-CM-GGYoY.js → chunk-SVP7TREG-BV-zPslF.js} +1 -1
- package/out/web/assets/{chunk-TEH6E4GO-K2A8Vg2C.js → chunk-TEH6E4GO-B2RGEohZ.js} +1 -1
- package/out/web/assets/classDiagram-DTDB5LWJ-Dq0smRWk.js +1 -0
- package/out/web/assets/classDiagram-v2-JRS7N3AN-Dq0smRWk.js +1 -0
- package/out/web/assets/config-C-ZFtipU.css +1 -0
- package/out/web/assets/config-eEDngJXO.js +1 -0
- package/out/web/assets/core-hQ2VA9Ti.js +1 -0
- package/out/web/assets/{cynefin-OW5HDTMX-ClzaB6cc.js → cynefin-OW5HDTMX-Cas4csHM.js} +1 -1
- package/out/web/assets/{cynefinDiagram-5FMLGOSQ-D_JzZBzf.js → cynefinDiagram-5FMLGOSQ-Bbpj66Y3.js} +1 -1
- package/out/web/assets/{dagre-3AP2YEHR-BDuUeMHj.js → dagre-3AP2YEHR-B0-2RDEd.js} +1 -1
- package/out/web/assets/{diagram-S7CK7UJ4-DgtcfE8P.js → diagram-S7CK7UJ4-BdzEjUyH.js} +1 -1
- package/out/web/assets/{diagram-UQ7AKVKN-DTLoEYti.js → diagram-UQ7AKVKN-CJDwyPxF.js} +1 -1
- package/out/web/assets/{diagram-VSXAHHWV-ChGl9pXz.js → diagram-VSXAHHWV-BxaG1r_B.js} +1 -1
- package/out/web/assets/{diagram-VX7I27RA-Be8DWz0S.js → diagram-VX7I27RA-BMmUqm8f.js} +1 -1
- package/out/web/assets/{diagram-Z3DM3KII-BMXjRTOF.js → diagram-Z3DM3KII-CFtd03eE.js} +1 -1
- package/out/web/assets/dist-js-Bf2bhoku.js +1 -0
- package/out/web/assets/{ebnfDiagram-PWID7BFC-8Q9pl1CJ.js → ebnfDiagram-PWID7BFC-DwAen1hO.js} +1 -1
- package/out/web/assets/{erDiagram-SSCWMZ5O-DjH8Lnkn.js → erDiagram-SSCWMZ5O-BCZSHACj.js} +1 -1
- package/out/web/assets/event-ChBVpRy0.js +1 -0
- package/out/web/assets/{eventmodeling-NTZA5JFV-BKUH_DOp.js → eventmodeling-NTZA5JFV-XHStE4E_.js} +1 -1
- package/out/web/assets/fira-code-latin-600-normal-BmSSHshu.woff +0 -0
- package/out/web/assets/fira-code-latin-600-normal-CvYZpPHg.woff2 +0 -0
- package/out/web/assets/fira-code-symbols2-600-normal-Dvq6IQE5.woff +0 -0
- package/out/web/assets/flowDiagram-A5DVABFB-BLg7nEcd.js +1 -0
- package/out/web/assets/{ganttDiagram-EL5Y4UJY-D7fZKJNM.js → ganttDiagram-EL5Y4UJY-BAEu1Cql.js} +1 -1
- package/out/web/assets/{gitGraph-4MIJSDKK-BypWaaBU.js → gitGraph-4MIJSDKK-r32Vyper.js} +1 -1
- package/out/web/assets/{gitGraphDiagram-WWUBYQGX-DWiw_3bq.js → gitGraphDiagram-WWUBYQGX-z9o9qt6V.js} +1 -1
- package/out/web/assets/index-Btsf4L8M.css +1 -0
- package/out/web/assets/index-D8D9MeRA.js +11 -0
- package/out/web/assets/{info-A6RAGUB7-KkAUxT4j.js → info-A6RAGUB7-BvLaC2cw.js} +1 -1
- package/out/web/assets/{infoDiagram-RXCK75RN-BNb6E48y.js → infoDiagram-RXCK75RN-Be6WAXXw.js} +1 -1
- package/out/web/assets/{ishikawaDiagram-5VMMS53U-Caa-JoAK.js → ishikawaDiagram-5VMMS53U-DZjoHJHC.js} +1 -1
- package/out/web/assets/{journeyDiagram-EYS64GPL-DOrmFGz6.js → journeyDiagram-EYS64GPL-CtVkjkGm.js} +1 -1
- package/out/web/assets/{kanban-definition-3QL26DDD-BOWCYcK0.js → kanban-definition-3QL26DDD-CW5FMv1w.js} +1 -1
- package/out/web/assets/lucide-react-Ip3L6Jtn.js +1 -0
- package/out/web/assets/markdown-BSeeUbJH.js +38 -0
- package/out/web/assets/{mermaid-parser.core-BQqjCkS_.js → mermaid-parser.core-aUgCqsZ5.js} +3 -3
- package/out/web/assets/mermaid.core-D57sEiSL.js +11 -0
- package/out/web/assets/{mindmap-definition-FBJOCRG2-jTceWpr0.js → mindmap-definition-FBJOCRG2-DAmSpM3a.js} +1 -1
- package/out/web/assets/modelManager-tMVmJVAW.js +2 -0
- package/out/web/assets/oauthManager-9jxicDWJ.js +1 -0
- package/out/web/assets/{packet-AYTQ26CC-CJ5RXgol.js → packet-AYTQ26CC-DJ27iIoJ.js} +1 -1
- package/out/web/assets/{pegDiagram-XKGWAZYB-y8wMtd0y.js → pegDiagram-XKGWAZYB-Bj3ii-gS.js} +1 -1
- package/out/web/assets/{pie-WAS4IAKB-72fTBbl4.js → pie-WAS4IAKB-DG5mCUbc.js} +1 -1
- package/out/web/assets/{pieDiagram-E7YTZNPT-e0O02qWq.js → pieDiagram-E7YTZNPT-BrJLPnDA.js} +1 -1
- 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-700-normal-Cb5ilR-c.woff +0 -0
- package/out/web/assets/prompt-thai-700-normal-Cg4aQ0Nn.woff2 +0 -0
- package/out/web/assets/{quadrantDiagram-AXDQQJYC-C9phYLom.js → quadrantDiagram-AXDQQJYC-3x_TmF3j.js} +1 -1
- package/out/web/assets/{radar-RG4KPBEZ-BAU2hzUT.js → radar-RG4KPBEZ-CrXaTTuV.js} +1 -1
- package/out/web/assets/{railroad-74A4TZTK-B0bCAVHC.js → railroad-74A4TZTK-BpqMH9BE.js} +1 -1
- package/out/web/assets/{railroad-abnf-HS5TGJTU-WhxO7ndE.js → railroad-abnf-HS5TGJTU-DHI0uLlV.js} +1 -1
- package/out/web/assets/{railroad-ebnf-LZEXJU2U-BkA0m7-F.js → railroad-ebnf-LZEXJU2U-CQCb316y.js} +1 -1
- package/out/web/assets/{railroad-peg-WCYAUIDC-BsVpHIaj.js → railroad-peg-WCYAUIDC-Ph1giKkH.js} +1 -1
- package/out/web/assets/{railroadDiagram-O6MQD6OU-CyOLN65Q.js → railroadDiagram-O6MQD6OU-BRq6wIxx.js} +1 -1
- package/out/web/assets/{requirementDiagram-EFPCY7ZU-DQXJ7Dud.js → requirementDiagram-EFPCY7ZU-DIJj6G1g.js} +1 -1
- package/out/web/assets/{sankeyDiagram-P5KCCOFB-B2XLyIIc.js → sankeyDiagram-P5KCCOFB-DZajSryq.js} +1 -1
- package/out/web/assets/{sequenceDiagram-WJ2MYXX4-DGQY4wwm.js → sequenceDiagram-WJ2MYXX4-DxJCWyZS.js} +1 -1
- package/out/web/assets/{stateDiagram-HBIQ2CUA-DHZpDwNy.js → stateDiagram-HBIQ2CUA-k6voi8Vf.js} +1 -1
- package/out/web/assets/stateDiagram-v2-4QOOHH4V-DeEc0lGZ.js +1 -0
- package/out/web/assets/studio-B1mk40wD.js +1 -0
- package/out/web/assets/{swimlanes-XN3QIQJK-Bn_jcZz-.js → swimlanes-XN3QIQJK-igYhTjXB.js} +1 -1
- package/out/web/assets/swimlanesDiagram-VK2B7HYN-DpVik9v7.js +8 -0
- package/out/web/assets/{timeline-definition-24CTP7MA-CdTERuJZ.js → timeline-definition-24CTP7MA-Dke2p93p.js} +1 -1
- package/out/web/assets/{treeView-Q6P3EWNA-TKBKQG6R.js → treeView-Q6P3EWNA-hNELnXUs.js} +1 -1
- package/out/web/assets/{treemap-WGGIJYW6-BP7gQoiC.js → treemap-WGGIJYW6-D2B6wDsx.js} +1 -1
- package/out/web/assets/{vennDiagram-4TSXK5OY-BHUmxDuK.js → vennDiagram-4TSXK5OY-CDkW7rUP.js} +1 -1
- package/out/web/assets/{wardley-WFR3VGLG-9bct5AAN.js → wardley-WFR3VGLG-BECdXETi.js} +1 -1
- package/out/web/assets/{wardleyDiagram-VM6X3IG4-BfJwDDso.js → wardleyDiagram-VM6X3IG4-B5BycpQ4.js} +1 -1
- package/out/web/assets/window-5qYXKoW7.js +1 -0
- package/out/web/assets/{xychartDiagram-S5SC5T6Z-Dqd9Mvpo.js → xychartDiagram-S5SC5T6Z-CtJS3b--.js} +1 -1
- package/out/web/index-web.html +3 -4
- package/out/web/index.html +3 -4
- package/package.json +17 -6
- package/skills-lock.json +36 -0
- package/slash-commands.json +9 -0
- package/src/renderer/index-web.html +1 -0
- package/src/renderer/index.html +1 -0
- package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/src/renderer/shared/agentProgress.ts +168 -3
- package/src/renderer/shared/components/AgentActivityDrawer.tsx +6 -6
- package/src/renderer/shared/components/AgentActivityTable.tsx +192 -132
- package/src/renderer/shared/components/ArtifactPreviewPanel.tsx +4 -4
- package/src/renderer/shared/components/AuthGate.tsx +4 -9
- package/src/renderer/shared/components/CalculationCard.tsx +41 -15
- package/src/renderer/shared/components/ChatMessageItem.tsx +20 -21
- package/src/renderer/shared/components/ChatPanel.tsx +432 -193
- package/src/renderer/shared/components/ChunkErrorBoundary.tsx +198 -49
- package/src/renderer/shared/components/CliSessionsView.tsx +495 -0
- package/src/renderer/shared/components/CodeReviewPage.tsx +565 -0
- package/src/renderer/shared/components/CommandPalette.tsx +8 -8
- package/src/renderer/shared/components/DashboardSidebar.tsx +862 -125
- package/src/renderer/shared/components/DesktopTitlebar.tsx +345 -0
- package/src/renderer/shared/components/DiffReviewModal.tsx +3 -3
- package/src/renderer/shared/components/GitBranchSelector.tsx +442 -0
- package/src/renderer/shared/components/ImageGenCard.tsx +31 -12
- package/src/renderer/shared/components/ImageSearchCard.tsx +59 -38
- package/src/renderer/shared/components/ImageStudioPanel.tsx +40 -41
- package/src/renderer/shared/components/InlineThoughtNote.tsx +27 -0
- package/src/renderer/shared/components/LinkedFoldersView.tsx +365 -46
- package/src/renderer/shared/components/McpRegistryPicker.tsx +1 -1
- package/src/renderer/shared/components/McpServersView.tsx +24 -22
- package/src/renderer/shared/components/MermaidCard.tsx +14 -8
- package/src/renderer/shared/components/MintDashboard.tsx +1141 -508
- package/src/renderer/shared/components/ModelSelectorPopover.tsx +535 -214
- package/src/renderer/shared/components/PlanChecklistWidget.tsx +1 -1
- package/src/renderer/shared/components/PluginsView.tsx +1 -1
- package/src/renderer/shared/components/RewindModal.tsx +6 -6
- package/src/renderer/shared/components/RunSummaryDashboard.tsx +3 -3
- package/src/renderer/shared/components/ScheduledTasksView.tsx +4 -4
- package/src/renderer/shared/components/ScreenCaptureDialog.css +221 -0
- package/src/renderer/shared/components/ScreenCaptureDialog.tsx +199 -0
- package/src/renderer/shared/components/Settings/AgentsTab.tsx +34 -39
- package/src/renderer/shared/components/Settings/AudioTab.tsx +8 -8
- package/src/renderer/shared/components/Settings/AutomationTab.tsx +8 -58
- package/src/renderer/shared/components/Settings/AvatarCropModal.tsx +368 -0
- package/src/renderer/shared/components/Settings/GeneralTab.tsx +335 -81
- package/src/renderer/shared/components/Settings/MemoryTab.tsx +3 -3
- package/src/renderer/shared/components/Settings/PluginsTab.tsx +20 -12
- package/src/renderer/shared/components/Settings/ProfileTab.tsx +29 -5
- package/src/renderer/shared/components/Settings/SearchableModelCombobox.tsx +411 -0
- package/src/renderer/shared/components/Settings/ThemeTab.tsx +401 -158
- package/src/renderer/shared/components/SkillsView.tsx +41 -16
- package/src/renderer/shared/components/SlashSuggestions.tsx +3 -2
- package/src/renderer/shared/components/SourcesBlock.tsx +44 -32
- package/src/renderer/shared/components/StockCard.tsx +58 -24
- package/src/renderer/shared/components/SubagentDagView.tsx +4 -4
- package/src/renderer/shared/components/TerminalDock.tsx +241 -0
- package/src/renderer/shared/components/ThinkingBlock.tsx +14 -8
- package/src/renderer/shared/components/ToolSurfacePage.tsx +520 -0
- package/src/renderer/shared/components/UiFeatureCard.tsx +267 -0
- package/src/renderer/shared/components/UiGridCard.tsx +188 -0
- package/src/renderer/shared/components/UiMockupWidget.tsx +149 -0
- package/src/renderer/shared/components/VeoStudioPanel.tsx +13 -29
- package/src/renderer/shared/components/WeatherCard.tsx +3 -3
- package/src/renderer/shared/components/WorkspaceContextPopover.tsx +133 -0
- package/src/renderer/shared/components/WorkspaceSelector.tsx +149 -0
- package/src/renderer/shared/constants/config.ts +28 -3
- package/src/renderer/shared/constants/models.ts +189 -2
- package/src/renderer/shared/conversation/coordinator.test.ts +27 -0
- package/src/renderer/shared/conversation/coordinator.ts +82 -0
- package/src/renderer/shared/conversation/syncView.test.ts +46 -0
- package/src/renderer/shared/conversation/syncView.ts +43 -0
- package/src/renderer/shared/conversation/useConversationCoordinator.ts +106 -0
- package/src/renderer/shared/css/agent-observability.css +35 -3
- package/src/renderer/shared/css/auth-gate.css +2 -2
- package/src/renderer/shared/css/chat-ui-cards.css +495 -0
- package/src/renderer/shared/css/code-review.css +823 -0
- package/src/renderer/shared/css/code-sessions.css +452 -0
- package/src/renderer/shared/css/file-changes.css +132 -0
- package/src/renderer/shared/css/image-studio.css +16 -16
- package/src/renderer/shared/css/management-views.css +277 -27
- package/src/renderer/shared/css/model-selector.css +442 -42
- package/src/renderer/shared/css/settings/agents.css +1 -1
- package/src/renderer/shared/css/settings/automation.css +13 -13
- package/src/renderer/shared/css/settings/general.css +526 -5
- package/src/renderer/shared/css/settings/plugins.css +5 -5
- package/src/renderer/shared/css/settings/profile.css +331 -1
- package/src/renderer/shared/css/settings/theme.css +651 -3
- package/src/renderer/shared/css/veo-studio.css +16 -15
- package/src/renderer/shared/fonts.ts +19 -37
- package/src/renderer/shared/platform.test.ts +34 -0
- package/src/renderer/shared/platform.ts +173 -6
- package/src/renderer/shared/theme/themeManager.ts +144 -0
- package/src/renderer/shared/theme/themes.ts +116 -0
- package/src/renderer/shared/theme/tokens.css +318 -0
- package/src/renderer/shared/types.ts +82 -1
- package/src/renderer/shared/utils/agentActivity.ts +126 -5
- package/src/renderer/shared/utils/cardJson.test.ts +35 -0
- package/src/renderer/shared/utils/cardJson.ts +30 -0
- package/src/renderer/shared/utils/fileIcons.ts +1 -1
- package/src/renderer/shared/utils/markdown.tsx +329 -80
- package/src/renderer/shared/utils/markdown.webImages.test.tsx +40 -0
- package/src/renderer/shared/utils/providers.ts +8 -3
- package/src/renderer/shared/utils/screenCapture.ts +43 -0
- package/src/renderer/shared/utils/slashCommandProcessor.ts +4 -1
- package/src/renderer/shared/utils/ui.ts +4 -64
- package/src/renderer/src/App.tsx +7 -0
- package/src/renderer/src/components/BrowserShell.tsx +108 -0
- package/src/renderer/src/components/BrowserSuggestionsPopup.tsx +91 -0
- package/src/renderer/src/components/Live2DStage.tsx +32 -9
- package/src/renderer/src/components/MintDashboard.tsx +4 -2
- package/src/renderer/src/components/ModelPanel.tsx +2 -1
- package/src/renderer/src/components/PicturesLibrary.tsx +3 -2
- package/src/renderer/src/components/ScreenPicker.css +190 -0
- package/src/renderer/src/components/ScreenPicker.tsx +185 -540
- package/src/renderer/src/components/SettingsWindow.tsx +40 -2
- package/src/renderer/src/components/SpotlightWindow.tsx +1 -0
- package/src/renderer/src/components/WidgetWindow.tsx +27 -3
- package/src/renderer/src/components/WorkspacePanel.tsx +61 -43
- package/src/renderer/src/components/browser-shell.css +185 -0
- package/src/renderer/src/components/browser-suggestions-popup.css +55 -0
- package/src/renderer/src/components/liveTranslateFrames.test.ts +23 -0
- package/src/renderer/src/components/liveTranslateFrames.ts +34 -0
- package/src/renderer/src/css/app-shell.css +394 -113
- package/src/renderer/src/css/chat.css +1926 -380
- package/src/renderer/src/css/dashboard.css +11 -0
- package/src/renderer/src/css/desktop-tools.css +730 -0
- package/src/renderer/src/css/misc.css +19 -22
- package/src/renderer/src/css/model.css +49 -48
- package/src/renderer/src/css/pictures.css +144 -136
- package/src/renderer/src/css/settings/base.css +357 -123
- package/src/renderer/src/css/sidebar-workspace.css +930 -291
- package/src/renderer/src/css/spotlight.css +15 -26
- package/src/renderer/src/css/tool-surfaces.css +728 -0
- package/src/renderer/src/css/widget.css +35 -7
- package/src/renderer/src/env.d.ts +3 -0
- package/src/renderer/src/hooks/useProviderModels.ts +2 -9
- package/src/renderer/src/index.css +53 -46
- package/src/renderer/src/main.tsx +11 -0
- package/src/renderer/src/tauri.ts +201 -30
- package/src/renderer/src-web/App.tsx +40 -0
- package/src/renderer/src-web/components/MintDashboard.tsx +4 -2
- package/src/renderer/src-web/components/PicturesLibrary.tsx +3 -2
- package/src/renderer/src-web/components/PullToRefresh.tsx +107 -0
- package/src/renderer/src-web/components/SettingsWindow.tsx +28 -2
- package/src/renderer/src-web/css/app-shell.css +174 -96
- package/src/renderer/src-web/css/chat.css +1519 -316
- package/src/renderer/src-web/css/dashboard.css +11 -0
- package/src/renderer/src-web/css/misc.css +17 -17
- package/src/renderer/src-web/css/mobile.css +678 -0
- package/src/renderer/src-web/css/model.css +46 -46
- package/src/renderer/src-web/css/pictures.css +81 -34
- package/src/renderer/src-web/css/pull-to-refresh.css +54 -0
- package/src/renderer/src-web/css/settings/base.css +316 -100
- package/src/renderer/src-web/css/settings/mobile.css +320 -0
- package/src/renderer/src-web/css/sidebar-workspace.css +545 -100
- package/src/renderer/src-web/css/spotlight.css +9 -26
- package/src/renderer/src-web/css/widget.css +0 -5
- package/src/renderer/src-web/env.d.ts +3 -0
- package/src/renderer/src-web/hooks/useProviderModels.ts +2 -9
- package/src/renderer/src-web/index.css +53 -96
- package/src/renderer/src-web/main.tsx +11 -0
- package/src/renderer/src-web/tauri.ts +200 -30
- package/vite.config.web.ts +1 -0
- package/out/web/assets/MintDashboard-DWSqucgb.js +0 -57
- package/out/web/assets/SettingsWindow-DFM2aj5B.js +0 -2
- package/out/web/assets/chunk-CKBBP62Z-nr1uKPj9.js +0 -2
- package/out/web/assets/chunk-DSNCTWBM-Dtj8Rqsd.js +0 -2
- package/out/web/assets/chunk-DU6HZSFF-2tv5K1wT.js +0 -128
- package/out/web/assets/classDiagram-DTDB5LWJ-BdSFArgw.js +0 -1
- package/out/web/assets/classDiagram-v2-JRS7N3AN-BdSFArgw.js +0 -1
- package/out/web/assets/core-DImejIUG.js +0 -1
- package/out/web/assets/dist-js-BxuKEVWM.js +0 -1
- package/out/web/assets/event-BNl8Zahw.js +0 -1
- package/out/web/assets/fira-code-cyrillic-400-normal-UC0NFL4U.woff2 +0 -0
- package/out/web/assets/fira-code-cyrillic-400-normal-dc1q3VD7.woff +0 -0
- package/out/web/assets/fira-code-cyrillic-500-normal-Bu7VCL72.woff +0 -0
- package/out/web/assets/fira-code-cyrillic-500-normal-CX35h3Mg.woff2 +0 -0
- package/out/web/assets/fira-code-cyrillic-ext-400-normal-Buh61xzB.woff +0 -0
- package/out/web/assets/fira-code-cyrillic-ext-400-normal-txZ9Fk_1.woff2 +0 -0
- package/out/web/assets/fira-code-cyrillic-ext-500-normal-CYRspHgm.woff2 +0 -0
- package/out/web/assets/fira-code-cyrillic-ext-500-normal-CwtLmSFD.woff +0 -0
- package/out/web/assets/fira-code-greek-400-normal-B2Gh_Y8s.woff2 +0 -0
- package/out/web/assets/fira-code-greek-400-normal-DobhaxvF.woff +0 -0
- package/out/web/assets/fira-code-greek-500-normal-CVO0IRNt.woff2 +0 -0
- package/out/web/assets/fira-code-greek-500-normal-DEXIWiey.woff +0 -0
- package/out/web/assets/fira-code-greek-ext-400-normal-DR7mBgIM.woff2 +0 -0
- package/out/web/assets/fira-code-greek-ext-400-normal-DSPUheWg.woff +0 -0
- package/out/web/assets/fira-code-greek-ext-500-normal-BO3iq16n.woff2 +0 -0
- package/out/web/assets/fira-code-greek-ext-500-normal-CMiNLXER.woff +0 -0
- package/out/web/assets/fira-code-latin-ext-400-normal-DG73JjOf.woff +0 -0
- package/out/web/assets/fira-code-latin-ext-400-normal-KSMg0QLl.woff2 +0 -0
- package/out/web/assets/fira-code-latin-ext-500-normal-CaANqXjI.woff +0 -0
- package/out/web/assets/fira-code-latin-ext-500-normal-Crd9e_oI.woff2 +0 -0
- package/out/web/assets/flowDiagram-A5DVABFB-BpnHUSU7.js +0 -1
- package/out/web/assets/index-CVRg270z.css +0 -1
- package/out/web/assets/index-DxZQr9yD.js +0 -10
- package/out/web/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
- package/out/web/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
- package/out/web/assets/inter-cyrillic-600-normal-4D_pXhcN.woff +0 -0
- package/out/web/assets/inter-cyrillic-600-normal-CWCymEST.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
- package/out/web/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
- package/out/web/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff +0 -0
- package/out/web/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2 +0 -0
- package/out/web/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
- package/out/web/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
- package/out/web/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
- package/out/web/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
- package/out/web/assets/inter-greek-600-normal-BZpKdvQh.woff +0 -0
- package/out/web/assets/inter-greek-600-normal-plRanbMR.woff2 +0 -0
- package/out/web/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
- package/out/web/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
- package/out/web/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
- package/out/web/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
- package/out/web/assets/inter-greek-ext-600-normal-B8X0CLgF.woff +0 -0
- package/out/web/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2 +0 -0
- package/out/web/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
- package/out/web/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
- package/out/web/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
- package/out/web/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
- package/out/web/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
- package/out/web/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
- package/out/web/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
- package/out/web/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
- package/out/web/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
- package/out/web/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
- package/out/web/assets/inter-latin-ext-600-normal-CIVaiw4L.woff +0 -0
- package/out/web/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2 +0 -0
- package/out/web/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
- package/out/web/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
- package/out/web/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
- package/out/web/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
- package/out/web/assets/inter-vietnamese-600-normal-BuLX-rYi.woff +0 -0
- package/out/web/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2 +0 -0
- package/out/web/assets/kanit-latin-400-normal-BUlsQXH3.woff2 +0 -0
- package/out/web/assets/kanit-latin-400-normal-HVZWrfev.woff +0 -0
- package/out/web/assets/kanit-latin-500-normal-CX_4XcKi.woff2 +0 -0
- package/out/web/assets/kanit-latin-500-normal-qBha86O4.woff +0 -0
- package/out/web/assets/kanit-latin-600-normal-0lS78yAd.woff2 +0 -0
- package/out/web/assets/kanit-latin-600-normal-DlacpeTg.woff +0 -0
- package/out/web/assets/kanit-latin-ext-400-normal-B2xPf58u.woff2 +0 -0
- package/out/web/assets/kanit-latin-ext-400-normal-CHvNKHHD.woff +0 -0
- package/out/web/assets/kanit-latin-ext-500-normal-BFdWrG22.woff +0 -0
- package/out/web/assets/kanit-latin-ext-500-normal-EHs3suD6.woff2 +0 -0
- package/out/web/assets/kanit-latin-ext-600-normal-BBDzoLaj.woff +0 -0
- package/out/web/assets/kanit-latin-ext-600-normal-DAX3wtuq.woff2 +0 -0
- package/out/web/assets/kanit-thai-400-normal-D2nliXKa.woff +0 -0
- package/out/web/assets/kanit-thai-400-normal-D80Pglro.woff2 +0 -0
- package/out/web/assets/kanit-thai-500-normal-BB8gri04.woff2 +0 -0
- package/out/web/assets/kanit-thai-500-normal-Iy_w0a80.woff +0 -0
- package/out/web/assets/kanit-thai-600-normal-Bj6oiaDc.woff2 +0 -0
- package/out/web/assets/kanit-thai-600-normal-wbTlPO7I.woff +0 -0
- package/out/web/assets/kanit-vietnamese-400-normal-DRI8YWyb.woff2 +0 -0
- package/out/web/assets/kanit-vietnamese-400-normal-dow6Bnto.woff +0 -0
- package/out/web/assets/kanit-vietnamese-500-normal-BzTrpCM3.woff2 +0 -0
- package/out/web/assets/kanit-vietnamese-500-normal-T1ndT7MF.woff +0 -0
- package/out/web/assets/kanit-vietnamese-600-normal-Bi7fw3I4.woff2 +0 -0
- package/out/web/assets/kanit-vietnamese-600-normal-DXCRAa_8.woff +0 -0
- package/out/web/assets/mali-latin-400-normal-ByPcZ8Gx.woff +0 -0
- package/out/web/assets/mali-latin-400-normal-Cuc6Ykl8.woff2 +0 -0
- package/out/web/assets/mali-latin-500-normal-B01M-LP6.woff2 +0 -0
- package/out/web/assets/mali-latin-500-normal-Thgqw_9f.woff +0 -0
- package/out/web/assets/mali-latin-600-normal-B9TiUNpT.woff2 +0 -0
- package/out/web/assets/mali-latin-600-normal-CycpQCwU.woff +0 -0
- package/out/web/assets/mali-latin-ext-400-normal-DUVdJQO4.woff +0 -0
- package/out/web/assets/mali-latin-ext-400-normal-iCG1ALuJ.woff2 +0 -0
- package/out/web/assets/mali-latin-ext-500-normal-BfJE4UMZ.woff2 +0 -0
- package/out/web/assets/mali-latin-ext-500-normal-FoLEyOKS.woff +0 -0
- package/out/web/assets/mali-latin-ext-600-normal-BTvIfiCp.woff2 +0 -0
- package/out/web/assets/mali-latin-ext-600-normal-DAQcLVmD.woff +0 -0
- package/out/web/assets/mali-thai-400-normal-BO7A3bLZ.woff2 +0 -0
- package/out/web/assets/mali-thai-400-normal-CX9VrjTn.woff +0 -0
- package/out/web/assets/mali-thai-500-normal-Cph2qha9.woff +0 -0
- package/out/web/assets/mali-thai-500-normal-D89rMT77.woff2 +0 -0
- package/out/web/assets/mali-thai-600-normal-DPgXT6rZ.woff +0 -0
- package/out/web/assets/mali-thai-600-normal-DlTx3OsB.woff2 +0 -0
- package/out/web/assets/mali-vietnamese-400-normal-DUKxzXaz.woff2 +0 -0
- package/out/web/assets/mali-vietnamese-400-normal-gFDLvsOK.woff +0 -0
- package/out/web/assets/mali-vietnamese-500-normal--OY07q2c.woff +0 -0
- package/out/web/assets/mali-vietnamese-500-normal-KDrNRwfW.woff2 +0 -0
- package/out/web/assets/mali-vietnamese-600-normal-CtTMUU7T.woff +0 -0
- package/out/web/assets/mali-vietnamese-600-normal-DFlJr66u.woff2 +0 -0
- package/out/web/assets/mermaid.core-DYGGlqOf.js +0 -11
- package/out/web/assets/mitr-latin-400-normal-Cb18vPmZ.woff +0 -0
- package/out/web/assets/mitr-latin-400-normal-CwbkLQ4a.woff2 +0 -0
- package/out/web/assets/mitr-latin-500-normal-B4JOhzAi.woff +0 -0
- package/out/web/assets/mitr-latin-500-normal-BmClOow2.woff2 +0 -0
- package/out/web/assets/mitr-latin-ext-400-normal-B3CPG7Yo.woff2 +0 -0
- package/out/web/assets/mitr-latin-ext-400-normal-DTx65hiC.woff +0 -0
- package/out/web/assets/mitr-latin-ext-500-normal-B_Diajz1.woff2 +0 -0
- package/out/web/assets/mitr-latin-ext-500-normal-DYOsVaVT.woff +0 -0
- package/out/web/assets/mitr-thai-400-normal-CS4Do6aM.woff +0 -0
- package/out/web/assets/mitr-thai-400-normal-CXWaAdVj.woff2 +0 -0
- package/out/web/assets/mitr-thai-500-normal-CVdBrEVH.woff2 +0 -0
- package/out/web/assets/mitr-thai-500-normal-CW_B5D-i.woff +0 -0
- package/out/web/assets/mitr-vietnamese-400-normal-CPMGWyoC.woff2 +0 -0
- package/out/web/assets/mitr-vietnamese-400-normal-Z6XhIpdY.woff +0 -0
- package/out/web/assets/mitr-vietnamese-500-normal-CDrv0585.woff2 +0 -0
- package/out/web/assets/mitr-vietnamese-500-normal-CMQWyq-Z.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-400-normal-BaWBrqFP.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-400-normal-Dh_FJjOB.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-500-normal-BC04jsmm.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-500-normal-fyBKClF1.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-600-normal-CemcjnVR.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-600-normal-Dbc-dNO6.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-400-normal-B-1H0YkH.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-400-normal-nBP59pVw.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-500-normal-B-ZWb8SZ.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-500-normal-CrEHO7C8.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-600-normal-PkMZo0xa.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-600-normal-maHHBsez.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-thai-400-normal-BEOmKAI1.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-thai-400-normal-Dxb_unuU.woff +0 -0
- package/out/web/assets/noto-sans-thai-thai-500-normal-BOGXO2tw.woff +0 -0
- package/out/web/assets/noto-sans-thai-thai-500-normal-CU_X86GQ.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-thai-600-normal-CmgB8_JK.woff +0 -0
- package/out/web/assets/noto-sans-thai-thai-600-normal-Cs73sVjl.woff2 +0 -0
- package/out/web/assets/oauthManager-DNiwM8Rn.js +0 -2
- package/out/web/assets/oauthManager-DuamS1Av.css +0 -1
- package/out/web/assets/outfit-latin-300-normal-AWLfuCom.woff +0 -0
- package/out/web/assets/outfit-latin-300-normal-CRYTwY6m.woff2 +0 -0
- package/out/web/assets/outfit-latin-400-normal-BGsTXAXT.woff2 +0 -0
- package/out/web/assets/outfit-latin-400-normal-DMwTpYkH.woff +0 -0
- package/out/web/assets/outfit-latin-500-normal-ClnHRwRh.woff +0 -0
- package/out/web/assets/outfit-latin-500-normal-DKnIMDSk.woff2 +0 -0
- package/out/web/assets/outfit-latin-600-normal-B7SfZ07L.woff2 +0 -0
- package/out/web/assets/outfit-latin-600-normal-BEfTtDA7.woff +0 -0
- package/out/web/assets/outfit-latin-ext-300-normal-BYS6Dpvq.woff2 +0 -0
- package/out/web/assets/outfit-latin-ext-300-normal-CMIMshEd.woff +0 -0
- package/out/web/assets/outfit-latin-ext-400-normal-5tcqmc2S.woff2 +0 -0
- package/out/web/assets/outfit-latin-ext-400-normal-DHm7mdGe.woff +0 -0
- package/out/web/assets/outfit-latin-ext-500-normal-DrCvqoFD.woff +0 -0
- package/out/web/assets/outfit-latin-ext-500-normal-zeox_O30.woff2 +0 -0
- package/out/web/assets/outfit-latin-ext-600-normal-B85nYjL1.woff2 +0 -0
- package/out/web/assets/outfit-latin-ext-600-normal-CWJcPgd7.woff +0 -0
- package/out/web/assets/preload-helper-D0uHx7FM.js +0 -1
- package/out/web/assets/prompt-latin-ext-400-normal-1pHbN9uy.woff +0 -0
- package/out/web/assets/prompt-latin-ext-400-normal-DdSafGZ9.woff2 +0 -0
- package/out/web/assets/prompt-latin-ext-500-normal--EZ1um7s.woff2 +0 -0
- package/out/web/assets/prompt-latin-ext-500-normal-D-lI1EVT.woff +0 -0
- package/out/web/assets/prompt-latin-ext-600-normal-C8b50x67.woff +0 -0
- package/out/web/assets/prompt-latin-ext-600-normal-Cg9L7iJU.woff2 +0 -0
- package/out/web/assets/prompt-vietnamese-400-normal-BCPzsgPT.woff2 +0 -0
- package/out/web/assets/prompt-vietnamese-400-normal-D4pLyeNK.woff +0 -0
- package/out/web/assets/prompt-vietnamese-500-normal-DmzxmPwa.woff2 +0 -0
- package/out/web/assets/prompt-vietnamese-500-normal-w_sS_j2F.woff +0 -0
- package/out/web/assets/prompt-vietnamese-600-normal-1dyohtNh.woff +0 -0
- package/out/web/assets/prompt-vietnamese-600-normal-7QWjJBsF.woff2 +0 -0
- package/out/web/assets/sarabun-latin-400-normal-DmguIS-7.woff +0 -0
- package/out/web/assets/sarabun-latin-400-normal-URPBxl-K.woff2 +0 -0
- package/out/web/assets/sarabun-latin-500-normal-BjUTcdxu.woff2 +0 -0
- package/out/web/assets/sarabun-latin-500-normal-CJexTAnl.woff +0 -0
- package/out/web/assets/sarabun-latin-600-normal-9FonlkKx.woff +0 -0
- package/out/web/assets/sarabun-latin-600-normal-DMD3TROr.woff2 +0 -0
- package/out/web/assets/sarabun-latin-ext-400-normal-BbP7GpLL.woff2 +0 -0
- package/out/web/assets/sarabun-latin-ext-400-normal-BsmTn0SS.woff +0 -0
- package/out/web/assets/sarabun-latin-ext-500-normal-Cot16Bd2.woff +0 -0
- package/out/web/assets/sarabun-latin-ext-500-normal-DE1LysqI.woff2 +0 -0
- package/out/web/assets/sarabun-latin-ext-600-normal-0tXu9x_i.woff +0 -0
- package/out/web/assets/sarabun-latin-ext-600-normal-BRxjKUQ6.woff2 +0 -0
- package/out/web/assets/sarabun-thai-400-normal-C2DaJlKK.woff2 +0 -0
- package/out/web/assets/sarabun-thai-400-normal-CH-UbWgg.woff +0 -0
- package/out/web/assets/sarabun-thai-500-normal-BVssjame.woff2 +0 -0
- package/out/web/assets/sarabun-thai-500-normal-C3iDLEzi.woff +0 -0
- package/out/web/assets/sarabun-thai-600-normal-3dIAQ-_s.woff2 +0 -0
- package/out/web/assets/sarabun-thai-600-normal-DWKeTe6a.woff +0 -0
- package/out/web/assets/sarabun-vietnamese-400-normal-DPfkSCMy.woff +0 -0
- package/out/web/assets/sarabun-vietnamese-400-normal-igdrzAGY.woff2 +0 -0
- package/out/web/assets/sarabun-vietnamese-500-normal-CpRVvGRU.woff +0 -0
- package/out/web/assets/sarabun-vietnamese-500-normal-Dpe7Br9m.woff2 +0 -0
- package/out/web/assets/sarabun-vietnamese-600-normal-BMbOgAW-.woff +0 -0
- package/out/web/assets/sarabun-vietnamese-600-normal-DkxrOlLB.woff2 +0 -0
- package/out/web/assets/stateDiagram-v2-4QOOHH4V-s6P9g2_b.js +0 -1
- package/out/web/assets/swimlanesDiagram-VK2B7HYN-CV9fOLrV.js +0 -8
- package/out/web/assets/tauri-CNrHQ2Gb.js +0 -3
- package/out/web/assets/window-DkIhgI7B.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use std::path::{Path, PathBuf};
|
|
2
2
|
|
|
3
|
-
use rusqlite::{Connection, OptionalExtension, params};
|
|
3
|
+
use rusqlite::{Connection, OptionalExtension, TransactionBehavior, params};
|
|
4
4
|
use serde::{Deserialize, Serialize};
|
|
5
5
|
use thiserror::Error;
|
|
6
6
|
|
|
@@ -27,11 +27,17 @@ pub fn scoped_chat_id(chat_id: &str, _workspace_path: Option<&str>) -> String {
|
|
|
27
27
|
/// (`"cli::subagent::<name>"`, or `"cli::<hash>::subagent::<name>"`) —
|
|
28
28
|
/// those must keep behaving like a normal, deletable conversation exactly
|
|
29
29
|
/// as they do today, unaffected by workspace scoping.
|
|
30
|
-
fn is_cli_chat_id(chat_id: &str) -> bool {
|
|
30
|
+
pub fn is_cli_chat_id(chat_id: &str) -> bool {
|
|
31
31
|
chat_id == CHAT_CLI_ID
|
|
32
32
|
|| (chat_id.starts_with(&format!("{CHAT_CLI_ID}::")) && !chat_id.contains("::subagent::"))
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
pub fn generate_cli_session_id() -> String {
|
|
36
|
+
let raw_uuid = uuid::Uuid::new_v4().to_string();
|
|
37
|
+
let short_id = &raw_uuid.replace('-', "")[..12];
|
|
38
|
+
format!("cli::{short_id}")
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
/// The subagent name embedded in a chat id of the form
|
|
36
42
|
/// `"<parent>::subagent::<name>"` (see `dispatch_one_subagent`), or `None` for
|
|
37
43
|
/// an ordinary conversation. Used to scope which agent a fact belongs to and
|
|
@@ -72,11 +78,36 @@ pub struct InteractionMemory {
|
|
|
72
78
|
pub model: String,
|
|
73
79
|
pub fallback_provider: Option<String>,
|
|
74
80
|
pub created_at: String,
|
|
81
|
+
pub status: String,
|
|
75
82
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
76
83
|
pub agent_activity: Option<serde_json::Value>,
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
#[derive(Debug, Clone, Serialize
|
|
86
|
+
#[derive(Debug, Clone, Serialize)]
|
|
87
|
+
#[serde(rename_all = "camelCase")]
|
|
88
|
+
pub struct ConversationChange {
|
|
89
|
+
pub sequence: i64,
|
|
90
|
+
pub interaction_id: i64,
|
|
91
|
+
pub interaction: Option<InteractionMemory>,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#[derive(Debug, Clone, Serialize)]
|
|
95
|
+
#[serde(rename_all = "camelCase")]
|
|
96
|
+
pub struct ConversationChanges {
|
|
97
|
+
pub changes: Vec<ConversationChange>,
|
|
98
|
+
pub cursor: i64,
|
|
99
|
+
pub has_more: bool,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#[derive(Debug, Clone, Serialize)]
|
|
103
|
+
#[serde(rename_all = "camelCase")]
|
|
104
|
+
pub struct ConversationSnapshot {
|
|
105
|
+
pub interactions: Vec<InteractionMemory>,
|
|
106
|
+
pub cursor: i64,
|
|
107
|
+
pub has_older: bool,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
80
111
|
#[serde(rename_all = "camelCase")]
|
|
81
112
|
pub struct ChatSession {
|
|
82
113
|
pub id: String,
|
|
@@ -84,6 +115,16 @@ pub struct ChatSession {
|
|
|
84
115
|
pub kind: String,
|
|
85
116
|
pub created_at: String,
|
|
86
117
|
pub updated_at: String,
|
|
118
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
119
|
+
pub workspace_path: Option<String>,
|
|
120
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
121
|
+
pub git_branch: Option<String>,
|
|
122
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
123
|
+
pub main_language: Option<String>,
|
|
124
|
+
#[serde(default)]
|
|
125
|
+
pub message_count: usize,
|
|
126
|
+
#[serde(default)]
|
|
127
|
+
pub total_bytes: usize,
|
|
87
128
|
}
|
|
88
129
|
|
|
89
130
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
@@ -220,6 +261,232 @@ impl MemoryStore {
|
|
|
220
261
|
Ok(connection.last_insert_rowid())
|
|
221
262
|
}
|
|
222
263
|
|
|
264
|
+
/// Insert a visible prompt before any provider or tool work begins.
|
|
265
|
+
pub fn start_turn(&self, chat_id: &str, user_text: &str) -> Result<i64, MemoryError> {
|
|
266
|
+
let chat_id = normalized_chat_id(chat_id);
|
|
267
|
+
let mut connection = self.connection()?;
|
|
268
|
+
let tx = connection.transaction_with_behavior(TransactionBehavior::Immediate)?;
|
|
269
|
+
ensure_chat_session_row(&tx, &chat_id)?;
|
|
270
|
+
tx.execute(
|
|
271
|
+
"INSERT INTO interaction_memories (chat_id, user_text, ai_text, status, lease_until)
|
|
272
|
+
VALUES (?1, ?2, '', 'queued', unixepoch() + 45)",
|
|
273
|
+
params![chat_id, user_text],
|
|
274
|
+
)?;
|
|
275
|
+
let id = tx.last_insert_rowid();
|
|
276
|
+
tx.execute(
|
|
277
|
+
"UPDATE chat_sessions SET title = CASE
|
|
278
|
+
WHEN title = 'New chat' AND trim(?2) != '' THEN substr(trim(?2), 1, 80)
|
|
279
|
+
ELSE title END, updated_at = CURRENT_TIMESTAMP WHERE id = ?1",
|
|
280
|
+
params![chat_id, user_text],
|
|
281
|
+
)?;
|
|
282
|
+
tx.commit()?;
|
|
283
|
+
Ok(id)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/// Claim only the oldest unfinished turn. The short transaction never spans AI work.
|
|
287
|
+
pub fn claim_turn(&self, chat_id: &str, id: i64) -> Result<bool, MemoryError> {
|
|
288
|
+
let chat_id = normalized_chat_id(chat_id);
|
|
289
|
+
let mut connection = self.connection()?;
|
|
290
|
+
let tx = connection.transaction_with_behavior(TransactionBehavior::Immediate)?;
|
|
291
|
+
tx.execute(
|
|
292
|
+
"UPDATE interaction_memories SET status = 'interrupted', lease_until = 0
|
|
293
|
+
WHERE chat_id = ?1 AND status IN ('queued', 'running') AND lease_until < unixepoch()",
|
|
294
|
+
params![chat_id],
|
|
295
|
+
)?;
|
|
296
|
+
let own_status: Option<String> = tx
|
|
297
|
+
.query_row(
|
|
298
|
+
"SELECT status FROM interaction_memories WHERE id = ?1 AND chat_id = ?2",
|
|
299
|
+
params![id, chat_id],
|
|
300
|
+
|row| row.get(0),
|
|
301
|
+
)
|
|
302
|
+
.optional()?;
|
|
303
|
+
if own_status.as_deref() != Some("queued") {
|
|
304
|
+
tx.commit()?;
|
|
305
|
+
return Err(MemoryError::Database(rusqlite::Error::QueryReturnedNoRows));
|
|
306
|
+
}
|
|
307
|
+
let earlier: i64 = tx.query_row(
|
|
308
|
+
"SELECT count(*) FROM interaction_memories
|
|
309
|
+
WHERE chat_id = ?1 AND id < ?2 AND status IN ('queued', 'running')",
|
|
310
|
+
params![chat_id, id],
|
|
311
|
+
|row| row.get(0),
|
|
312
|
+
)?;
|
|
313
|
+
let claimed = if earlier == 0 {
|
|
314
|
+
tx.execute(
|
|
315
|
+
"UPDATE interaction_memories SET status = 'running', lease_until = unixepoch() + 45
|
|
316
|
+
WHERE id = ?1 AND chat_id = ?2 AND status = 'queued'",
|
|
317
|
+
params![id, chat_id],
|
|
318
|
+
)? == 1
|
|
319
|
+
} else {
|
|
320
|
+
false
|
|
321
|
+
};
|
|
322
|
+
tx.commit()?;
|
|
323
|
+
Ok(claimed)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
pub fn renew_turn(&self, id: i64) -> Result<(), MemoryError> {
|
|
327
|
+
self.connection()?.execute(
|
|
328
|
+
"UPDATE interaction_memories SET lease_until = unixepoch() + 45
|
|
329
|
+
WHERE id = ?1 AND status IN ('queued', 'running')",
|
|
330
|
+
params![id],
|
|
331
|
+
)?;
|
|
332
|
+
Ok(())
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
fn expire_abandoned_turns(&self, chat_id: &str) -> Result<(), MemoryError> {
|
|
336
|
+
self.connection()?.execute(
|
|
337
|
+
"UPDATE interaction_memories SET status = 'interrupted', lease_until = 0
|
|
338
|
+
WHERE chat_id = ?1 AND status IN ('queued', 'running') AND lease_until < unixepoch()",
|
|
339
|
+
params![normalized_chat_id(chat_id)],
|
|
340
|
+
)?;
|
|
341
|
+
Ok(())
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
pub fn finish_turn(
|
|
345
|
+
&self,
|
|
346
|
+
id: i64,
|
|
347
|
+
ai_text: &str,
|
|
348
|
+
provider: &str,
|
|
349
|
+
model: &str,
|
|
350
|
+
fallback_provider: Option<&str>,
|
|
351
|
+
) -> Result<(), MemoryError> {
|
|
352
|
+
let connection = self.connection()?;
|
|
353
|
+
let changed = connection.execute(
|
|
354
|
+
"UPDATE interaction_memories SET ai_text = ?2, provider = ?3, model = ?4,
|
|
355
|
+
fallback_provider = ?5, status = 'completed', lease_until = 0
|
|
356
|
+
WHERE id = ?1 AND status = 'running'",
|
|
357
|
+
params![id, ai_text, provider, model, fallback_provider],
|
|
358
|
+
)?;
|
|
359
|
+
if changed != 1 {
|
|
360
|
+
return Err(MemoryError::Database(rusqlite::Error::QueryReturnedNoRows));
|
|
361
|
+
}
|
|
362
|
+
Ok(())
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
pub fn end_turn(&self, id: i64, status: &str) -> Result<(), MemoryError> {
|
|
366
|
+
if !matches!(status, "failed" | "interrupted") {
|
|
367
|
+
return Ok(());
|
|
368
|
+
}
|
|
369
|
+
self.connection()?.execute(
|
|
370
|
+
"UPDATE interaction_memories SET status = ?2, lease_until = 0
|
|
371
|
+
WHERE id = ?1 AND status IN ('queued', 'running')",
|
|
372
|
+
params![id, status],
|
|
373
|
+
)?;
|
|
374
|
+
Ok(())
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
pub fn conversation_changes(
|
|
378
|
+
&self,
|
|
379
|
+
chat_id: &str,
|
|
380
|
+
after: i64,
|
|
381
|
+
limit: usize,
|
|
382
|
+
) -> Result<ConversationChanges, MemoryError> {
|
|
383
|
+
let chat_id = normalized_chat_id(chat_id);
|
|
384
|
+
self.expire_abandoned_turns(&chat_id)?;
|
|
385
|
+
let connection = self.connection()?;
|
|
386
|
+
let mut statement = connection.prepare(
|
|
387
|
+
"SELECT c.sequence, c.interaction_id,
|
|
388
|
+
m.id, m.chat_id, m.user_text, m.ai_text, m.provider, m.model,
|
|
389
|
+
m.fallback_provider, m.created_at, m.agent_activity_json, m.status
|
|
390
|
+
FROM conversation_changes c
|
|
391
|
+
LEFT JOIN interaction_memories m ON m.id = c.interaction_id AND m.chat_id = c.chat_id
|
|
392
|
+
WHERE c.chat_id = ?1 AND c.sequence > ?2 ORDER BY c.sequence LIMIT ?3",
|
|
393
|
+
)?;
|
|
394
|
+
let rows = statement.query_map(
|
|
395
|
+
params![chat_id, after, limit.clamp(1, 200) as i64 + 1],
|
|
396
|
+
|row| {
|
|
397
|
+
let sequence: i64 = row.get(0)?;
|
|
398
|
+
let interaction_id: i64 = row.get(1)?;
|
|
399
|
+
let interaction = if row.get::<_, Option<i64>>(2)?.is_some() {
|
|
400
|
+
let agent_activity = row
|
|
401
|
+
.get::<_, Option<String>>(10)?
|
|
402
|
+
.and_then(|raw| serde_json::from_str(&raw).ok());
|
|
403
|
+
let created: String = row.get(9)?;
|
|
404
|
+
Some(InteractionMemory {
|
|
405
|
+
id: row.get(2)?,
|
|
406
|
+
chat_id: row.get(3)?,
|
|
407
|
+
user_text: row.get(4)?,
|
|
408
|
+
ai_text: row.get(5)?,
|
|
409
|
+
provider: row.get(6)?,
|
|
410
|
+
model: row.get(7)?,
|
|
411
|
+
fallback_provider: row.get(8)?,
|
|
412
|
+
created_at: normalize_sqlite_utc_timestamp(&created),
|
|
413
|
+
agent_activity,
|
|
414
|
+
status: row.get(11)?,
|
|
415
|
+
})
|
|
416
|
+
} else {
|
|
417
|
+
None
|
|
418
|
+
};
|
|
419
|
+
Ok(ConversationChange {
|
|
420
|
+
sequence,
|
|
421
|
+
interaction_id,
|
|
422
|
+
interaction,
|
|
423
|
+
})
|
|
424
|
+
},
|
|
425
|
+
)?;
|
|
426
|
+
let mut changes = rows.collect::<Result<Vec<_>, _>>()?;
|
|
427
|
+
let has_more = changes.len() > limit.clamp(1, 200);
|
|
428
|
+
if has_more {
|
|
429
|
+
changes.pop();
|
|
430
|
+
}
|
|
431
|
+
let cursor = changes.last().map_or(after, |change| change.sequence);
|
|
432
|
+
Ok(ConversationChanges {
|
|
433
|
+
changes,
|
|
434
|
+
cursor,
|
|
435
|
+
has_more,
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
pub fn latest_conversation_sequence(&self, chat_id: &str) -> Result<i64, MemoryError> {
|
|
440
|
+
self.expire_abandoned_turns(chat_id)?;
|
|
441
|
+
let connection = self.connection()?;
|
|
442
|
+
Ok(connection.query_row(
|
|
443
|
+
"SELECT coalesce(max(sequence), 0) FROM conversation_changes WHERE chat_id = ?1",
|
|
444
|
+
params![normalized_chat_id(chat_id)],
|
|
445
|
+
|row| row.get(0),
|
|
446
|
+
)?)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
pub fn conversation_snapshot(
|
|
450
|
+
&self,
|
|
451
|
+
chat_id: &str,
|
|
452
|
+
before_id: Option<i64>,
|
|
453
|
+
limit: usize,
|
|
454
|
+
) -> Result<ConversationSnapshot, MemoryError> {
|
|
455
|
+
let chat_id = normalized_chat_id(chat_id);
|
|
456
|
+
self.expire_abandoned_turns(&chat_id)?;
|
|
457
|
+
let mut connection = self.connection()?;
|
|
458
|
+
let tx = connection.transaction()?;
|
|
459
|
+
let cursor: i64 = tx.query_row(
|
|
460
|
+
"SELECT coalesce(max(sequence), 0) FROM conversation_changes WHERE chat_id = ?1",
|
|
461
|
+
params![chat_id],
|
|
462
|
+
|row| row.get(0),
|
|
463
|
+
)?;
|
|
464
|
+
let mut statement = tx.prepare(
|
|
465
|
+
"SELECT id, chat_id, user_text, ai_text, provider, model, fallback_provider,
|
|
466
|
+
created_at, agent_activity_json, status FROM interaction_memories
|
|
467
|
+
WHERE chat_id = ?1 AND (?2 IS NULL OR id < ?2)
|
|
468
|
+
ORDER BY id DESC LIMIT ?3",
|
|
469
|
+
)?;
|
|
470
|
+
let page_size = limit.clamp(1, 200);
|
|
471
|
+
let rows = statement.query_map(
|
|
472
|
+
params![chat_id, before_id, page_size as i64 + 1],
|
|
473
|
+
interaction_row,
|
|
474
|
+
)?;
|
|
475
|
+
let mut interactions = rows.collect::<Result<Vec<_>, _>>()?;
|
|
476
|
+
let has_older = interactions.len() > page_size;
|
|
477
|
+
if has_older {
|
|
478
|
+
interactions.pop();
|
|
479
|
+
}
|
|
480
|
+
interactions.reverse();
|
|
481
|
+
drop(statement);
|
|
482
|
+
tx.commit()?;
|
|
483
|
+
Ok(ConversationSnapshot {
|
|
484
|
+
interactions,
|
|
485
|
+
cursor,
|
|
486
|
+
has_older,
|
|
487
|
+
})
|
|
488
|
+
}
|
|
489
|
+
|
|
223
490
|
pub fn recent_interactions(&self, limit: usize) -> Result<Vec<InteractionMemory>, MemoryError> {
|
|
224
491
|
self.recent_interactions_for_chat(DEFAULT_CONVERSATION_ID, limit)
|
|
225
492
|
}
|
|
@@ -233,7 +500,7 @@ impl MemoryStore {
|
|
|
233
500
|
let connection = self.connection()?;
|
|
234
501
|
ensure_builtin_chat_sessions(&connection)?;
|
|
235
502
|
let mut statement = connection.prepare(
|
|
236
|
-
"SELECT id, chat_id, user_text, ai_text, provider, model, fallback_provider, created_at, agent_activity_json
|
|
503
|
+
"SELECT id, chat_id, user_text, ai_text, provider, model, fallback_provider, created_at, agent_activity_json, status
|
|
237
504
|
FROM interaction_memories
|
|
238
505
|
WHERE chat_id = ?1
|
|
239
506
|
ORDER BY id DESC
|
|
@@ -243,6 +510,45 @@ impl MemoryStore {
|
|
|
243
510
|
rows.collect::<Result<Vec<_>, _>>().map_err(Into::into)
|
|
244
511
|
}
|
|
245
512
|
|
|
513
|
+
pub fn recent_completed_interactions_for_chat(
|
|
514
|
+
&self,
|
|
515
|
+
chat_id: &str,
|
|
516
|
+
limit: usize,
|
|
517
|
+
) -> Result<Vec<InteractionMemory>, MemoryError> {
|
|
518
|
+
let connection = self.connection()?;
|
|
519
|
+
let mut statement = connection.prepare(
|
|
520
|
+
"SELECT id, chat_id, user_text, ai_text, provider, model, fallback_provider,
|
|
521
|
+
created_at, agent_activity_json, status FROM interaction_memories
|
|
522
|
+
WHERE chat_id = ?1 AND status = 'completed' ORDER BY id DESC LIMIT ?2",
|
|
523
|
+
)?;
|
|
524
|
+
let rows = statement.query_map(
|
|
525
|
+
params![normalized_chat_id(chat_id), limit as i64],
|
|
526
|
+
interaction_row,
|
|
527
|
+
)?;
|
|
528
|
+
rows.collect::<Result<Vec<_>, _>>().map_err(Into::into)
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/// Returns the complete transcript for one conversation in display order.
|
|
532
|
+
/// This deliberately has no caller supplied `LIMIT`: passing `usize::MAX`
|
|
533
|
+
/// through SQLite's signed limit is not portable and can wrap on 64-bit
|
|
534
|
+
/// platforms.
|
|
535
|
+
pub fn interactions_for_chat(
|
|
536
|
+
&self,
|
|
537
|
+
chat_id: &str,
|
|
538
|
+
) -> Result<Vec<InteractionMemory>, MemoryError> {
|
|
539
|
+
let chat_id = normalized_chat_id(chat_id);
|
|
540
|
+
let connection = self.connection()?;
|
|
541
|
+
ensure_builtin_chat_sessions(&connection)?;
|
|
542
|
+
let mut statement = connection.prepare(
|
|
543
|
+
"SELECT id, chat_id, user_text, ai_text, provider, model, fallback_provider, created_at, agent_activity_json, status
|
|
544
|
+
FROM interaction_memories
|
|
545
|
+
WHERE chat_id = ?1
|
|
546
|
+
ORDER BY id ASC",
|
|
547
|
+
)?;
|
|
548
|
+
let rows = statement.query_map(params![chat_id], interaction_row)?;
|
|
549
|
+
rows.collect::<Result<Vec<_>, _>>().map_err(Into::into)
|
|
550
|
+
}
|
|
551
|
+
|
|
246
552
|
/// Full-text search over `chat_id`'s past interactions for the turns most
|
|
247
553
|
/// relevant to `query` (BM25 ranked), skipping the `exclude_recent` newest
|
|
248
554
|
/// rows since those are already injected verbatim elsewhere. Returns
|
|
@@ -262,11 +568,12 @@ impl MemoryStore {
|
|
|
262
568
|
let connection = self.connection()?;
|
|
263
569
|
let mut statement = connection.prepare(
|
|
264
570
|
"SELECT m.id, m.chat_id, m.user_text, m.ai_text, m.provider, m.model,
|
|
265
|
-
m.fallback_provider, m.created_at, m.agent_activity_json
|
|
571
|
+
m.fallback_provider, m.created_at, m.agent_activity_json, m.status
|
|
266
572
|
FROM interaction_fts
|
|
267
573
|
JOIN interaction_memories m ON m.id = interaction_fts.rowid
|
|
268
574
|
WHERE interaction_fts MATCH ?1
|
|
269
575
|
AND m.chat_id = ?2
|
|
576
|
+
AND m.status = 'completed'
|
|
270
577
|
AND m.id NOT IN (
|
|
271
578
|
SELECT id FROM interaction_memories
|
|
272
579
|
WHERE chat_id = ?2 ORDER BY id DESC LIMIT ?3
|
|
@@ -286,31 +593,121 @@ impl MemoryStore {
|
|
|
286
593
|
ensure_builtin_chat_sessions(&connection)?;
|
|
287
594
|
let _ = connection.execute(
|
|
288
595
|
"DELETE FROM chat_sessions
|
|
289
|
-
WHERE
|
|
290
|
-
AND id != ?
|
|
596
|
+
WHERE id != ?1
|
|
597
|
+
AND id != ?2
|
|
291
598
|
AND id NOT LIKE 'cron::%'
|
|
292
599
|
AND (SELECT COUNT(*) FROM interaction_memories WHERE interaction_memories.chat_id = chat_sessions.id) = 0",
|
|
293
|
-
params![DEFAULT_CONVERSATION_ID],
|
|
600
|
+
params![DEFAULT_CONVERSATION_ID, CHAT_CLI_ID],
|
|
294
601
|
);
|
|
295
602
|
let mut statement = connection.prepare(
|
|
296
|
-
"SELECT id, title, kind, created_at, updated_at
|
|
297
|
-
|
|
298
|
-
|
|
603
|
+
"SELECT s.id, s.title, s.kind, s.created_at, s.updated_at,
|
|
604
|
+
s.workspace_path, s.git_branch, s.main_language,
|
|
605
|
+
COUNT(m.id) AS msg_count,
|
|
606
|
+
COALESCE(SUM(LENGTH(m.user_text) + LENGTH(m.ai_text)), 0) AS total_bytes
|
|
607
|
+
FROM chat_sessions s
|
|
608
|
+
LEFT JOIN interaction_memories m ON m.chat_id = s.id
|
|
609
|
+
GROUP BY s.id
|
|
610
|
+
ORDER BY CASE WHEN s.id = ?1 THEN 0 ELSE 1 END, s.updated_at DESC",
|
|
299
611
|
)?;
|
|
300
612
|
let rows = statement.query_map(params![CHAT_CLI_ID], |row| {
|
|
301
613
|
let created_raw: String = row.get(3)?;
|
|
302
614
|
let updated_raw: String = row.get(4)?;
|
|
615
|
+
let msg_count: i64 = row.get(8)?;
|
|
616
|
+
let bytes: i64 = row.get(9)?;
|
|
303
617
|
Ok(ChatSession {
|
|
304
618
|
id: row.get(0)?,
|
|
305
619
|
title: row.get(1)?,
|
|
306
620
|
kind: row.get(2)?,
|
|
307
621
|
created_at: normalize_sqlite_utc_timestamp(&created_raw),
|
|
308
622
|
updated_at: normalize_sqlite_utc_timestamp(&updated_raw),
|
|
623
|
+
workspace_path: row.get(5)?,
|
|
624
|
+
git_branch: row.get(6)?,
|
|
625
|
+
main_language: row.get(7)?,
|
|
626
|
+
message_count: msg_count.max(0) as usize,
|
|
627
|
+
total_bytes: bytes.max(0) as usize,
|
|
309
628
|
})
|
|
310
629
|
})?;
|
|
311
630
|
rows.collect::<Result<Vec<_>, _>>().map_err(Into::into)
|
|
312
631
|
}
|
|
313
632
|
|
|
633
|
+
pub fn update_chat_session_metadata(
|
|
634
|
+
&self,
|
|
635
|
+
chat_id: &str,
|
|
636
|
+
workspace_path: Option<&str>,
|
|
637
|
+
git_branch: Option<&str>,
|
|
638
|
+
main_language: Option<&str>,
|
|
639
|
+
) -> Result<(), MemoryError> {
|
|
640
|
+
let chat_id = normalized_chat_id(chat_id);
|
|
641
|
+
let connection = self.connection()?;
|
|
642
|
+
ensure_builtin_chat_sessions(&connection)?;
|
|
643
|
+
ensure_chat_session_row(&connection, &chat_id)?;
|
|
644
|
+
connection.execute(
|
|
645
|
+
"UPDATE chat_sessions
|
|
646
|
+
SET workspace_path = COALESCE(?2, workspace_path),
|
|
647
|
+
git_branch = COALESCE(?3, git_branch),
|
|
648
|
+
main_language = COALESCE(?4, main_language),
|
|
649
|
+
updated_at = CURRENT_TIMESTAMP
|
|
650
|
+
WHERE id = ?1",
|
|
651
|
+
params![chat_id, workspace_path, git_branch, main_language],
|
|
652
|
+
)?;
|
|
653
|
+
Ok(())
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
pub fn set_chat_session_workspace(
|
|
657
|
+
&self,
|
|
658
|
+
chat_id: &str,
|
|
659
|
+
workspace_path: Option<&str>,
|
|
660
|
+
) -> Result<(), MemoryError> {
|
|
661
|
+
let chat_id = normalized_chat_id(chat_id);
|
|
662
|
+
let connection = self.connection()?;
|
|
663
|
+
ensure_builtin_chat_sessions(&connection)?;
|
|
664
|
+
ensure_chat_session_row(&connection, &chat_id)?;
|
|
665
|
+
connection.execute(
|
|
666
|
+
"UPDATE chat_sessions
|
|
667
|
+
SET workspace_path = ?2,
|
|
668
|
+
updated_at = CURRENT_TIMESTAMP
|
|
669
|
+
WHERE id = ?1",
|
|
670
|
+
params![chat_id, workspace_path],
|
|
671
|
+
)?;
|
|
672
|
+
Ok(())
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
pub fn create_cli_session(
|
|
676
|
+
&self,
|
|
677
|
+
workspace_path: Option<&str>,
|
|
678
|
+
git_branch: Option<&str>,
|
|
679
|
+
main_language: Option<&str>,
|
|
680
|
+
) -> Result<String, MemoryError> {
|
|
681
|
+
let session_id = generate_cli_session_id();
|
|
682
|
+
self.register_cli_session(&session_id, workspace_path, git_branch, main_language)?;
|
|
683
|
+
Ok(session_id)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
pub fn register_cli_session(
|
|
687
|
+
&self,
|
|
688
|
+
session_id: &str,
|
|
689
|
+
workspace_path: Option<&str>,
|
|
690
|
+
git_branch: Option<&str>,
|
|
691
|
+
main_language: Option<&str>,
|
|
692
|
+
) -> Result<(), MemoryError> {
|
|
693
|
+
let connection = self.connection()?;
|
|
694
|
+
ensure_builtin_chat_sessions(&connection)?;
|
|
695
|
+
connection.execute(
|
|
696
|
+
"INSERT OR IGNORE INTO chat_sessions (id, title, kind, workspace_path, git_branch, main_language)
|
|
697
|
+
VALUES (?1, 'New chat', 'cli', ?2, ?3, ?4)",
|
|
698
|
+
params![session_id, workspace_path, git_branch, main_language],
|
|
699
|
+
)?;
|
|
700
|
+
Ok(())
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
pub fn get_session_preview(
|
|
704
|
+
&self,
|
|
705
|
+
chat_id: &str,
|
|
706
|
+
limit: usize,
|
|
707
|
+
) -> Result<Vec<InteractionMemory>, MemoryError> {
|
|
708
|
+
self.recent_interactions_for_chat(chat_id, limit)
|
|
709
|
+
}
|
|
710
|
+
|
|
314
711
|
/// Registers a chat session with an explicit title/kind up front instead
|
|
315
712
|
/// of the "New chat" placeholder — used to give a scheduled task's
|
|
316
713
|
/// conversation its task name immediately (visible in the sidebar even
|
|
@@ -361,7 +758,7 @@ impl MemoryStore {
|
|
|
361
758
|
|
|
362
759
|
pub fn delete_chat_session(&self, chat_id: &str) -> Result<usize, MemoryError> {
|
|
363
760
|
let chat_id = normalized_chat_id(chat_id);
|
|
364
|
-
if
|
|
761
|
+
if chat_id == CHAT_CLI_ID {
|
|
365
762
|
return Ok(0);
|
|
366
763
|
}
|
|
367
764
|
let connection = self.connection()?;
|
|
@@ -910,13 +1307,23 @@ fn initialize(
|
|
|
910
1307
|
keywords TEXT DEFAULT '',
|
|
911
1308
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
912
1309
|
);
|
|
913
|
-
CREATE TABLE IF NOT EXISTS
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
918
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
1310
|
+
CREATE TABLE IF NOT EXISTS conversation_changes (
|
|
1311
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1312
|
+
chat_id TEXT NOT NULL,
|
|
1313
|
+
interaction_id INTEGER NOT NULL
|
|
919
1314
|
);
|
|
1315
|
+
CREATE INDEX IF NOT EXISTS idx_conversation_changes_chat_sequence
|
|
1316
|
+
ON conversation_changes(chat_id, sequence);
|
|
1317
|
+
CREATE TABLE IF NOT EXISTS chat_sessions (
|
|
1318
|
+
id TEXT PRIMARY KEY,
|
|
1319
|
+
title TEXT NOT NULL DEFAULT 'New chat',
|
|
1320
|
+
kind TEXT NOT NULL DEFAULT 'conversation',
|
|
1321
|
+
workspace_path TEXT DEFAULT NULL,
|
|
1322
|
+
git_branch TEXT DEFAULT NULL,
|
|
1323
|
+
main_language TEXT DEFAULT NULL,
|
|
1324
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
1325
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
1326
|
+
);
|
|
920
1327
|
CREATE TABLE IF NOT EXISTS learned_skills (
|
|
921
1328
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
922
1329
|
name TEXT NOT NULL,
|
|
@@ -970,7 +1377,7 @@ fn initialize(
|
|
|
970
1377
|
VALUES ('delete', old.id, old.user_text, old.ai_text);
|
|
971
1378
|
END;
|
|
972
1379
|
CREATE TRIGGER IF NOT EXISTS trg_interaction_fts_au
|
|
973
|
-
AFTER UPDATE ON interaction_memories BEGIN
|
|
1380
|
+
AFTER UPDATE OF user_text, ai_text ON interaction_memories BEGIN
|
|
974
1381
|
INSERT INTO interaction_fts(interaction_fts, rowid, user_text, ai_text)
|
|
975
1382
|
VALUES ('delete', old.id, old.user_text, old.ai_text);
|
|
976
1383
|
INSERT INTO interaction_fts(rowid, user_text, ai_text)
|
|
@@ -1007,12 +1414,66 @@ fn initialize(
|
|
|
1007
1414
|
"agent_activity_json",
|
|
1008
1415
|
"TEXT DEFAULT NULL",
|
|
1009
1416
|
)?;
|
|
1417
|
+
ensure_column(
|
|
1418
|
+
connection,
|
|
1419
|
+
"interaction_memories",
|
|
1420
|
+
"status",
|
|
1421
|
+
"TEXT NOT NULL DEFAULT 'completed'",
|
|
1422
|
+
)?;
|
|
1423
|
+
ensure_column(
|
|
1424
|
+
connection,
|
|
1425
|
+
"interaction_memories",
|
|
1426
|
+
"lease_until",
|
|
1427
|
+
"INTEGER NOT NULL DEFAULT 0",
|
|
1428
|
+
)?;
|
|
1429
|
+
connection.execute_batch(
|
|
1430
|
+
"DROP TRIGGER IF EXISTS trg_interaction_fts_au;
|
|
1431
|
+
CREATE TRIGGER trg_interaction_fts_au
|
|
1432
|
+
AFTER UPDATE OF user_text, ai_text ON interaction_memories BEGIN
|
|
1433
|
+
INSERT INTO interaction_fts(interaction_fts, rowid, user_text, ai_text)
|
|
1434
|
+
VALUES ('delete', old.id, old.user_text, old.ai_text);
|
|
1435
|
+
INSERT INTO interaction_fts(rowid, user_text, ai_text)
|
|
1436
|
+
VALUES (new.id, new.user_text, new.ai_text);
|
|
1437
|
+
END;
|
|
1438
|
+
DROP TRIGGER IF EXISTS trg_conversation_change_update;
|
|
1439
|
+
CREATE TRIGGER IF NOT EXISTS trg_conversation_change_insert
|
|
1440
|
+
AFTER INSERT ON interaction_memories BEGIN
|
|
1441
|
+
INSERT INTO conversation_changes(chat_id, interaction_id) VALUES (new.chat_id, new.id);
|
|
1442
|
+
END;
|
|
1443
|
+
CREATE TRIGGER trg_conversation_change_update
|
|
1444
|
+
AFTER UPDATE OF user_text, ai_text, provider, model, fallback_provider,
|
|
1445
|
+
status, agent_activity_json, chat_id ON interaction_memories BEGIN
|
|
1446
|
+
INSERT INTO conversation_changes(chat_id, interaction_id) VALUES (new.chat_id, new.id);
|
|
1447
|
+
END;
|
|
1448
|
+
CREATE TRIGGER IF NOT EXISTS trg_conversation_change_delete
|
|
1449
|
+
AFTER DELETE ON interaction_memories BEGIN
|
|
1450
|
+
INSERT INTO conversation_changes(chat_id, interaction_id) VALUES (old.chat_id, old.id);
|
|
1451
|
+
END;",
|
|
1452
|
+
)?;
|
|
1010
1453
|
ensure_column(
|
|
1011
1454
|
connection,
|
|
1012
1455
|
"chat_sessions",
|
|
1013
1456
|
"kind",
|
|
1014
1457
|
"TEXT NOT NULL DEFAULT 'conversation'",
|
|
1015
1458
|
)?;
|
|
1459
|
+
ensure_column(
|
|
1460
|
+
connection,
|
|
1461
|
+
"chat_sessions",
|
|
1462
|
+
"workspace_path",
|
|
1463
|
+
"TEXT DEFAULT NULL",
|
|
1464
|
+
)?;
|
|
1465
|
+
ensure_column(
|
|
1466
|
+
connection,
|
|
1467
|
+
"chat_sessions",
|
|
1468
|
+
"git_branch",
|
|
1469
|
+
"TEXT DEFAULT NULL",
|
|
1470
|
+
)?;
|
|
1471
|
+
ensure_column(
|
|
1472
|
+
connection,
|
|
1473
|
+
"chat_sessions",
|
|
1474
|
+
"main_language",
|
|
1475
|
+
"TEXT DEFAULT NULL",
|
|
1476
|
+
)?;
|
|
1016
1477
|
// `agent_id` scopes a fact to the subagent that produced it (NULL = shared /
|
|
1017
1478
|
// user-authored); `embedding` is the on-device similarity vector used for
|
|
1018
1479
|
// relevance-ranked recall. Both are additive for databases created before
|
|
@@ -1233,6 +1694,7 @@ fn interaction_row(row: &rusqlite::Row<'_>) -> Result<InteractionMemory, rusqlit
|
|
|
1233
1694
|
model: row.get(5)?,
|
|
1234
1695
|
fallback_provider: row.get(6)?,
|
|
1235
1696
|
created_at: normalize_sqlite_utc_timestamp(&raw_created),
|
|
1697
|
+
status: row.get(9)?,
|
|
1236
1698
|
agent_activity,
|
|
1237
1699
|
})
|
|
1238
1700
|
}
|
|
@@ -1267,8 +1729,10 @@ fn ensure_builtin_chat_sessions(connection: &Connection) -> Result<(), rusqlite:
|
|
|
1267
1729
|
}
|
|
1268
1730
|
|
|
1269
1731
|
fn ensure_chat_session_row(connection: &Connection, chat_id: &str) -> Result<(), rusqlite::Error> {
|
|
1270
|
-
let (title, kind) = if
|
|
1732
|
+
let (title, kind) = if chat_id == CHAT_CLI_ID {
|
|
1271
1733
|
("Chat CLI", "cli")
|
|
1734
|
+
} else if is_cli_chat_id(chat_id) {
|
|
1735
|
+
("New chat", "cli")
|
|
1272
1736
|
} else {
|
|
1273
1737
|
("New chat", "conversation")
|
|
1274
1738
|
};
|
|
@@ -1280,6 +1744,180 @@ fn ensure_chat_session_row(connection: &Connection, chat_id: &str) -> Result<(),
|
|
|
1280
1744
|
Ok(())
|
|
1281
1745
|
}
|
|
1282
1746
|
|
|
1747
|
+
#[cfg(test)]
|
|
1748
|
+
mod conversation_sync_tests {
|
|
1749
|
+
use super::*;
|
|
1750
|
+
|
|
1751
|
+
fn store() -> MemoryStore {
|
|
1752
|
+
MemoryStore::open(std::env::temp_dir().join(format!(
|
|
1753
|
+
"mint-conversation-sync-{}.sqlite",
|
|
1754
|
+
uuid::Uuid::new_v4()
|
|
1755
|
+
)))
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
#[test]
|
|
1759
|
+
fn prompt_and_reply_use_one_id_and_emit_changes() {
|
|
1760
|
+
let memory = store();
|
|
1761
|
+
let first = memory.start_turn("cli::one", "hello").unwrap();
|
|
1762
|
+
let other_process = MemoryStore::open(memory.path.clone());
|
|
1763
|
+
let second = other_process.start_turn("cli::one", "next").unwrap();
|
|
1764
|
+
let other = memory.start_turn("cli::two", "separate").unwrap();
|
|
1765
|
+
assert!(memory.claim_turn("cli::one", first).unwrap());
|
|
1766
|
+
assert!(!other_process.claim_turn("cli::one", second).unwrap());
|
|
1767
|
+
assert!(memory.claim_turn("cli::two", other).unwrap());
|
|
1768
|
+
assert_eq!(
|
|
1769
|
+
memory
|
|
1770
|
+
.recent_completed_interactions_for_chat("cli::one", 3)
|
|
1771
|
+
.unwrap()
|
|
1772
|
+
.len(),
|
|
1773
|
+
0
|
|
1774
|
+
);
|
|
1775
|
+
|
|
1776
|
+
memory
|
|
1777
|
+
.finish_turn(first, "world", "test", "model", None)
|
|
1778
|
+
.unwrap();
|
|
1779
|
+
assert!(other_process.claim_turn("cli::one", second).unwrap());
|
|
1780
|
+
memory
|
|
1781
|
+
.finish_turn(second, "done", "test", "model", None)
|
|
1782
|
+
.unwrap();
|
|
1783
|
+
|
|
1784
|
+
let rows = memory.interactions_for_chat("cli::one").unwrap();
|
|
1785
|
+
assert_eq!(rows.len(), 2);
|
|
1786
|
+
assert_eq!(rows[0].id, first);
|
|
1787
|
+
assert_eq!(rows[0].ai_text, "world");
|
|
1788
|
+
assert_eq!(rows[1].status, "completed");
|
|
1789
|
+
let changes = memory.conversation_changes("cli::one", 0, 100).unwrap();
|
|
1790
|
+
assert!(changes.changes.iter().any(|c| {
|
|
1791
|
+
c.interaction_id == first
|
|
1792
|
+
&& c.interaction
|
|
1793
|
+
.as_ref()
|
|
1794
|
+
.is_some_and(|row| row.ai_text == "world")
|
|
1795
|
+
}));
|
|
1796
|
+
assert!(changes.changes.iter().all(|c| c.interaction_id != other));
|
|
1797
|
+
assert!(
|
|
1798
|
+
memory
|
|
1799
|
+
.conversation_changes("cli::one", changes.cursor, 100)
|
|
1800
|
+
.unwrap()
|
|
1801
|
+
.changes
|
|
1802
|
+
.is_empty()
|
|
1803
|
+
);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
#[test]
|
|
1807
|
+
fn interrupted_turn_unblocks_queue_and_snapshot_pages() {
|
|
1808
|
+
let memory = store();
|
|
1809
|
+
let first = memory.start_turn("cli::one", "first").unwrap();
|
|
1810
|
+
let second = memory.start_turn("cli::one", "second").unwrap();
|
|
1811
|
+
assert!(memory.claim_turn("cli::one", first).unwrap());
|
|
1812
|
+
memory.end_turn(first, "interrupted").unwrap();
|
|
1813
|
+
assert!(memory.claim_turn("cli::one", second).unwrap());
|
|
1814
|
+
let page = memory.conversation_snapshot("cli::one", None, 1).unwrap();
|
|
1815
|
+
assert_eq!(page.interactions[0].id, second);
|
|
1816
|
+
assert!(page.has_older);
|
|
1817
|
+
let older = memory
|
|
1818
|
+
.conversation_snapshot("cli::one", Some(second), 1)
|
|
1819
|
+
.unwrap();
|
|
1820
|
+
assert_eq!(older.interactions[0].status, "interrupted");
|
|
1821
|
+
assert!(!older.has_older);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
#[test]
|
|
1825
|
+
fn abandoned_turn_is_reported_after_lease_expires() {
|
|
1826
|
+
let memory = store();
|
|
1827
|
+
let id = memory.start_turn("cli::one", "unfinished").unwrap();
|
|
1828
|
+
assert!(memory.claim_turn("cli::one", id).unwrap());
|
|
1829
|
+
memory
|
|
1830
|
+
.connection()
|
|
1831
|
+
.unwrap()
|
|
1832
|
+
.execute(
|
|
1833
|
+
"UPDATE interaction_memories SET lease_until = 0 WHERE id = ?1",
|
|
1834
|
+
params![id],
|
|
1835
|
+
)
|
|
1836
|
+
.unwrap();
|
|
1837
|
+
let changes = memory.conversation_changes("cli::one", 0, 100).unwrap();
|
|
1838
|
+
assert_eq!(
|
|
1839
|
+
changes
|
|
1840
|
+
.changes
|
|
1841
|
+
.last()
|
|
1842
|
+
.unwrap()
|
|
1843
|
+
.interaction
|
|
1844
|
+
.as_ref()
|
|
1845
|
+
.unwrap()
|
|
1846
|
+
.status,
|
|
1847
|
+
"interrupted"
|
|
1848
|
+
);
|
|
1849
|
+
assert!(
|
|
1850
|
+
memory
|
|
1851
|
+
.finish_turn(id, "late", "test", "model", None)
|
|
1852
|
+
.is_err()
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
#[test]
|
|
1857
|
+
fn old_completed_rows_migrate_without_losing_content() {
|
|
1858
|
+
let path = std::env::temp_dir().join(format!(
|
|
1859
|
+
"mint-conversation-legacy-{}.sqlite",
|
|
1860
|
+
uuid::Uuid::new_v4()
|
|
1861
|
+
));
|
|
1862
|
+
let connection = Connection::open(&path).unwrap();
|
|
1863
|
+
connection
|
|
1864
|
+
.execute_batch(
|
|
1865
|
+
"CREATE TABLE interaction_memories (
|
|
1866
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1867
|
+
chat_id TEXT NOT NULL DEFAULT 'conversation-default',
|
|
1868
|
+
user_text TEXT NOT NULL, ai_text TEXT NOT NULL,
|
|
1869
|
+
provider TEXT NOT NULL DEFAULT '', model TEXT NOT NULL DEFAULT '',
|
|
1870
|
+
fallback_provider TEXT DEFAULT NULL, keywords TEXT DEFAULT '',
|
|
1871
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
1872
|
+
);
|
|
1873
|
+
INSERT INTO interaction_memories(chat_id, user_text, ai_text)
|
|
1874
|
+
VALUES ('cli::old', 'old prompt', 'old reply');",
|
|
1875
|
+
)
|
|
1876
|
+
.unwrap();
|
|
1877
|
+
drop(connection);
|
|
1878
|
+
let memory = MemoryStore::open(path);
|
|
1879
|
+
let rows = memory.interactions_for_chat("cli::old").unwrap();
|
|
1880
|
+
assert_eq!(rows.len(), 1);
|
|
1881
|
+
assert_eq!(rows[0].status, "completed");
|
|
1882
|
+
assert_eq!(rows[0].ai_text, "old reply");
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
#[test]
|
|
1886
|
+
fn clearing_history_emits_tombstones_for_open_views() {
|
|
1887
|
+
let memory = store();
|
|
1888
|
+
let id = memory
|
|
1889
|
+
.add_interaction_for_chat("cli::one", "hello", "world", "test", "model")
|
|
1890
|
+
.unwrap();
|
|
1891
|
+
let cursor = memory.latest_conversation_sequence("cli::one").unwrap();
|
|
1892
|
+
memory.clear_interactions_for_chat("cli::one").unwrap();
|
|
1893
|
+
let changes = memory
|
|
1894
|
+
.conversation_changes("cli::one", cursor, 100)
|
|
1895
|
+
.unwrap();
|
|
1896
|
+
assert!(
|
|
1897
|
+
changes
|
|
1898
|
+
.changes
|
|
1899
|
+
.iter()
|
|
1900
|
+
.any(|change| change.interaction_id == id && change.interaction.is_none())
|
|
1901
|
+
);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
#[test]
|
|
1905
|
+
fn heartbeat_does_not_emit_visible_changes() {
|
|
1906
|
+
let memory = store();
|
|
1907
|
+
let id = memory.start_turn("cli::one", "hello").unwrap();
|
|
1908
|
+
assert!(memory.claim_turn("cli::one", id).unwrap());
|
|
1909
|
+
let cursor = memory.latest_conversation_sequence("cli::one").unwrap();
|
|
1910
|
+
memory.renew_turn(id).unwrap();
|
|
1911
|
+
assert!(
|
|
1912
|
+
memory
|
|
1913
|
+
.conversation_changes("cli::one", cursor, 100)
|
|
1914
|
+
.unwrap()
|
|
1915
|
+
.changes
|
|
1916
|
+
.is_empty()
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1283
1921
|
#[cfg(test)]
|
|
1284
1922
|
mod scoped_chat_id_tests {
|
|
1285
1923
|
use super::*;
|