@runfusion/fusion 0.43.1 → 0.45.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/README.md +2 -1
- package/dist/automation-store-EKXXEE2S.js +10 -0
- package/dist/automation-store-PLNG3X72.js +10 -0
- package/dist/automation-store-RDDYZFUJ.js +10 -0
- package/dist/bin.js +14420 -3360
- package/dist/chunk-2JI3F6MG.js +948 -0
- package/dist/chunk-4L6CLWJC.js +8345 -0
- package/dist/chunk-4VNS5WPM.js +42 -0
- package/dist/chunk-5RGECGQL.js +1139 -0
- package/dist/chunk-6JDLN7IL.js +409 -0
- package/dist/chunk-AICJATVW.js +948 -0
- package/dist/chunk-AWKNRUOR.js +1148 -0
- package/dist/chunk-BHM3UYWS.js +4651 -0
- package/dist/chunk-BMUXDSBF.js +4647 -0
- package/dist/chunk-GETECT6V.js +353 -0
- package/dist/chunk-GUV4QQT3.js +1143 -0
- package/dist/chunk-JKQO7YYZ.js +8369 -0
- package/dist/chunk-LYZVU6UV.js +409 -0
- package/dist/chunk-MAN4WYHO.js +353 -0
- package/dist/chunk-MCUG2Z7M.js +4651 -0
- package/dist/chunk-PSDYB45U.js +948 -0
- package/dist/chunk-RR2HM46R.js +8345 -0
- package/dist/chunk-RXMQ5OMY.js +9226 -0
- package/dist/chunk-S2EVJUY3.js +353 -0
- package/dist/chunk-UXR5XZFZ.js +409 -0
- package/dist/client/.vite/manifest.json +1138 -340
- package/dist/client/assets/AgentDetailView-CgbB51Iw.css +1 -0
- package/dist/client/assets/AgentDetailView-DJTi6hKC.js +18 -0
- package/dist/client/assets/{AgentPermissionPolicyEditor-BiY1a4KR.js → AgentPermissionPolicyEditor-C5DR9dUS.js} +2 -2
- package/dist/client/assets/AgentsView-BNd90PSx.js +29 -0
- package/dist/client/assets/AgentsView-CzMa5Olf.css +1 -0
- package/dist/client/assets/ChatView-B7JaZCL_.js +19 -0
- package/dist/client/assets/ChatView-BtjkE0V7.css +1 -0
- package/dist/client/assets/CommandCenter-CHO21xEW.js +74 -0
- package/dist/client/assets/CommandCenter-Xs5Ftv8s.css +1 -0
- package/dist/client/assets/DevServerView-B_p91Mj_.css +1 -0
- package/dist/client/assets/DevServerView-H9OkT1ld.js +1 -0
- package/dist/client/assets/{DirectoryPicker-DTKtd8L2.css → DirectoryPicker-0rspvcMz.css} +1 -1
- package/dist/client/assets/DirectoryPicker-DegxT8ia.js +1 -0
- package/dist/client/assets/DocumentsView-kVVQYISc.js +1 -0
- package/dist/client/assets/EvalsView-C9-iTSat.js +1 -0
- package/dist/client/assets/EvalsView-CTbgPUne.css +1 -0
- package/dist/client/assets/ExperimentalAgentOnboardingModal-3ZEFv8sr.css +1 -0
- package/dist/client/assets/ExperimentalAgentOnboardingModal-DjP_xNkc.js +1 -0
- package/dist/client/assets/GoalsView-CUh8JBnJ.css +1 -0
- package/dist/client/assets/GoalsView-VoIHJJ5l.js +2 -0
- package/dist/client/assets/InsightsView-BIZaUcpd.js +11 -0
- package/dist/client/assets/InsightsView-C7yRTMPn.css +1 -0
- package/dist/client/assets/MemoryView-7v7rv6ns.js +2 -0
- package/dist/client/assets/MemoryView-CBDUr2Jy.css +1 -0
- package/dist/client/assets/PiExtensionsManager-CB89rV4U.js +11 -0
- package/dist/client/assets/PluginManager-BNSuW_R4.js +1 -0
- package/dist/client/assets/PluginManager-DSiHizRD.css +1 -0
- package/dist/client/assets/PullRequestView-CI5jdKBL.js +6 -0
- package/dist/client/assets/PullRequestView-CpdE3Yo_.css +1 -0
- package/dist/client/assets/ResearchView-BT5dR_m8.js +1 -0
- package/dist/client/assets/ResearchView-DIVNQF4_.css +1 -0
- package/dist/client/assets/SecretsView-CvFioX7R.css +1 -0
- package/dist/client/assets/SecretsView-WPYI58Uo.js +1 -0
- package/dist/client/assets/SessionTerminal-C6gyOWLt.js +2 -0
- package/dist/client/assets/SessionTerminal-D4R3ScvQ.css +1 -0
- package/dist/client/assets/SettingsModal-BESN59Ao.css +1 -0
- package/dist/client/assets/SettingsModal-BZokaNyx.css +1 -0
- package/dist/client/assets/SettingsModal-Bq_3AbaB.js +1 -0
- package/dist/client/assets/SettingsModal-JHACoQJe.js +21 -0
- package/dist/client/assets/SetupWizardModal-CrgbnR_d.js +2 -0
- package/dist/client/assets/ShadcnColorPicker-1icHuwhC.js +11 -0
- package/dist/client/assets/ShadcnColorPicker-pqVWCC5-.css +1 -0
- package/dist/client/assets/SkillsView-CJ8snrma.css +1 -0
- package/dist/client/assets/SkillsView-byuSe3e0.js +1 -0
- package/dist/client/assets/TodoView-CIsL_uvQ.css +1 -0
- package/dist/client/assets/TodoView-CQ9GjYBg.js +6 -0
- package/dist/client/assets/WorkflowNodeEditor-DW7sJZUX.js +8 -0
- package/dist/client/assets/WorkflowNodeEditor-f_XPV5Jd.css +1 -0
- package/dist/client/assets/{addon-fit-B4jkcKk4.js → addon-fit-C1WQXRvu.js} +1 -1
- package/dist/client/assets/{addon-unicode11-Cdne2Sj4.js → addon-unicode11-BVmNDUBM.js} +1 -1
- package/dist/client/assets/{addon-web-links-CRNLzj7m.js → addon-web-links-CkVew5xs.js} +1 -1
- package/dist/client/assets/{addon-webgl-CeCSULF1.js → addon-webgl-uLc0RKcC.js} +1 -1
- package/dist/client/assets/agentSkills-CeytXLHF.js +1 -0
- package/dist/client/assets/agentSkills-bQJ4X1gH.css +1 -0
- package/dist/client/assets/agentsOrgChartLayout-BMvyuaLA.js +1 -0
- package/dist/client/assets/app-3wbZ08YC.js +13 -0
- package/dist/client/assets/app-7-5DWLtg.js +13 -0
- package/dist/client/assets/app-CSvE5Noc.js +13 -0
- package/dist/client/assets/app-CZ2LTFce.js +13 -0
- package/dist/client/assets/app-Cv9xEnll.js +13 -0
- package/dist/client/assets/app-x_yPcO5Y.js +13 -0
- package/dist/client/assets/arc-cxkkNsnK.js +1 -0
- package/dist/client/assets/architectureDiagram-3BPJPVTR-BRI0QwDf.js +36 -0
- package/dist/client/assets/band-CquvqAHh.js +1 -0
- package/dist/client/assets/blockDiagram-GPEHLZMM-BXkN8Hv_.js +132 -0
- package/dist/client/assets/c4Diagram-AAUBKEIU-C_Kcokep.js +10 -0
- package/dist/client/assets/channel-Clzpzu32.js +1 -0
- package/dist/client/assets/chunk-2J33WTMH-BGeuCz9C.js +1 -0
- package/dist/client/assets/chunk-4BX2VUAB-DcVdctr0.js +1 -0
- package/dist/client/assets/chunk-55IACEB6-Dg6gDigQ.js +1 -0
- package/dist/client/assets/chunk-727SXJPM-B9B-vlEb.js +206 -0
- package/dist/client/assets/chunk-AQP2D5EJ-LrWQ-2yi.js +231 -0
- package/dist/client/assets/chunk-FMBD7UC4-Bnd6hAED.js +15 -0
- package/dist/client/assets/chunk-ND2GUHAM-nAw93UB5.js +1 -0
- package/dist/client/assets/chunk-QZHKN3VN-CgprKcZo.js +1 -0
- package/dist/client/assets/classDiagram-4FO5ZUOK-CzYHP-af.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CzYHP-af.js +1 -0
- package/dist/client/assets/common-BDp2y6CF.js +1 -0
- package/dist/client/assets/common-BnEo5fLN.js +1 -0
- package/dist/client/assets/common-Bzr_Y8h4.js +1 -0
- package/dist/client/assets/common-CvwyFzfv.js +1 -0
- package/dist/client/assets/common-CztCXRkz.js +1 -0
- package/dist/client/assets/common-DPe2mbb7.js +1 -0
- package/dist/client/assets/cose-bilkent-S5V4N54A-Bhn0g_iK.js +1 -0
- package/dist/client/assets/cytoscape.esm-OyMbaexL.js +331 -0
- package/dist/client/assets/dagre-BM42HDAG-CP4kfwqd.js +4 -0
- package/dist/client/assets/{dashboard-view-C3SsjXo1.js → dashboard-view-CM6Xin_H.js} +1680 -1620
- package/dist/client/assets/dashboard-view-DV1nEhh9.js +1 -0
- package/dist/client/assets/dashboard-view-D_zsA-kz.css +1 -0
- package/dist/client/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/client/assets/diagram-2AECGRRQ-DPhzVEUf.js +43 -0
- package/dist/client/assets/diagram-5GNKFQAL-8QOC3K_L.js +10 -0
- package/dist/client/assets/diagram-KO2AKTUF-C1twgEch.js +3 -0
- package/dist/client/assets/diagram-LMA3HP47-3bEegSuf.js +24 -0
- package/dist/client/assets/diagram-OG6HWLK6-BJ_x0rm6.js +24 -0
- package/dist/client/assets/erDiagram-TEJ5UH35-DNiiS5q6.js +85 -0
- package/dist/client/assets/flowDiagram-I6XJVG4X-DoouPklt.js +162 -0
- package/dist/client/assets/{folder-open-Bw1mxVfa.js → folder-open-CuUs2W7f.js} +1 -1
- package/dist/client/assets/ganttDiagram-6RSMTGT7-BrR6VnP5.js +292 -0
- package/dist/client/assets/gitGraphDiagram-PVQCEYII-BV8_J6Ee.js +106 -0
- package/dist/client/assets/graph--OzhPTMs.js +1 -0
- package/dist/client/assets/index-BZBQ_KsL.css +1 -0
- package/dist/client/assets/index-CZcWjLAb.js +1459 -0
- package/dist/client/assets/infoDiagram-5YYISTIA-DGwIIFkR.js +2 -0
- package/dist/client/assets/init-Dmth1JHB.js +1 -0
- package/dist/client/assets/ishikawaDiagram-YF4QCWOH-D5dEH4RE.js +70 -0
- package/dist/client/assets/journeyDiagram-JHISSGLW-BuuSWPw5.js +139 -0
- package/dist/client/assets/kanban-definition-UN3LZRKU-DA34uUla.js +89 -0
- package/dist/client/assets/katex-HP8lGamR.js +257 -0
- package/dist/client/assets/layout-SsrduOYp.js +1 -0
- package/dist/client/assets/linear-dXivciGL.js +1 -0
- package/dist/client/assets/mermaid.core-Ch4HatqA.js +309 -0
- package/dist/client/assets/mindmap-definition-RKZ34NQL-DUCG-qD3.js +96 -0
- package/dist/client/assets/ordinal-DILIJJjt.js +1 -0
- package/dist/client/assets/pieDiagram-4H26LBE5-DWvFGiLa.js +30 -0
- package/dist/client/assets/quadrantDiagram-W4KKPZXB-OPTVW3WT.js +7 -0
- package/dist/client/assets/requirementDiagram-4Y6WPE33-D7feZvlU.js +84 -0
- package/dist/client/assets/sankeyDiagram-5OEKKPKP-AtnSsEX1.js +40 -0
- package/dist/client/assets/sequenceDiagram-3UESZ5HK-BZjHUeI5.js +162 -0
- package/dist/client/assets/shield-alert-CzaF9ROV.js +6 -0
- package/dist/client/assets/stateDiagram-AJRCARHV-C8j23Pvn.js +1 -0
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CiZR_S5-.js +1 -0
- package/dist/client/assets/step-yuy0qJer.js +1 -0
- package/dist/client/assets/time-DoDTxsna.js +1 -0
- package/dist/client/assets/timeline-definition-PNZ67QCA-DvfNz25n.js +120 -0
- package/dist/client/assets/{upload-BCmd-1I0.js → upload-DTsYywhN.js} +1 -1
- package/dist/client/assets/{users-DO_t3V1p.js → users-yAd09tKP.js} +1 -1
- package/dist/client/assets/{vendor-i18n-CV0mwX_-.js → vendor-i18n-GF3RlKbq.js} +2 -2
- package/dist/client/assets/vendor-react-SZ_qB27O.js +49 -0
- package/dist/client/assets/vendor-reactflow-Bg-Qjzh8.js +15 -0
- package/dist/client/assets/{vendor-xterm-tcedd2Jt.js → vendor-xterm-BNq00GSj.js} +1 -1
- package/dist/client/assets/vennDiagram-CIIHVFJN-CG2aMt3v.js +34 -0
- package/dist/client/assets/wardley-L42UT6IY-BnhBteen.js +173 -0
- package/dist/client/assets/wardleyDiagram-YWT4CUSO-DwqTzM3T.js +78 -0
- package/dist/client/assets/xychartDiagram-2RQKCTM6-5Cv2ZKMV.js +7 -0
- package/dist/client/assets/zoom-out-NWbVkreR.js +41 -0
- package/dist/client/icons/icon-192.png +0 -0
- package/dist/client/icons/icon-512.png +0 -0
- package/dist/client/index.html +30 -10
- package/dist/client/sw.js +1 -1
- package/dist/client/theme-data.css +3348 -469
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/index.ts +56 -40
- package/dist/droid-cli/package.json +1 -1
- package/dist/droid-cli/src/__tests__/execsync-guard.test.ts +17 -0
- package/dist/droid-cli/src/__tests__/index.test.ts +47 -21
- package/dist/droid-cli/src/__tests__/process-manager.test.ts +51 -78
- package/dist/extension.js +9651 -1556
- package/dist/extract-zip-WL55Z3AU.js +2497 -0
- package/dist/index.min-CHQ3ABYJ.js +2926 -0
- package/dist/memory-dreams-7USISKVE.js +32 -0
- package/dist/memory-dreams-PBPISUDD.js +32 -0
- package/dist/memory-dreams-XNO2B2QF.js +32 -0
- package/dist/migration-EH2YOCYQ.js +15 -0
- package/dist/migration-PLGTTKL5.js +15 -0
- package/dist/migration-WZEZK22R.js +15 -0
- package/dist/pi-claude-cli/index.ts +47 -8
- package/dist/pi-claude-cli/package.json +4 -1
- package/dist/pi-claude-cli/src/__tests__/acp-dispatch.test.ts +80 -0
- package/dist/pi-claude-cli/src/__tests__/acp-driver.test.ts +423 -0
- package/dist/pi-claude-cli/src/__tests__/execsync-guard.test.ts +14 -0
- package/dist/pi-claude-cli/src/__tests__/process-manager.test.ts +26 -45
- package/dist/pi-claude-cli/src/__tests__/provider.test.ts +0 -1
- package/dist/pi-claude-cli/src/acp-driver.ts +585 -0
- package/dist/pi-claude-cli/src/event-bridge.ts +7 -0
- package/dist/pi-claude-cli/src/mcp-config.ts +29 -0
- package/dist/pi-claude-cli/src/process-manager.ts +17 -39
- package/dist/plugin-sdk/index.d.ts +5305 -5148
- package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +263 -19
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +37 -7
- package/dist/plugins/fusion-plugin-compound-engineering/manifest.json +4 -4
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +69 -45
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/bundled.js +5 -11
- package/dist/plugins/fusion-plugin-roadmap/manifest.json +1 -11
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/index.test.ts +5 -1
- package/dist/routine-store-GIJ6MXWY.js +10 -0
- package/dist/routine-store-HTVC2PUQ.js +10 -0
- package/dist/routine-store-KGHZBPDF.js +10 -0
- package/package.json +3 -3
- package/skill/fusion/SKILL.md +1 -1
- package/skill/fusion/references/cli-commands.md +2 -1
- package/skill/fusion/references/engine-tools.md +6 -4
- package/skill/fusion/references/extension-tools.md +10 -0
- package/skill/fusion/references/fusion-capabilities.md +3 -1
- package/dist/client/assets/AgentDetailView-BGN03dyz.css +0 -1
- package/dist/client/assets/AgentDetailView-DKMz3r04.js +0 -18
- package/dist/client/assets/AgentsView-Co-k4vjh.js +0 -29
- package/dist/client/assets/AgentsView-DfznJDek.css +0 -1
- package/dist/client/assets/ChatView-BkAG99bj.js +0 -6
- package/dist/client/assets/ChatView-DwJAd5G1.css +0 -1
- package/dist/client/assets/DevServerView-C6DC0URX.js +0 -1
- package/dist/client/assets/DevServerView-VTQY28P7.css +0 -1
- package/dist/client/assets/DirectoryPicker-CkcGjyv0.js +0 -1
- package/dist/client/assets/DocumentsView-OCK1Vsw_.js +0 -1
- package/dist/client/assets/DocumentsView-WfVoTZt8.css +0 -1
- package/dist/client/assets/EvalsView-CUNJ1TLc.css +0 -1
- package/dist/client/assets/EvalsView-CUfyj5GW.js +0 -1
- package/dist/client/assets/ExperimentalAgentOnboardingModal-BNpveCEk.css +0 -1
- package/dist/client/assets/ExperimentalAgentOnboardingModal-Dau9RTU_.js +0 -499
- package/dist/client/assets/GoalsView-CNLTn0rX.js +0 -2
- package/dist/client/assets/GoalsView-DI66UsjT.css +0 -1
- package/dist/client/assets/InsightsView-BQPxHxKI.js +0 -11
- package/dist/client/assets/InsightsView-C9-mqVvf.css +0 -1
- package/dist/client/assets/MemoryView-D3xQIFjj.js +0 -2
- package/dist/client/assets/MemoryView-zaXewZzi.css +0 -1
- package/dist/client/assets/NodesView-7VInxyf_.css +0 -1
- package/dist/client/assets/NodesView-CSMvGwDU.js +0 -14
- package/dist/client/assets/PiExtensionsManager-BtqXF3fl.js +0 -11
- package/dist/client/assets/PluginManager-CSSDb3VO.js +0 -1
- package/dist/client/assets/PluginManager-UHVLvWWl.css +0 -1
- package/dist/client/assets/PullRequestView-Bjyfffdu.js +0 -6
- package/dist/client/assets/PullRequestView-WQIS0zUC.css +0 -1
- package/dist/client/assets/ReliabilityView-QKMUDebT.js +0 -1
- package/dist/client/assets/ReliabilityView-eoKBhBc4.css +0 -1
- package/dist/client/assets/ResearchView-CRU_NPho.js +0 -1
- package/dist/client/assets/ResearchView-DTh8FLUm.css +0 -1
- package/dist/client/assets/SecretsView-Bs8ySyC5.js +0 -1
- package/dist/client/assets/SecretsView-Djo1rahi.css +0 -1
- package/dist/client/assets/SessionTerminal-BiigPP2l.css +0 -1
- package/dist/client/assets/SessionTerminal-Dk1NgmN2.js +0 -2
- package/dist/client/assets/SettingsModal-DpAxDq6M.css +0 -1
- package/dist/client/assets/SettingsModal-JCaNvdAb.js +0 -31
- package/dist/client/assets/SettingsModal-Nw5cTKAK.js +0 -1
- package/dist/client/assets/SettingsModal-nF_GaeeK.css +0 -1
- package/dist/client/assets/SetupWizardModal-CE79myEy.js +0 -1
- package/dist/client/assets/SetupWizardModal-CIqFbkrD.css +0 -1
- package/dist/client/assets/SkillsView-3bhGyWs1.css +0 -1
- package/dist/client/assets/SkillsView-Dc8YskAX.js +0 -1
- package/dist/client/assets/StashRecoveryView-B_8WIQEo.css +0 -1
- package/dist/client/assets/StashRecoveryView-CqKOFiXT.js +0 -1
- package/dist/client/assets/TodoView-7xIh-Fmi.css +0 -1
- package/dist/client/assets/TodoView-BqIRwcZw.js +0 -6
- package/dist/client/assets/WorkflowNodeEditor-2jDuk2FR.js +0 -8
- package/dist/client/assets/WorkflowNodeEditor-BokhW3de.css +0 -1
- package/dist/client/assets/app-B2ZxMfJT.js +0 -13
- package/dist/client/assets/app-BffGU_HQ.js +0 -13
- package/dist/client/assets/app-C8AHhs5N.js +0 -13
- package/dist/client/assets/app-CUu1sm3x.js +0 -13
- package/dist/client/assets/app-Ri9Te2Dv.js +0 -13
- package/dist/client/assets/app-sEkTyT-w.js +0 -13
- package/dist/client/assets/common-BRyX4VxB.js +0 -1
- package/dist/client/assets/common-CN9QPI2d.js +0 -1
- package/dist/client/assets/common-CNbjPELi.js +0 -1
- package/dist/client/assets/common-DFDqu_n0.js +0 -1
- package/dist/client/assets/common-DgWWKG8Z.js +0 -1
- package/dist/client/assets/common-X6PzBnVA.js +0 -1
- package/dist/client/assets/createLucideIcon-BYvOGRiO.js +0 -21
- package/dist/client/assets/dashboard-view-BhbN8LPh.js +0 -1
- package/dist/client/assets/dashboard-view-DdGlfuu-.css +0 -1
- package/dist/client/assets/dashboard-view-DlKsVySD.js +0 -3
- package/dist/client/assets/dashboard-view-Du53JbmQ.css +0 -1
- package/dist/client/assets/index-CpTBp1RG.js +0 -793
- package/dist/client/assets/index-DqhCiU35.css +0 -1
- package/dist/client/assets/vendor-react-D37HfzIf.js +0 -49
- package/dist/client/assets/vendor-reactflow-CRZcPCXF.js +0 -15
- package/dist/client/assets/x-CqNX_9E2.js +0 -61
- package/dist/client/assets/zoom-out-CSTHbJgD.js +0 -21
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isEphemeralAgent
|
|
3
|
+
} from "./chunk-AWKNRUOR.js";
|
|
4
|
+
|
|
5
|
+
// ../core/dist/memory-dreams.js
|
|
6
|
+
import { appendFile, mkdir as mkdir2, readFile as readFile2, readdir as readdir2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
7
|
+
import { existsSync as existsSync2 } from "fs";
|
|
8
|
+
import { join as join2 } from "path";
|
|
9
|
+
|
|
10
|
+
// ../core/dist/memory-backend.js
|
|
11
|
+
import { readFile, writeFile, mkdir, access, constants, readdir, stat } from "fs/promises";
|
|
12
|
+
import { existsSync } from "fs";
|
|
13
|
+
import { basename, dirname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
|
|
14
|
+
import { createHash } from "crypto";
|
|
15
|
+
var MEMORY_WORKSPACE_PATH = ".fusion/memory";
|
|
16
|
+
var MEMORY_LONG_TERM_FILENAME = "MEMORY.md";
|
|
17
|
+
var MEMORY_DREAMS_FILENAME = "DREAMS.md";
|
|
18
|
+
var QMD_INSTALL_COMMAND = "bun install -g @tobilu/qmd";
|
|
19
|
+
var QMD_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
20
|
+
var DAILY_MEMORY_RE = /^\d{4}-\d{2}-\d{2}\.md$/;
|
|
21
|
+
var MAX_MEMORY_SNIPPET_CHARS = 700;
|
|
22
|
+
var DEFAULT_MEMORY_GET_LINES = 120;
|
|
23
|
+
var MAX_MEMORY_GET_LINES = 400;
|
|
24
|
+
var QMD_COLLECTION_PREFIX = "fusion-memory";
|
|
25
|
+
var qmdRefreshState = /* @__PURE__ */ new Map();
|
|
26
|
+
var qmdAgentRefreshState = /* @__PURE__ */ new Map();
|
|
27
|
+
var qmdInstallPromise = null;
|
|
28
|
+
function shouldSkipBackgroundQmdRefresh() {
|
|
29
|
+
return (process.env.VITEST === "true" || process.env.NODE_ENV === "test") && process.env.FUSION_ENABLE_QMD_REFRESH_IN_TESTS !== "1";
|
|
30
|
+
}
|
|
31
|
+
var MemoryBackendError = class extends Error {
|
|
32
|
+
code;
|
|
33
|
+
backend;
|
|
34
|
+
constructor(code, message, backend) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = "MemoryBackendError";
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.backend = backend;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var backendRegistry = /* @__PURE__ */ new Map();
|
|
42
|
+
var FileMemoryBackend = class {
|
|
43
|
+
type = "file";
|
|
44
|
+
name = "File (.fusion/memory/MEMORY.md)";
|
|
45
|
+
capabilities = {
|
|
46
|
+
readable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
supportsAtomicWrite: true,
|
|
49
|
+
hasConflictResolution: false,
|
|
50
|
+
persistent: true
|
|
51
|
+
};
|
|
52
|
+
getLongTermPath(rootDir) {
|
|
53
|
+
return join(rootDir, MEMORY_WORKSPACE_PATH, MEMORY_LONG_TERM_FILENAME);
|
|
54
|
+
}
|
|
55
|
+
async read(rootDir) {
|
|
56
|
+
const filePath = this.getLongTermPath(rootDir);
|
|
57
|
+
try {
|
|
58
|
+
const content = await readFile(filePath, "utf-8");
|
|
59
|
+
return {
|
|
60
|
+
content,
|
|
61
|
+
exists: true,
|
|
62
|
+
backend: this.type
|
|
63
|
+
};
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (err.code === "ENOENT") {
|
|
66
|
+
return {
|
|
67
|
+
content: "",
|
|
68
|
+
exists: false,
|
|
69
|
+
backend: this.type
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
throw new MemoryBackendError("READ_FAILED", `Failed to read memory file: ${err.message}`, this.type);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async write(rootDir, content) {
|
|
76
|
+
const filePath = this.getLongTermPath(rootDir);
|
|
77
|
+
const dir = join(rootDir, MEMORY_WORKSPACE_PATH);
|
|
78
|
+
try {
|
|
79
|
+
if (!existsSync(dir)) {
|
|
80
|
+
await mkdir(dir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
const tmpPath = filePath + ".tmp";
|
|
83
|
+
await writeFile(tmpPath, content, "utf-8");
|
|
84
|
+
const { rename } = await import("fs/promises");
|
|
85
|
+
await rename(tmpPath, filePath);
|
|
86
|
+
return {
|
|
87
|
+
success: true,
|
|
88
|
+
backend: this.type
|
|
89
|
+
};
|
|
90
|
+
} catch (err) {
|
|
91
|
+
throw new MemoryBackendError("WRITE_FAILED", `Failed to write memory file: ${err.message}`, this.type);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async exists(rootDir) {
|
|
95
|
+
try {
|
|
96
|
+
await access(this.getLongTermPath(rootDir), constants.R_OK);
|
|
97
|
+
return true;
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async get(rootDir, options) {
|
|
103
|
+
return getMemoryFile(rootDir, options, this.type);
|
|
104
|
+
}
|
|
105
|
+
async search(rootDir, options) {
|
|
106
|
+
return searchMemoryFiles(rootDir, options, this.type);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var ReadOnlyMemoryBackend = class {
|
|
110
|
+
type = "readonly";
|
|
111
|
+
name = "Read-Only";
|
|
112
|
+
capabilities = {
|
|
113
|
+
readable: true,
|
|
114
|
+
writable: false,
|
|
115
|
+
supportsAtomicWrite: false,
|
|
116
|
+
hasConflictResolution: false,
|
|
117
|
+
persistent: false
|
|
118
|
+
};
|
|
119
|
+
async read(_rootDir) {
|
|
120
|
+
return {
|
|
121
|
+
content: "",
|
|
122
|
+
exists: false,
|
|
123
|
+
backend: this.type
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
async write(_rootDir, _content) {
|
|
127
|
+
throw new MemoryBackendError("READ_ONLY", "This backend is read-only and cannot write memory", this.type);
|
|
128
|
+
}
|
|
129
|
+
async get(_rootDir, options) {
|
|
130
|
+
throw new MemoryBackendError("NOT_FOUND", `Memory path '${options.path}' not found`, this.type);
|
|
131
|
+
}
|
|
132
|
+
async search(_rootDir, _options) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
var QmdMemoryBackend = class {
|
|
137
|
+
type = "qmd";
|
|
138
|
+
name = "QMD (qmd index/query integration)";
|
|
139
|
+
capabilities = {
|
|
140
|
+
readable: true,
|
|
141
|
+
writable: true,
|
|
142
|
+
supportsAtomicWrite: false,
|
|
143
|
+
hasConflictResolution: false,
|
|
144
|
+
persistent: true
|
|
145
|
+
};
|
|
146
|
+
/** Delegate file backend for actual I/O operations */
|
|
147
|
+
fileBackend = new FileMemoryBackend();
|
|
148
|
+
/**
|
|
149
|
+
* Read memory content from the filesystem.
|
|
150
|
+
*
|
|
151
|
+
* @param rootDir - The project root directory
|
|
152
|
+
* @returns Promise resolving to memory read result
|
|
153
|
+
*/
|
|
154
|
+
async read(rootDir) {
|
|
155
|
+
const result = await this.fileBackend.read(rootDir);
|
|
156
|
+
return {
|
|
157
|
+
...result,
|
|
158
|
+
backend: this.type
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Write memory content to the filesystem.
|
|
163
|
+
*
|
|
164
|
+
* @param rootDir - The project root directory
|
|
165
|
+
* @param content - The content to write
|
|
166
|
+
* @returns Promise resolving to write result
|
|
167
|
+
*/
|
|
168
|
+
async write(rootDir, content) {
|
|
169
|
+
const result = await this.fileBackend.write(rootDir, content);
|
|
170
|
+
scheduleQmdProjectMemoryRefresh(rootDir);
|
|
171
|
+
return {
|
|
172
|
+
...result,
|
|
173
|
+
backend: this.type
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if memory file exists.
|
|
178
|
+
*
|
|
179
|
+
* @param rootDir - The project root directory
|
|
180
|
+
* @returns Promise resolving to true if memory exists
|
|
181
|
+
*/
|
|
182
|
+
async exists(rootDir) {
|
|
183
|
+
return this.fileBackend.exists(rootDir);
|
|
184
|
+
}
|
|
185
|
+
async get(rootDir, options) {
|
|
186
|
+
return getMemoryFile(rootDir, options, this.type);
|
|
187
|
+
}
|
|
188
|
+
async search(rootDir, options) {
|
|
189
|
+
const qmdResults = await searchWithQmd(rootDir, options);
|
|
190
|
+
if (qmdResults.length > 0) {
|
|
191
|
+
return qmdResults.map((result) => ({ ...result, backend: this.type }));
|
|
192
|
+
}
|
|
193
|
+
return searchMemoryFiles(rootDir, options, this.type);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
function memoryWorkspacePath(rootDir) {
|
|
197
|
+
return join(rootDir, MEMORY_WORKSPACE_PATH);
|
|
198
|
+
}
|
|
199
|
+
function memoryLongTermPath(rootDir) {
|
|
200
|
+
return join(memoryWorkspacePath(rootDir), MEMORY_LONG_TERM_FILENAME);
|
|
201
|
+
}
|
|
202
|
+
function memoryDreamsPath(rootDir) {
|
|
203
|
+
return join(memoryWorkspacePath(rootDir), MEMORY_DREAMS_FILENAME);
|
|
204
|
+
}
|
|
205
|
+
function qmdMemoryCollectionName(rootDir) {
|
|
206
|
+
const absoluteRoot = resolve(rootDir);
|
|
207
|
+
const slug = basename(absoluteRoot).toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
208
|
+
const hash = createHash("sha1").update(absoluteRoot).digest("hex").slice(0, 12);
|
|
209
|
+
return `${QMD_COLLECTION_PREFIX}-${slug}-${hash}`;
|
|
210
|
+
}
|
|
211
|
+
function buildQmdSearchArgs(rootDir, options) {
|
|
212
|
+
const limit = Math.max(1, Math.min(options.limit ?? 5, 20));
|
|
213
|
+
return [
|
|
214
|
+
"search",
|
|
215
|
+
options.query,
|
|
216
|
+
"--json",
|
|
217
|
+
"--collection",
|
|
218
|
+
qmdMemoryCollectionName(rootDir),
|
|
219
|
+
"-n",
|
|
220
|
+
String(limit)
|
|
221
|
+
];
|
|
222
|
+
}
|
|
223
|
+
function buildQmdCollectionAddArgs(rootDir) {
|
|
224
|
+
return [
|
|
225
|
+
"collection",
|
|
226
|
+
"add",
|
|
227
|
+
memoryWorkspacePath(rootDir),
|
|
228
|
+
"--name",
|
|
229
|
+
qmdMemoryCollectionName(rootDir),
|
|
230
|
+
"--mask",
|
|
231
|
+
"**/*.md"
|
|
232
|
+
];
|
|
233
|
+
}
|
|
234
|
+
function qmdAgentMemoryCollectionName(rootDir, agentId) {
|
|
235
|
+
const absoluteRoot = resolve(rootDir);
|
|
236
|
+
const safeAgentId = agentId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
237
|
+
const hash = createHash("sha1").update(`${absoluteRoot}:${agentId}`).digest("hex").slice(0, 12);
|
|
238
|
+
return `fusion-agent-memory-${safeAgentId.toLowerCase()}-${hash}`;
|
|
239
|
+
}
|
|
240
|
+
function dailyMemoryPath(rootDir, date = /* @__PURE__ */ new Date()) {
|
|
241
|
+
return join(memoryWorkspacePath(rootDir), `${date.toISOString().slice(0, 10)}.md`);
|
|
242
|
+
}
|
|
243
|
+
function getDefaultLongTermMemoryScaffold() {
|
|
244
|
+
return `# Project Memory
|
|
245
|
+
|
|
246
|
+
<!-- Curated long-term memory. Store durable decisions, conventions, preferences, and pitfalls. -->
|
|
247
|
+
|
|
248
|
+
## Decisions
|
|
249
|
+
|
|
250
|
+
## Conventions
|
|
251
|
+
|
|
252
|
+
## Pitfalls
|
|
253
|
+
|
|
254
|
+
## Context
|
|
255
|
+
`;
|
|
256
|
+
}
|
|
257
|
+
function getDefaultDailyMemoryScaffold(date = /* @__PURE__ */ new Date()) {
|
|
258
|
+
return `# Daily Memory ${date.toISOString().slice(0, 10)}
|
|
259
|
+
|
|
260
|
+
<!-- Append running observations, open loops, and day-to-day notes here. Promote evergreen facts to MEMORY.md. -->
|
|
261
|
+
`;
|
|
262
|
+
}
|
|
263
|
+
function getDefaultDreamsScaffold() {
|
|
264
|
+
return `# Memory Dreams
|
|
265
|
+
|
|
266
|
+
<!-- Periodic synthesized patterns from daily notes. Promote durable lessons to MEMORY.md. -->
|
|
267
|
+
`;
|
|
268
|
+
}
|
|
269
|
+
async function ensureOpenClawMemoryFiles(rootDir, date = /* @__PURE__ */ new Date()) {
|
|
270
|
+
const workspacePath = memoryWorkspacePath(rootDir);
|
|
271
|
+
await mkdir(workspacePath, { recursive: true });
|
|
272
|
+
const longTermPath = memoryLongTermPath(rootDir);
|
|
273
|
+
let longTermCreated = false;
|
|
274
|
+
if (!existsSync(longTermPath)) {
|
|
275
|
+
await writeFile(longTermPath, getDefaultLongTermMemoryScaffold(), "utf-8");
|
|
276
|
+
longTermCreated = true;
|
|
277
|
+
}
|
|
278
|
+
const todayPath = dailyMemoryPath(rootDir, date);
|
|
279
|
+
let dailyCreated = false;
|
|
280
|
+
if (!existsSync(todayPath)) {
|
|
281
|
+
await writeFile(todayPath, getDefaultDailyMemoryScaffold(date), "utf-8");
|
|
282
|
+
dailyCreated = true;
|
|
283
|
+
}
|
|
284
|
+
const dreamsPath = memoryDreamsPath(rootDir);
|
|
285
|
+
if (!existsSync(dreamsPath)) {
|
|
286
|
+
await writeFile(dreamsPath, getDefaultDreamsScaffold(), "utf-8");
|
|
287
|
+
}
|
|
288
|
+
return { longTermCreated, dailyCreated };
|
|
289
|
+
}
|
|
290
|
+
function isPathTraversal(path) {
|
|
291
|
+
return path.split(/[\\/]+/).includes("..");
|
|
292
|
+
}
|
|
293
|
+
function normalizeMemoryRequestPath(rawPath) {
|
|
294
|
+
const trimmed = rawPath.trim();
|
|
295
|
+
if (!trimmed) {
|
|
296
|
+
throw new MemoryBackendError("NOT_FOUND", "Memory path is required", "memory");
|
|
297
|
+
}
|
|
298
|
+
if (isAbsolute(trimmed) || isPathTraversal(trimmed)) {
|
|
299
|
+
throw new MemoryBackendError("UNSUPPORTED", "Memory paths must be workspace-relative", "memory");
|
|
300
|
+
}
|
|
301
|
+
const normalized = normalize(trimmed).replace(/\\/g, "/");
|
|
302
|
+
if (normalized === MEMORY_LONG_TERM_FILENAME || normalized === MEMORY_DREAMS_FILENAME || normalized === `memory/${MEMORY_LONG_TERM_FILENAME}` || normalized === `memory/${MEMORY_DREAMS_FILENAME}`) {
|
|
303
|
+
return `${MEMORY_WORKSPACE_PATH}/${basename(normalized)}`;
|
|
304
|
+
}
|
|
305
|
+
if (DAILY_MEMORY_RE.test(basename(normalized)) && (normalized === basename(normalized) || normalized.startsWith("memory/"))) {
|
|
306
|
+
return `${MEMORY_WORKSPACE_PATH}/${basename(normalized)}`;
|
|
307
|
+
}
|
|
308
|
+
if (normalized.startsWith(`${MEMORY_WORKSPACE_PATH}/`)) {
|
|
309
|
+
const file = basename(normalized);
|
|
310
|
+
if (file === MEMORY_LONG_TERM_FILENAME || file === MEMORY_DREAMS_FILENAME || DAILY_MEMORY_RE.test(file)) {
|
|
311
|
+
return `${MEMORY_WORKSPACE_PATH}/${file}`;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
throw new MemoryBackendError("UNSUPPORTED", `Memory path '${rawPath}' is outside allowed files: .fusion/memory/MEMORY.md, .fusion/memory/DREAMS.md, .fusion/memory/YYYY-MM-DD.md`, "memory");
|
|
315
|
+
}
|
|
316
|
+
function resolveMemoryFilePath(rootDir, requestedPath) {
|
|
317
|
+
const displayPath = normalizeMemoryRequestPath(requestedPath);
|
|
318
|
+
const absPath = resolve(rootDir, displayPath);
|
|
319
|
+
const rel = relative(rootDir, absPath);
|
|
320
|
+
if (!rel || rel.startsWith(`..${sep}`) || rel === ".." || isAbsolute(rel)) {
|
|
321
|
+
throw new MemoryBackendError("UNSUPPORTED", "Memory path escapes project root", "memory");
|
|
322
|
+
}
|
|
323
|
+
return { absPath, displayPath };
|
|
324
|
+
}
|
|
325
|
+
async function getMemoryFile(rootDir, options, backend) {
|
|
326
|
+
const { absPath, displayPath } = resolveMemoryFilePath(rootDir, options.path);
|
|
327
|
+
let content;
|
|
328
|
+
try {
|
|
329
|
+
content = await readFile(absPath, "utf-8");
|
|
330
|
+
} catch (err) {
|
|
331
|
+
if (err.code === "ENOENT") {
|
|
332
|
+
throw new MemoryBackendError("NOT_FOUND", `Memory path '${options.path}' not found`, backend);
|
|
333
|
+
}
|
|
334
|
+
throw new MemoryBackendError("READ_FAILED", `Failed to read memory path '${options.path}': ${err.message}`, backend);
|
|
335
|
+
}
|
|
336
|
+
const lines = content.split("\n");
|
|
337
|
+
const startLine = Math.max(1, Math.floor(options.startLine ?? 1));
|
|
338
|
+
const requestedCount = Math.max(1, Math.floor(options.lineCount ?? DEFAULT_MEMORY_GET_LINES));
|
|
339
|
+
const lineCount = Math.min(requestedCount, MAX_MEMORY_GET_LINES);
|
|
340
|
+
const startIndex = Math.min(startLine - 1, lines.length);
|
|
341
|
+
const endIndex = Math.min(startIndex + lineCount, lines.length);
|
|
342
|
+
return {
|
|
343
|
+
path: displayPath,
|
|
344
|
+
content: lines.slice(startIndex, endIndex).join("\n"),
|
|
345
|
+
startLine,
|
|
346
|
+
endLine: endIndex,
|
|
347
|
+
totalLines: lines.length,
|
|
348
|
+
backend
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
async function listMemoryFiles(rootDir) {
|
|
352
|
+
const files = [];
|
|
353
|
+
const workspacePath = memoryWorkspacePath(rootDir);
|
|
354
|
+
const longTerm = memoryLongTermPath(rootDir);
|
|
355
|
+
if (existsSync(longTerm)) {
|
|
356
|
+
files.push({ absPath: longTerm, displayPath: `${MEMORY_WORKSPACE_PATH}/${MEMORY_LONG_TERM_FILENAME}` });
|
|
357
|
+
}
|
|
358
|
+
const dreams = memoryDreamsPath(rootDir);
|
|
359
|
+
if (existsSync(dreams)) {
|
|
360
|
+
files.push({ absPath: dreams, displayPath: `${MEMORY_WORKSPACE_PATH}/${MEMORY_DREAMS_FILENAME}` });
|
|
361
|
+
}
|
|
362
|
+
if (existsSync(workspacePath)) {
|
|
363
|
+
for (const entry of await readdir(workspacePath)) {
|
|
364
|
+
if (!DAILY_MEMORY_RE.test(entry))
|
|
365
|
+
continue;
|
|
366
|
+
const absPath = join(workspacePath, entry);
|
|
367
|
+
const fileStat = await stat(absPath);
|
|
368
|
+
if (fileStat.isFile()) {
|
|
369
|
+
files.push({ absPath, displayPath: `${MEMORY_WORKSPACE_PATH}/${entry}` });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return files;
|
|
374
|
+
}
|
|
375
|
+
function scoreSnippet(snippet, queryTerms) {
|
|
376
|
+
const normalized = snippet.toLowerCase();
|
|
377
|
+
return queryTerms.reduce((score, term) => score + (normalized.includes(term) ? 1 : 0), 0);
|
|
378
|
+
}
|
|
379
|
+
async function searchMemoryFiles(rootDir, options, backend) {
|
|
380
|
+
const queryTerms = options.query.toLowerCase().split(/[^a-z0-9_-]+/i).map((term) => term.trim()).filter((term) => term.length >= 2);
|
|
381
|
+
if (queryTerms.length === 0) {
|
|
382
|
+
return [];
|
|
383
|
+
}
|
|
384
|
+
const limit = Math.max(1, Math.min(options.limit ?? 5, 20));
|
|
385
|
+
const results = [];
|
|
386
|
+
for (const file of await listMemoryFiles(rootDir)) {
|
|
387
|
+
const lines = (await readFile(file.absPath, "utf-8")).split("\n");
|
|
388
|
+
for (let index = 0; index < lines.length; index += 8) {
|
|
389
|
+
const chunkLines = lines.slice(index, index + 12);
|
|
390
|
+
const snippet = chunkLines.join("\n").trim();
|
|
391
|
+
if (!snippet)
|
|
392
|
+
continue;
|
|
393
|
+
const score = scoreSnippet(snippet, queryTerms);
|
|
394
|
+
if (score === 0)
|
|
395
|
+
continue;
|
|
396
|
+
results.push({
|
|
397
|
+
path: file.displayPath,
|
|
398
|
+
lineStart: index + 1,
|
|
399
|
+
lineEnd: Math.min(index + chunkLines.length, lines.length),
|
|
400
|
+
snippet: snippet.slice(0, MAX_MEMORY_SNIPPET_CHARS),
|
|
401
|
+
score,
|
|
402
|
+
backend
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return results.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path)).slice(0, limit);
|
|
407
|
+
}
|
|
408
|
+
function normalizeQmdSearchResultPath(rootDir, rawPath) {
|
|
409
|
+
const original = String(rawPath ?? "").trim();
|
|
410
|
+
if (!original) {
|
|
411
|
+
return "";
|
|
412
|
+
}
|
|
413
|
+
let candidate = original.replace(/\\/g, "/");
|
|
414
|
+
const uriMatch = candidate.match(/^qmd:\/\/[^/]+\/(.+)$/i);
|
|
415
|
+
if (uriMatch?.[1]) {
|
|
416
|
+
candidate = uriMatch[1];
|
|
417
|
+
}
|
|
418
|
+
candidate = candidate.split("?")[0]?.split("#")[0] ?? "";
|
|
419
|
+
candidate = candidate.replace(/^\.\/+/, "");
|
|
420
|
+
if (isAbsolute(candidate)) {
|
|
421
|
+
const rel = relative(resolve(rootDir), resolve(candidate)).replace(/\\/g, "/");
|
|
422
|
+
if (rel && rel !== "." && rel !== ".." && !rel.startsWith("../") && !isAbsolute(rel)) {
|
|
423
|
+
candidate = rel;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const lowerCandidate = candidate.toLowerCase();
|
|
427
|
+
const normalizedBaseName = basename(candidate).toLowerCase();
|
|
428
|
+
const normalizedDirName = dirname(lowerCandidate).replace(/\\/g, "/");
|
|
429
|
+
if (normalizedBaseName === "memory.md" && (normalizedDirName === ".fusion" || normalizedDirName.endsWith("/.fusion"))) {
|
|
430
|
+
return `${MEMORY_WORKSPACE_PATH}/${MEMORY_LONG_TERM_FILENAME}`;
|
|
431
|
+
}
|
|
432
|
+
if (normalizedBaseName === MEMORY_LONG_TERM_FILENAME.toLowerCase()) {
|
|
433
|
+
return `${MEMORY_WORKSPACE_PATH}/${MEMORY_LONG_TERM_FILENAME}`;
|
|
434
|
+
}
|
|
435
|
+
if (normalizedBaseName === MEMORY_DREAMS_FILENAME.toLowerCase()) {
|
|
436
|
+
return `${MEMORY_WORKSPACE_PATH}/${MEMORY_DREAMS_FILENAME}`;
|
|
437
|
+
}
|
|
438
|
+
if (DAILY_MEMORY_RE.test(normalizedBaseName)) {
|
|
439
|
+
return `${MEMORY_WORKSPACE_PATH}/${normalizedBaseName}`;
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
return normalizeMemoryRequestPath(candidate);
|
|
443
|
+
} catch {
|
|
444
|
+
return original;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async function searchWithQmd(rootDir, options) {
|
|
448
|
+
if (shouldSkipBackgroundQmdRefresh()) {
|
|
449
|
+
return [];
|
|
450
|
+
}
|
|
451
|
+
const command = "qmd";
|
|
452
|
+
const limit = Math.max(1, Math.min(options.limit ?? 5, 20));
|
|
453
|
+
try {
|
|
454
|
+
const { execFile } = await import("child_process");
|
|
455
|
+
const { promisify } = await import("util");
|
|
456
|
+
const execFileAsync = promisify(execFile);
|
|
457
|
+
await ensureQmdProjectMemoryCollection(rootDir, execFileAsync);
|
|
458
|
+
scheduleQmdProjectMemoryRefresh(rootDir);
|
|
459
|
+
const args = buildQmdSearchArgs(rootDir, options);
|
|
460
|
+
const { stdout } = await execFileAsync(command, args, {
|
|
461
|
+
cwd: rootDir,
|
|
462
|
+
timeout: 4e3,
|
|
463
|
+
maxBuffer: 1024 * 1024
|
|
464
|
+
});
|
|
465
|
+
const parsed = JSON.parse(stdout);
|
|
466
|
+
const rawResults = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.results) ? parsed.results : [];
|
|
467
|
+
return rawResults.slice(0, limit).map((result, index) => {
|
|
468
|
+
const rawPath = result.path ?? result.file ?? `qmd/result-${index + 1}`;
|
|
469
|
+
return {
|
|
470
|
+
path: normalizeQmdSearchResultPath(rootDir, rawPath) || String(rawPath),
|
|
471
|
+
lineStart: Number(result.lineStart ?? result.startLine ?? 1),
|
|
472
|
+
lineEnd: Number(result.lineEnd ?? result.endLine ?? result.startLine ?? 1),
|
|
473
|
+
snippet: String(result.snippet ?? result.text ?? result.content ?? "").slice(0, MAX_MEMORY_SNIPPET_CHARS),
|
|
474
|
+
score: Number(result.score ?? 1),
|
|
475
|
+
backend: "qmd"
|
|
476
|
+
};
|
|
477
|
+
}).filter((result) => result.snippet.trim().length > 0);
|
|
478
|
+
} catch {
|
|
479
|
+
return [];
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
async function ensureQmdProjectMemoryCollection(rootDir, execFileAsync) {
|
|
483
|
+
const collectionName = qmdMemoryCollectionName(rootDir);
|
|
484
|
+
const memoryDir = memoryWorkspacePath(rootDir);
|
|
485
|
+
await mkdir(memoryDir, { recursive: true });
|
|
486
|
+
try {
|
|
487
|
+
await execFileAsync("qmd", buildQmdCollectionAddArgs(rootDir), {
|
|
488
|
+
cwd: rootDir,
|
|
489
|
+
timeout: 4e3,
|
|
490
|
+
maxBuffer: 512 * 1024
|
|
491
|
+
});
|
|
492
|
+
} catch (err) {
|
|
493
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
494
|
+
const stderr = typeof err === "object" && err && "stderr" in err ? String(err.stderr ?? "") : "";
|
|
495
|
+
if (!/already exists|exists/i.test(`${message}
|
|
496
|
+
${stderr}`)) {
|
|
497
|
+
throw err;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return collectionName;
|
|
501
|
+
}
|
|
502
|
+
async function getDefaultExecFileAsync() {
|
|
503
|
+
const { execFile } = await import("child_process");
|
|
504
|
+
const { promisify } = await import("util");
|
|
505
|
+
return promisify(execFile);
|
|
506
|
+
}
|
|
507
|
+
async function refreshQmdProjectMemoryIndex(rootDir, options) {
|
|
508
|
+
const key = resolve(rootDir);
|
|
509
|
+
const now = Date.now();
|
|
510
|
+
const current = qmdRefreshState.get(key);
|
|
511
|
+
if (!options?.force) {
|
|
512
|
+
if (current?.inFlight) {
|
|
513
|
+
return current.inFlight;
|
|
514
|
+
}
|
|
515
|
+
if (current && now - current.lastStartedAt < QMD_REFRESH_INTERVAL_MS) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
const promise = (async () => {
|
|
520
|
+
const execFileAsync = options?.execFileAsync ?? await getDefaultExecFileAsync();
|
|
521
|
+
await ensureQmdProjectMemoryCollection(rootDir, execFileAsync);
|
|
522
|
+
await execFileAsync("qmd", ["update"], {
|
|
523
|
+
cwd: rootDir,
|
|
524
|
+
timeout: 3e4,
|
|
525
|
+
maxBuffer: 1024 * 1024
|
|
526
|
+
});
|
|
527
|
+
await execFileAsync("qmd", ["embed"], {
|
|
528
|
+
cwd: rootDir,
|
|
529
|
+
timeout: 12e4,
|
|
530
|
+
maxBuffer: 1024 * 1024
|
|
531
|
+
});
|
|
532
|
+
})();
|
|
533
|
+
qmdRefreshState.set(key, { lastStartedAt: now, inFlight: promise });
|
|
534
|
+
try {
|
|
535
|
+
await promise;
|
|
536
|
+
} finally {
|
|
537
|
+
const latest = qmdRefreshState.get(key);
|
|
538
|
+
if (latest?.inFlight === promise) {
|
|
539
|
+
qmdRefreshState.set(key, { lastStartedAt: latest.lastStartedAt });
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function scheduleQmdProjectMemoryRefresh(rootDir) {
|
|
544
|
+
if (shouldSkipBackgroundQmdRefresh()) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
void refreshQmdProjectMemoryIndex(rootDir).catch(() => {
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
async function refreshQmdAgentMemoryIndex(rootDir, agentId, options) {
|
|
551
|
+
const key = `${resolve(rootDir)}:${agentId}`;
|
|
552
|
+
const now = Date.now();
|
|
553
|
+
const current = qmdAgentRefreshState.get(key);
|
|
554
|
+
if (!options?.force) {
|
|
555
|
+
if (current?.inFlight) {
|
|
556
|
+
return current.inFlight;
|
|
557
|
+
}
|
|
558
|
+
if (current && now - current.lastStartedAt < QMD_REFRESH_INTERVAL_MS) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const promise = (async () => {
|
|
563
|
+
const execFileAsync = options?.execFileAsync ?? await getDefaultExecFileAsync();
|
|
564
|
+
const { agentMemoryWorkspacePath: agentMemoryWorkspacePath2 } = await import("./memory-dreams-PBPISUDD.js");
|
|
565
|
+
const workspacePath = agentMemoryWorkspacePath2(rootDir, agentId);
|
|
566
|
+
await mkdir(workspacePath, { recursive: true });
|
|
567
|
+
try {
|
|
568
|
+
await execFileAsync("qmd", ["collection", "add", workspacePath, "--name", qmdAgentMemoryCollectionName(rootDir, agentId), "--mask", "**/*.md"], {
|
|
569
|
+
cwd: rootDir,
|
|
570
|
+
timeout: 4e3,
|
|
571
|
+
maxBuffer: 512 * 1024
|
|
572
|
+
});
|
|
573
|
+
} catch (err) {
|
|
574
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
575
|
+
const stderr = typeof err === "object" && err && "stderr" in err ? String(err.stderr ?? "") : "";
|
|
576
|
+
if (!/already exists|exists/i.test(`${message}
|
|
577
|
+
${stderr}`)) {
|
|
578
|
+
throw err;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
await execFileAsync("qmd", ["update"], { cwd: rootDir, timeout: 3e4, maxBuffer: 1024 * 1024 });
|
|
582
|
+
await execFileAsync("qmd", ["embed"], { cwd: rootDir, timeout: 12e4, maxBuffer: 1024 * 1024 });
|
|
583
|
+
})();
|
|
584
|
+
qmdAgentRefreshState.set(key, { lastStartedAt: now, inFlight: promise });
|
|
585
|
+
try {
|
|
586
|
+
await promise;
|
|
587
|
+
} finally {
|
|
588
|
+
const latest = qmdAgentRefreshState.get(key);
|
|
589
|
+
if (latest?.inFlight === promise) {
|
|
590
|
+
qmdAgentRefreshState.set(key, { lastStartedAt: latest.lastStartedAt });
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
function scheduleQmdAgentMemoryRefresh(rootDir, agentId) {
|
|
595
|
+
if (shouldSkipBackgroundQmdRefresh()) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
void refreshQmdAgentMemoryIndex(rootDir, agentId).catch(() => {
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
async function isQmdAvailable() {
|
|
602
|
+
try {
|
|
603
|
+
const execFileAsync = await getDefaultExecFileAsync();
|
|
604
|
+
await execFileAsync("qmd", ["--help"], {
|
|
605
|
+
timeout: 3e3,
|
|
606
|
+
maxBuffer: 128 * 1024
|
|
607
|
+
});
|
|
608
|
+
return true;
|
|
609
|
+
} catch {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
async function installQmd(options) {
|
|
614
|
+
const execFileAsync = options?.execFileAsync ?? await getDefaultExecFileAsync();
|
|
615
|
+
const [command, ...args] = QMD_INSTALL_COMMAND.split(" ");
|
|
616
|
+
if (!command || args.length === 0) {
|
|
617
|
+
throw new MemoryBackendError("BACKEND_UNAVAILABLE", "qmd install command is not configured", "qmd");
|
|
618
|
+
}
|
|
619
|
+
await execFileAsync(command, args, {
|
|
620
|
+
timeout: 12e4,
|
|
621
|
+
maxBuffer: 1024 * 1024
|
|
622
|
+
});
|
|
623
|
+
return true;
|
|
624
|
+
}
|
|
625
|
+
async function ensureQmdInstalled(options) {
|
|
626
|
+
const checkAvailable = options?.isAvailable ?? isQmdAvailable;
|
|
627
|
+
if (await checkAvailable()) {
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
if (!qmdInstallPromise) {
|
|
631
|
+
qmdInstallPromise = installQmd({ execFileAsync: options?.execFileAsync }).then(async () => checkAvailable()).finally(() => {
|
|
632
|
+
qmdInstallPromise = null;
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
return qmdInstallPromise;
|
|
636
|
+
}
|
|
637
|
+
async function ensureQmdInstalledAndRefresh(rootDir) {
|
|
638
|
+
const available = await ensureQmdInstalled();
|
|
639
|
+
if (available) {
|
|
640
|
+
await refreshQmdProjectMemoryIndex(rootDir, { force: true });
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
function scheduleQmdInstallAndRefresh(rootDir) {
|
|
644
|
+
if (shouldSkipBackgroundQmdRefresh()) {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
void ensureQmdInstalledAndRefresh(rootDir).catch(() => {
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
var fileBackendInstance = new FileMemoryBackend();
|
|
651
|
+
backendRegistry.set("file", fileBackendInstance);
|
|
652
|
+
backendRegistry.set("readonly", new ReadOnlyMemoryBackend());
|
|
653
|
+
backendRegistry.set("qmd", new QmdMemoryBackend());
|
|
654
|
+
var MEMORY_BACKEND_SETTINGS_KEYS = {
|
|
655
|
+
/** Backend type to use (default: "file") */
|
|
656
|
+
MEMORY_BACKEND_TYPE: "memoryBackendType"
|
|
657
|
+
};
|
|
658
|
+
var DEFAULT_MEMORY_BACKEND = "qmd";
|
|
659
|
+
function resolveMemoryBackend(settings) {
|
|
660
|
+
const backendType = settings?.[MEMORY_BACKEND_SETTINGS_KEYS.MEMORY_BACKEND_TYPE] || DEFAULT_MEMORY_BACKEND;
|
|
661
|
+
const backend = backendRegistry.get(backendType);
|
|
662
|
+
if (backend) {
|
|
663
|
+
return backend;
|
|
664
|
+
}
|
|
665
|
+
return backendRegistry.get(DEFAULT_MEMORY_BACKEND);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// ../core/dist/memory-dreams.js
|
|
669
|
+
var MEMORY_DREAMS_SCHEDULE_NAME = "Memory Dreams";
|
|
670
|
+
var DEFAULT_MEMORY_DREAMS_SCHEDULE = "0 4 * * *";
|
|
671
|
+
var AGENT_MEMORY_ROOT = ".fusion/agent-memory";
|
|
672
|
+
var AGENT_MEMORY_FILENAME = "MEMORY.md";
|
|
673
|
+
var AGENT_DREAMS_FILENAME = "DREAMS.md";
|
|
674
|
+
var DAILY_AGENT_MEMORY_RE = /^\d{4}-\d{2}-\d{2}\.md$/;
|
|
675
|
+
function agentMemoryWorkspacePath(rootDir, agentId) {
|
|
676
|
+
const safeAgentId = agentId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
677
|
+
return join2(rootDir, AGENT_MEMORY_ROOT, safeAgentId);
|
|
678
|
+
}
|
|
679
|
+
function agentMemoryLongTermPath(rootDir, agentId) {
|
|
680
|
+
return join2(agentMemoryWorkspacePath(rootDir, agentId), AGENT_MEMORY_FILENAME);
|
|
681
|
+
}
|
|
682
|
+
function agentMemoryDreamsPath(rootDir, agentId) {
|
|
683
|
+
return join2(agentMemoryWorkspacePath(rootDir, agentId), AGENT_DREAMS_FILENAME);
|
|
684
|
+
}
|
|
685
|
+
function agentDailyMemoryPath(rootDir, agentId, date = /* @__PURE__ */ new Date()) {
|
|
686
|
+
return join2(agentMemoryWorkspacePath(rootDir, agentId), `${date.toISOString().slice(0, 10)}.md`);
|
|
687
|
+
}
|
|
688
|
+
async function ensureAgentMemoryFiles(rootDir, agent, date = /* @__PURE__ */ new Date()) {
|
|
689
|
+
const workspacePath = agentMemoryWorkspacePath(rootDir, agent.id);
|
|
690
|
+
await mkdir2(workspacePath, { recursive: true });
|
|
691
|
+
const longTermPath = agentMemoryLongTermPath(rootDir, agent.id);
|
|
692
|
+
if (!existsSync2(longTermPath)) {
|
|
693
|
+
const title = agent.name?.trim() ? `# Agent Memory: ${agent.name.trim()}` : "# Agent Memory";
|
|
694
|
+
await writeFile2(longTermPath, `${title}
|
|
695
|
+
|
|
696
|
+
<!-- Per-agent memory. Keep separate from workspace Project Memory. -->
|
|
697
|
+
|
|
698
|
+
${agent.memory?.trim() ?? ""}
|
|
699
|
+
`, "utf-8");
|
|
700
|
+
}
|
|
701
|
+
const dreamsPath = agentMemoryDreamsPath(rootDir, agent.id);
|
|
702
|
+
if (!existsSync2(dreamsPath)) {
|
|
703
|
+
await writeFile2(dreamsPath, "# Agent Memory Dreams\n\n<!-- Synthesized patterns from this agent's daily notes. -->\n", "utf-8");
|
|
704
|
+
}
|
|
705
|
+
const dailyPath = agentDailyMemoryPath(rootDir, agent.id, date);
|
|
706
|
+
if (!existsSync2(dailyPath)) {
|
|
707
|
+
await writeFile2(dailyPath, `# Agent Daily Memory ${date.toISOString().slice(0, 10)}
|
|
708
|
+
|
|
709
|
+
<!-- Running observations for this agent. -->
|
|
710
|
+
`, "utf-8");
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function buildDreamProcessingPrompt(input) {
|
|
714
|
+
return `You are processing project memory in an OpenClaw-style memory system.
|
|
715
|
+
|
|
716
|
+
Read today's daily notes and existing long-term memory. Produce:
|
|
717
|
+
1. DREAMS: synthesized patterns, open loops, contradictions, and emerging themes.
|
|
718
|
+
2. LONG_TERM_UPDATES: only durable conventions, decisions, pitfalls, or constraints worth keeping.
|
|
719
|
+
|
|
720
|
+
Rules:
|
|
721
|
+
- Do not copy task logs or changelog entries.
|
|
722
|
+
- Do not invent facts not present in the input.
|
|
723
|
+
- Keep output concise and actionable.
|
|
724
|
+
- Return exactly these Markdown headings:
|
|
725
|
+
|
|
726
|
+
## DREAMS
|
|
727
|
+
|
|
728
|
+
## LONG_TERM_UPDATES
|
|
729
|
+
|
|
730
|
+
Date: ${input.date}
|
|
731
|
+
|
|
732
|
+
## Existing Long-Term Memory
|
|
733
|
+
|
|
734
|
+
${input.longTermMemory || "(empty)"}
|
|
735
|
+
|
|
736
|
+
## Previous Dreams
|
|
737
|
+
|
|
738
|
+
${input.previousDreams || "(empty)"}
|
|
739
|
+
|
|
740
|
+
## Daily Notes
|
|
741
|
+
|
|
742
|
+
${input.dailyMemory || "(empty)"}
|
|
743
|
+
`;
|
|
744
|
+
}
|
|
745
|
+
function extractDreamProcessorResult(output) {
|
|
746
|
+
const text = typeof output === "string" ? output : "";
|
|
747
|
+
const dreamsMatch = text.match(/## DREAMS\s*([\s\S]*?)(?=## LONG_TERM_UPDATES|$)/i);
|
|
748
|
+
const updatesMatch = text.match(/## LONG_TERM_UPDATES\s*([\s\S]*?)$/i);
|
|
749
|
+
return {
|
|
750
|
+
dreams: dreamsMatch?.[1]?.trim() ?? "",
|
|
751
|
+
longTermUpdates: updatesMatch?.[1]?.trim() ?? ""
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
async function readIfExists(path) {
|
|
755
|
+
if (!existsSync2(path)) {
|
|
756
|
+
return "";
|
|
757
|
+
}
|
|
758
|
+
return readFile2(path, "utf-8");
|
|
759
|
+
}
|
|
760
|
+
async function processMemoryDreams(rootDir, executePrompt, date = /* @__PURE__ */ new Date()) {
|
|
761
|
+
await ensureOpenClawMemoryFiles(rootDir, date);
|
|
762
|
+
const dateKey = date.toISOString().slice(0, 10);
|
|
763
|
+
const longTermPath = memoryLongTermPath(rootDir);
|
|
764
|
+
const dreamsPath = memoryDreamsPath(rootDir);
|
|
765
|
+
const dailyPath = dailyMemoryPath(rootDir, date);
|
|
766
|
+
const prompt = buildDreamProcessingPrompt({
|
|
767
|
+
date: dateKey,
|
|
768
|
+
longTermMemory: await readIfExists(longTermPath),
|
|
769
|
+
previousDreams: await readIfExists(dreamsPath),
|
|
770
|
+
dailyMemory: await readIfExists(dailyPath)
|
|
771
|
+
});
|
|
772
|
+
const result = extractDreamProcessorResult(await executePrompt(prompt));
|
|
773
|
+
if (result.dreams) {
|
|
774
|
+
await appendFile(dreamsPath, `
|
|
775
|
+
## ${dateKey}
|
|
776
|
+
|
|
777
|
+
${result.dreams}
|
|
778
|
+
`, "utf-8");
|
|
779
|
+
}
|
|
780
|
+
if (result.longTermUpdates) {
|
|
781
|
+
await appendFile(longTermPath, `
|
|
782
|
+
## Dream Updates ${dateKey}
|
|
783
|
+
|
|
784
|
+
${result.longTermUpdates}
|
|
785
|
+
`, "utf-8");
|
|
786
|
+
}
|
|
787
|
+
await writeFile2(dailyPath, `# Daily Memory ${dateKey}
|
|
788
|
+
|
|
789
|
+
<!-- Processed into dreams on ${(/* @__PURE__ */ new Date()).toISOString()} -->
|
|
790
|
+
`, "utf-8");
|
|
791
|
+
return result;
|
|
792
|
+
}
|
|
793
|
+
async function readAgentDailyNotes(rootDir, agentId, date) {
|
|
794
|
+
const workspacePath = agentMemoryWorkspacePath(rootDir, agentId);
|
|
795
|
+
const dateKey = date.toISOString().slice(0, 10);
|
|
796
|
+
const dailyPath = agentDailyMemoryPath(rootDir, agentId, date);
|
|
797
|
+
if (existsSync2(dailyPath)) {
|
|
798
|
+
return readFile2(dailyPath, "utf-8");
|
|
799
|
+
}
|
|
800
|
+
const files = await readdir2(workspacePath).catch(() => []);
|
|
801
|
+
const chunks = [];
|
|
802
|
+
for (const file of files) {
|
|
803
|
+
if (!DAILY_AGENT_MEMORY_RE.test(file))
|
|
804
|
+
continue;
|
|
805
|
+
if (!file.startsWith(dateKey))
|
|
806
|
+
continue;
|
|
807
|
+
const absPath = join2(workspacePath, file);
|
|
808
|
+
if ((await stat2(absPath)).isFile()) {
|
|
809
|
+
chunks.push(await readFile2(absPath, "utf-8"));
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return chunks.join("\n\n");
|
|
813
|
+
}
|
|
814
|
+
async function processAgentMemoryDreams(rootDir, agents, executePrompt, date = /* @__PURE__ */ new Date(), settings) {
|
|
815
|
+
const dateKey = date.toISOString().slice(0, 10);
|
|
816
|
+
const results = [];
|
|
817
|
+
for (const agent of agents) {
|
|
818
|
+
if (isEphemeralAgent(agent)) {
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
await ensureAgentMemoryFiles(rootDir, agent, date);
|
|
822
|
+
const longTermPath = agentMemoryLongTermPath(rootDir, agent.id);
|
|
823
|
+
const dreamsPath = agentMemoryDreamsPath(rootDir, agent.id);
|
|
824
|
+
const dailyPath = agentDailyMemoryPath(rootDir, agent.id, date);
|
|
825
|
+
const prompt = buildDreamProcessingPrompt({
|
|
826
|
+
date: dateKey,
|
|
827
|
+
longTermMemory: await readIfExists(longTermPath),
|
|
828
|
+
previousDreams: await readIfExists(dreamsPath),
|
|
829
|
+
dailyMemory: await readAgentDailyNotes(rootDir, agent.id, date)
|
|
830
|
+
}).replace("You are processing project memory in an OpenClaw-style memory system.", `You are processing private memory for agent ${agent.name} (${agent.id}) in an OpenClaw-style memory system.`);
|
|
831
|
+
const result = extractDreamProcessorResult(await executePrompt(prompt));
|
|
832
|
+
if (result.dreams) {
|
|
833
|
+
await appendFile(dreamsPath, `
|
|
834
|
+
## ${dateKey}
|
|
835
|
+
|
|
836
|
+
${result.dreams}
|
|
837
|
+
`, "utf-8");
|
|
838
|
+
}
|
|
839
|
+
if (result.longTermUpdates) {
|
|
840
|
+
await appendFile(longTermPath, `
|
|
841
|
+
## Dream Updates ${dateKey}
|
|
842
|
+
|
|
843
|
+
${result.longTermUpdates}
|
|
844
|
+
`, "utf-8");
|
|
845
|
+
}
|
|
846
|
+
await writeFile2(dailyPath, `# Agent Daily Memory ${dateKey}
|
|
847
|
+
|
|
848
|
+
<!-- Processed into dreams on ${(/* @__PURE__ */ new Date()).toISOString()} -->
|
|
849
|
+
`, "utf-8");
|
|
850
|
+
results.push({ agentId: agent.id, ...result });
|
|
851
|
+
if (resolveMemoryBackend(settings).type === "qmd") {
|
|
852
|
+
scheduleQmdAgentMemoryRefresh(rootDir, agent.id);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return results;
|
|
856
|
+
}
|
|
857
|
+
function createMemoryDreamsAutomation(settings, modelProvider, modelId) {
|
|
858
|
+
const schedule = settings.memoryDreamsSchedule ?? DEFAULT_MEMORY_DREAMS_SCHEDULE;
|
|
859
|
+
const prompt = `You are the Memory Dream Processor for an OpenClaw-style project memory system.
|
|
860
|
+
|
|
861
|
+
## Your Task
|
|
862
|
+
|
|
863
|
+
1. Read today's daily notes from \`.fusion/memory/YYYY-MM-DD.md\`.
|
|
864
|
+
2. Read existing dreams from \`.fusion/memory/DREAMS.md\`.
|
|
865
|
+
3. Read long-term memory from \`.fusion/memory/MEMORY.md\`.
|
|
866
|
+
4. Append a dated synthesis to \`.fusion/memory/DREAMS.md\` with patterns, open loops, contradictions, and emerging themes.
|
|
867
|
+
5. Append only durable conventions, decisions, pitfalls, or constraints to \`.fusion/memory/MEMORY.md\`.
|
|
868
|
+
6. Reset today's daily note to a short processed marker after successful synthesis.
|
|
869
|
+
7. For every persisted non-ephemeral agent in \`.fusion/agents/*.json\`, repeat the same process for that agent's private memory workspace at \`.fusion/agent-memory/{agentId}/\`:
|
|
870
|
+
- Read \`MEMORY.md\`, \`DREAMS.md\`, and today's \`YYYY-MM-DD.md\`.
|
|
871
|
+
- If the agent workspace is missing, create it and seed \`MEMORY.md\` from the agent JSON \`memory\` field when present.
|
|
872
|
+
- Append agent-specific synthesis to that agent's \`DREAMS.md\`.
|
|
873
|
+
- Promote only durable agent-specific operating preferences, habits, or constraints to that agent's \`MEMORY.md\`.
|
|
874
|
+
- Reset that agent's daily note after successful synthesis.
|
|
875
|
+
|
|
876
|
+
## Rules
|
|
877
|
+
|
|
878
|
+
- Do not copy task logs or changelog entries into long-term memory.
|
|
879
|
+
- Do not invent facts.
|
|
880
|
+
- Keep dreams useful for future agents, not a transcript of the day.
|
|
881
|
+
- Preserve the three-layer model for both workspace and agent memory: daily notes are raw, DREAMS.md is synthesis, MEMORY.md is curated durable knowledge.
|
|
882
|
+
- Keep agent memory separate from workspace memory. Do not promote private agent operating notes into project memory unless they are useful to every agent in the workspace.`;
|
|
883
|
+
return {
|
|
884
|
+
name: MEMORY_DREAMS_SCHEDULE_NAME,
|
|
885
|
+
description: "Synthesizes daily memory notes into dreams and promotes durable lessons to long-term memory",
|
|
886
|
+
scheduleType: "custom",
|
|
887
|
+
cronExpression: schedule,
|
|
888
|
+
command: "",
|
|
889
|
+
enabled: true,
|
|
890
|
+
steps: [
|
|
891
|
+
{
|
|
892
|
+
id: "memory-dream-processor",
|
|
893
|
+
type: "ai-prompt",
|
|
894
|
+
name: "Process Memory Dreams",
|
|
895
|
+
prompt,
|
|
896
|
+
...modelProvider && modelId ? { modelProvider, modelId } : {},
|
|
897
|
+
timeoutMs: 12e4
|
|
898
|
+
}
|
|
899
|
+
]
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
async function syncMemoryDreamsAutomation(automationStore, settings) {
|
|
903
|
+
const { AutomationStore } = await import("./automation-store-RDDYZFUJ.js");
|
|
904
|
+
const schedules = await automationStore.listSchedules();
|
|
905
|
+
const existingSchedule = schedules.find((schedule2) => schedule2.name === MEMORY_DREAMS_SCHEDULE_NAME);
|
|
906
|
+
if (!settings.memoryDreamsEnabled) {
|
|
907
|
+
if (existingSchedule) {
|
|
908
|
+
await automationStore.deleteSchedule(existingSchedule.id);
|
|
909
|
+
}
|
|
910
|
+
return void 0;
|
|
911
|
+
}
|
|
912
|
+
const schedule = settings.memoryDreamsSchedule ?? DEFAULT_MEMORY_DREAMS_SCHEDULE;
|
|
913
|
+
if (!AutomationStore.isValidCron(schedule)) {
|
|
914
|
+
throw new Error(`Invalid memory dreams schedule: ${schedule}`);
|
|
915
|
+
}
|
|
916
|
+
const input = createMemoryDreamsAutomation(settings);
|
|
917
|
+
if (existingSchedule) {
|
|
918
|
+
return automationStore.updateSchedule(existingSchedule.id, {
|
|
919
|
+
scheduleType: "custom",
|
|
920
|
+
cronExpression: schedule,
|
|
921
|
+
command: input.command,
|
|
922
|
+
steps: input.steps,
|
|
923
|
+
enabled: true
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
return automationStore.createSchedule(input);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
export {
|
|
930
|
+
MEMORY_DREAMS_SCHEDULE_NAME,
|
|
931
|
+
DEFAULT_MEMORY_DREAMS_SCHEDULE,
|
|
932
|
+
agentMemoryWorkspacePath,
|
|
933
|
+
agentMemoryLongTermPath,
|
|
934
|
+
agentMemoryDreamsPath,
|
|
935
|
+
agentDailyMemoryPath,
|
|
936
|
+
ensureAgentMemoryFiles,
|
|
937
|
+
buildDreamProcessingPrompt,
|
|
938
|
+
extractDreamProcessorResult,
|
|
939
|
+
processMemoryDreams,
|
|
940
|
+
processAgentMemoryDreams,
|
|
941
|
+
createMemoryDreamsAutomation,
|
|
942
|
+
syncMemoryDreamsAutomation,
|
|
943
|
+
ensureOpenClawMemoryFiles,
|
|
944
|
+
scheduleQmdInstallAndRefresh,
|
|
945
|
+
MEMORY_BACKEND_SETTINGS_KEYS,
|
|
946
|
+
DEFAULT_MEMORY_BACKEND,
|
|
947
|
+
resolveMemoryBackend
|
|
948
|
+
};
|